diff --git a/.github/workflows/build_cmake.yml b/.github/workflows/build_cmake.yml index a1d4c93fa00..eaf3013fcfa 100644 --- a/.github/workflows/build_cmake.yml +++ b/.github/workflows/build_cmake.yml @@ -62,9 +62,11 @@ jobs: execute_process(COMMAND git submodule set-url -- perfparser https://code.qt.io/qt-creator/perfparser.git) execute_process(COMMAND git submodule update --init --recursive) file(MAKE_DIRECTORY release) - if (${{github.ref}} MATCHES "tags/v(.*)") + if (${{github.ref}} MATCHES "tags/v([0-9.]+)") file(APPEND "$ENV{GITHUB_OUTPUT}" "tag=${CMAKE_MATCH_1}\n") - file(READ "dist/changelog/changes-${CMAKE_MATCH_1}.md" changelog_md) + if (EXISTS "dist/changelog/changes-${CMAKE_MATCH_1}.md") + file(READ "dist/changelog/changes-${CMAKE_MATCH_1}.md" changelog_md) + endif() file(WRITE "release/changelog.md" "These packages are not officially supported, for official packages please check out https://download.qt.io/official_releases/qtcreator\n\n") file(APPEND "release/changelog.md" "${changelog_md}") else() diff --git a/cmake/QtCreatorIDEBranding.cmake b/cmake/QtCreatorIDEBranding.cmake index 0d9e4b81860..8f985e5986b 100644 --- a/cmake/QtCreatorIDEBranding.cmake +++ b/cmake/QtCreatorIDEBranding.cmake @@ -1,6 +1,6 @@ -set(IDE_VERSION "10.0.82") # The IDE version. -set(IDE_VERSION_COMPAT "10.0.82") # The IDE Compatibility version. -set(IDE_VERSION_DISPLAY "11.0.0-beta1") # The IDE display version. +set(IDE_VERSION "10.0.84") # The IDE version. +set(IDE_VERSION_COMPAT "10.0.84") # The IDE Compatibility version. +set(IDE_VERSION_DISPLAY "11.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. diff --git a/cmake/QtCreatorTranslations.cmake b/cmake/QtCreatorTranslations.cmake index 4803d66be5e..7379f1e60cc 100644 --- a/cmake/QtCreatorTranslations.cmake +++ b/cmake/QtCreatorTranslations.cmake @@ -30,7 +30,19 @@ function(_extract_ts_data_from_targets outprefix) set(_target_sources "") if(_source_files) - list(FILTER _source_files EXCLUDE REGEX ".*[.]json[.]in|.*[.]svg|.*[.]pro|.*[.]css") + # exclude various funny source files, and anything generated + # like *metatypes.json.gen, moc_*.cpp, qrc_*.cpp, */qmlcache/*.cpp, + # *qmltyperegistrations.cpp + string(REGEX REPLACE "(\\^|\\$|\\.|\\[|\\]|\\*|\\+|\\?|\\(|\\)|\\|)" "\\\\\\1" binary_dir_regex "${PROJECT_BINARY_DIR}") + set(_exclude_patterns + .*[.]json[.]in + .*[.]svg + .*[.]pro + .*[.]css + "${binary_dir_regex}/.*" + ) + list(JOIN _exclude_patterns "|" _exclude_pattern) + list(FILTER _source_files EXCLUDE REGEX "${_exclude_pattern}") list(APPEND _target_sources ${_source_files}) endif() if(_extra_translations) diff --git a/dist/changelog/changes-10.0.2.md b/dist/changelog/changes-10.0.2.md new file mode 100644 index 00000000000..baf99955de7 --- /dev/null +++ b/dist/changelog/changes-10.0.2.md @@ -0,0 +1,76 @@ +Qt Creator 10.0.2 +================= + +Qt Creator version 10.0.2 contains bug fixes. + +The most important changes are listed in this document. For a complete list of +changes, see the Git log for the Qt Creator sources that you can check out from +the public Git repository. For example: + + git clone git://code.qt.io/qt-creator/qt-creator.git + git log --cherry-pick --pretty=oneline origin/v10.0.1..v10.0.2 + +General +------- + +* Fixed freezes due to excessive file watching (QTCREATORBUG-28957) + +Editing +------- + +### C++ + +* Fixed a crash when following symbols (QTCREATORBUG-28989) +* Fixed the highlighting of raw string literals with empty lines + (QTCREATORBUG-29200) +* Clang Format + * Fixed the editing of custom code styles (QTCREATORBUG-29129) + * Fixed that the wrong code style could be used (QTCREATORBUG-29145) + +Projects +-------- + +* Fixed a crash when triggering a build with unconfigured projects present + (QTCREATORBUG-29207) + +### CMake + +* Fixed that the global `Autorun CMake` option could be overridden by old + settings +* Fixed the `Build CMake Target` locator filter in case a build is already + running (QTCREATORBUG-26699) +* Presets + * Added the expansion of `${hostSystemName}` (QTCREATORBUG-28935) + * Fixed the Qt detection when `CMAKE_TOOLCHAIN_FILE` and `CMAKE_PREFIX_PATH` + are set + +Debugging +--------- + +* Fixed that debugger tooltips in the editor vanished after expanding + (QTCREATORBUG-29083) + +Test Integration +---------------- + +* GoogleTest + * Fixed the reporting of failed tests (QTCREATORBUG-29146) + +Credits for these changes go to: +-------------------------------- +Alessandro Portale +André Pönitz +Artem Sokolovskii +Björn Schäpers +Christian Kandeler +Christian Stenger +Cristian Adam +David Schulz +Eike Ziller +Jaroslaw Kobus +Karim Abdelrahman +Leena Miettinen +Miikka Heikkinen +Patrik Teivonen +Robert Löhning +Sivert Krøvel diff --git a/dist/changelog/changes-11.0.0.md b/dist/changelog/changes-11.0.0.md index 32860826363..a637950a445 100644 --- a/dist/changelog/changes-11.0.0.md +++ b/dist/changelog/changes-11.0.0.md @@ -10,21 +10,85 @@ the public Git repository. For example: git clone git://code.qt.io/qt-creator/qt-creator.git git log --cherry-pick --pretty=oneline origin/10.0..v11.0.0 +What's new? +------------ + +* Markdown editor with preview + ([QTCREATORBUG-27883](https://bugreports.qt.io/browse/QTCREATORBUG-27883)) +* Internal terminal + ([QTCREATORBUG-8511](https://bugreports.qt.io/browse/QTCREATORBUG-8511)) +* Experimental support for GitHub Copilot +* Experimental support for the `vcpkg` C/C++ package manager +* Experimental support for the Axivion static analyzer + +### Markdown + +You can open markdown (.md) files for editing or select `File > New File > +General > Markdown File` to create a new file. + +([Documentation](https://doc-snapshots.qt.io/qtcreator-11.0/creator-markdown-editor.html)) + +### Terminal + +When you select the `Run in Terminal` check box and run an application or the +`Open Terminal` button to open a terminal, the default terminal opens in the +`Terminal` output view. It supports multiple tabs, as well as various +shells, colors, and fonts. + +To use an external terminal, deselect the `Use internal terminal` check box in +`Preferences > Terminal`. + +([Documentation](https://doc-snapshots.qt.io/qtcreator-11.0/creator-output-panes.html#terminal)) + +### Copilot + +The Copilot plugin (disabled by default) integrates +[GitHub Copilot](https://github.com/features/copilot), which uses OpenAI to +suggest code in the `Edit` mode. + +To set Copilot preferences, select `Preferences > Copilot`. + +([Documentation](https://doc-snapshots.qt.io/qtcreator-11.0/creator-copilot.html)) + +### vcpkg + +The experimental vcpkg plugin integrates the [vcpgk](https://vcpkg.io) +package manager for downloading and managing libraries. + +Select the `vcpkg` installation location in `Preferences > CMake > Vcpkg > Path`. + +To create a new `vcpkg.json` package manifest file, select `File > New File > +vcpkg`. The file is automatically added to the CMakeLists.txt file for the +project. + +Edit manifest files in the manifest editor. To search for packages to add to the +file, select the `Search Package` button on the manifest editor toolbar. + +([Documentation](https://doc-snapshots.qt.io/qtcreator-11.0/creator-vcpkg.html)) + +### Axivion + +After you configure access to the [Axivion](https://www.axivion.com) Dashboard +and link a project to an Axivion project in the project settings, Qt Creator +shows annotations of the latest run in the editors and allows you to view some +details on the issues. + +([Documentation](https://doc-snapshots.qt.io/qtcreator-11.0/creator-axivion.html)) + General ------- -* Added a `Terminal` view (QTCREATORBUG-8511) - ([Documentation](https://doc-snapshots.qt.io/qtcreator-11.0/creator-output-panes.html#terminal)) - * Opt-out via `Preferences` > `Terminal` preferences - * Added support for - * different shells, colors, fonts, and multiple tabs - * opening file paths in Qt Creator with `Ctrl+click` (`Cmd+click` on - macOS) -* Added a more spacious "relaxed" toolbar style `Environment > Interface` +* Added a more spacious `Relaxed` toolbar style to `Preferences > Environment > + Interface` * Added a pin button to progress details instead of automatically resetting - their position (QTCREATORBUG-28829) + their position + ([QTCREATORBUG-28829](https://bugreports.qt.io/browse/QTCREATORBUG-28829)) * Improved the selection and navigation in the `Issues` view - (QTCREATORBUG-26128, QTCREATORBUG-27006, QTCREATORBUG-27506) + ([QTCREATORBUG-26128](https://bugreports.qt.io/browse/QTCREATORBUG-26128), + [QTCREATORBUG-27006](https://bugreports.qt.io/browse/QTCREATORBUG-27006), + [QTCREATORBUG-27506](https://bugreports.qt.io/browse/QTCREATORBUG-27506)) +* Fixed a crash with a large number of search hits from Silver Searcher + ([QTCREATORBUG-29130](https://bugreports.qt.io/browse/QTCREATORBUG-29130)) * Locator * Improved performance * Added the creation of directories to the `Files in File System` filter @@ -35,95 +99,137 @@ Editing ------- * Improved the performance of the multi-cursor support -* Fixed the saving of hardlinked files (QTCREATORBUG-19651) -* Fixed an issue of copy and paste with multiple cursors (QTCREATORBUG-29117) +* Fixed the saving of hardlinked files + ([QTCREATORBUG-19651](https://bugreports.qt.io/browse/QTCREATORBUG-19651)) +* Fixed an issue of copy and paste with multiple cursors + ([QTCREATORBUG-29117](https://bugreports.qt.io/browse/QTCREATORBUG-29117)) +* Fixed the handling of pre-edit text for input methods + ([QTCREATORBUG-29134](https://bugreports.qt.io/browse/QTCREATORBUG-29134)) ### C++ -* Improved the style of forward declarations in the outline (QTCREATORBUG-312) +* Improved the style of forward declarations in the outline + ([QTCREATORBUG-312](https://bugreports.qt.io/browse/QTCREATORBUG-312)) * Added highlighting for typed string literals and user-defined literals - (QTCREATORBUG-28869) -* Added the option to create class members from assignments (QTCREATORBUG-1918) + ([QTCREATORBUG-28869](https://bugreports.qt.io/browse/QTCREATORBUG-28869)) +* Extended the `Add Class Member` refactoring action to create class + members from assignments + ([QTCREATORBUG-1918](https://bugreports.qt.io/browse/QTCREATORBUG-1918)) +* Fixed that generated functions did not have a `const` qualifier when + required + ([QTCREATORBUG-29274](https://bugreports.qt.io/browse/QTCREATORBUG-29274)) * Fixed that locator showed both the declaration and the definition of symbols - (QTCREATORBUG-13894) + ([QTCREATORBUG-13894](https://bugreports.qt.io/browse/QTCREATORBUG-13894)) * Fixed the handling of C++20 keywords and concepts +* Clangd + * Fixed that the index could be outdated after VCS operations + * Fixed the highlighting of labels + ([QTCREATORBUG-27338](https://bugreports.qt.io/browse/QTCREATORBUG-27338)) * Built-in - * Fixed support for `if`-statements with initializer (QTCREATORBUG-29182) + * Fixed support for `if`-statements with initializer + ([QTCREATORBUG-29182](https://bugreports.qt.io/browse/QTCREATORBUG-29182)) +* Clang Format + * Fixed the conversion of tab indentation settings to Clang Format + ([QTCREATORBUG-29185](https://bugreports.qt.io/browse/QTCREATORBUG-29185)) ### Language Server Protocol -* Added experimental support for GitHub Copilot - ([GitHub documentation](https://github.com/features/copilot)) - ([Qt Creator documentation](https://doc-snapshots.qt.io/qtcreator-11.0/creator-copilot.html)) -* Added missing actions for opening the `Call Hierarchy` (QTCREATORBUG-28839, - QTCREATORBUG-28842) +* Added actions for opening the `Call Hierarchy` to the context menu of the + editor + ([QTCREATORBUG-28839](https://bugreports.qt.io/browse/QTCREATORBUG-28839), + [QTCREATORBUG-28842](https://bugreports.qt.io/browse/QTCREATORBUG-28842)) ### QML * Fixed the reformatting in the presence of JavaScript directives and function - return type annotations (QTCREATORBUG-29001, QTCREATORBUG-29046) -* Fixed that reformatting changed `of` to `in` (QTCREATORBUG-29123) -* Fixed the completion for Qt Quick Controls (QTCREATORBUG-28648) + return type annotations + ([QTCREATORBUG-29001](https://bugreports.qt.io/browse/QTCREATORBUG-29001), + [QTCREATORBUG-29046](https://bugreports.qt.io/browse/QTCREATORBUG-29046)) +* Fixed that reformatting changed `of` to `in` + ([QTCREATORBUG-29123](https://bugreports.qt.io/browse/QTCREATORBUG-29123)) +* Fixed the completion for Qt Quick Controls + ([QTCREATORBUG-28648](https://bugreports.qt.io/browse/QTCREATORBUG-28648)) +* Fixed that `qmllint` issues were not shown in the `Issues` view + ([QTCREATORBUG-28720](https://bugreports.qt.io/browse/QTCREATORBUG-28720)) ### Python * Added the option to create a virtual environment (`venv`) to the Python - interpreter selector and the wizard (PYSIDE-2152) + interpreter selector and the wizard + ([PYSIDE-2152](https://bugreports.qt.io/browse/PYSIDE-2152)) +* Fixed that too many progress indicators could be created + ([QTCREATORBUG-29224](https://bugreports.qt.io/browse/QTCREATORBUG-29224)) -### Markdown - -* Added a Markdown editor with preview (QTCREATORBUG-27883) -* Added a wizard for Markdown files (QTCREATORBUG-29056) + ([Documentation](https://doc-snapshots.qt.io/qtcreator-11.0/creator-python-development.html)) Projects -------- -* Made it possible to add devices without going through the wizard -* Added support for moving files to a different directory when renaming - (QTCREATORBUG-15981) +* Made it possible to add devices in `Preferences > Devices > Add` without going + through the wizard +* Added support for moving files to a different directory when renaming them in + the `File System` view + ([QTCREATORBUG-15981](https://bugreports.qt.io/browse/QTCREATORBUG-15981)) + + ([Documentation](https://doc.qt.io/qtcreator/creator-file-system-view.html)) ### CMake -* Implemented adding files to the project (QTCREATORBUG-25922, - QTCREATORBUG-26006, QTCREATORBUG-27213, QTCREATORBUG-27538, - QTCREATORBUG-28493, QTCREATORBUG-28904, QTCREATORBUG-28985, - QTCREATORBUG-29006) +* Implemented adding files to the project + ([QTCREATORBUG-25922](https://bugreports.qt.io/browse/QTCREATORBUG-25922), + [QTCREATORBUG-26006](https://bugreports.qt.io/browse/QTCREATORBUG-26006), + [QTCREATORBUG-27213](https://bugreports.qt.io/browse/QTCREATORBUG-27213), + [QTCREATORBUG-27538](https://bugreports.qt.io/browse/QTCREATORBUG-27538), + [QTCREATORBUG-28493](https://bugreports.qt.io/browse/QTCREATORBUG-28493), + [QTCREATORBUG-28904](https://bugreports.qt.io/browse/QTCREATORBUG-28904), + [QTCREATORBUG-28985](https://bugreports.qt.io/browse/QTCREATORBUG-28985), + [QTCREATORBUG-29006](https://bugreports.qt.io/browse/QTCREATORBUG-29006)) +* Added `Build > Reload CMake Presets` to reload CMake presets after making + changes to them +* Added support for `block()` and `endblock()` +* Fixed that CMake Presets were not visible in `Projects` view + ([QTCREATORBUG-28966](https://bugreports.qt.io/browse/QTCREATORBUG-28966)) * Fixed issues with detecting a configured Qt version when importing a build - (QTCREATORBUG-29075) + ([QTCREATORBUG-29075](https://bugreports.qt.io/browse/QTCREATORBUG-29075)) ### Python -* Added an option for the interpreter to the wizards - -### vcpkg - -* Added experimental support for `vcpkg` - ([vcpgk documentation](https://vcpkg.io/en/)) -* Added an option for the `vcpkg` installation location -* Added a search dialog for packages -* Added a wizard and an editor for `vcpkg.json` files +* Added an option for selecting the interpreter to the wizards in + `File > New Project > Application (Qt for Python)` Debugging --------- -* Improved the UI for enabling and disabling debuggers (QTCREATORBUG-28627) +* Improved the UI for enabling and disabling debuggers in `Projects > Run > + Debugger settings` + ([QTCREATORBUG-28627](https://bugreports.qt.io/browse/QTCREATORBUG-28627)) +* Fixed the automatic source mapping for Qt versions from an installer + ([QTCREATORBUG-28950](https://bugreports.qt.io/browse/QTCREATORBUG-28950)) +* Fixed pretty printer for `std::string` for recent `libc++` + ([QTCREATORBUG-29230](https://bugreports.qt.io/browse/QTCREATORBUG-29230)) ### C++ * Added an option for the default number of array elements to show (`Preferences > Debugger > Locals & Expressions > Default array size`) +* Fixed debugging in a terminal as the root user + ([QTCREATORBUG-27519](https://bugreports.qt.io/browse/QTCREATORBUG-27519)) * CDB * Added automatic source file mapping for Qt packages - * Fixed the variables view on remote Windows devices (QTCREATORBUG-29000) + * Fixed the variables view on remote Windows devices + ([QTCREATORBUG-29000](https://bugreports.qt.io/browse/QTCREATORBUG-29000)) * LLDB * Fixed that long lines in the application output were broken into multiple - lines (QTCREATORBUG-29098) + lines + ([QTCREATORBUG-29098](https://bugreports.qt.io/browse/QTCREATORBUG-29098)) ### Qt Quick -* Improved the auto-detection if QML debugging is required (QTCREATORBUG-28627) +* Improved the auto-detection if QML debugging is required + ([QTCREATORBUG-28627](https://bugreports.qt.io/browse/QTCREATORBUG-28627)) * Added an option for disabling static analyzer messages to - `Qt Quick > QML/JS Editing` (QTCREATORBUG-29095) + `Qt Quick > QML/JS Editing` + ([QTCREATORBUG-29095](https://bugreports.qt.io/browse/QTCREATORBUG-29095)) Analyzer -------- @@ -131,11 +237,8 @@ Analyzer ### Clang * Fixed that a `.clang-tidy` file in the project directory was not used by - default (QTCREATORBUG-28852) - -### Axivion - -* Added experimental support + default + ([QTCREATORBUG-28852](https://bugreports.qt.io/browse/QTCREATORBUG-28852)) Version Control Systems ----------------------- @@ -143,7 +246,8 @@ Version Control Systems ### Git * Instant Blame - * Improved the performance (QTCREATORBUG-29151) + * Improved the performance + ([QTCREATORBUG-29151](https://bugreports.qt.io/browse/QTCREATORBUG-29151)) * Fixed that it did not show at the end of the document Platforms @@ -151,7 +255,8 @@ Platforms ### Android -* Fixed an issue with building library targets (QTCREATORBUG-26980) +* Fixed an issue with building library targets + ([QTCREATORBUG-26980](https://bugreports.qt.io/browse/QTCREATORBUG-26980)) ### Remote Linux @@ -159,14 +264,16 @@ Platforms ### Docker -* Added support for `qmake` based projects (QTCREATORBUG-29140) +* Added support for `qmake` based projects + ([QTCREATORBUG-29140](https://bugreports.qt.io/browse/QTCREATORBUG-29140)) * Fixed issues after deleting the Docker image for a registered Docker device - (QTCREATORBUG-28880) + ([QTCREATORBUG-28880](https://bugreports.qt.io/browse/QTCREATORBUG-28880)) ### QNX * Added `slog2info` as a requirement for devices -* Fixed the support for remote working directories (QTCREATORBUG-28900) +* Fixed the support for remote working directories + ([QTCREATORBUG-28900](https://bugreports.qt.io/browse/QTCREATORBUG-28900)) Credits for these changes go to: -------------------------------- diff --git a/doc/qtcreator/images/icons/home.png b/doc/qtcreator/images/icons/home.png new file mode 100644 index 00000000000..21e7d63a104 Binary files /dev/null and b/doc/qtcreator/images/icons/home.png differ diff --git a/doc/qtcreator/images/icons/info.png b/doc/qtcreator/images/icons/info.png new file mode 100644 index 00000000000..eace805de31 Binary files /dev/null and b/doc/qtcreator/images/icons/info.png differ diff --git a/doc/qtcreator/images/qtcreator-axivion-annotation.webp b/doc/qtcreator/images/qtcreator-axivion-annotation.webp new file mode 100644 index 00000000000..5879a1c19f5 Binary files /dev/null and b/doc/qtcreator/images/qtcreator-axivion-annotation.webp differ diff --git a/doc/qtcreator/images/qtcreator-axivion-view-rule.webp b/doc/qtcreator/images/qtcreator-axivion-view-rule.webp new file mode 100644 index 00000000000..30a5427885f Binary files /dev/null and b/doc/qtcreator/images/qtcreator-axivion-view-rule.webp differ diff --git a/doc/qtcreator/images/qtcreator-axivion-view.webp b/doc/qtcreator/images/qtcreator-axivion-view.webp new file mode 100644 index 00000000000..c9003d5cb48 Binary files /dev/null and b/doc/qtcreator/images/qtcreator-axivion-view.webp differ diff --git a/doc/qtcreator/images/qtcreator-boot2qt-device-configurations.png b/doc/qtcreator/images/qtcreator-boot2qt-device-configurations.png deleted file mode 100644 index 584c595dcb3..00000000000 Binary files a/doc/qtcreator/images/qtcreator-boot2qt-device-configurations.png and /dev/null differ diff --git a/doc/qtcreator/images/qtcreator-boot2qt-device-configurations.webp b/doc/qtcreator/images/qtcreator-boot2qt-device-configurations.webp new file mode 100644 index 00000000000..b2068f155e8 Binary files /dev/null and b/doc/qtcreator/images/qtcreator-boot2qt-device-configurations.webp differ diff --git a/doc/qtcreator/images/qtcreator-edit-dashboard-configuration.webp b/doc/qtcreator/images/qtcreator-edit-dashboard-configuration.webp new file mode 100644 index 00000000000..3f76b4427a3 Binary files /dev/null and b/doc/qtcreator/images/qtcreator-edit-dashboard-configuration.webp differ diff --git a/doc/qtcreator/images/qtcreator-file-new-file-vcpkg-manifest-file.webp b/doc/qtcreator/images/qtcreator-file-new-file-vcpkg-manifest-file.webp new file mode 100644 index 00000000000..f8690aab650 Binary files /dev/null and b/doc/qtcreator/images/qtcreator-file-new-file-vcpkg-manifest-file.webp differ diff --git a/doc/qtcreator/images/qtcreator-file-new-file-vcpkg.webp b/doc/qtcreator/images/qtcreator-file-new-file-vcpkg.webp new file mode 100644 index 00000000000..9cebc6932aa Binary files /dev/null and b/doc/qtcreator/images/qtcreator-file-new-file-vcpkg.webp differ diff --git a/doc/qtcreator/images/qtcreator-issues.png b/doc/qtcreator/images/qtcreator-issues.png deleted file mode 100644 index b0bde84e455..00000000000 Binary files a/doc/qtcreator/images/qtcreator-issues.png and /dev/null differ diff --git a/doc/qtcreator/images/qtcreator-issues.webp b/doc/qtcreator/images/qtcreator-issues.webp new file mode 100644 index 00000000000..329a66b29b3 Binary files /dev/null and b/doc/qtcreator/images/qtcreator-issues.webp differ diff --git a/doc/qtcreator/images/qtcreator-preferences-axivion-project.webp b/doc/qtcreator/images/qtcreator-preferences-axivion-project.webp new file mode 100644 index 00000000000..4656ab1d80d Binary files /dev/null and b/doc/qtcreator/images/qtcreator-preferences-axivion-project.webp differ diff --git a/doc/qtcreator/images/qtcreator-preferences-axivion.webp b/doc/qtcreator/images/qtcreator-preferences-axivion.webp new file mode 100644 index 00000000000..f884259a54a Binary files /dev/null and b/doc/qtcreator/images/qtcreator-preferences-axivion.webp differ diff --git a/doc/qtcreator/images/qtcreator-preferences-cmake-general.webp b/doc/qtcreator/images/qtcreator-preferences-cmake-general.webp index 2eef4e57ee2..e6b7de61f33 100644 Binary files a/doc/qtcreator/images/qtcreator-preferences-cmake-general.webp and b/doc/qtcreator/images/qtcreator-preferences-cmake-general.webp differ diff --git a/doc/qtcreator/images/qtcreator-preferences-cmake-vcpkg.webp b/doc/qtcreator/images/qtcreator-preferences-cmake-vcpkg.webp new file mode 100644 index 00000000000..62a8b8d7857 Binary files /dev/null and b/doc/qtcreator/images/qtcreator-preferences-cmake-vcpkg.webp differ diff --git a/doc/qtcreator/images/qtcreator-preferences-devices-remote-linux.webp b/doc/qtcreator/images/qtcreator-preferences-devices-remote-linux.webp index a7fcb124adf..4b97b88b7a2 100644 Binary files a/doc/qtcreator/images/qtcreator-preferences-devices-remote-linux.webp and b/doc/qtcreator/images/qtcreator-preferences-devices-remote-linux.webp differ diff --git a/doc/qtcreator/images/qtcreator-vcpkg-manifest-file-editor.webp b/doc/qtcreator/images/qtcreator-vcpkg-manifest-file-editor.webp new file mode 100644 index 00000000000..1d341a081f4 Binary files /dev/null and b/doc/qtcreator/images/qtcreator-vcpkg-manifest-file-editor.webp differ diff --git a/doc/qtcreator/images/qtcreator-vcpkg-package-selector.webp b/doc/qtcreator/images/qtcreator-vcpkg-package-selector.webp new file mode 100644 index 00000000000..97295b6b485 Binary files /dev/null and b/doc/qtcreator/images/qtcreator-vcpkg-package-selector.webp differ diff --git a/doc/qtcreator/images/qtquick-debugger-settings.webp b/doc/qtcreator/images/qtquick-debugger-settings.webp new file mode 100644 index 00000000000..117e21fdcd0 Binary files /dev/null and b/doc/qtcreator/images/qtquick-debugger-settings.webp differ diff --git a/doc/qtcreator/images/qtquick-debugging-settings.png b/doc/qtcreator/images/qtquick-debugging-settings.png deleted file mode 100644 index c6b1f7bca2e..00000000000 Binary files a/doc/qtcreator/images/qtquick-debugging-settings.png and /dev/null differ diff --git a/doc/qtcreator/src/analyze/creator-analyze.qdoc b/doc/qtcreator/src/analyze/creator-analyze.qdoc index 4a4e05f413c..f1eb052ea02 100644 --- a/doc/qtcreator/src/analyze/creator-analyze.qdoc +++ b/doc/qtcreator/src/analyze/creator-analyze.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 // ********************************************************************** @@ -44,6 +44,11 @@ example, and use the results to make the tests more efficient and complete. + \li \l{Static Code Analysis}{Axivion} + + Do static code analysis and architecture analysis to detect and + eliminate unnecessary complexity of code. + \li \l{Using Valgrind Code Analysis Tools}{Valgrind Code Analysis Tools} Detect problems in memory management by using the Memcheck diff --git a/doc/qtcreator/src/analyze/creator-axivion.qdoc b/doc/qtcreator/src/analyze/creator-axivion.qdoc new file mode 100644 index 00000000000..244991c7834 --- /dev/null +++ b/doc/qtcreator/src/analyze/creator-axivion.qdoc @@ -0,0 +1,109 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only + +/*! + \previouspage creator-coco.html + \page creator-axivion.html + \nextpage creator-valgrind-overview.html + + \title Static Code Analysis + + \l{https://www.axivion.com/en/products/axivion-suite/}{Axivion Suite} is + a tool suite for protecting software from erosion. Static code analysis, + architecture analysis, and code-smells-detection enable you to: + + \list + \li Check the source code for potential runtime errors. + \li Use metrics to generate quantitative information about the + internal quality of the source code. + \li Run style checks to achieve compliance with coding guidelines. + \li Detect both duplicates and similar pieces of code in the source code. + \li Recognize cyclical dependencies at different levels. + \li Detect unreachable code. + \endlist + + The experimental Axivion plugin integrates the Axivion dashboard server into + \QC. + + 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 + \uicontrol Axivion view. + + The editor shows style violations as inline annotations. Hover the mouse over + an annotation to bring up a tool tip with a short description of the issue. + + \image qtcreator-axivion-annotation.webp {Annotation popup} + + Select the \inlineimage icons/info.png + button to view detailed information about the issue in the \uicontrol Axivion + view. + + \image qtcreator-axivion-view-rule.webp {Axivion view} + + 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. + \image qtcreator-preferences-axivion.webp {General tab in Axivion Preferences} + \li Select \uicontrol Edit to create a connection to the Axivion + dashboard server. + \image qtcreator-edit-dashboard-configuration.webp {Edit Dashboard Configuration dialog} + \li In \uicontrol {Dashboard URL}, enter the URL of the server. + \li In \uicontrol Description, enter a free-text description of the + server. + \li In \uicontrol {Access token}, enter the IDE application token that + you created in the server, in user preferences. + \endlist + + \section1 Linking to Dashboards + + To link a project to a dashboard: + + \list 1 + \li \uicontrol Projects > \uicontrol {Project Settings} > + \uicontrol Axivion. + \image qtcreator-preferences-axivion-project.webp {Axivion settings in Project Settings} + \li Select \uicontrol {Fetch Projects} to list projects from Axivion. + \li Select a project, and then select \uicontrol {Link Project} to link + to it. + \endlist + + To unlink a project, select \uicontrol {Unlink Project}. + + \section1 Viewing Issue Counts + + \image qtcreator-axivion-view.webp {Axivion view} + + The \uicontrol Axivion view lists the numbers of the following types of + issues that Axivion found in the linked project: + + \list + \li \uicontrol AV - architecture violations, such as hidden dependencies + \li \uicontrol CL - duplicates and similar pieces of code + \li \uicontrol CY - call, component, and include cycles + \li \uicontrol DE - dead code + \li \uicontrol MV - violations of metrics based on lines and tokens, + nesting, cyclomatic complexity, control flow, and so on. + \li \uicontrol SV - style violations, such as deviations from the naming + or coding conventions + \endlist + + To clear the view, select \inlineimage icons/clean_pane_small.png + (\uicontrol Clear). +*/ diff --git a/doc/qtcreator/src/analyze/creator-coco.qdoc b/doc/qtcreator/src/analyze/creator-coco.qdoc index 3a714d9bc08..0818e9a5b92 100644 --- a/doc/qtcreator/src/analyze/creator-coco.qdoc +++ b/doc/qtcreator/src/analyze/creator-coco.qdoc @@ -4,7 +4,7 @@ /*! \previouspage creator-qml-performance-monitor.html \page creator-coco.html - \nextpage creator-valgrind-overview.html + \nextpage creator-axivion.html \title Checking Code Coverage diff --git a/doc/qtcreator/src/analyze/creator-valgrind-overview.qdoc b/doc/qtcreator/src/analyze/creator-valgrind-overview.qdoc index 96b1195f3e6..9a2f914648f 100644 --- a/doc/qtcreator/src/analyze/creator-valgrind-overview.qdoc +++ b/doc/qtcreator/src/analyze/creator-valgrind-overview.qdoc @@ -8,7 +8,7 @@ // ********************************************************************** /*! - \previouspage creator-coco.html + \previouspage creator-axivion.html \page creator-valgrind-overview.html \nextpage creator-analyzer.html diff --git a/doc/qtcreator/src/cmake/creator-projects-cmake-building.qdoc b/doc/qtcreator/src/cmake/creator-projects-cmake-building.qdoc index dac77727a83..2c302e5fcc5 100644 --- a/doc/qtcreator/src/cmake/creator-projects-cmake-building.qdoc +++ b/doc/qtcreator/src/cmake/creator-projects-cmake-building.qdoc @@ -1,10 +1,10 @@ -// 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-build-settings.html \page creator-build-settings-cmake.html - \nextpage creator-build-settings-qmake.html + \nextpage creator-build-settings-cmake-presets.html \title CMake Build Configuration @@ -30,6 +30,9 @@ Select \uicontrol {Kit Configuration} to edit the CMake settings for the build and run kit selected for the project. + You can use \l{CMake Presets}{CMake presets} files to specify common + configure, build, and test options and share them with others. + \section1 Initial Configuration \image qtcreator-build-settings-cmake-initial.webp {Initial CMake configuration} @@ -57,224 +60,6 @@ \l{CMake: cmake-variables(7)}. For more information about Qt-specific variables, see \l{CMake Variable Reference}. - \section1 CMake Presets - - You can use CMake presets files to specify common configure, build, and test - options and share them with others. \c CMakePresets.json has options for - project-wide builds, whereas \c CMakeUserPresets.json has options for - your local builds. - - Create the presets files in the format described in - \l{https://cmake.org/cmake/help/latest/manual/cmake-presets.7.html} - {cmake-presets(7)} and store them in project's root directory. - - \QC supports presets up to version 3 (introduced in CMake 3.21), but does not - 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} - {open a project}, when no \c CMakeLists.txt.user file exists or you have - disabled all kits in the project. To update changes to the - \c CMakePresets.json file, delete the \c CMakeLists.txt.user file. - - \image qtcreator-cmake-presets-configure.webp {Opening a project that has CMake presets} - - You can view the presets in the \uicontrol {Initial Configuration} field and - in the environment configuration field below it. - - \image qtcreator-cmake-presets-environment.webp {CMake environment configuration} - - \section2 Configure Presets - - The following configure presets instruct CMake to use the default generator - on the platform and specify the build directory for all build types. - \c NOT_COMMON_VALUE is displayed in \uicontrol {Initial Parameters} - and \c AN_ENVIRONMENT_FLAG in the environment configuration field. - - \badcode - { - "version": 1, - "configurePresets": [ - { - "name": "preset", - "displayName": "preset", - "binaryDir": "${sourceDir}/build/preset", - "cacheVariables": { - "NOT_COMMON_VALUE": "NOT_COMMON_VALUE" - }, - "environment": { - "AN_ENVIRONMENT_FLAG": "1" - } - } - ] - } - \endcode - - \section2 MinGW Example - - The following example configures a Qt project with: - - \list - \li MinGW compiler - \li build directory – \c /build-release - \li build type – \c CMAKE_BUILD_TYPE as \c Release - \li generator – MinGW Makefiles - \li path to a CMake executable - \li path to the Qt installation via \c CMAKE_PREFIX_PATH - \endlist - - \badcode - { - "version": 1, - "configurePresets": [ - { - "name": "mingw", - "displayName": "MinGW 11.2.0", - "generator": "MinGW Makefiles", - "binaryDir": "${sourceDir}/build-release", - "cmakeExecutable": "C:/Qt/Tools/CMake_64/bin/cmake.exe", - "cacheVariables": { - "CMAKE_BUILD_TYPE": "Release", - "CMAKE_PREFIX_PATH": "C:/Qt/6.4.0/mingw_64" - }, - "environment": { - "PATH": "C:/Qt/Tools/mingw1120_64/bin;$penv{PATH}" - } - } - ] - } - \endcode - - To speed up the process on Windows, specify the \c CMAKE_C_COMPILER and - \c CMAKE_CXX_COMPILER in the \c cacheVariables section. - - \section2 Ninja Generator Example - - The following configure and build presets set Ninja Multi-Config as the - generator, add \c Debug and \c Release build steps, and specify the path - to \c ninja.exe as a value of the \c CMAKE_MAKE_PROGRAM variable: - - \badcode - { - "version": 2, - "configurePresets": [ - { - "name": "ninja-nmc", - "displayName": "Ninja Multi-Config MinGW", - "generator": "Ninja Multi-Config", - "binaryDir": "${sourceDir}/build", - "cacheVariables": { - "CMAKE_BUILD_TYPE": "Debug;Release", - "CMAKE_PREFIX_PATH": "C:/Qt/6.4.0/mingw_64" - "CMAKE_MAKE_PROGRAM": "C:/Qt/Tools/Ninja/ninja.exe" - }, - "environment": { - "PATH": "c:/Qt/Tools/mingw1120_64/bin;$penv{PATH}" - } - } - ], - "buildPresets": [ - { - "name": "release", - "displayName": "Ninja Release", - "configurePreset": "ninja-nmc", - "configuration": "Release" - }, - { - "name": "debug", - "displayName": "Ninja Debug", - "configurePreset": "ninja-nmc", - "configuration": "Debug" - } - ] - } - \endcode - - This example assumes that the CMake executable path is set in - \uicontrol Edit > \uicontrol Preferences > \uicontrol CMake > - \uicontrol Tools. - - \section2 MSVC Example - - When using MSVC compilers with NMAKE Makefiles, Ninja, or Ninja - Multi-Config generators, you can use the \c external strategy for - the \c architecture and \c toolset fields. This lets \QC set up - the Visual C++ environment before invoking CMake. - - For example: - - \badcode - "generator": "Ninja Multi-Config", - "toolset": { - "value": "v142,host=x64", - "strategy": "external" - }, - "architecture": { - "value": "x64", - "strategy": "external" - }, - \endcode - - If you use MSVC compilers with non-VS generators and have several compilers - in the \c PATH, you might also have to specify the compiler to use in - \c cacheVariables or \c environmentVariables: - - \badcode - "generator": "Ninja Multi-Config", - "toolset": { - "value": "v142,host=x64", - "strategy": "external" - }, - "architecture": { - "value": "x64", - "strategy": "external" - }, - "cacheVariables": { - "CMAKE_C_COMPILER": "cl.exe", - "CMAKE_CXX_COMPILER": "cl.exe" - } - \endcode - - \section2 Using Conditions - - The following configure presets are used if they match \c condition. That is, - if the \c hostSystemName equals \c Linux, the \c linux presets are used and - if it equals \c Windows, the \c windows presets are used. - - \badcode - { - "version": 3, - "configurePresets": [ - { - "name": "linux", - "displayName": "Linux GCC", - "binaryDir": "${sourceDir}/build", - "cacheVariables": { - "CMAKE_PREFIX_PATH": "$env{HOME}/Qt/6.4.0/gcc_64" - }, - "condition": { - "type": "equals", - "lhs": "${hostSystemName}", - "rhs": "Linux" - } - }, - { - "name": "windows", - "displayName": "Windows MSVC", - "binaryDir": "${sourceDir}/build", - "cacheVariables": { - "CMAKE_PREFIX_PATH": "$env{SYSTEMDRIVE}/Qt/6.4.0/msvc2019_64" - }, - "condition": { - "type": "equals", - "lhs": "${hostSystemName}", - "rhs": "Windows" - } - } - ] - } - \endcode - \section1 Multi-Config Support \QC supports diff --git a/doc/qtcreator/src/cmake/creator-projects-cmake-presets.qdoc b/doc/qtcreator/src/cmake/creator-projects-cmake-presets.qdoc new file mode 100644 index 00000000000..587790fa0dc --- /dev/null +++ b/doc/qtcreator/src/cmake/creator-projects-cmake-presets.qdoc @@ -0,0 +1,227 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only + +/*! + \previouspage creator-build-settings-cmake.html + \page creator-build-settings-cmake-presets.html + \nextpage creator-build-settings-qmake.html + + \title CMake Presets + + \c CMakePresets.json has options for project-wide builds, whereas + \c CMakeUserPresets.json has options for your local builds. + + Create the presets files in the format described in + \l{https://cmake.org/cmake/help/latest/manual/cmake-presets.7.html} + {cmake-presets(7)} and store them in the project's root directory. + You can then see them in the \l {Projects} view. + + \QC supports presets up to version 3 (introduced in CMake 3.21), but does not + 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} + {open a project}, when no \c CMakeLists.txt.user file exists or you have + disabled all kits in the project. + + \image qtcreator-cmake-presets-configure.webp {Opening a project that has CMake presets} + + You can view the presets in the \uicontrol {Initial Configuration} field and + in the environment configuration field below it. + + \image qtcreator-cmake-presets-environment.webp {CMake environment configuration} + + To update changes to the \c CMakePresets.json file, select \uicontrol Build > + \uicontrol {Reload CMake Presets}, and then select the presets file to load. + + \section1 Configure Presets + + The following configure presets instruct CMake to use the default generator + on the platform and specify the build directory for all build types. + \c NOT_COMMON_VALUE is displayed in \uicontrol {Initial Parameters} + and \c AN_ENVIRONMENT_FLAG in the environment configuration field. + + \badcode + { + "version": 1, + "configurePresets": [ + { + "name": "preset", + "displayName": "preset", + "binaryDir": "${sourceDir}/build/preset", + "cacheVariables": { + "NOT_COMMON_VALUE": "NOT_COMMON_VALUE" + }, + "environment": { + "AN_ENVIRONMENT_FLAG": "1" + } + } + ] + } + \endcode + + \section1 MinGW Example + + The following example configures a Qt project with: + + \list + \li MinGW compiler + \li build directory – \c /build-release + \li build type – \c CMAKE_BUILD_TYPE as \c Release + \li generator – MinGW Makefiles + \li path to a CMake executable + \li path to the Qt installation via \c CMAKE_PREFIX_PATH + \endlist + + \badcode + { + "version": 1, + "configurePresets": [ + { + "name": "mingw", + "displayName": "MinGW 11.2.0", + "generator": "MinGW Makefiles", + "binaryDir": "${sourceDir}/build-release", + "cmakeExecutable": "C:/Qt/Tools/CMake_64/bin/cmake.exe", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release", + "CMAKE_PREFIX_PATH": "C:/Qt/6.4.0/mingw_64" + }, + "environment": { + "PATH": "C:/Qt/Tools/mingw1120_64/bin;$penv{PATH}" + } + } + ] + } + \endcode + + To speed up the process on Windows, specify the \c CMAKE_C_COMPILER and + \c CMAKE_CXX_COMPILER in the \c cacheVariables section. + + \section1 Ninja Generator Example + + The following configure and build presets set Ninja Multi-Config as the + generator, add \c Debug and \c Release build steps, and specify the path + to \c ninja.exe as a value of the \c CMAKE_MAKE_PROGRAM variable: + + \badcode + { + "version": 2, + "configurePresets": [ + { + "name": "ninja-nmc", + "displayName": "Ninja Multi-Config MinGW", + "generator": "Ninja Multi-Config", + "binaryDir": "${sourceDir}/build", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug;Release", + "CMAKE_PREFIX_PATH": "C:/Qt/6.4.0/mingw_64" + "CMAKE_MAKE_PROGRAM": "C:/Qt/Tools/Ninja/ninja.exe" + }, + "environment": { + "PATH": "c:/Qt/Tools/mingw1120_64/bin;$penv{PATH}" + } + } + ], + "buildPresets": [ + { + "name": "release", + "displayName": "Ninja Release", + "configurePreset": "ninja-nmc", + "configuration": "Release" + }, + { + "name": "debug", + "displayName": "Ninja Debug", + "configurePreset": "ninja-nmc", + "configuration": "Debug" + } + ] + } + \endcode + + This example assumes that the CMake executable path is set in + \uicontrol Edit > \uicontrol Preferences > \uicontrol CMake > + \uicontrol Tools. + + \section1 MSVC Example + + When using MSVC compilers with NMAKE Makefiles, Ninja, or Ninja + Multi-Config generators, you can use the \c external strategy for + the \c architecture and \c toolset fields. This lets \QC set up + the Visual C++ environment before invoking CMake. + + For example: + + \badcode + "generator": "Ninja Multi-Config", + "toolset": { + "value": "v142,host=x64", + "strategy": "external" + }, + "architecture": { + "value": "x64", + "strategy": "external" + }, + \endcode + + If you use MSVC compilers with non-VS generators and have several compilers + in the \c PATH, you might also have to specify the compiler to use in + \c cacheVariables or \c environmentVariables: + + \badcode + "generator": "Ninja Multi-Config", + "toolset": { + "value": "v142,host=x64", + "strategy": "external" + }, + "architecture": { + "value": "x64", + "strategy": "external" + }, + "cacheVariables": { + "CMAKE_C_COMPILER": "cl.exe", + "CMAKE_CXX_COMPILER": "cl.exe" + } + \endcode + + \section1 Using Conditions + + The following configure presets are used if they match \c condition. That is, + if the \c hostSystemName equals \c Linux, the \c linux presets are used and + if it equals \c Windows, the \c windows presets are used. + + \badcode + { + "version": 3, + "configurePresets": [ + { + "name": "linux", + "displayName": "Linux GCC", + "binaryDir": "${sourceDir}/build", + "cacheVariables": { + "CMAKE_PREFIX_PATH": "$env{HOME}/Qt/6.4.0/gcc_64" + }, + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Linux" + } + }, + { + "name": "windows", + "displayName": "Windows MSVC", + "binaryDir": "${sourceDir}/build", + "cacheVariables": { + "CMAKE_PREFIX_PATH": "$env{SYSTEMDRIVE}/Qt/6.4.0/msvc2019_64" + }, + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Windows" + } + } + ] + } + \endcode +*/ diff --git a/doc/qtcreator/src/cmake/creator-projects-cmake.qdoc b/doc/qtcreator/src/cmake/creator-projects-cmake.qdoc index 1baa0b08f5f..d9809f09a38 100644 --- a/doc/qtcreator/src/cmake/creator-projects-cmake.qdoc +++ b/doc/qtcreator/src/cmake/creator-projects-cmake.qdoc @@ -181,6 +181,22 @@ current project. \endlist + \section1 Managing Files + + 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. + + If you use custom API, \QC uses \c {target_sources()} to add the files. + + For Qt Quick projects, the files are added to the \c {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 Through external libraries, \QC can support code completion and syntax diff --git a/doc/qtcreator/src/conan/creator-projects-conan.qdoc b/doc/qtcreator/src/conan/creator-projects-conan.qdoc index 2076a10f90b..905e486c9ea 100644 --- a/doc/qtcreator/src/conan/creator-projects-conan.qdoc +++ b/doc/qtcreator/src/conan/creator-projects-conan.qdoc @@ -4,7 +4,7 @@ /*! \page creator-project-conan.html \previouspage creator-project-incredibuild.html - \nextpage creator-cli.html + \nextpage creator-vcpkg.html \title Setting Up Conan diff --git a/doc/qtcreator/src/debugger/qtquick-debugging.qdoc b/doc/qtcreator/src/debugger/qtquick-debugging.qdoc index c0246fc7b6f..739095b5a03 100644 --- a/doc/qtcreator/src/debugger/qtquick-debugging.qdoc +++ b/doc/qtcreator/src/debugger/qtquick-debugging.qdoc @@ -42,9 +42,11 @@ \section2 Debugging Qt Quick UI Projects \endif - To debug Qt Quick UI projects (.qmlproject), select the - \uicontrol {Enable QML} check box in \uicontrol {Debugger settings} - in \uicontrol Projects mode \uicontrol {Run Settings}. + To debug Qt Quick UI projects (.qmlproject), select \uicontrol Automatic + or \uicontrol Enabled in \uicontrol{Run Settings} > + \uicontrol {Debugger Settings} > \uicontrol {QML debugger}. + + \image qtquick-debugger-settings.webp {Debugger settings section in Run Settings} \if defined(qtcreator) \section2 Debugging Qt Quick Applications @@ -65,9 +67,13 @@ functions. Therefore, you must make sure that the port is properly protected by a firewall. - \li In \uicontrol {Run Settings} > \uicontrol {Debugger settings}, select - the \uicontrol {Enable QML} check box to enable QML debugging for - running applications. + \li In \uicontrol {Run Settings} > \uicontrol {Debugger settings} > + \uicontrol {QML debugger}, select \uicontrol Automatic or + \uicontrol Enabled to enable QML debugging for running applications. + + To debug both the C++ and QML parts of your application at the same + time, also select \uicontrol Automatic or \uicontrol Enabled in + \uicontrol {C++ debugger}. \li Select \uicontrol Build > \uicontrol {Rebuild Project} to clean and rebuild the project. @@ -119,17 +125,8 @@ For example, for qmake the global setting only affects build configurations that are automatically created when enabling a kit. Also, CMake ignores the global setting. - - \section1 Mixed C++/QML Debugging - - To debug both the C++ and QML parts of your application at the same time, - select the \uicontrol {Enable C++} and \uicontrol {Enable QML} checkboxes for both - languages in the \uicontrol {Debugger Settings} section in the project - \uicontrol{Run Settings}. \endif - \image qtquick-debugging-settings.png {Debugger settings section in Run Settings} - \section1 Starting QML Debugging To start the application, choose \uicontrol Debug > \uicontrol {Start Debugging} diff --git a/doc/qtcreator/src/docker/creator-docker.qdoc b/doc/qtcreator/src/docker/creator-docker.qdoc index 67604dae44a..c6b62b6dd83 100644 --- a/doc/qtcreator/src/docker/creator-docker.qdoc +++ b/doc/qtcreator/src/docker/creator-docker.qdoc @@ -13,8 +13,8 @@ container operates like a virtual machine but uses less system resources at the cost of being less flexible. - Docker support is experimental. While Linux, \macos, and Windows hosts are - supported in principle, Linux is the recommended platform. + While Linux, \macos, and Windows hosts are supported in principle, Linux is + the recommended platform. Currently, only CMake is supported for building applications in the Docker container. @@ -29,16 +29,6 @@ \l{https://docs.docker.com/engine/reference/commandline/pull/}{docker pull} command. - \section1 Enabling Docker Plugin - - To enable the experimental Docker plugin: - - \list 1 - \li In \QC, select \uicontrol Help > \uicontrol {About Plugins} > - \uicontrol Utilities > \uicontrol {Docker (experimental)}. - \li Select \uicontrol {Restart Now} to restart \QC and load the plugin. - \endlist - \section1 Adding Docker Images as Devices To add a Docker image as a device: diff --git a/doc/qtcreator/src/editors/creator-coding.qdoc b/doc/qtcreator/src/editors/creator-coding.qdoc index e7b44dd57e4..77cfc2e000d 100644 --- a/doc/qtcreator/src/editors/creator-coding.qdoc +++ b/doc/qtcreator/src/editors/creator-coding.qdoc @@ -81,7 +81,7 @@ \li \l {Using GitHub Copilot} - The experimental Copilot plugin integrates + The Copilot plugin (disabled by default) integrates \l{https://github.com/features/copilot}{GitHub Copilot} into \QC. You can view suggestions from Copilot in the code editor. diff --git a/doc/qtcreator/src/editors/creator-only/creator-copilot.qdoc b/doc/qtcreator/src/editors/creator-only/creator-copilot.qdoc index 69f3a35454e..15b5aae4c6c 100644 --- a/doc/qtcreator/src/editors/creator-only/creator-copilot.qdoc +++ b/doc/qtcreator/src/editors/creator-only/creator-copilot.qdoc @@ -8,7 +8,7 @@ \title Using GitHub Copilot - The experimental Copilot plugin integrates + The Copilot plugin (disabled by default) integrates \l{https://github.com/features/copilot}{GitHub Copilot} into \QC. You can view suggestions from Copilot in the \uicontrol Edit mode. diff --git a/doc/qtcreator/src/howto/creator-keyboard-shortcuts.qdoc b/doc/qtcreator/src/howto/creator-keyboard-shortcuts.qdoc index 62d34751441..3cec4df91d1 100644 --- a/doc/qtcreator/src/howto/creator-keyboard-shortcuts.qdoc +++ b/doc/qtcreator/src/howto/creator-keyboard-shortcuts.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 // ********************************************************************** @@ -433,13 +433,19 @@ \row \li Find references to symbol under cursor \li Ctrl+Shift+U + \if defined(qtcreator) + \note If this keyboard shortcut does not work on Linux, see + \l {Editing Issues}. + \endif \row \li Follow symbol under cursor Works with namespaces, classes, functions, variables, include statements, and macros. Also, opens URLs in the default browser + \if defined(qtcreator) and Qt resource files (.qrc) in the \l{Resource Files} {resource editor} + \endif \li F2 \row \li Rename symbol under cursor diff --git a/doc/qtcreator/src/howto/creator-only/creator-cli.qdoc b/doc/qtcreator/src/howto/creator-only/creator-cli.qdoc index 4a7d5435781..96c63abfd1d 100644 --- a/doc/qtcreator/src/howto/creator-only/creator-cli.qdoc +++ b/doc/qtcreator/src/howto/creator-only/creator-cli.qdoc @@ -8,7 +8,7 @@ // ********************************************************************** /*! - \previouspage creator-project-conan.html + \previouspage creator-vcpkg.html \page creator-cli.html \nextpage creator-keyboard-shortcuts.html diff --git a/doc/qtcreator/src/howto/creator-only/creator-vcpkg.qdoc b/doc/qtcreator/src/howto/creator-only/creator-vcpkg.qdoc new file mode 100644 index 00000000000..84b3580bd69 --- /dev/null +++ b/doc/qtcreator/src/howto/creator-only/creator-vcpkg.qdoc @@ -0,0 +1,88 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only + +/*! + \page creator-vcpkg.html + \previouspage creator-project-conan.html + \nextpage creator-cli.html + + \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 + \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 + + \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}. + + \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. + + \image qtcreator-preferences-cmake-vcpkg.webp {Vcpkg tab in CMake Preferences} + + Select \inlineimage icons/online.png + to download vcpkg if you have not installed it yet. + + \section1 Creating vcpkg Manifest Files + + To create a new vcpkg package manager manifest (vcpkg.json) file: + + \list 1 + \li Select \uicontrol File > \uicontrol {New File} > + \uicontrol Vcpkg. + \image qtcreator-file-new-file-vcpkg-manifest-file.webp {vcpkg.json Manifest File wizard page} + \li In \uicontrol Name, enter a name for the manifest file. + \li In \uicontrol Version, enter a version number for the file. + \li In \uicontrol Dependencies, enter the packages to manage. + + You can add packages later in a manifest editor. + \li Select \uicontrol Next to open the \uicontrol {Project Management} + page. + \li Select \uicontrol Finish to create the file. + \endlist + + The wizard automatically adds the vcpkg.json file to the CMakeLists.txt file + of the project. + + \section1 Selecting Packages to Manage + + When you open a vcpkg.json file, it opens in the manifest file editor: + + \image qtcreator-vcpkg-manifest-file-editor.webp {vcpkg.json file in the manifest file editor} + + To add a package to your project: + + \list 1 + \li Place the cursor on the line where you want to add a package. + \li Select the \inlineimage icons/zoom.png + (\uicontrol {Search Package}) button. + \image qtcreator-vcpkg-package-selector.webp {Package selector dialog} + \li Select the package to add to your project. + \li Select \uicontrol OK to return to the editor. + \li Repeat to add more packages. + \endlist + + To set the path to the directory where you installed vcpkg, select + \inlineimage icons/settings.png + (\uicontrol Configure) on the editor toolbar. +*/ diff --git a/doc/qtcreator/src/linux-mobile/b2qtdev.qdoc b/doc/qtcreator/src/linux-mobile/b2qtdev.qdoc index dd3a884f8e3..789b090ed65 100644 --- a/doc/qtcreator/src/linux-mobile/b2qtdev.qdoc +++ b/doc/qtcreator/src/linux-mobile/b2qtdev.qdoc @@ -33,9 +33,15 @@ \section1 Adding Boot2Qt Devices - If \QC does not automatically detect a device you connected with USB, you can - use a wizard to create either a network connection or a USB connection to - it. + If \QC does not automatically detect a device you connected with USB, select + \uicontrol Edit > \uicontrol Preferences > \uicontrol Devices > + \uicontrol Devices > \uicontrol Add > \uicontrol {Boot2Qt Device} to create + either a network connection or a USB connection to it. + + \image qtcreator-boot2qt-device-configurations.webp {Devices tab in Preferences} + + To add a device without using a wizard, select \uicontrol {Boot2Qt Device} in + the pull-down menu of the \uicontrol Add button. \note On Ubuntu Linux, the development user account must have access to the plugged-in devices. To grant them access to the device via USB, create a new @@ -46,8 +52,6 @@ You can edit the settings later in \uicontrol Edit > \uicontrol Preferences > \uicontrol Devices > \uicontrol Devices. - \image qtcreator-boot2qt-device-configurations.png {Devices dialog} - To reboot the selected device, select \uicontrol {Reboot Device}. To restore the default application to the device, select @@ -122,6 +126,10 @@ parameters that have sensible default values. One of these is the SSH port number, which is available in the variable \c %{Device:SshPort}. + + To add a device without using the wizard, select + \uicontrol {Boot2Qt Device} in the pull-down menu of the + \uicontrol Add button. \endlist \li Select \uicontrol Edit > \uicontrol Preferences > \uicontrol Kits > \uicontrol Add to add a kit for building applications for the diff --git a/doc/qtcreator/src/linux-mobile/linuxdev.qdoc b/doc/qtcreator/src/linux-mobile/linuxdev.qdoc index 028619c7fad..41820632d36 100644 --- a/doc/qtcreator/src/linux-mobile/linuxdev.qdoc +++ b/doc/qtcreator/src/linux-mobile/linuxdev.qdoc @@ -107,6 +107,9 @@ All of these parameters can be edited later, as well as additional ones that the wizard does not show because there are sensible default values. + To add a device without using the wizard, select + \uicontrol {Add Remote Linux Device} in the pull-down + menu of the \uicontrol Add button. \li Select \uicontrol Edit > \uicontrol Preferences > \uicontrol Kits > \uicontrol Add to add a kit for building for the device. Select the diff --git a/doc/qtcreator/src/overview/creator-only/creator-issues.qdoc b/doc/qtcreator/src/overview/creator-only/creator-issues.qdoc index 9a09a24b093..ca3b44304f7 100644 --- a/doc/qtcreator/src/overview/creator-only/creator-issues.qdoc +++ b/doc/qtcreator/src/overview/creator-only/creator-issues.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 // ********************************************************************** @@ -68,6 +68,23 @@ \li Code completion does not support typedefs for nested classes. + \li When developing on Linux, the \key {Ctrl+Shift+U} keyboard shortcut + might not work because it conflicts with a shortcut of the + Intelligent Input Bus (ibus). You can change either the shortcut in + \QC or the conflicting shortcut in ibus. + + To set another \l {Keyboard Shortcuts}{keyboard shortcut} + in \QC, select \uicontrol Edit > \uicontrol Preferences > + \uicontrol Environment > \uicontrol Keyboard. + + To change the ibus shortcut, enter the following command on the + command line to start ibus setup: + \badcode + ibus-setup + \endcode + + Then, change the unicode code point shortcut in the \uicontrol Emoji + tab to something else than \key {u}. \endlist \section1 Projects Issues 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 f70ba9a0ea4..e0f2d11ac10 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 @@ -2,7 +2,7 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! - \previouspage creator-build-settings-cmake.html + \previouspage creator-build-settings-cmake-presets.html \page creator-build-settings-qmake.html \nextpage creator-build-settings-qbs.html 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 b0f188c0834..11319133271 100644 --- a/doc/qtcreator/src/projects/creator-only/creator-files-creating.qdoc +++ b/doc/qtcreator/src/projects/creator-only/creator-files-creating.qdoc @@ -11,7 +11,8 @@ \image qtcreator-new-file.webp {New File wizard} Use wizard templates to add individual files to your \l{Creating Projects} - {projects}: + {projects}. Select \uicontrol File > \uicontrol {New File} and + select the type of the file: \list \li \uicontrol {C/C++}: header and source files for new classes. @@ -28,6 +29,8 @@ 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. \endlist 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 6ea2b20b036..7fb958592de 100644 --- a/doc/qtcreator/src/projects/creator-only/creator-projects-creating.qdoc +++ b/doc/qtcreator/src/projects/creator-only/creator-projects-creating.qdoc @@ -55,6 +55,12 @@ \section1 Using 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 + on the project type and the \l{glossary-buildandrun-kit}{kits} that you + select in the \uicontrol {Kit Selection} dialog. Follow the instructions of + the wizard. + In the first step, you select a template for the project. You can filter templates (1) to view only those that apply to a particular target platform. @@ -117,11 +123,6 @@ \endtable - 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 - on the project type and the \l{glossary-buildandrun-kit}{kits} that you select in the - \uicontrol {Kit Selection} dialog. Follow the instructions of the wizard. - For examples of creating different types of projects, see \l{Tutorials}. diff --git a/doc/qtcreator/src/projects/creator-only/creator-projects-other.qdoc b/doc/qtcreator/src/projects/creator-only/creator-projects-other.qdoc index d22e6f4266d..232bb7d4fa1 100644 --- a/doc/qtcreator/src/projects/creator-only/creator-projects-other.qdoc +++ b/doc/qtcreator/src/projects/creator-only/creator-projects-other.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 // ********************************************************************** @@ -67,6 +67,12 @@ 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-settings-run-analyze.qdocinc b/doc/qtcreator/src/projects/creator-only/creator-projects-settings-run-analyze.qdocinc index eb3ea2992ac..f7adfa4cd2d 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 @@ -35,7 +35,7 @@ \endlist - For more information about the CallGrind and MemCheck settings, see: + For more information about the Callgrind and Memcheck settings, see: \list diff --git a/doc/qtcreator/src/projects/creator-only/creator-projects-settings-run-debug.qdocinc b/doc/qtcreator/src/projects/creator-only/creator-projects-settings-run-debug.qdocinc index 0ae34f8ecdf..43277fbae18 100644 --- a/doc/qtcreator/src/projects/creator-only/creator-projects-settings-run-debug.qdocinc +++ b/doc/qtcreator/src/projects/creator-only/creator-projects-settings-run-debug.qdocinc @@ -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 /*! @@ -6,10 +6,11 @@ \section1 Enabling Debugging - \image qtquick-debugging-settings.png "Debugger Settings" + \image qtquick-debugger-settings.webp "Debugger Settings" - To select the languages to debug, select the \uicontrol {Enable C++} and - \uicontrol {Enable QML} check boxes. + To select the languages to debug, select \uicontrol Automatic + or \uicontrol Enabled in \uicontrol {Debugger Settings} > + \uicontrol {C++ debugger} and \uicontrol {QML debugger}. \note Opening a socket at a well-known port presents a security risk. Anyone on the Internet could connect to the application that you are debugging and @@ -28,6 +29,11 @@ However, you can usually leave this field empty. + \note To create a build configuration that supports debugging for a + Qt Quick application project, you also need to \l {Using Default Values} + {enable QML debugging} either globally or in the \uicontrol {Build Settings} + of the project. + For more information about debugging, see \l{Debugging}. //! [run settings debugger] diff --git a/doc/qtcreator/src/qtcreator-toc.qdoc b/doc/qtcreator/src/qtcreator-toc.qdoc index b9649bb2b4b..9b0cd51c993 100644 --- a/doc/qtcreator/src/qtcreator-toc.qdoc +++ b/doc/qtcreator/src/qtcreator-toc.qdoc @@ -68,6 +68,9 @@ \li \l{Specifying Build Settings} \list \li \l{Cmake Build Configuration} + \list + \li \l{CMake Presets} + \endlist \li \l{qmake Build Configuration} \li \l{Qbs Build Configuration} \li \l{Meson Build Configuration} @@ -202,6 +205,7 @@ \list \li \l{Profiling QML Applications} \li \l{Checking Code Coverage} + \li \l{Static Code Analysis} \li \l{Using Valgrind Code Analysis Tools} \list \li \l{Detecting Memory Leaks with Memcheck} @@ -235,6 +239,7 @@ \li \l{Setting Up Meson} \li \l{Setting Up IncrediBuild} \li \l{Setting Up Conan} + \li \l{Managing Packages with vcpkg} \endlist \li \l{Using Command Line Options} \li \l{Keyboard Shortcuts} 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 c8ef1ee206f..da554a3ab08 100644 --- a/doc/qtcreator/src/user-interface/creator-file-system-view.qdoc +++ b/doc/qtcreator/src/user-interface/creator-file-system-view.qdoc @@ -60,9 +60,12 @@ \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. To use an \l{Terminal} - {internal terminal}, select \uicontrol Edit > \uicontrol Preferences - > \uicontrol Terminal > \uicontrol {Use internal terminal}. + \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}. + \endif \li Search from the selected directory. \li View file properties, such as name, path, MIME type, default editor, line endings, indentation, owner, size, last read and modified @@ -73,7 +76,10 @@ \else \l{Creating Files}. \endif - \li Rename or remove existing files. + \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 + new filename. + \li Remove existing files. \li Create new folders. \li Compare the selected file with the currently open file in the diff editor. For more information, see \l{Comparing Files}. diff --git a/doc/qtcreator/src/user-interface/creator-projects-view.qdoc b/doc/qtcreator/src/user-interface/creator-projects-view.qdoc index 84453cf61b6..cd8fd9a62a6 100644 --- a/doc/qtcreator/src/user-interface/creator-projects-view.qdoc +++ b/doc/qtcreator/src/user-interface/creator-projects-view.qdoc @@ -66,10 +66,11 @@ \else \l{Creating Files}. \endif - \li Rename or remove existing files. If you change the base name of a + \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 and offers to rename them as well. If you rename a UI file (.ui), \QC also changes corresponding include statements accordingly. + \li Remove existing files. \if defined(qtcreator) \li Remove existing directories from \l{Setting Up a Generic Project} {generic projects}. @@ -79,14 +80,16 @@ \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. + \if defined(qtcreator) To use an \l{Terminal}{internal terminal}, select \uicontrol Edit > \uicontrol 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. \li Expand or collapse the tree view to show or hide all files and diff --git a/doc/qtcreator/src/user-interface/creator-ui.qdoc b/doc/qtcreator/src/user-interface/creator-ui.qdoc index 031e15cedbc..f5e59218f46 100644 --- a/doc/qtcreator/src/user-interface/creator-ui.qdoc +++ b/doc/qtcreator/src/user-interface/creator-ui.qdoc @@ -392,6 +392,10 @@ \li \uicontrol{General Messages} + \if defined(qtcreator) + \li \l {Static Code Analysis}{Axivion} + \endif + \li \uicontrol{Version Control} \if defined(qtcreator) @@ -476,6 +480,9 @@ \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 @@ -490,6 +497,8 @@ \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 @@ -521,14 +530,19 @@ \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 The view filters out irrelevant output from the build tools 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. To find output in the + view, enter search criteria in the \uicontrol Filter field. - \image qtcreator-issues.png "Issues" + \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. @@ -536,15 +550,20 @@ contents of the line as search criteria or open a version control annotation view of the line that causes the error message. + To view detailed information about the selected line (where available), press + \key Space. + To navigate to the corresponding source code, click an issue or select \uicontrol {Show in Editor} in the context menu. The entry must contain the name of the file where the issue was found. To view more information about an issue in \l {Compile Output}, - select \uicontrol {Show Output} in the context menu. + select \uicontrol {Show Compile Output} in the context menu. - To jump from one issue to the next or previous one, press \key F6 and - \key Shift+F6. + To jump from one issue to the next or previous + one, select \inlineimage icons/arrowup.png + and \inlineimage icons/arrowdown.png + 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 > diff --git a/doc/qtcreator/src/widgets/qtdesigner-plugins.qdoc b/doc/qtcreator/src/widgets/qtdesigner-plugins.qdoc index 82b64a127cc..f782199c379 100644 --- a/doc/qtcreator/src/widgets/qtdesigner-plugins.qdoc +++ b/doc/qtcreator/src/widgets/qtdesigner-plugins.qdoc @@ -34,10 +34,10 @@ and integrated into \QC. The correct folder to place the plugins depends on whether you use the standalone \QD or the integrated \QD. - The integrated \QD fetches plugins from the \c {\bin\plugins\designer} - directory in the \QC installation directory on Windows and Linux. For - information about how to configure plugins on \macos, see - \l{Configuring Qt Designer Plugins on \macos}. + The integrated \QD fetches plugins from the \QC installation directory. + Designer plugins are loaded from \c {\bin\plugins\designer} on Windows, + \c {/lib/Qt/plugins/designer} on Linux, and + \c {Qt Creator.app/Contents/PlugIns/designer} on macOS. To check which plugins were loaded successfully and which failed, choose \uicontrol Tools > \uicontrol {Form Editor} > @@ -49,81 +49,21 @@ of \c bin. To check which plugins were loaded successfully and which failed, choose \uicontrol Help > \uicontrol {About Plugins}. - \section2 Configuring Qt Designer Plugins on \macos - - On \macos, a GUI application must be built and run from a bundle. A bundle - is a directory structure that appears as a single entity when viewed in the - Finder. A bundle for an application typically has the executable and - all the resources it needs. - - \QC uses its own set of Qt Libraries located in the bundle, and therefore, - you need to configure the \QD plugins that you want to use with \QC. - For more information about how to deploy applications to \macos, see - \l{Qt for macOS - Deployment}. - - The following example illustrates how to configure version 5.2.1 of the - \l{http://qwt.sourceforge.net/}{Qwt - Qt Widgets for Technical Applications} - library for use with \QC: - - \list 1 - - \li To check the paths used in the Qwt library, enter the following - \c otool command: - - \include doc_src_plugins.qdocinc 0 - - The output for Qwt 5.2.1 indicates that the plugin uses Qt core - libraries (QtDesigner, QtScript, QtXml, QtGui and QtCore) and - libqwt.5.dylib: - - \include doc_src_plugins.qdocinc 1 - - - \li You must copy the \QD plugin and the Qwt library files to the - following locations: - - \list - - \li \c {libqwt_designer_plugin.dylib} to - \c {Qt Creator.app/Contents/PlugIns/designer} - - \li \c {libqwt.*.dylib} to \c {Qt Creator.app/Contents/Frameworks} - - \endlist - - Enter the following commands: - - \include doc_src_plugins.qdocinc 4 - - \li Enter the following \c otool command to check the libraries that are - used by the Qwt library: - - \include doc_src_plugins.qdocinc 2 - - The command returns the following output: - - \include doc_src_plugins.qdocinc 3 - - \li Enter the following \c install_name_tool command to fix the - references of the libraries: - - \include doc_src_plugins.qdocinc 5 - - \endlist - \section1 Matching Build Keys The \QC that is included in pre-built Qt packages on Windows is built with - the Microsoft Visual Studio compiler, whereas the version of Qt shipped for - building applications is configured and built to use the \MinGW/g++ compiler. - Plugins built by using this version of Qt cannot be loaded by \QC because - the build-keys do not match. The plugins can only be used in the standalone + the Microsoft Visual Studio compiler. If you install a Qt version that was built + with the \MinGW/g++ compiler, plugins built with this version of Qt cannot be + loaded by \QC because the build-keys do not match. + The same is true if you use a Qt version that is newer than the Qt version that + Qt Creator was built with, or that is otherwise incompatible. + The plugins can then only be used in the standalone version of \QD. Choose \uicontrol Help > \uicontrol {About \QC} to check the - Qt version \QC was built with. + Qt version and compiler that \QC was built with. To use \QD plugins that were built for the shipped Qt version, make sure that \QC is built with the same compiler by either recompiling \QC using - \MinGW or recompiling Qt with Microsoft Visual Studio, depending on which - configuration you want to use for your applications. + \MinGW or installing a Qt version that was built with Microsoft Visual Studio, + depending on which configuration you want to use for your applications. */ diff --git a/doc/qtcreatordev/config/qtcreator-developer.qdocconf b/doc/qtcreatordev/config/qtcreator-developer.qdocconf index 0ce340b2b78..0266d6a1cdf 100644 --- a/doc/qtcreatordev/config/qtcreator-developer.qdocconf +++ b/doc/qtcreatordev/config/qtcreator-developer.qdocconf @@ -16,7 +16,7 @@ headerdirs = . \ ../src \ ../../../src/libs/aggregation \ ../../../src/libs/extensionsystem \ - ../../../src/libs/solutions/tasking \ + ../../../src/libs/solutions \ ../../../src/libs/utils \ ../../../src/plugins/coreplugin @@ -24,7 +24,7 @@ sourcedirs = . \ ../src \ ../../../src/libs/aggregation \ ../../../src/libs/extensionsystem \ - ../../../src/libs/solutions/tasking \ + ../../../src/libs/solutions \ ../../../src/libs/utils \ ../../../src/plugins/coreplugin @@ -42,7 +42,8 @@ sources.fileextensions = "*.cpp *.qdoc" imagedirs = ../images \ ../../config/images \ - ../../qtcreator/images + ../../qtcreator/images \ + ../../../src/libs/solutions exampledirs = ../examples depends += qtwidgets \ diff --git a/doc/qtcreatordev/src/qtcreator-module.qdoc b/doc/qtcreatordev/src/qtcreator-module.qdoc index 18a56103353..83d0b3fe157 100644 --- a/doc/qtcreatordev/src/qtcreator-module.qdoc +++ b/doc/qtcreatordev/src/qtcreator-module.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 /*! @@ -109,12 +109,34 @@ \endomit \endtable + \section1 Solutions + + \QC uses object libraries that are independent of any \QC-specific code, and + are threfore ready to be a part of Qt. + + \table + \header + \li Solution Name + \li Description + + \row + \li \l{Spinner Solution}{Spinner} + \li Renders a circular, endlessly animated progress indicator, + which may be attached to any widget as an overlay. + + \row + \li \l{Tasking Solution}{Tasking} + \li Enables you to build extensible, declarative task tree structures + that contain possibly asynchronous tasks. + \endtable + \section1 Reference \list \li \l {Qt Creator C++ Classes} \li \l {Qt Creator Namespaces} \li \l {Qt Creator Functions} + \li \l {Solutions} \endlist */ diff --git a/doc/qtcreatordev/src/solutions-index.qdoc b/doc/qtcreatordev/src/solutions-index.qdoc new file mode 100644 index 00000000000..1eb4135b890 --- /dev/null +++ b/doc/qtcreatordev/src/solutions-index.qdoc @@ -0,0 +1,12 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +/*! + \page solutions-index.html + \title Solutions + \brief A collection of reusable object libraries. + + This topic lists the available solutions: + + \annotatedlist solutions-modules +*/ diff --git a/qbs/imports/QtcProduct.qbs b/qbs/imports/QtcProduct.qbs index 9c2eb6be880..e5f341704df 100644 --- a/qbs/imports/QtcProduct.qbs +++ b/qbs/imports/QtcProduct.qbs @@ -13,6 +13,7 @@ Product { property string fileName: FileInfo.fileName(sourceDirectory) + ".qbs" property bool useNonGuiPchFile: false property bool useGuiPchFile: false + property bool useQt: true property string pathToSharedSources: FileInfo.joinPaths(path, FileInfo.relativePath(FileInfo.joinPaths('/', qtc.ide_qbs_imports_path), FileInfo.joinPaths('/', qtc.ide_shared_sources_path))) @@ -28,8 +29,12 @@ Product { enableFallback: false } } - Depends { name: "Qt.core"; versionAtLeast: "6.2.0" } - Depends { name: "Qt.core5compat" } + Depends { + name: "Qt" + condition: useQt + submodules: ["core", "core5compat"] + 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, @@ -75,7 +80,7 @@ Product { cpp.cxxLanguageVersion: "c++17" cpp.defines: qtc.generalDefines cpp.minimumWindowsVersion: "6.1" - cpp.useCxxPrecompiledHeader: useNonGuiPchFile || useGuiPchFile + cpp.useCxxPrecompiledHeader: useQt && (useNonGuiPchFile || useGuiPchFile) cpp.visibility: "minimal" Group { diff --git a/qbs/modules/qtc/qtc.qbs b/qbs/modules/qtc/qtc.qbs index 226d432b181..01dd13cc9de 100644 --- a/qbs/modules/qtc/qtc.qbs +++ b/qbs/modules/qtc/qtc.qbs @@ -6,16 +6,16 @@ import qbs.Utilities Module { Depends { name: "cpp"; required: false } - property string qtcreator_display_version: '11.0.0-beta1' + property string qtcreator_display_version: '11.0.0-rc1' property string ide_version_major: '10' property string ide_version_minor: '0' - property string ide_version_release: '82' + 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: '10' property string ide_compat_version_minor: '0' - property string ide_compat_version_release: '82' + property string ide_compat_version_release: '84' property string qtcreator_compat_version: ide_compat_version_major + '.' + ide_compat_version_minor + '.' + ide_compat_version_release diff --git a/scripts/common.py b/scripts/common.py index d295b5763b4..081469963f1 100644 --- a/scripts/common.py +++ b/scripts/common.py @@ -1,6 +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 +import argparse import os import locale import shutil @@ -196,13 +197,13 @@ def is_not_debug(path, filenames): files = [fn for fn in filenames if os.path.isfile(os.path.join(path, fn))] return [fn for fn in files if not is_debug_file(os.path.join(path, fn))] -def codesign_call(): - signing_identity = os.environ.get('SIGNING_IDENTITY') +def codesign_call(identity=None, flags=None): + signing_identity = identity or os.environ.get('SIGNING_IDENTITY') if not signing_identity: return None codesign_call = ['codesign', '-o', 'runtime', '--force', '-s', signing_identity, '-v'] - signing_flags = os.environ.get('SIGNING_FLAGS') + signing_flags = flags or os.environ.get('SIGNING_FLAGS') if signing_flags: codesign_call.extend(signing_flags.split()) return codesign_call @@ -228,8 +229,8 @@ def conditional_sign_recursive(path, filter): if is_mac_platform(): os_walk(path, filter, lambda fp: codesign_executable(fp)) -def codesign(app_path): - codesign = codesign_call() +def codesign(app_path, identity=None, flags=None): + codesign = codesign_call(identity, flags) if not codesign or not is_mac_platform(): return # sign all executables in Resources @@ -243,3 +244,20 @@ def codesign(app_path): entitlements_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), '..', 'dist', 'installer', 'mac', 'entitlements.plist') subprocess.check_call(codesign + ['--deep', app_path, '--entitlements', entitlements_path]) + +def codesign_main(args): + codesign(args.app_bundle, args.identity, args.flags) + +def main(): + parser = argparse.ArgumentParser(description='Qt Creator build tools') + subparsers = parser.add_subparsers(title='subcommands', required=True) + parser_codesign = subparsers.add_parser('codesign', description='Codesign macOS app bundle') + parser_codesign.add_argument('app_bundle') + parser_codesign.add_argument('-s', '--identity', help='Codesign identity') + parser_codesign.add_argument('--flags', help='Additional flags') + parser_codesign.set_defaults(func=codesign_main) + args = parser.parse_args() + args.func(args) + +if __name__ == '__main__': + main() diff --git a/share/qtcreator/debugger/cdbbridge.py b/share/qtcreator/debugger/cdbbridge.py index f19d8dedf9b..a9d86e24ee1 100644 --- a/share/qtcreator/debugger/cdbbridge.py +++ b/share/qtcreator/debugger/cdbbridge.py @@ -147,11 +147,12 @@ class Dumper(DumperBase): code = nativeType.code() if code == TypeCode.Pointer: - if not nativeType.name().startswith(''): + if nativeType.name().startswith(''): + code = TypeCode.Function + elif nativeType.targetName() != nativeType.name(): targetType = self.lookupType(nativeType.targetName(), nativeType.moduleId()) - if targetType is not None: + if targetType is not None and targetType is not nativeType: return self.createPointerType(targetType) - code = TypeCode.Function if code == TypeCode.Array: # cdb reports virtual function tables as arrays those ar handled separetly by diff --git a/share/qtcreator/debugger/gdbbridge.py b/share/qtcreator/debugger/gdbbridge.py index 71dfde08706..b25c139f10f 100644 --- a/share/qtcreator/debugger/gdbbridge.py +++ b/share/qtcreator/debugger/gdbbridge.py @@ -1509,9 +1509,10 @@ class CliDumper(Dumper): self.setupDumpers({}) def put(self, line): - if self.output.endswith('\n'): - self.output = self.output[0:-1] - self.output += line + if self.output: + if self.output[-1].endswith('\n'): + self.output[-1] = self.output[-1][0:-1] + self.output.append(line) def putNumChild(self, numchild): pass diff --git a/share/qtcreator/debugger/libcpp_stdtypes.py b/share/qtcreator/debugger/libcpp_stdtypes.py index 5a72629da75..1e0b852dc0b 100644 --- a/share/qtcreator/debugger/libcpp_stdtypes.py +++ b/share/qtcreator/debugger/libcpp_stdtypes.py @@ -163,7 +163,123 @@ def qdump__std____1__stack(d, value): d.putBetterType(value.type) -def std_1_string_dumper(d, value): +def GetChildMemberWithName(value: DumperBase.Value, name: str) -> DumperBase.Value: + members: list[DumperBase.Value] = value.members(True) + + for member in members: + if member.name == name: + return member + return None + + +def GetIndexOfChildWithName(value: DumperBase.Value, name: str) -> int: + members: list[DumperBase.Value] = value.members(True) + + for i, member in enumerate(members): + if member.name == name: + return i + return None + + +class StringLayout: + CSD = 0 + DSC = 1 + + +def std_1_string_dumper_v2(d, value): + charType = value['__l']['__data_'].dereference().type + + R = GetChildMemberWithName(value, "__r_") + if not R: + raise Exception("Could not find __r_") + + # __r_ is a compressed_pair of the actual data and the allocator. The data we + # want is in the first base class. + R_Base_SP = R[0] + + if not R_Base_SP: + raise Exception("Could not find R_Base_SP") + + Rep_Sp = GetChildMemberWithName(R_Base_SP, "__value_") + + if not Rep_Sp: + raise Exception("Could not find __value_") + + # Our layout seems a little different + Rep_Sp = Rep_Sp[0] + + if not Rep_Sp: + raise Exception("Could not find Rep_Sp") + + L = GetChildMemberWithName(Rep_Sp, "__l") + + if not L: + raise Exception("Could not find __l") + + layout = StringLayout.CSD + if GetIndexOfChildWithName(L, "__data_") == 0: + layout = StringLayout.DSC + + short_mode = False + using_bitmasks = True + size = 0 + size_mode_value = 0 + + Short_Sp = GetChildMemberWithName(Rep_Sp, "__s") + if not Short_Sp: + raise Exception("Could not find __s") + + Is_Long: DumperBase.Value = GetChildMemberWithName(Short_Sp, "__is_long_") + Size_Sp: DumperBase.Value = GetChildMemberWithName(Short_Sp, "__size_") + if not Size_Sp: + raise Exception("Could not find __size_") + + if Is_Long: + using_bitmasks = False + short_mode = Is_Long.integer() == 0 + size = Size_Sp.integer() + else: + size_mode_value = Size_Sp.integer() + mode_mask = 1 + if layout == StringLayout.DSC: + mode_mask = 0x80 + short_mode = (size_mode_value & mode_mask) == 0 + + if short_mode: + Location_Sp = GetChildMemberWithName(Short_Sp, "__data_") + + if using_bitmasks: + size = ((size_mode_value >> 1) % 256) + if layout == StringLayout.DSC: + size = size_mode_value + + # The string is most likely not initialized yet + if size > 100 or not Location_Sp: + raise Exception("Probably not initialized yet") + + d.putCharArrayHelper(d.extractPointer(Location_Sp), size, + charType, d.currentItemFormat()) + return + + Location_Sp = GetChildMemberWithName(L, "__data_") + Size_Vo = GetChildMemberWithName(L, "__size_") + Capacity_Vo = GetChildMemberWithName(L, "__cap_") + + if not Location_Sp or not Size_Vo or not Capacity_Vo: + raise Exception("Could not find Location_Sp, Size_Vo or Capacity_Vo") + + size = Size_Vo.integer() + capacity = Capacity_Vo.integer() + if not using_bitmasks and layout == StringLayout.CSD: + capacity *= 2 + if capacity < size: + raise Exception("Capacity is less than size") + + d.putCharArrayHelper(d.extractPointer(Location_Sp), size, + charType, d.currentItemFormat()) + + +def std_1_string_dumper_v1(d, value): charType = value['__l']['__data_'].dereference().type D = None @@ -245,13 +361,24 @@ def std_1_string_dumper(d, value): return - def qdump__std____1__string(d, value): - std_1_string_dumper(d, value) + try: + std_1_string_dumper_v2(d, value) + except Exception as eV2: + try: + std_1_string_dumper_v1(d, value) + except Exception as eV1: + d.putValue("Could not parse: %s, %s" % (eV1, eV2)) def qdump__std____1__wstring(d, value): - std_1_string_dumper(d, value) + try: + std_1_string_dumper_v2(d, value) + except Exception as eV2: + try: + std_1_string_dumper_v1(d, value) + except Exception as eV1: + d.putValue("Could not parse: %s, %s" % (eV1, eV2)) def qdump__std____1__basic_string(d, value): diff --git a/share/qtcreator/templates/wizards/projects/qtforpythonapplication/main.pyproject b/share/qtcreator/templates/wizards/projects/qtforpythonapplication/main.pyproject index 5c790aa7453..a490f3bd45f 100644 --- a/share/qtcreator/templates/wizards/projects/qtforpythonapplication/main.pyproject +++ b/share/qtcreator/templates/wizards/projects/qtforpythonapplication/main.pyproject @@ -1,3 +1,5 @@ { - "files": ["%{SrcFileName}"] + "files": [ + "%{SrcFileName}" + ] } diff --git a/share/qtcreator/templates/wizards/projects/qtforpythonapplication/qtquickapplication/main.pyproject b/share/qtcreator/templates/wizards/projects/qtforpythonapplication/qtquickapplication/main.pyproject index b7922c9fab9..896598fde5f 100644 --- a/share/qtcreator/templates/wizards/projects/qtforpythonapplication/qtquickapplication/main.pyproject +++ b/share/qtcreator/templates/wizards/projects/qtforpythonapplication/qtquickapplication/main.pyproject @@ -1,3 +1,6 @@ { - "files": ["%{SrcFileName}", "%{QmlFileName}"] + "files": [ + "%{SrcFileName}", + "%{QmlFileName}" + ] } diff --git a/share/qtcreator/templates/wizards/projects/qtforpythonapplication/widget/main.pyproject b/share/qtcreator/templates/wizards/projects/qtforpythonapplication/widget/main.pyproject index 64c2987a8fb..af1f60f084c 100644 --- a/share/qtcreator/templates/wizards/projects/qtforpythonapplication/widget/main.pyproject +++ b/share/qtcreator/templates/wizards/projects/qtforpythonapplication/widget/main.pyproject @@ -1,3 +1,6 @@ { - "files": ["%{SrcFileName}", "form.ui"] + "files": [ + "%{SrcFileName}", + "form.ui" + ] } diff --git a/share/qtcreator/templates/wizards/projects/qtquickapplication/wizard.json b/share/qtcreator/templates/wizards/projects/qtquickapplication/wizard.json index 42b2d1ec0c1..806787d4591 100644 --- a/share/qtcreator/templates/wizards/projects/qtquickapplication/wizard.json +++ b/share/qtcreator/templates/wizards/projects/qtquickapplication/wizard.json @@ -161,6 +161,11 @@ "target": "%{ProjectDirectory}/qmlcomponents", "condition": "%{QdsProjectStyle}" }, + { + "source": "%{QdsWizardPath}/common/insight.tpl", + "target": "%{ProjectDirectory}/insight", + "condition": "%{QdsProjectStyle}" + }, { "source": "%{QdsWizardPath}/common/main.qml", "target": "%{ProjectDirectory}/main.qml", diff --git a/src/app/main.cpp b/src/app/main.cpp index e6a7bdcd7ec..d4c4a47b8c8 100644 --- a/src/app/main.cpp +++ b/src/app/main.cpp @@ -278,7 +278,6 @@ static Utils::QtcSettings *createUserSettings() static void setHighDpiEnvironmentVariable() { - if (Utils::HostOsInfo::isMacHost()) return; @@ -293,10 +292,12 @@ static void setHighDpiEnvironmentVariable() && !qEnvironmentVariableIsSet("QT_AUTO_SCREEN_SCALE_FACTOR") && !qEnvironmentVariableIsSet("QT_SCALE_FACTOR") && !qEnvironmentVariableIsSet("QT_SCREEN_SCALE_FACTORS")) { - } else { + return; + } + + if (!qEnvironmentVariableIsSet("QT_SCALE_FACTOR_ROUNDING_POLICY")) QGuiApplication::setHighDpiScaleFactorRoundingPolicy( Qt::HighDpiScaleFactorRoundingPolicy::Floor); - } } void setPixmapCacheLimit() @@ -494,11 +495,30 @@ int main(int argc, char **argv) Options options = parseCommandLine(argc, argv); applicationDirPath(argv[0]); + const bool hasStyleOption = Utils::findOrDefault(options.appArguments, [](char *arg) { + return strcmp(arg, "-style") == 0; + }); + if (qEnvironmentVariableIsSet("QTC_DO_NOT_PROPAGATE_LD_PRELOAD")) { Utils::Environment::modifySystemEnvironment( {{"LD_PRELOAD", "", Utils::EnvironmentItem::Unset}}); } + auto restoreEnvVarFromSquish = [](const QByteArray &squishVar, const QString &var) { + if (qEnvironmentVariableIsSet(squishVar)) { + Utils::Environment::modifySystemEnvironment( + {{var, "", Utils::EnvironmentItem::Unset}}); + const QString content = qEnvironmentVariable(squishVar); + if (!content.isEmpty()) { + Utils::Environment::modifySystemEnvironment( + {{var, content, Utils::EnvironmentItem::Prepend}}); + } + } + }; + + restoreEnvVarFromSquish("SQUISH_SHELL_ORIG_DYLD_LIBRARY_PATH", "DYLD_LIBRARY_PATH"); + restoreEnvVarFromSquish("SQUISH_ORIG_DYLD_FRAMEWORK_PATH", "DYLD_FRAMEWORK_PATH"); + if (options.userLibraryPath) { if ((*options.userLibraryPath).isEmpty()) { Utils::Environment::modifySystemEnvironment( @@ -606,10 +626,8 @@ int main(int argc, char **argv) setPixmapCacheLimit(); loadFonts(); - if (Utils::HostOsInfo::isWindowsHost() - && !qFuzzyCompare(qApp->devicePixelRatio(), 1.0) - && QApplication::style()->objectName().startsWith( - QLatin1String("windows"), Qt::CaseInsensitive)) { + if (Utils::HostOsInfo::isWindowsHost() && !qFuzzyCompare(qApp->devicePixelRatio(), 1.0) + && !hasStyleOption) { QApplication::setStyle(QLatin1String("fusion")); } const int threadCount = QThreadPool::globalInstance()->maxThreadCount(); diff --git a/src/libs/3rdparty/winpty/winpty.qbs b/src/libs/3rdparty/winpty/winpty.qbs index 63d76113646..f6160fe9e62 100644 --- a/src/libs/3rdparty/winpty/winpty.qbs +++ b/src/libs/3rdparty/winpty/winpty.qbs @@ -56,8 +56,7 @@ Project { Depends { name: "winpty_genversion_header" } Depends { name: "cpp" } - useNonGuiPchFile: false - useGuiPchFile: false + useQt: false cpp.includePaths: base.concat([sourceDirectory + "/include", buildDirectory]) cpp.defines: base.concat(["WINPTY_AGENT_ASSERT", diff --git a/src/libs/advanceddockingsystem/dockmanager.cpp b/src/libs/advanceddockingsystem/dockmanager.cpp index 1b2a1c322a1..60a5aa35c8c 100644 --- a/src/libs/advanceddockingsystem/dockmanager.cpp +++ b/src/libs/advanceddockingsystem/dockmanager.cpp @@ -839,7 +839,7 @@ expected_str DockManager::reloadActiveWorkspace() if (!workspaces().contains(*wrk)) return make_unexpected( - Tr::tr("Cannot reload \"%1\", it is not contained in the list of workspaces") + Tr::tr("Cannot reload \"%1\". It is not in the list of workspaces.") .arg(wrk->filePath().toUserOutput())); const expected_str data = loadWorkspace(*wrk); @@ -903,7 +903,7 @@ expected_str DockManager::cloneWorkspace(const QString &originalFileNam const expected_str copyResult = originalPath.copyFile(clonePath); if (!copyResult) - return make_unexpected(Tr::tr("Could not clone '%1' due to: %2") + return make_unexpected(Tr::tr("Could not clone \"%1\" due to: %2") .arg(originalPath.toUserOutput(), copyResult.error())); writeDisplayName(clonePath, cloneName); @@ -1023,7 +1023,7 @@ expected_str DockManager::exportWorkspace(const QString &targetFilePath const FilePath workspaceFile = userDirectory().pathAppended(sourceFileName); if (!workspaceFile.exists()) return make_unexpected( - Tr::tr("Workspace does not exist '%1'").arg(workspaceFile.toUserOutput())); + Tr::tr("Workspace does not exist \"%1\"").arg(workspaceFile.toUserOutput())); // Finally copy the workspace to the target const expected_str copyResult = workspaceFile.copyFile(targetFile); diff --git a/src/libs/qmleditorwidgets/easingpane/easingcontextpane.h b/src/libs/qmleditorwidgets/easingpane/easingcontextpane.h index 570de97fd7a..b1690a79810 100644 --- a/src/libs/qmleditorwidgets/easingpane/easingcontextpane.h +++ b/src/libs/qmleditorwidgets/easingpane/easingcontextpane.h @@ -12,10 +12,10 @@ class QSpinBox; class QDoubleSpinBox; class QGraphicsView; class QVariant; -QT_END_NAMESPACE - class EasingGraph; +QT_END_NAMESPACE + namespace QmlJS { class PropertyReader; } namespace QmlEditorWidgets { diff --git a/src/libs/qmljs/parser/qmljslexer_p.h b/src/libs/qmljs/parser/qmljslexer_p.h index 1d2195369ad..9182f2ead88 100644 --- a/src/libs/qmljs/parser/qmljslexer_p.h +++ b/src/libs/qmljs/parser/qmljslexer_p.h @@ -20,9 +20,11 @@ #include #include -QT_QML_BEGIN_NAMESPACE - +QT_BEGIN_NAMESPACE class QDebug; +QT_END_NAMESPACE + +QT_QML_BEGIN_NAMESPACE namespace QmlJS { diff --git a/src/libs/qmljs/qmljscheck.cpp b/src/libs/qmljs/qmljscheck.cpp index 78d93fb6c5d..2a41f472d7b 100644 --- a/src/libs/qmljs/qmljscheck.cpp +++ b/src/libs/qmljs/qmljscheck.cpp @@ -697,12 +697,19 @@ Check::Check(Document::Ptr doc, const ContextPtr &context, Utils::QtcSettings *q _enabledMessages = Utils::toSet(Message::allMessageTypes()); if (qtcSettings && qtcSettings->value("J.QtQuick/QmlJSEditor.useCustomAnalyzer").toBool()) { - auto disabled = qtcSettings->value("J.QtQuick/QmlJSEditor.disabledMessages").toList(); + auto toIntList = [](const QList list) { + return Utils::transform(list, [](StaticAnalysis::Type t) { return int(t); }); + }; + auto disabled = qtcSettings->value("J.QtQuick/QmlJSEditor.disabledMessages", + QVariant::fromValue( + toIntList(defaultDisabledMessages()))).toList(); for (const QVariant &disabledNumber : disabled) disableMessage(StaticAnalysis::Type(disabledNumber.toInt())); if (!isQtQuick2Ui()) { - auto disabled = qtcSettings->value("J.QtQuick/QmlJSEditor.disabledMessagesNonQuickUI").toList(); + auto disabled = qtcSettings->value("J.QtQuick/QmlJSEditor.disabledMessagesNonQuickUI", + QVariant::fromValue( + toIntList(defaultDisabledMessagesForNonQuickUi()))).toList(); for (const QVariant &disabledNumber : disabled) disableMessage(StaticAnalysis::Type(disabledNumber.toInt())); } diff --git a/src/libs/qmljs/qmljsdocument.cpp b/src/libs/qmljs/qmljsdocument.cpp index 4b79bdafccc..e61552a6396 100644 --- a/src/libs/qmljs/qmljsdocument.cpp +++ b/src/libs/qmljs/qmljsdocument.cpp @@ -370,7 +370,7 @@ QByteArray LibraryInfo::calculateFingerprint() const { QCryptographicHash hash(QCryptographicHash::Sha1); auto addData = [&hash](auto p, size_t len) { - hash.addData(QByteArrayView(reinterpret_cast(p), len)); + hash.addData(reinterpret_cast(p), len); }; addData(&_status, sizeof(_status)); diff --git a/src/libs/solutions/CMakeLists.txt b/src/libs/solutions/CMakeLists.txt index 694d940195d..2a47fbee5fb 100644 --- a/src/libs/solutions/CMakeLists.txt +++ b/src/libs/solutions/CMakeLists.txt @@ -1 +1,2 @@ +add_subdirectory(spinner) add_subdirectory(tasking) diff --git a/src/libs/solutions/README.md b/src/libs/solutions/README.md index ab4cc9727d3..4fd7234a286 100644 --- a/src/libs/solutions/README.md +++ b/src/libs/solutions/README.md @@ -46,3 +46,23 @@ integrated into Qt when they: - Have full docs. - Have auto tests. - Have at least one example (however, autotests often play this role, too). + +## Documentation + +The common Solutions doc (doc/qtcreatordev/src/solutions-index.qdoc) +lists all the solutions added to the solutions-modules group. +In order to gather all the available solutions in this common page, +define the module and refer to it like: + +/*! + \module TaskingSolution + \title The Tasking Solution + \ingroup solutions-modules + \brief (... add a brief description here...) +*/ + +The \ingroup will put the item above to the common Solutions page. +Don't add more \ingroup references from class docs, add \inmodule instead. + + + diff --git a/src/libs/solutions/solutions.qbs b/src/libs/solutions/solutions.qbs index 6184dce2af7..3978235666e 100644 --- a/src/libs/solutions/solutions.qbs +++ b/src/libs/solutions/solutions.qbs @@ -2,6 +2,7 @@ Project { name: "Solutions" references: [ + "spinner/spinner.qbs", "tasking/tasking.qbs", ].concat(project.additionalLibs) } diff --git a/src/libs/solutions/spinner/CMakeLists.txt b/src/libs/solutions/spinner/CMakeLists.txt new file mode 100644 index 00000000000..fad094ccb3d --- /dev/null +++ b/src/libs/solutions/spinner/CMakeLists.txt @@ -0,0 +1,9 @@ +add_qtc_library(Spinner OBJECT +# Never add dependencies to non-Qt libraries for this library + DEPENDS Qt::Core Qt::Widgets + PUBLIC_DEFINES SPINNER_LIBRARY + SOURCES + spinner.cpp spinner.h + spinner.qrc + spinner_global.h +) diff --git a/src/libs/solutions/spinner/icons/spinner_large.png b/src/libs/solutions/spinner/icons/spinner_large.png new file mode 100644 index 00000000000..c24ff1b77cf Binary files /dev/null and b/src/libs/solutions/spinner/icons/spinner_large.png differ diff --git a/src/libs/solutions/spinner/icons/spinner_medium.png b/src/libs/solutions/spinner/icons/spinner_medium.png new file mode 100644 index 00000000000..d64cc514e1b Binary files /dev/null and b/src/libs/solutions/spinner/icons/spinner_medium.png differ diff --git a/src/libs/solutions/spinner/icons/spinner_small.png b/src/libs/solutions/spinner/icons/spinner_small.png new file mode 100644 index 00000000000..254e9c82fc0 Binary files /dev/null and b/src/libs/solutions/spinner/icons/spinner_small.png differ diff --git a/src/libs/solutions/spinner/spinner.cpp b/src/libs/solutions/spinner/spinner.cpp new file mode 100644 index 00000000000..2dc241db7e1 --- /dev/null +++ b/src/libs/solutions/spinner/spinner.cpp @@ -0,0 +1,249 @@ +// 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 "spinner.h" + +#include +#include +#include +#include + +namespace SpinnerSolution { + +class OverlayWidget : public QWidget +{ +public: + using PaintFunction = std::function; + + explicit OverlayWidget(QWidget *parent = nullptr) + { + setAttribute(Qt::WA_TransparentForMouseEvents); + if (parent) + attachToWidget(parent); + } + + void attachToWidget(QWidget *parent) + { + if (parentWidget()) + parentWidget()->removeEventFilter(this); + setParent(parent); + if (parent) { + parent->installEventFilter(this); + resizeToParent(); + raise(); + } + } + void setPaintFunction(const PaintFunction &paint) { m_paint = paint; } + +protected: + bool eventFilter(QObject *obj, QEvent *ev) override + { + if (obj == parent() && ev->type() == QEvent::Resize) + resizeToParent(); + return QWidget::eventFilter(obj, ev); + } + + void paintEvent(QPaintEvent *ev) override + { + if (m_paint) { + QPainter p(this); + m_paint(this, p, ev); + } + } + +private: + void resizeToParent() { setGeometry(QRect({}, parentWidget()->size())); } + + PaintFunction m_paint; +}; + +class SpinnerPainter +{ +public: + using UpdateCallback = std::function; + + SpinnerPainter(SpinnerSize size); + + void setSize(SpinnerSize size); + + void setUpdateCallback(const UpdateCallback &cb) { m_callback = cb; } + + QSize size() const { return m_pixmap.size() / m_pixmap.devicePixelRatio(); } + void paint(QPainter &painter, const QRect &rect) const; + void startAnimation() { m_timer.start(); } + void stopAnimation() { m_timer.stop(); } + +protected: + void nextAnimationStep() { m_rotation = (m_rotation + m_rotationStep + 360) % 360; } + +private: + SpinnerSize m_size = SpinnerSize::Small; + int m_rotationStep = 45; + int m_rotation = 0; + QTimer m_timer; + QPixmap m_pixmap; + UpdateCallback m_callback; +}; + +static QString imageFileNameForSpinnerSize(SpinnerSize size) +{ + switch (size) { + case SpinnerSize::Large: + return ":/icons/spinner_large.png"; + case SpinnerSize::Medium: + return ":/icons/spinner_medium.png"; + case SpinnerSize::Small: + return ":/icons/spinner_small.png"; + } + return {}; +} + +SpinnerPainter::SpinnerPainter(SpinnerSize size) +{ + m_timer.setSingleShot(false); + QObject::connect(&m_timer, &QTimer::timeout, &m_timer, [this] { + nextAnimationStep(); + if (m_callback) + m_callback(); + }); + setSize(size); +} + +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)); +} + +void SpinnerPainter::paint(QPainter &painter, const QRect &rect) const +{ + painter.save(); + painter.setRenderHint(QPainter::SmoothPixmapTransform); + QPoint translate(rect.x() + rect.width() / 2, rect.y() + rect.height() / 2); + QTransform t; + t.translate(translate.x(), translate.y()); + t.rotate(m_rotation); + t.translate(-translate.x(), -translate.y()); + painter.setTransform(t); + QSize pixmapUserSize(m_pixmap.size() / m_pixmap.devicePixelRatio()); + painter.drawPixmap(QPoint(rect.x() + ((rect.width() - pixmapUserSize.width()) / 2), + rect.y() + ((rect.height() - pixmapUserSize.height()) / 2)), + m_pixmap); + painter.restore(); +} + +class SpinnerWidget : public OverlayWidget +{ +public: + explicit SpinnerWidget(SpinnerSize size, QWidget *parent = nullptr) + : OverlayWidget(parent) + , m_paint(size) + { + setPaintFunction( + [this](QWidget *w, QPainter &p, QPaintEvent *) { m_paint.paint(p, w->rect()); }); + m_paint.setUpdateCallback([this] { update(); }); + updateGeometry(); + } + + void setSize(SpinnerSize size) + { + m_paint.setSize(size); + updateGeometry(); + } + QSize sizeHint() const final { return m_paint.size(); } + +protected: + void showEvent(QShowEvent *) final { m_paint.startAnimation(); } + void hideEvent(QHideEvent *) final { m_paint.stopAnimation(); } + +private: + SpinnerPainter m_paint; +}; + +/*! + \module SpinnerSolution + \title Spinner Solution + \ingroup solutions-modules + \brief Contains a Spinner solution. + + The Spinner solution depends on Qt only, and doesn't depend on any \QC specific code. +*/ + +/*! + \namespace SpinnerSolution + \inmodule SpinnerSolution + \brief The SpinnerSolution namespace encloses the Spinner class. +*/ + +/*! + \enum SpinnerSolution::SpinnerSize + + This enum describes the possible spinner sizes. + + \value Small \inlineimage spinner/icons/spinner_small.png + \value Medium \inlineimage spinner/icons/spinner_medium.png + \value Large \inlineimage spinner/icons/spinner_large.png +*/ + +/*! + \class SpinnerSolution::Spinner + \inheaderfile solutions/spinner/spinner.h + \inmodule SpinnerSolution + \brief The Spinner class renders a circular, endlessly animated progress indicator, + that may be attached to any widget as an overlay. +*/ + +/*! + Creates a spinner overlay with a given \a size for the passed \a parent widget. + + The \a parent widget takes the ownership of the created spinner. +*/ +Spinner::Spinner(SpinnerSize size, QWidget *parent) + : QObject(parent) + , m_widget(new SpinnerWidget(size, parent)) {} + +/*! + Sets the size of the spinner to the given \a size. +*/ +void Spinner::setSize(SpinnerSize size) +{ + m_widget->setSize(size); +} + +/*! + Shows the animated spinner as an overlay for the parent widget + set previously in the constructor. +*/ +void Spinner::show() +{ + m_widget->show(); +} + +/*! + Hides the spinner. +*/ +void Spinner::hide() +{ + m_widget->hide(); +} + +/*! + Returns \c true if the spinner is visible; otherwise, returns \c false. +*/ +bool Spinner::isVisible() const +{ + return m_widget->isVisible(); +} + +/*! + Shows or hides the spinner depending on the value of \a visible. + By default, the spinner is visible. +*/ +void Spinner::setVisible(bool visible) +{ + m_widget->setVisible(visible); +} + +} // namespace SpinnerSolution diff --git a/src/libs/solutions/spinner/spinner.h b/src/libs/solutions/spinner/spinner.h new file mode 100644 index 00000000000..86dfbee6ebd --- /dev/null +++ b/src/libs/solutions/spinner/spinner.h @@ -0,0 +1,37 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#ifndef SPINNER_H +#define SPINNER_H + +#include "spinner_global.h" + +#include + +namespace SpinnerSolution { + +Q_NAMESPACE_EXPORT(SPINNER_EXPORT) + +enum class SpinnerSize { Small, Medium, Large }; +Q_ENUM_NS(SpinnerSize); + +// TODO: SpinnerOverlay and SpinnerWidget? + +class SPINNER_EXPORT Spinner : public QObject +{ + Q_OBJECT +public: + explicit Spinner(SpinnerSize size, QWidget *parent = nullptr); + void setSize(SpinnerSize size); + void show(); + void hide(); + bool isVisible() const; + void setVisible(bool visible); + +private: + class SpinnerWidget *m_widget = nullptr; +}; + +} // namespace SpinnerSolution + +#endif // SPINNER_H diff --git a/src/libs/solutions/spinner/spinner.qbs b/src/libs/solutions/spinner/spinner.qbs new file mode 100644 index 00000000000..cd830d107ec --- /dev/null +++ b/src/libs/solutions/spinner/spinner.qbs @@ -0,0 +1,13 @@ +QtcLibrary { + name: "Spinner" + Depends { name: "Qt"; submodules: ["core", "widgets"] } + cpp.defines: base.concat("SPINNER_LIBRARY") + + files: [ + "spinner.cpp", + "spinner.h", + "spinner.qrc", + "spinner_global.h", + ] +} + diff --git a/src/libs/solutions/spinner/spinner.qrc b/src/libs/solutions/spinner/spinner.qrc new file mode 100644 index 00000000000..5ad85953e8d --- /dev/null +++ b/src/libs/solutions/spinner/spinner.qrc @@ -0,0 +1,7 @@ + + + icons/spinner_large.png + icons/spinner_medium.png + icons/spinner_small.png + + diff --git a/src/libs/solutions/spinner/spinner_global.h b/src/libs/solutions/spinner/spinner_global.h new file mode 100644 index 00000000000..f50b09f23f2 --- /dev/null +++ b/src/libs/solutions/spinner/spinner_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(SPINNER_LIBRARY) +# define SPINNER_EXPORT Q_DECL_EXPORT +#elif defined(SPINNER_STATIC_LIBRARY) +# define SPINNER_EXPORT +#else +# define SPINNER_EXPORT Q_DECL_IMPORT +#endif diff --git a/src/libs/solutions/tasking/barrier.h b/src/libs/solutions/tasking/barrier.h index 6f1afe39b09..705ddd5f5b7 100644 --- a/src/libs/solutions/tasking/barrier.h +++ b/src/libs/solutions/tasking/barrier.h @@ -83,14 +83,14 @@ public: "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 will finish with error. "); - return TaskAction::StopWithError; + return SetupResult::StopWithError; } Barrier *activeSharedBarrier = activeBarrier->barrier(); const std::optional result = activeSharedBarrier->result(); if (result.has_value()) - return result.value() ? TaskAction::StopWithDone : TaskAction::StopWithError; + return result.value() ? SetupResult::StopWithDone : SetupResult::StopWithError; QObject::connect(activeSharedBarrier, &Barrier::done, &barrier, &Barrier::stopWithResult); - return TaskAction::Continue; + return SetupResult::Continue; }) {} }; diff --git a/src/libs/solutions/tasking/tasktree.cpp b/src/libs/solutions/tasking/tasktree.cpp index 43a47121b2a..b9417a6e46b 100644 --- a/src/libs/solutions/tasking/tasktree.cpp +++ b/src/libs/solutions/tasking/tasktree.cpp @@ -42,11 +42,126 @@ private: Guard &m_guard; }; +/*! + \module TaskingSolution + \title Tasking Solution + \ingroup solutions-modules + \brief Contains a general purpose Tasking solution. + + The Tasking solution depends on Qt only, and doesn't depend on any \QC specific code. +*/ + +/*! + \namespace Tasking + \inmodule TaskingSolution + \brief The Tasking namespace encloses all classes and global functions of the Tasking solution. +*/ + +/*! + \class Tasking::TaskInterface + \inheaderfile solutions/tasking/tasktree.h + \inmodule TaskingSolution + \brief TaskInterface is the abstract base class for implementing custom task adapters. + + To implement a custom task adapter, derive your adapter from the + \c TaskAdapter class template. TaskAdapter automatically creates and destroys + the custom task instance and associates the adapter with a given \c Task type. +*/ + +/*! + \fn virtual void TaskInterface::start() + + This method is called by the running TaskTree for starting the \c Task instance. + Reimplement this method in \c TaskAdapter's subclass in order to start the + associated task. + + Use TaskAdapter::task() to access the associated \c Task instance. + + \sa done(), TaskAdapter::task() +*/ + +/*! + \fn void TaskInterface::done(bool success) + + Emit this signal from the \c TaskAdapter's subclass, when the \c Task is finished. + Pass \c true as a \a success argument when the task finishes with success; + otherwise, when an error occurs, pass \c false. +*/ + +/*! + \class Tasking::TaskAdapter + \inheaderfile solutions/tasking/tasktree.h + \inmodule TaskingSolution + \brief A class template for implementing custom task adapters. + + The TaskAdapter class template is responsible for creating a task of the \c Task type, + starting it, and reporting success or an error when the task is finished. + It also associates the adapter with a given \c Task type. + + Reimplement this class with the actual \c Task type to adapt the task's interface + into the general TaskTree's interface for managing the \c Task instances. + + Each subclass needs to provide a public default constructor, + implement the start() method, and emit the done() signal when the task is finished. + Use task() to access the associated \c Task instance. + + For more information on implementing the custom task adapters, refer to \l {Task Adapters}. + + \sa start(), done(), task() +*/ + +/*! + \typealias TaskAdapter::Type + + Type alias for the \c Task type. +*/ + +/*! + \fn template TaskAdapter::TaskAdapter() + + Creates a task adapter for the given \c Task type. Internally, it creates + an instance of \c Task, which is accessible via the task() method. + + \sa task() +*/ + +/*! + \fn template Task *TaskAdapter::task() + + Returns the pointer to the associated \c Task instance. +*/ + +/*! + \fn template Task *TaskAdapter::task() const + \overload + + Returns the const pointer to the associated \c Task instance. +*/ + +/*! + \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}. +*/ + /*! \class Tasking::GroupItem \inheaderfile solutions/tasking/tasktree.h - \inmodule QtCreator - \ingroup mainclasses + \inmodule TaskingSolution \brief The GroupItem class represents the basic element for composing nested tree structures. */ @@ -104,7 +219,7 @@ private: the Group stops the other running child tasks (if any - for example in parallel mode), and skips executing tasks it has not started yet (for example, in the sequential mode - those, that are placed after the failed task). Both stopping and skipping child tasks - may happen when parallelLimit is used. + may happen when parallelLimit() is used. The table below summarizes the differences between various workflow policies: @@ -170,7 +285,7 @@ private: as input to the next task before it starts. This mode guarantees that the next task is started only after the previous task finishes. - \sa parallel, parallelLimit + \sa parallel, parallelLimit() */ /*! @@ -181,7 +296,7 @@ private: without waiting for the previous child tasks to finish. In this mode, all child tasks run simultaneously. - \sa sequential, parallelLimit + \sa sequential, parallelLimit() */ /*! @@ -222,25 +337,27 @@ private: */ /*! - \enum Tasking::TaskAction + \enum Tasking::SetupResult This enum is optionally returned from the group's or task's setup handler function. It instructs the running task tree on how to proceed after the setup handler's execution finished. \value Continue - Default. The group's or task's execution continues nomally. + Default. The group's or task's execution continues normally. When a group's or task's setup handler returns void, it's assumed that it returned Continue. \value StopWithDone The group's or task's execution stops immediately with success. When returned from the group's setup handler, all child tasks are skipped, - and the group's onGroupDone handler is invoked (if provided). + and the group's onGroupDone() handler is invoked (if provided). + The group reports success to its parent. The group's workflow policy is ignored. When returned from the task's setup handler, the task isn't started, its done handler isn't invoked, and the task reports success to its parent. \value StopWithError The group's or task's execution stops immediately with an error. When returned from the group's setup handler, all child tasks are skipped, - and the group's onGroupError handler is invoked (if provided). + and the group's onGroupError() handler is invoked (if provided). + The group reports an error to its parent. The group's workflow policy is ignored. When returned from the task's setup handler, the task isn't started, its error handler isn't invoked, and the task reports an error to its parent. */ @@ -248,45 +365,45 @@ private: /*! \typealias GroupItem::GroupSetupHandler - Type alias for \c std::function. + Type alias for \c std::function. - The GroupSetupHandler is used when constructing the onGroupSetup element. + The GroupSetupHandler is used when constructing the onGroupSetup() element. Any function with the above signature, when passed as a group setup handler, - will be called by the running task tree when the group executions starts. + will be called by the running task tree when the group execution starts. The return value of the handler instructs the running group on how to proceed - after the handler's invocation is finished. The default return value of TaskAction::Continue + after the handler's invocation is finished. The default return value of SetupResult::Continue instructs the group to continue running, i.e. to start executing its child tasks. - The return value of TaskAction::StopWithDone or TaskAction::StopWithError + The return value of SetupResult::StopWithDone or SetupResult::StopWithError instructs the group to skip the child tasks' execution and finish immediately with success or an error, respectively. - When the return type is either TaskAction::StopWithDone - of TaskAction::StopWithError, the group's done or error handler (if provided) + When the return type is either SetupResult::StopWithDone + of SetupResult::StopWithError, the group's done or error handler (if provided) is called synchronously immediately afterwards. \note Even if the group setup handler returns StopWithDone or StopWithError, one of the group's done or error handlers is invoked. This behavior differs from that of task handlers and might change in the future. - The onGroupSetup accepts also functions in the shortened form of \c std::function, + The onGroupSetup() accepts also functions in the shortened form of \c std::function, i.e. the return value is void. In this case it's assumed that the return value - is TaskAction::Continue by default. + is SetupResult::Continue by default. - \sa onGroupSetup + \sa onGroupSetup() */ /*! \typealias GroupItem::GroupEndHandler - Type alias for \c std::function\. + Type alias for \c std::function. - The GroupEndHandler is used when constructing the onGroupDone and onGroupError elements. + The GroupEndHandler is used when constructing the onGroupDone() and onGroupError() elements. Any function with the above signature, when passed as a group done or error handler, will be called by the running task tree when the group ends with success or an error, respectively. - \sa onGroupDone, onGroupError + \sa onGroupDone(), onGroupError() */ /*! @@ -295,7 +412,7 @@ private: Constructs a group's element holding the group setup handler. The \a handler is invoked whenever the group starts. - The passed \a handler is either of \c std::function or \c std::function + The passed \a handler is either of \c std::function or \c std::function type. For more information on possible argument type, refer to \l {GroupItem::GroupSetupHandler}. @@ -305,7 +422,7 @@ private: after the storages are constructed, so that the \a handler may already perform some initial modifications to the active storages. - \sa GroupItem::GroupSetupHandler, onGroupDone, onGroupError + \sa GroupItem::GroupSetupHandler, onGroupDone(), onGroupError() */ /*! @@ -320,7 +437,7 @@ private: before the storages are destructed, so that the \a handler may still perform a last read of the active storages' data. - \sa GroupItem::GroupEndHandler, onGroupSetup, onGroupError + \sa GroupItem::GroupEndHandler, onGroupSetup(), onGroupError() */ GroupItem onGroupDone(const GroupItem::GroupEndHandler &handler) { @@ -339,7 +456,7 @@ GroupItem onGroupDone(const GroupItem::GroupEndHandler &handler) before the storages are destructed, so that the \a handler may still perform a last read of the active storages' data. - \sa GroupItem::GroupEndHandler, onGroupSetup, onGroupDone + \sa GroupItem::GroupEndHandler, onGroupSetup(), onGroupDone() */ GroupItem onGroupError(const GroupItem::GroupEndHandler &handler) { @@ -416,9 +533,9 @@ const GroupItem stopOnFinished = workflowPolicy(WorkflowPolicy::StopOnFinished); const GroupItem finishAllAndDone = workflowPolicy(WorkflowPolicy::FinishAllAndDone); const GroupItem finishAllAndError = workflowPolicy(WorkflowPolicy::FinishAllAndError); -static TaskAction toTaskAction(bool success) +static SetupResult toSetupResult(bool success) { - return success ? TaskAction::StopWithDone : TaskAction::StopWithError; + return success ? SetupResult::StopWithDone : SetupResult::StopWithError; } bool TreeStorageBase::isValid() const @@ -644,10 +761,10 @@ public: TaskContainer(TaskTreePrivate *taskTreePrivate, const GroupItem &task, TaskNode *parentNode, TaskContainer *parentContainer) : m_constData(taskTreePrivate, task, parentNode, parentContainer, this) {} - TaskAction start(); - TaskAction continueStart(TaskAction startAction, int nextChild); - TaskAction startChildren(int nextChild); - TaskAction childDone(bool success); + SetupResult start(); + SetupResult continueStart(SetupResult startAction, int nextChild); + SetupResult startChildren(int nextChild); + SetupResult childDone(bool success); void stop(); void invokeEndHandler(); bool isRunning() const { return m_runtimeData.has_value(); } @@ -702,7 +819,7 @@ public: // If returned value != Continue, childDone() needs to be called in parent container (in caller) // in order to unwind properly. - TaskAction start(); + SetupResult start(); void stop(); void invokeEndHandler(bool success); bool isRunning() const { return m_task || m_container.isRunning(); } @@ -931,31 +1048,34 @@ int TaskContainer::RuntimeData::currentLimit() const ? qMin(m_doneCount + m_constData.m_parallelLimit, childCount) : childCount; } -TaskAction TaskContainer::start() +SetupResult TaskContainer::start() { QTC_CHECK(!isRunning()); m_runtimeData.emplace(m_constData); - TaskAction startAction = TaskAction::Continue; + SetupResult startAction = SetupResult::Continue; if (m_constData.m_groupHandler.m_setupHandler) { startAction = invokeHandler(this, m_constData.m_groupHandler.m_setupHandler); - if (startAction != TaskAction::Continue) + if (startAction != SetupResult::Continue) { m_constData.m_taskTreePrivate->advanceProgress(m_constData.m_taskCount); + // Non-Continue SetupResult takes precedence over the workflow policy. + m_runtimeData->m_successBit = startAction == SetupResult::StopWithDone; + } } - if (startAction == TaskAction::Continue) { + if (startAction == SetupResult::Continue) { if (m_constData.m_children.isEmpty()) - startAction = toTaskAction(m_runtimeData->m_successBit); + startAction = toSetupResult(m_runtimeData->m_successBit); } return continueStart(startAction, 0); } -TaskAction TaskContainer::continueStart(TaskAction startAction, int nextChild) +SetupResult TaskContainer::continueStart(SetupResult startAction, int nextChild) { - const TaskAction groupAction = startAction == TaskAction::Continue ? startChildren(nextChild) + const SetupResult groupAction = startAction == SetupResult::Continue ? startChildren(nextChild) : startAction; QTC_CHECK(isRunning()); // TODO: superfluous - if (groupAction != TaskAction::Continue) { - const bool success = m_runtimeData->updateSuccessBit(groupAction == TaskAction::StopWithDone); + if (groupAction != SetupResult::Continue) { + const bool success = m_runtimeData->updateSuccessBit(groupAction == SetupResult::StopWithDone); invokeEndHandler(); if (TaskContainer *parentContainer = m_constData.m_parentContainer) { QTC_CHECK(parentContainer->isRunning()); @@ -970,7 +1090,7 @@ TaskAction TaskContainer::continueStart(TaskAction startAction, int nextChild) return groupAction; } -TaskAction TaskContainer::startChildren(int nextChild) +SetupResult TaskContainer::startChildren(int nextChild) { QTC_CHECK(isRunning()); GuardLocker locker(m_runtimeData->m_startGuard); @@ -979,12 +1099,12 @@ TaskAction TaskContainer::startChildren(int nextChild) if (i >= limit) break; - const TaskAction startAction = m_constData.m_children.at(i)->start(); - if (startAction == TaskAction::Continue) + const SetupResult startAction = m_constData.m_children.at(i)->start(); + if (startAction == SetupResult::Continue) continue; - const TaskAction finalizeAction = childDone(startAction == TaskAction::StopWithDone); - if (finalizeAction == TaskAction::Continue) + const SetupResult finalizeAction = childDone(startAction == SetupResult::StopWithDone); + if (finalizeAction == SetupResult::Continue) continue; int skippedTaskCount = 0; @@ -994,10 +1114,10 @@ TaskAction TaskContainer::startChildren(int nextChild) m_constData.m_taskTreePrivate->advanceProgress(skippedTaskCount); return finalizeAction; } - return TaskAction::Continue; + return SetupResult::Continue; } -TaskAction TaskContainer::childDone(bool success) +SetupResult TaskContainer::childDone(bool success) { QTC_CHECK(isRunning()); const int limit = m_runtimeData->currentLimit(); // Read before bumping m_doneCount and stop() @@ -1009,9 +1129,9 @@ TaskAction TaskContainer::childDone(bool success) ++m_runtimeData->m_doneCount; const bool updatedSuccess = m_runtimeData->updateSuccessBit(success); - const TaskAction startAction + const SetupResult startAction = (shouldStop || m_runtimeData->m_doneCount == m_constData.m_children.size()) - ? toTaskAction(updatedSuccess) : TaskAction::Continue; + ? toSetupResult(updatedSuccess) : SetupResult::Continue; if (isStarting()) return startAction; @@ -1045,30 +1165,30 @@ void TaskContainer::invokeEndHandler() m_runtimeData.reset(); } -TaskAction TaskNode::start() +SetupResult TaskNode::start() { QTC_CHECK(!isRunning()); if (!isTask()) return m_container.start(); m_task.reset(m_taskHandler.m_createHandler()); - const TaskAction startAction = m_taskHandler.m_setupHandler + const SetupResult startAction = m_taskHandler.m_setupHandler ? invokeHandler(parentContainer(), m_taskHandler.m_setupHandler, *m_task.get()) - : TaskAction::Continue; - if (startAction != TaskAction::Continue) { + : SetupResult::Continue; + if (startAction != SetupResult::Continue) { m_container.m_constData.m_taskTreePrivate->advanceProgress(1); m_task.reset(); return startAction; } - const std::shared_ptr unwindAction - = std::make_shared(TaskAction::Continue); + const std::shared_ptr unwindAction + = std::make_shared(SetupResult::Continue); QObject::connect(m_task.get(), &TaskInterface::done, taskTree(), [=](bool success) { invokeEndHandler(success); QObject::disconnect(m_task.get(), &TaskInterface::done, taskTree(), nullptr); m_task.release()->deleteLater(); QTC_ASSERT(parentContainer() && parentContainer()->isRunning(), return); if (parentContainer()->isStarting()) - *unwindAction = toTaskAction(success); + *unwindAction = toSetupResult(success); else parentContainer()->childDone(success); }); @@ -1104,26 +1224,15 @@ void TaskNode::invokeEndHandler(bool success) m_container.m_constData.m_taskTreePrivate->advanceProgress(1); } -/*! - \namespace Tasking - \inmodule QtCreator - \brief The Tasking namespace contains a general purpose TaskTree solution. - - The Tasking namespace depends on Qt only, and doesn't depend on any \QC - specific code. -*/ - /*! \class Tasking::TaskTree \inheaderfile solutions/tasking/tasktree.h - \inmodule QtCreator - \ingroup mainclasses - \brief The TaskTree class runs an async task tree structure defined in a - declarative way. + \inmodule TaskingSolution + \brief The TaskTree class runs an async task tree structure defined in a declarative way. Use the Tasking namespace to build extensible, declarative task tree structures that contain possibly asynchronous tasks, such as Process, - FileTransfer, or Async. TaskTree structures enable you + FileTransfer, or ConcurrentCall. TaskTree structures enable you to create a sophisticated mixture of a parallel or sequential flow of tasks in the form of a tree and to run it any time later. @@ -1131,14 +1240,14 @@ void TaskNode::invokeEndHandler(bool success) The TaskTree has a mandatory Group root element, which may contain any number of tasks of various types, such as ProcessTask, FileTransferTask, - or AsyncTask: + or ConcurrentCallTask: \code using namespace Tasking; const Group root { ProcessTask(...), - AsyncTask(...), + ConcurrentCallTask(...), FileTransferTask(...) }; @@ -1149,10 +1258,10 @@ void TaskNode::invokeEndHandler(bool success) \endcode The task tree above has a top level element of the Group type that contains - tasks of the type ProcessTask, FileTransferTask, and AsyncTask. + tasks of the type ProcessTask, FileTransferTask, and ConcurrentCallTask. After taskTree->start() is called, the tasks are run in a chain, starting - with ProcessTask. When the ProcessTask finishes successfully, the AsyncTask task is - started. Finally, when the asynchronous task finishes successfully, the + with ProcessTask. When the ProcessTask finishes successfully, the ConcurrentCallTask + task is started. Finally, when the asynchronous task finishes successfully, the FileTransferTask task is started. When the last running task finishes with success, the task tree is considered @@ -1172,26 +1281,26 @@ void TaskNode::invokeEndHandler(bool success) Group { parallel, ProcessTask(...), - AsyncTask(...) + ConcurrentCallTask(...) }, FileTransferTask(...) }; \endcode The example above differs from the first example in that the root element has - a subgroup that contains the ProcessTask and AsyncTask. The subgroup is a + a subgroup that contains the ProcessTask and ConcurrentCallTask. The subgroup is a sibling element of the FileTransferTask in the root. The subgroup contains an additional \e parallel element that instructs its Group to execute its tasks in parallel. - So, when the tree above is started, the ProcessTask and AsyncTask start + So, when the tree above is started, the ProcessTask and ConcurrentCallTask start immediately and run in parallel. Since the root group doesn't contain a \e parallel element, its direct child tasks are run in sequence. Thus, the FileTransferTask starts when the whole subgroup finishes. The group is considered as finished when all its tasks have finished. The order in which the tasks finish is not relevant. - So, depending on which task lasts longer (ProcessTask or AsyncTask), the + So, depending on which task lasts longer (ProcessTask or ConcurrentCallTask), the following scenarios can take place: \table @@ -1208,19 +1317,19 @@ void TaskNode::invokeEndHandler(bool success) \li ProcessTask starts \li ProcessTask starts \row - \li AsyncTask starts - \li AsyncTask starts + \li ConcurrentCallTask starts + \li ConcurrentCallTask starts \row \li ... \li ... \row \li \b {ProcessTask finishes} - \li \b {AsyncTask finishes} + \li \b {ConcurrentCallTask finishes} \row \li ... \li ... \row - \li \b {AsyncTask finishes} + \li \b {ConcurrentCallTask finishes} \li \b {ProcessTask finishes} \row \li Sub Group finishes @@ -1246,8 +1355,8 @@ void TaskNode::invokeEndHandler(bool success) The presented scenarios assume that all tasks run successfully. If a task fails during execution, the task tree finishes with an error. In particular, - when ProcessTask finishes with an error while AsyncTask is still being executed, - the AsyncTask is automatically stopped, the subgroup finishes with an error, + when ProcessTask finishes with an error while ConcurrentCallTask is still being executed, + the ConcurrentCallTask is automatically stopped, the subgroup finishes with an error, the FileTransferTask is skipped, and the tree finishes with an error. \section1 Task Types @@ -1277,14 +1386,14 @@ void TaskNode::invokeEndHandler(bool success) \row \li ProcessTask \li Utils::Process - \li Starts processes. + \li Starts process. \row - \li AsyncTask - \li Utils::Async - \li Starts asynchronous tasks; run in separate thread. + \li ConcurrentCallTask + \li Tasking::ConcurrentCall + \li Starts asynchronous task, runs in separate thread. \row \li TaskTreeTask - \li Utils::TaskTree + \li Tasking::TaskTree \li Starts a nested task tree. \row \li FileTransferTask @@ -1318,18 +1427,18 @@ void TaskNode::invokeEndHandler(bool success) as the task tree calls it when needed. The setup handler is optional. When used, it must be the first argument of the task's constructor. - Optionally, the setup handler may return a TaskAction. The returned - TaskAction influences the further start behavior of a given task. The + Optionally, the setup handler may return a SetupResult. The returned + SetupResult influences the further start behavior of a given task. The possible values are: \table \header - \li TaskAction Value + \li SetupResult Value \li Brief Description \row \li Continue \li The task will be started normally. This is the default behavior when the - setup handler doesn't return TaskAction (that is, its return type is + setup handler doesn't return SetupResult (that is, its return type is void). \row \li StopWithDone @@ -1397,16 +1506,16 @@ void TaskNode::invokeEndHandler(bool success) \endcode The group setup handler is optional. To define a group setup handler, add an - onGroupSetup element to a group. The argument of onGroupSetup is a user - handler. If you add more than one onGroupSetup element to a group, an assert + onGroupSetup() element to a group. The argument of onGroupSetup() is a user + handler. If you add more than one onGroupSetup() element to a group, an assert is triggered at runtime that includes an error message. - Like the task's start handler, the group start handler may return TaskAction. - The returned TaskAction value affects the start behavior of the + Like the task's start handler, the group start handler may return SetupResult. + The returned SetupResult value affects the start behavior of the whole group. If you do not specify a group start handler or its return type - is void, the default group's action is TaskAction::Continue, so that all + is void, the default group's action is SetupResult::Continue, so that all tasks are started normally. Otherwise, when the start handler returns - TaskAction::StopWithDone or TaskAction::StopWithError, the tasks are not + SetupResult::StopWithDone or SetupResult::StopWithError, the tasks are not started (they are skipped) and the group itself reports success or failure, depending on the returned value, respectively. @@ -1414,15 +1523,15 @@ void TaskNode::invokeEndHandler(bool success) const Group root { onGroupSetup([] { qDebug() << "Root setup"; }), Group { - onGroupSetup([] { qDebug() << "Group 1 setup"; return TaskAction::Continue; }), + onGroupSetup([] { qDebug() << "Group 1 setup"; return SetupResult::Continue; }), ProcessTask(...) // Process 1 }, Group { - onGroupSetup([] { qDebug() << "Group 2 setup"; return TaskAction::StopWithDone; }), + onGroupSetup([] { qDebug() << "Group 2 setup"; return SetupResult::StopWithDone; }), ProcessTask(...) // Process 2 }, Group { - onGroupSetup([] { qDebug() << "Group 3 setup"; return TaskAction::StopWithError; }), + onGroupSetup([] { qDebug() << "Group 3 setup"; return SetupResult::StopWithError; }), ProcessTask(...) // Process 3 }, ProcessTask(...) // Process 4 @@ -1438,7 +1547,7 @@ void TaskNode::invokeEndHandler(bool success) \li Comment \row \li Root Group starts - \li Doesn't return TaskAction, so its tasks are executed. + \li Doesn't return SetupResult, so its tasks are executed. \row \li Group 1 starts \li Returns Continue, so its tasks are executed. @@ -1481,7 +1590,7 @@ void TaskNode::invokeEndHandler(bool success) execution of its tasks, respectively. The final value reported by the group depends on its \l {Workflow Policy}. The handlers can apply other necessary actions. The done and error handlers are defined inside the - onGroupDone and onGroupError elements of a group, respectively. They do not + onGroupDone() and onGroupError() elements of a group, respectively. They do not take arguments: \code @@ -1494,7 +1603,7 @@ void TaskNode::invokeEndHandler(bool success) \endcode The group done and error handlers are optional. If you add more than one - onGroupDone or onGroupError each to a group, an assert is triggered at + onGroupDone() or onGroupError() each to a group, an assert is triggered at runtime that includes an error message. \note Even if the group setup handler returns StopWithDone or StopWithError, @@ -1513,7 +1622,7 @@ void TaskNode::invokeEndHandler(bool success) The execution mode element in a Group specifies how the direct child tasks of the Group are started. The most common execution modes are \l sequential and \l parallel. It's also possible to specify the limit of tasks running - in parallel by using the parallelLimit function. + in parallel by using the parallelLimit() function. In all execution modes, a group starts tasks in the oder in which they appear. @@ -1540,7 +1649,7 @@ void TaskNode::invokeEndHandler(bool success) static QByteArray load(const QString &fileName) { ... } static void save(const QString &fileName, const QByteArray &array) { ... } - static GroupItem copyRecipe(const QString &source, const QString &destination) + static Group copyRecipe(const QString &source, const QString &destination) { struct CopyStorage { // [1] custom inter-task struct QByteArray content; // [2] custom inter-task data @@ -1549,28 +1658,28 @@ void TaskNode::invokeEndHandler(bool success) // [3] instance of custom inter-task struct manageable by task tree const TreeStorage storage; - const auto onLoaderSetup = [source](Async &async) { + const auto onLoaderSetup = [source](ConcurrentCall &async) { async.setConcurrentCallData(&load, source); }; // [4] runtime: task tree activates the instance from [7] before invoking handler - const auto onLoaderDone = [storage](const Async &async) { + const auto onLoaderDone = [storage](const ConcurrentCall &async) { storage->content = async.result(); // [5] loader stores the result in storage }; // [4] runtime: task tree activates the instance from [7] before invoking handler - const auto onSaverSetup = [storage, destination](Async &async) { + const auto onSaverSetup = [storage, destination](ConcurrentCall &async) { const QByteArray content = storage->content; // [6] saver takes data from storage async.setConcurrentCallData(&save, destination, content); }; - const auto onSaverDone = [](const Async &async) { + const auto onSaverDone = [](const ConcurrentCall &async) { qDebug() << "Save done successfully"; }; const Group root { // [7] runtime: task tree creates an instance of CopyStorage when root is entered Storage(storage), - AsyncTask(onLoaderSetup, onLoaderDone), - AsyncTask(onSaverSetup, onSaverDone) + ConcurrentCallTask(onLoaderSetup, onLoaderDone), + ConcurrentCallTask(onSaverSetup, onSaverDone) }; return root; } @@ -1650,7 +1759,7 @@ void TaskNode::invokeEndHandler(bool success) \code TreeStorage storage; - Group root = ...; // storage placed inside root's group and inside handlers + const Group root = ...; // storage placed inside root's group and inside handlers TaskTree taskTree(root); auto initStorage = [](CopyStorage *storage){ storage->content = "initial content"; @@ -1670,7 +1779,7 @@ void TaskNode::invokeEndHandler(bool success) \code TreeStorage storage; - Group root = ...; // storage placed inside root's group and inside handlers + const Group root = ...; // storage placed inside root's group and inside handlers TaskTree taskTree(root); auto collectStorage = [](CopyStorage *storage){ qDebug() << "final content" << storage->content; @@ -1745,24 +1854,67 @@ void TaskNode::invokeEndHandler(bool success) to finish (that is, safe non-blocking destructor of a running task). */ +/*! + Constructs an empty task tree. Use setRecipe() to pass a declarative description + on how the task tree should execute the tasks and how it should handle the finished tasks. + + Starting an empty task tree is no-op and the relevant warning message is issued. + + \sa setRecipe(), start() +*/ TaskTree::TaskTree() : d(new TaskTreePrivate(this)) -{ -} +{} +/*! + Constructs a task tree with a given \a recipe. After the task tree is started, + it executes the tasks contained inside the \a recipe and + handles finished tasks according to the passed description. + + \sa setRecipe(), start() +*/ TaskTree::TaskTree(const Group &recipe) : TaskTree() { setRecipe(recipe); } +/*! + Destroys the task tree. + + When the task tree is running while being destructed, it stops all the running tasks + immediately. In this case, no handlers are called, not even the groups' and + tasks' error handlers or onStorageDone() handlers. The task tree also doesn't emit any + signals from the destructor, not even errorOccurred() or progressValueChanged() signals. + This behavior may always be relied on. + It is completely safe to destruct the running task tree. + + It's a usual pattern to destruct the running task tree, even from the main thread. + It's guaranteed that the destruction will run quickly, without having to wait for + the currently running tasks to finish, provided that the used tasks implement + their destructors in a non-blocking way. + + \note Do not call the destructor directly from any of the running task's handlers + or task tree's signals. In these cases, use \l deleteLater() instead. + + \sa stop() +*/ TaskTree::~TaskTree() { QTC_ASSERT(!d->m_guard.isLocked(), qWarning("Deleting TaskTree instance directly from " - "one of its handlers will lead to crash!")); + "one of its handlers will lead to a crash!")); // TODO: delete storages explicitly here? delete d; } +/*! + Sets a given \a recipe for the task tree. After the task tree is started, + it executes the tasks contained inside the \a recipe and + handles finished tasks according to the passed description. + + \note When called for a running task tree, the call is ignored. + + \sa TaskTree(const Tasking::Group &recipe), start() +*/ void TaskTree::setRecipe(const Group &recipe) { QTC_ASSERT(!isRunning(), qWarning("The TaskTree is already running, ignoring..."); return); @@ -1772,6 +1924,32 @@ void TaskTree::setRecipe(const Group &recipe) d->m_root.reset(new TaskNode(d, recipe, nullptr)); } +/*! + Starts the task tree. + + Use setRecipe() or the constructor to set the declarative description according to which + the task tree will execute the contained tasks and handle finished tasks. + + When the task tree is empty, that is, constructed with a default constructor, + a call to \e start is no-op and the relevant warning message is issued. + + Otherwise, when the task tree is already running, a call to \e start is ignored and the + relevant warning message is issued. + + Otherwise, the task tree is started. + + The started task tree may finish synchronously, + for example when the main group's start handler returns SetupResult::StopWithError. + For this reason, the connections to the done and errorOccurred signals should be + established before calling start. Use isRunning() in order to detect whether + the task tree is still running after a call to start(). + + The task tree implementation relies on the running event loop for listening to the tasks' + done signals. Make sure you have a QEventLoop or QCoreApplication or one of its + subclasses running (or about to be run) when calling this method. + + \sa TaskTree(const Tasking::Group &recipe), setRecipe(), isRunning(), stop() +*/ void TaskTree::start() { QTC_ASSERT(!isRunning(), qWarning("The TaskTree is already running, ignoring..."); return); @@ -1780,6 +1958,65 @@ void TaskTree::start() d->start(); } +/*! + \fn void TaskTree::started() + + This signal is emitted when the task tree is started. The emission of this signal is + followed synchronously by the progressValueChanged() signal with an initial \c 0 value. + + \sa start(), done(), errorOccurred() +*/ + +/*! + \fn void TaskTree::done() + + This signal is emitted when the task tree finished with success. + The task tree neither calls any handler, nor emits any signal anymore after this signal + was emitted. + + Don't delete the task tree directly from this signal's handler. Use deleteLater() instead. + + \sa started(), errorOccurred() +*/ + +/*! + \fn void TaskTree::errorOccurred() + + This signal is emitted when the task tree finished with an error. + The task tree neither calls any handler, nor emits any signal anymore after this signal + was emitted. + + Don't delete the task tree directly from this signal's handler. Use deleteLater() instead. + + \sa started(), done() +*/ + +/*! + Stops the running task tree. + + Stops all the running tasks immediately. + All running tasks finish with an error, invoking their error handlers. + All running groups dispatch their handlers according to their workflow policies, + invoking one of their end handlers. The storages' onStorageDone() handlers are invoked, too. + The \l progressValueChanged signals are also being sent. + This behavior may always be relied on. + + The \l stop is executed synchronously, so that after a call to \e stop + all running tasks are finished and the tree is already stopped. + It's guaranteed that the stop will run quickly, without any blocking wait for + the currently running tasks to finish, provided the used tasks implement their destructors + in a non-blocking way. + + When the task tree is empty, that is, constructed with a default constructor, + a call to \e stop is no-op and the relevant warning message is issued. + + Otherwise, when the task tree wasn't started, a call to stop is ignored. + + \note Do not call this function directly from any of the running task's handlers + or task tree's signals. + + \sa ~TaskTree() +*/ void TaskTree::stop() { QTC_ASSERT(!d->m_guard.isLocked(), qWarning("The stop() is called from one of the" @@ -1787,11 +2024,26 @@ void TaskTree::stop() d->stop(); } +/*! + Returns \c true if the task tree is currently running; otherwise returns \c false. + + \sa start(), stop() +*/ bool TaskTree::isRunning() const { return d->m_root && d->m_root->isRunning(); } +/*! + Executes a local event loop with QEventLoop::ExcludeUserInputEvents and starts the task tree. + + Returns \c true if the task tree finished successfully; otherwise returns \c false. + + \note Avoid using this method from the main thread. Use asynchronous start() instead. + This method is to be used in non-main threads or in auto tests. + + \sa start() +*/ bool TaskTree::runBlocking() { QPromise dummy; @@ -1799,6 +2051,12 @@ bool TaskTree::runBlocking() return runBlocking(dummy.future()); } +/*! + \overload runBlocking() + + The passed \a future is used for listening to the cancel event. + When the task tree finishes with an error, this method cancels the passed \a future. +*/ bool TaskTree::runBlocking(const QFuture &future) { if (future.isCanceled()) @@ -1830,6 +2088,19 @@ bool TaskTree::runBlocking(const QFuture &future) return ok; } +/*! + Constructs a temporary task tree using the passed \a recipe and runs it blocking. + + The optionally provided \a timeout is used to stop the tree automatically after + \a timeout milliseconds have passed. + + Returns \c true if the task tree finished successfully; otherwise returns \c false. + + \note Avoid using this method from the main thread. Use asynchronous start() instead. + This method is to be used in non-main threads or in auto tests. + + \sa start() +*/ bool TaskTree::runBlocking(const Group &recipe, milliseconds timeout) { QPromise dummy; @@ -1837,6 +2108,12 @@ bool TaskTree::runBlocking(const Group &recipe, milliseconds timeout) return TaskTree::runBlocking(recipe, dummy.future(), timeout); } +/*! + \overload runBlocking(const Group &recipe, milliseconds timeout) + + The passed \a future is used for listening to the cancel event. + When the task tree finishes with an error, this method cancels the passed \a future. +*/ bool TaskTree::runBlocking(const Group &recipe, const QFuture &future, milliseconds timeout) { const Group root = timeout == milliseconds::max() ? recipe @@ -1845,16 +2122,145 @@ bool TaskTree::runBlocking(const Group &recipe, const QFuture &future, mil return taskTree.runBlocking(future); } +/*! + Returns the number of asynchronous tasks contained in the stored recipe. + + \note The returned number doesn't include Sync tasks. + \note Any task or group that was set up using withTimeout() increases the total number of + tasks by \c 1. + + \sa setRecipe(), progressMaximum() +*/ int TaskTree::taskCount() const { return d->m_root ? d->m_root->taskCount() : 0; } +/*! + \fn void TaskTree::progressValueChanged(int value) + + This signal is emitted when the running task tree finished, stopped, or skipped some tasks. + The \a value gives the current total number of finished, stopped or skipped tasks. + When the task tree is started, and after the started() signal was emitted, + this signal is emitted with an initial \a value of \c 0. + When the task tree is about to finish, and before the done() or errorOccurred() signal + is emitted, this signal is emitted with the final \a value of progressMaximum(). + + \sa progressValue(), progressMaximum() +*/ + +/*! + \fn int TaskTree::progressMaximum() const + + Returns the maximum progressValue(). + + \note Currently, it's the same as taskCount(). This might change in the future. + + \sa progressValue() +*/ + +/*! + Returns the current progress value, which is between the \c 0 and progressMaximum(). + + The returned number indicates how many tasks have been already finished, stopped, or skipped + while the task tree is running. + When the task tree is started, this number is set to \c 0. + When the task tree is finished, this number always equals progressMaximum(). + + \sa progressMaximum() +*/ int TaskTree::progressValue() const { return d->m_progressValue; } +/*! + \fn template void TaskTree::onStorageSetup(const TreeStorage &storage, StorageHandler &&handler) + + 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: + + \code + static void save(const QString &fileName, const QByteArray &array) { ... } + + TreeStorage storage; + + const auto onSaverSetup = [storage](ConcurrentCall &concurrent) { + concurrent.setConcurrentCallData(&save, "foo.txt", *storage); + }; + + const Group root { + Storage(storage), + ConcurrentCallTask(onSaverSetup) + }; + + TaskTree taskTree(root); + auto initStorage = [](QByteArray *storage){ + *storage = "initial content"; + }; + taskTree.onStorageSetup(storage, initStorage); + taskTree.start(); + \endcode + + When the running task tree enters a Group where the \a storage is placed in, + it creates a \c StorageStruct instance, ready to be used inside this group. + Just after the \c StorageStruct instance is created, and before any handler of this group + is called, the task tree invokes the passed \a handler. This enables setting up + initial content for the given storage dynamically. Later, when any group's handler is invoked, + the task tree activates the created and initialized storage, so that it's available inside + any group's handler. + + \sa onStorageDone() +*/ + +/*! + \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 + dynamically from the running task tree. + + The \c StorageHandler takes the pointer to the \c StorageStruct instance: + + \code + static QByteArray load(const QString &fileName) { ... } + + TreeStorage storage; + + const auto onLoaderSetup = [storage](ConcurrentCall &concurrent) { + concurrent.setConcurrentCallData(&load, "foo.txt"); + }; + const auto onLoaderDone = [storage](const ConcurrentCall &concurrent) { + *storage = concurrent.result(); + }; + + const Group root { + Storage(storage), + ConcurrentCallTask(onLoaderDone, onLoaderDone) + }; + + TaskTree taskTree(root); + auto collectStorage = [](QByteArray *storage){ + qDebug() << "final content" << *storage; + }; + taskTree.onStorageDone(storage, collectStorage); + taskTree.start(); + \endcode + + When the running task tree is about to leave a Group where the \a storage is placed in, + it destructs a \c StorageStruct instance. + Just before the \c StorageStruct instance is destructed, and after all possible handlers from + this group were called, the task tree invokes the passed \a handler. This enables reading + the final content of the given storage dynamically and processing it further outside of + the task tree. + + This handler is called also when the running tree is stopped. However, it's not called + when the running tree is destructed. + + \sa onStorageSetup() +*/ + void TaskTree::setupStorageHandler(const TreeStorageBase &storage, StorageVoidHandler setupHandler, StorageVoidHandler doneHandler) diff --git a/src/libs/solutions/tasking/tasktree.h b/src/libs/solutions/tasking/tasktree.h index 11cc2708dd4..47ad221728a 100644 --- a/src/libs/solutions/tasking/tasktree.h +++ b/src/libs/solutions/tasking/tasktree.h @@ -26,12 +26,14 @@ class TASKING_EXPORT TaskInterface : public QObject { Q_OBJECT -public: - TaskInterface() = default; - virtual void start() = 0; - signals: void done(bool success); + +private: + template friend class TaskAdapter; + friend class TaskNode; + TaskInterface() = default; + virtual void start() = 0; }; class TASKING_EXPORT TreeStorageBase @@ -115,13 +117,13 @@ enum class WorkflowPolicy { }; Q_ENUM_NS(WorkflowPolicy); -enum class TaskAction +enum class SetupResult { Continue, StopWithDone, StopWithError }; -Q_ENUM_NS(TaskAction); +Q_ENUM_NS(SetupResult); class TASKING_EXPORT GroupItem { @@ -129,11 +131,11 @@ public: // Internal, provided by QTC_DECLARE_CUSTOM_TASK using TaskCreateHandler = std::function; // Called prior to task start, just after createHandler - using TaskSetupHandler = std::function; + using TaskSetupHandler = std::function; // Called on task done / error using TaskEndHandler = std::function; // Called when group entered - using GroupSetupHandler = std::function; + using GroupSetupHandler = std::function; // Called when group done / error using GroupEndHandler = std::function; @@ -228,17 +230,17 @@ private: static GroupSetupHandler wrapGroupSetup(SetupHandler &&handler) { static constexpr bool isDynamic - = std::is_same_v>>; + = std::is_same_v>>; constexpr bool isVoid = std::is_same_v>>; static_assert(isDynamic || isVoid, "Group setup handler needs to take no arguments and has to return " - "void or TaskAction. The passed handler doesn't fulfill these requirements."); + "void or SetupResult. The passed handler doesn't fulfill these requirements."); return [=] { if constexpr (isDynamic) return std::invoke(handler); std::invoke(handler); - return TaskAction::Continue; + return SetupResult::Continue; }; }; }; @@ -290,22 +292,24 @@ private: static_assert(isBool || isVoid, "Sync element: The synchronous function has to return void or bool."); if constexpr (isBool) { - return {onGroupSetup([function] { return function() ? TaskAction::StopWithDone - : TaskAction::StopWithError; })}; + return {onGroupSetup([function] { return function() ? SetupResult::StopWithDone + : SetupResult::StopWithError; })}; } - return {onGroupSetup([function] { function(); return TaskAction::StopWithDone; })}; + return {onGroupSetup([function] { function(); return SetupResult::StopWithDone; })}; }; }; template class TaskAdapter : public TaskInterface { -public: +protected: using Type = Task; TaskAdapter() = default; Task *task() { return &m_task; } const Task *task() const { return &m_task; } + private: + template friend class CustomTask; Task m_task; }; @@ -344,19 +348,19 @@ public: private: template static GroupItem::TaskSetupHandler wrapSetup(SetupFunction &&function) { - static constexpr bool isDynamic = std::is_same_v, typename Adapter::Type &>>; constexpr bool isVoid = std::is_same_v, typename Adapter::Type &>>; static_assert(isDynamic || isVoid, "Task setup handler needs to take (Task &) as an argument and has to return " - "void or TaskAction. The passed handler doesn't fulfill these requirements."); + "void or SetupResult. The passed handler doesn't fulfill these requirements."); return [=](TaskInterface &taskInterface) { Adapter &adapter = static_cast(taskInterface); if constexpr (isDynamic) return std::invoke(function, *adapter.task()); std::invoke(function, *adapter.task()); - return TaskAction::Continue; + return SetupResult::Continue; }; }; diff --git a/src/libs/utils/aspects.cpp b/src/libs/utils/aspects.cpp index 5ba4922d668..f0868666c84 100644 --- a/src/libs/utils/aspects.cpp +++ b/src/libs/utils/aspects.cpp @@ -671,6 +671,7 @@ public: // 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) { @@ -985,6 +986,13 @@ void StringAspect::setAllowPathFromDevice(bool allowPathFromDevice) d->m_pathChooserDisplay->setAllowPathFromDevice(allowPathFromDevice); } +void StringAspect::setValidatePlaceHolder(bool validatePlaceHolder) +{ + d->m_validatePlaceHolder = validatePlaceHolder; + if (d->m_pathChooserDisplay) + d->m_pathChooserDisplay->lineEdit()->setValidatePlaceHolder(validatePlaceHolder); +} + /*! Sets \a elideMode as label elide mode. */ @@ -1131,6 +1139,7 @@ void StringAspect::addToLayout(LayoutItem &parent) 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 @@ -1169,6 +1178,7 @@ void StringAspect::addToLayout(LayoutItem &parent) 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); @@ -1464,8 +1474,13 @@ void BoolAspect::addToLayout(Layouting::LayoutItem &parent) 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: @@ -2346,16 +2361,12 @@ void IntegersAspect::setDefaultValue(const QList &value) A text display does not have a real value. */ -TextDisplay::TextDisplay(AspectContainer *container) - : BaseAspect(container), d(new Internal::TextDisplayPrivate) -{} - /*! Constructs a text display showing the \a message with an icon representing type \a type. */ -TextDisplay::TextDisplay(const QString &message, InfoLabel::InfoType type) - : d(new Internal::TextDisplayPrivate) +TextDisplay::TextDisplay(AspectContainer *container, const QString &message, InfoLabel::InfoType type) + : BaseAspect(container), d(new Internal::TextDisplayPrivate) { d->m_message = message; d->m_type = type; diff --git a/src/libs/utils/aspects.h b/src/libs/utils/aspects.h index b99c28c0bef..22dcc9bb0df 100644 --- a/src/libs/utils/aspects.h +++ b/src/libs/utils/aspects.h @@ -237,7 +237,7 @@ public: bool defaultValue() const; void setDefaultValue(bool val); - enum class LabelPlacement { AtCheckBox, InExtraLabel }; + enum class LabelPlacement { AtCheckBox, AtCheckBoxWithoutDummyLabel, InExtraLabel }; void setLabel(const QString &labelText, LabelPlacement labelPlacement = LabelPlacement::InExtraLabel); void setLabelPlacement(LabelPlacement labelPlacement); @@ -407,6 +407,7 @@ public: void setAutoApplyOnEditingFinished(bool applyOnEditingFinished); void setElideMode(Qt::TextElideMode elideMode); void setAllowPathFromDevice(bool allowPathFromDevice); + void setValidatePlaceHolder(bool validatePlaceHolder); void validateInput(); @@ -610,9 +611,9 @@ class QTCREATOR_UTILS_EXPORT TextDisplay : public BaseAspect Q_OBJECT public: - explicit TextDisplay(AspectContainer *container); - TextDisplay(const QString &message = {}, - InfoLabel::InfoType type = InfoLabel::None); + explicit TextDisplay(AspectContainer *container, + const QString &message = {}, + InfoLabel::InfoType type = InfoLabel::None); ~TextDisplay() override; void addToLayout(Layouting::LayoutItem &parent) override; diff --git a/src/libs/utils/commandline.cpp b/src/libs/utils/commandline.cpp index ba1cc4edc46..60b8d72eed2 100644 --- a/src/libs/utils/commandline.cpp +++ b/src/libs/utils/commandline.cpp @@ -514,6 +514,9 @@ QString ProcessArgs::quoteArgUnix(const QString &arg) QString ret(arg); if (hasSpecialCharsUnix(ret)) { + if (arg == "&&" || arg == "||" || arg == "&" || arg == ';') + return ret; + ret.replace(QLatin1Char('\''), QLatin1String("'\\''")); ret.prepend(QLatin1Char('\'')); ret.append(QLatin1Char('\'')); @@ -550,6 +553,9 @@ static QString quoteArgWin(const QString &arg) QString ret(arg); if (hasSpecialCharsWin(ret)) { + if (arg == "&&" || arg == "||" || arg == "&" || arg == ';') + return ret; + // Quotes are escaped and their preceding backslashes are doubled. // It's impossible to escape anything inside a quoted string on cmd // level, so the outer quoting must be "suspended". @@ -1438,16 +1444,15 @@ CommandLine CommandLine::fromUserInput(const QString &cmdline, MacroExpander *ex QString input = cmdline.trimmed(); - QStringList result = ProcessArgs::splitArgs(cmdline, HostOsInfo::hostOs()); + if (expander) + input = expander->expand(input); + + const QStringList result = ProcessArgs::splitArgs(input, HostOsInfo::hostOs()); if (result.isEmpty()) return {}; - auto cmd = CommandLine(FilePath::fromUserInput(result.value(0)), result.mid(1)); - if (expander) - cmd.m_arguments = expander->expand(cmd.m_arguments); - - return cmd; + return {FilePath::fromUserInput(result.value(0)), result.mid(1)}; } void CommandLine::addArg(const QString &arg) diff --git a/src/libs/utils/devicefileaccess.cpp b/src/libs/utils/devicefileaccess.cpp index bd139d9b168..4653b691374 100644 --- a/src/libs/utils/devicefileaccess.cpp +++ b/src/libs/utils/devicefileaccess.cpp @@ -117,6 +117,9 @@ bool DeviceFileAccess::ensureWritableDirectory(const FilePath &filePath) const { if (isWritableDirectory(filePath)) return true; + if (exists(filePath)) + return false; + return createDirectory(filePath); } @@ -518,6 +521,9 @@ bool DesktopDeviceFileAccess::ensureWritableDirectory(const FilePath &filePath) const QFileInfo fi(filePath.path()); if (fi.isDir() && fi.isWritable()) return true; + if (fi.exists()) + return false; + return QDir().mkpath(filePath.path()); } diff --git a/src/libs/utils/externalterminalprocessimpl.cpp b/src/libs/utils/externalterminalprocessimpl.cpp index efdbc9aca31..2a316a22831 100644 --- a/src/libs/utils/externalterminalprocessimpl.cpp +++ b/src/libs/utils/externalterminalprocessimpl.cpp @@ -2,6 +2,8 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "externalterminalprocessimpl.h" + +#include "algorithm.h" #include "process.h" #include "terminalcommand.h" #include "utilstr.h" @@ -19,43 +21,73 @@ ProcessStubCreator::ProcessStubCreator(TerminalInterface *interface) : m_interface(interface) {} +static const QLatin1String TerminalAppScript{R"( + tell application "Terminal" + activate + set newTab to do script "echo Preparing terminal..." + set win to (the id of window 1 where its tab 1 = newTab) as text + do script "%1 && exit" in newTab + repeat until ((count of processes of newTab) = 0) + delay 0.1 + end repeat + close window id win + end tell +)"}; + expected_str ProcessStubCreator::startStubProcess(const ProcessSetupData &setupData) { const TerminalCommand terminal = TerminalCommand::terminalEmulator(); - - if (HostOsInfo::isMacHost() && terminal.command == "Terminal.app") { - QTemporaryFile f; - f.setAutoRemove(false); - f.open(); - f.setPermissions(QFile::ExeUser | QFile::ReadUser | QFile::WriteUser); - f.write("#!/bin/sh\n"); - f.write(QString("cd %1\n").arg(setupData.m_workingDirectory.nativePath()).toUtf8()); - f.write("clear\n"); - f.write(QString("exec '%1' %2\n") - .arg(setupData.m_commandLine.executable().nativePath()) - .arg(setupData.m_commandLine.arguments()) - .toUtf8()); - f.close(); - - const QString path = f.fileName(); - const QString exe - = QString("tell app \"Terminal\" to do script \"'%1'; rm -f '%1'; exit\"").arg(path); - - Process process; - - process.setCommand({"osascript", {"-e", "tell app \"Terminal\" to activate", "-e", exe}}); - process.runBlocking(); - - if (process.exitCode() != 0) { - return make_unexpected( - Tr::tr("Failed to start terminal process: \"%1\"").arg(process.errorString())); - } - - return 0; - } - bool detached = setupData.m_terminalMode == TerminalMode::Detached; + if (HostOsInfo::isMacHost()) { + static const QMap terminalMap = { + {"Terminal.app", TerminalAppScript}, + }; + + if (terminalMap.contains(terminal.command.toString())) { + const QString env + = Utils::transform(setupData.m_environment.toStringList(), [](const QString &env) { + return CommandLine{"export", {env}}.toUserOutput(); + }).join('\n'); + const QString shScript = QString("cd '%1'\n%2\nclear\n'%3' %4\n") + .arg(setupData.m_workingDirectory.nativePath()) + .arg(env) + .arg(setupData.m_commandLine.executable().nativePath()) + .arg(setupData.m_commandLine.arguments()); + + Process *process = new Process(detached ? nullptr : this); + if (detached) + QObject::connect(process, &Process::done, process, &Process::deleteLater); + + QTemporaryFile *shFile = new QTemporaryFile(process); + QTC_ASSERT(shFile->open(), + return make_unexpected(Tr::tr("Failed to open temporary script file."))); + shFile->write(shScript.toUtf8()); + shFile->close(); + + FilePath::fromUserInput(shFile->fileName()) + .setPermissions(QFile::ExeUser | QFile::ExeGroup | QFile::ExeOther | QFile::ReadUser + | QFile::ReadGroup | QFile::ReadOther | QFile::WriteUser + | QFile::WriteGroup | QFile::WriteOther); + + const QString script + = terminalMap.value(terminal.command.toString()).arg(shFile->fileName()); + + process->setCommand({"osascript", {"-"}}); + process->setWriteData(script.toUtf8()); + process->start(); + + if (!process->waitForStarted()) { + return make_unexpected( + Tr::tr("Failed to start terminal process: \"%1\".").arg(process->errorString())); + } + + QObject::connect(process, &Process::done, m_interface, &TerminalInterface::onStubExited); + + return 0; + } + } + Process *process = new Process(detached ? nullptr : this); if (detached) QObject::connect(process, &Process::done, process, &Process::deleteLater); @@ -76,12 +108,16 @@ expected_str ProcessStubCreator::startStubProcess(const ProcessSetupData cmdLine.addCommandLineAsArgs(setupData.m_commandLine, CommandLine::Raw); process->setCommand(cmdLine); } + process->setEnvironment( + setupData.m_environment.appliedToEnvironment(Environment::systemEnvironment())); + + process->setEnvironment(setupData.m_environment); process->start(); process->waitForStarted(); if (process->error() != QProcess::UnknownError) { return make_unexpected( - Tr::tr("Failed to start terminal process: \"%1\"").arg(process->errorString())); + Tr::tr("Failed to start terminal process: \"%1\".").arg(process->errorString())); } qint64 pid = process->processId(); diff --git a/src/libs/utils/fancylineedit.cpp b/src/libs/utils/fancylineedit.cpp index 08b480c88cf..2bf50a70fa1 100644 --- a/src/libs/utils/fancylineedit.cpp +++ b/src/libs/utils/fancylineedit.cpp @@ -112,6 +112,7 @@ public: bool m_isFiltering = false; bool m_firstChange = true; bool m_toolTipSet = false; + bool m_validatePlaceHolder = false; QString m_lastFilterText; @@ -469,6 +470,11 @@ QString FancyLineEdit::errorMessage() const return d->m_errorMessage; } +void FancyLineEdit::setValidatePlaceHolder(bool on) +{ + d->m_validatePlaceHolder = on; +} + void FancyLineEdit::validate() { const QString t = text(); @@ -501,7 +507,8 @@ void FancyLineEdit::validate() p.setColor(QPalette::Active, QPalette::Text, newState == Invalid ? d->m_errorTextColor : d->m_okTextColor); p.setColor(QPalette::Active, QPalette::PlaceholderText, - validates ? d->m_placeholderTextColor : d->m_errorTextColor); + validates || !d->m_validatePlaceHolder + ? d->m_placeholderTextColor : d->m_errorTextColor); setPalette(p); if (validHasChanged) @@ -545,11 +552,11 @@ IconButton::IconButton(QWidget *parent) void IconButton::paintEvent(QPaintEvent *) { - QWindow *window = this->window()->windowHandle(); - const QPixmap iconPixmap = icon().pixmap(window, sizeHint(), + const qreal pixelRatio = window()->windowHandle()->devicePixelRatio(); + const QPixmap iconPixmap = icon().pixmap(sizeHint(), pixelRatio, isEnabled() ? QIcon::Normal : QIcon::Disabled); QStylePainter painter(this); - QRect pixmapRect(QPoint(), iconPixmap.size() / window->devicePixelRatio()); + QRect pixmapRect(QPoint(), iconPixmap.size() / pixelRatio); pixmapRect.moveCenter(rect().center()); if (m_autoHide) diff --git a/src/libs/utils/fancylineedit.h b/src/libs/utils/fancylineedit.h index 6ec0f6d33d4..eb54bc8d866 100644 --- a/src/libs/utils/fancylineedit.h +++ b/src/libs/utils/fancylineedit.h @@ -105,6 +105,8 @@ public: bool isValid() const; QString errorMessage() const; + void setValidatePlaceHolder(bool on); + void setValidationFunction(const ValidationFunction &fn); static ValidationFunction defaultValidationFunction(); void validate(); diff --git a/src/libs/utils/filestreamermanager.cpp b/src/libs/utils/filestreamermanager.cpp index 11d05faee57..33df7b264ea 100644 --- a/src/libs/utils/filestreamermanager.cpp +++ b/src/libs/utils/filestreamermanager.cpp @@ -132,7 +132,7 @@ FileStreamHandle FileStreamerManager::copy(const FilePath &source, const FilePat if (streamer->result() == StreamResult::FinishedWithSuccess) cont({}); else - cont(make_unexpected(Tr::tr("Failed copying file"))); + cont(make_unexpected(Tr::tr("Failed copying file."))); }; return execute(onSetup, onDone, context); } @@ -156,7 +156,7 @@ FileStreamHandle FileStreamerManager::read(const FilePath &source, QObject *cont if (streamer->result() == StreamResult::FinishedWithSuccess) cont(streamer->readData()); else - cont(make_unexpected(Tr::tr("Failed reading file"))); + cont(make_unexpected(Tr::tr("Failed reading file."))); }; return execute(onSetup, onDone, context); } @@ -182,7 +182,7 @@ FileStreamHandle FileStreamerManager::write(const FilePath &destination, const Q if (streamer->result() == StreamResult::FinishedWithSuccess) cont(0); // TODO: return write count? else - cont(make_unexpected(Tr::tr("Failed writing file"))); + cont(make_unexpected(Tr::tr("Failed writing file."))); }; return execute(onSetup, onDone, context); } diff --git a/src/libs/utils/json.cpp b/src/libs/utils/json.cpp index 70bbc90990a..5afd4a685b7 100644 --- a/src/libs/utils/json.cpp +++ b/src/libs/utils/json.cpp @@ -65,7 +65,7 @@ QString JsonValue::kindToString(JsonValue::Kind kind) JsonValue *JsonValue::build(const QVariant &variant, JsonMemoryPool *pool) { - switch (variant.type()) { + switch (variant.typeId()) { case QVariant::List: { auto newValue = new (pool) JsonArrayValue; diff --git a/src/libs/utils/layoutbuilder.cpp b/src/libs/utils/layoutbuilder.cpp index 94aa80f02cc..881dc613587 100644 --- a/src/libs/utils/layoutbuilder.cpp +++ b/src/libs/utils/layoutbuilder.cpp @@ -229,6 +229,7 @@ struct ResultItem int space = -1; int stretch = -1; int span = 1; + bool empty = false; }; struct Slice @@ -287,6 +288,8 @@ static void addItemToBoxLayout(QBoxLayout *layout, const ResultItem &item) layout->addSpacing(item.space); } else if (!item.text.isEmpty()) { layout->addWidget(createLabel(item.text)); + } else if (item.empty) { + // Nothing to do, but no reason to warn, either. } else { QTC_CHECK(false); } @@ -346,9 +349,9 @@ void Slice::flush() formLayout->addRow(f0.widget, f1.widget); } else { if (f1.layout) - formLayout->addRow(f0.text, f1.layout); + formLayout->addRow(createLabel(f0.text), f1.layout); else if (f1.widget) - formLayout->addRow(f0.text, f1.widget); + formLayout->addRow(createLabel(f0.text), f1.widget); } } else { QTC_CHECK(false); @@ -654,8 +657,8 @@ LayoutItem empty() LayoutItem item; item.onAdd = [](LayoutBuilder &builder) { ResultItem ri; - ri.span = 1; - builder.stack.last().pendingItems.append(ResultItem()); + ri.empty = true; + builder.stack.last().pendingItems.append(ri); }; return item; } @@ -962,6 +965,9 @@ void createItem(LayoutItem *item, const std::function &t) void createItem(LayoutItem *item, QWidget *t) { + if (auto l = qobject_cast(t)) + l->setTextInteractionFlags(l->textInteractionFlags() | Qt::TextSelectableByMouse); + item->onAdd = [t](LayoutBuilder &builder) { doAddWidget(builder, t); }; } diff --git a/src/libs/utils/mimetypes2/mimetype.cpp b/src/libs/utils/mimetypes2/mimetype.cpp index eadfb9e53c7..926ca6d950f 100644 --- a/src/libs/utils/mimetypes2/mimetype.cpp +++ b/src/libs/utils/mimetypes2/mimetype.cpp @@ -516,9 +516,6 @@ void MimeType::setPreferredSuffix(const QString &suffix) d->globPatterns.prepend(QLatin1String("*.") + suffix); } -} // namespace Utils - -#ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug debug, const Utils::MimeType &mime) { QDebugStateSaver saver(debug); @@ -529,6 +526,8 @@ QDebug operator<<(QDebug debug, const Utils::MimeType &mime) } return debug; } -#endif + +} // namespace Utils + #include "moc_mimetype.cpp" diff --git a/src/libs/utils/mimetypes2/mimetype.h b/src/libs/utils/mimetypes2/mimetype.h index 52587ff3aa4..ec28140d6ae 100644 --- a/src/libs/utils/mimetypes2/mimetype.h +++ b/src/libs/utils/mimetypes2/mimetype.h @@ -11,13 +11,15 @@ #include #include +QT_BEGIN_NAMESPACE +class QDebug; +QT_END_NAMESPACE + namespace Utils { class MimeTypePrivate; class MimeType; -QTCREATOR_UTILS_EXPORT size_t qHash(const MimeType &key, size_t seed = 0) noexcept; - class QTCREATOR_UTILS_EXPORT MimeType { Q_GADGET @@ -89,18 +91,13 @@ protected: friend class MimeBinaryProvider; friend class MimeTypePrivate; friend QTCREATOR_UTILS_EXPORT size_t qHash(const MimeType &key, size_t seed) noexcept; + friend QTCREATOR_UTILS_EXPORT QDebug operator<<(QDebug debug, const MimeType &mime); QExplicitlySharedDataPointer d; }; - } // namespace Utils QT_BEGIN_NAMESPACE -#ifndef QT_NO_DEBUG_STREAM -class QDebug; -QTCREATOR_UTILS_EXPORT QDebug operator<<(QDebug debug, const Utils::MimeType &mime); -#endif - Q_DECLARE_SHARED(Utils::MimeType) QT_END_NAMESPACE diff --git a/src/libs/utils/process.cpp b/src/libs/utils/process.cpp index 970b0b4a296..43d57ded891 100644 --- a/src/libs/utils/process.cpp +++ b/src/libs/utils/process.cpp @@ -216,6 +216,13 @@ void DefaultImpl::start() return; if (!ensureProgramExists(program)) return; + + if (m_setup.m_runAsRoot && !HostOsInfo::isWindowsHost()) { + arguments.prepend(program); + arguments.prepend("-A"); + program = "sudo"; + } + s_start.measureAndRun(&DefaultImpl::doDefaultStart, this, program, arguments); } @@ -338,6 +345,23 @@ public: void doDefaultStart(const QString &program, const QStringList &arguments) final { + QString executable = program; + FilePath path = FilePath::fromUserInput(executable); + if (!path.isAbsolutePath()) { + path = path.searchInPath(); + if (path.isEmpty()) { + const ProcessResultData result + = {0, + QProcess::CrashExit, + QProcess::FailedToStart, + Tr::tr("The program \"%1\" could not be found.").arg(program)}; + emit done(result); + return; + } + + executable = path.nativePath(); + } + QTC_CHECK(m_setup.m_ptyData); m_setup.m_ptyData->setResizeHandler([this](const QSize &size) { if (m_ptyProcess) @@ -358,15 +382,15 @@ public: penv = Environment::systemEnvironment().toProcessEnvironment(); const QStringList senv = penv.toStringList(); - bool startResult - = m_ptyProcess->startProcess(program, - HostOsInfo::isWindowsHost() - ? QStringList{m_setup.m_nativeArguments} << arguments - : arguments, - m_setup.m_workingDirectory.nativePath(), - senv, - m_setup.m_ptyData->size().width(), - m_setup.m_ptyData->size().height()); + bool startResult = m_ptyProcess->startProcess(executable, + HostOsInfo::isWindowsHost() + ? QStringList{m_setup.m_nativeArguments} + << arguments + : arguments, + m_setup.m_workingDirectory.nativePath(), + senv, + m_setup.m_ptyData->size().width(), + m_setup.m_ptyData->size().height()); if (!startResult) { const ProcessResultData result = {-1, @@ -766,15 +790,6 @@ public: m_blockingInterface->setParent(this); } - CommandLine fullCommandLine() const - { - if (!m_setup.m_runAsRoot || HostOsInfo::isWindowsHost()) - return m_setup.m_commandLine; - CommandLine rootCommand("sudo", {"-A"}); - rootCommand.addCommandLineAsArgs(m_setup.m_commandLine); - return rootCommand; - } - Process *q; std::unique_ptr m_blockingInterface; std::unique_ptr m_process; @@ -1217,7 +1232,6 @@ void Process::start() d->setProcessInterface(processImpl); d->m_state = QProcess::Starting; d->m_process->m_setup = d->m_setup; - d->m_process->m_setup.m_commandLine = d->fullCommandLine(); d->emitGuardedSignal(&Process::starting); d->m_process->start(); } diff --git a/src/libs/utils/process_ctrlc_stub.qbs b/src/libs/utils/process_ctrlc_stub.qbs index c92215df8e3..13359b7c9d7 100644 --- a/src/libs/utils/process_ctrlc_stub.qbs +++ b/src/libs/utils/process_ctrlc_stub.qbs @@ -4,7 +4,7 @@ QtcTool { name: "qtcreator_ctrlc_stub" consoleApplication: true condition: qbs.targetOS.contains("windows") - + useQt: false files: [ "process_ctrlc_stub.cpp" ] diff --git a/src/libs/utils/styleanimator.h b/src/libs/utils/styleanimator.h index 08c4755cd1a..22ab0745b30 100644 --- a/src/libs/utils/styleanimator.h +++ b/src/libs/utils/styleanimator.h @@ -10,8 +10,10 @@ #include #include +QT_BEGIN_NAMESPACE class QPainter; class QStyleOption; +QT_END_NAMESPACE namespace Utils { /* diff --git a/src/libs/utils/terminalinterface.cpp b/src/libs/utils/terminalinterface.cpp index 6767c16c61f..fc69451c9c0 100644 --- a/src/libs/utils/terminalinterface.cpp +++ b/src/libs/utils/terminalinterface.cpp @@ -183,6 +183,8 @@ void TerminalInterface::onStubReadyRead() emitFinished(out.mid(5).toInt(), QProcess::NormalExit); } else if (out.startsWith("crash ")) { emitFinished(out.mid(6).toInt(), QProcess::CrashExit); + } else if (out.startsWith("ack ")) { + qCDebug(terminalInterfaceLog) << "Received ack from stub: " << out; } else { emitError(QProcess::UnknownError, msgUnexpectedOutput(out)); break; @@ -382,9 +384,21 @@ void TerminalInterface::start() QTC_ASSERT(d->stubCreator, return); - ProcessSetupData stubSetupData = m_setup; + ProcessSetupData stubSetupData; stubSetupData.m_commandLine = cmd; + stubSetupData.m_extraData[TERMINAL_SHELL_NAME] + = m_setup.m_extraData.value(TERMINAL_SHELL_NAME, + m_setup.m_commandLine.executable().fileName()); + + if (m_setup.m_runAsRoot && !HostOsInfo::isWindowsHost()) { + CommandLine rootCommand("sudo", {}); + rootCommand.addCommandLineAsArgs(cmd); + stubSetupData.m_commandLine = rootCommand; + } else { + stubSetupData.m_commandLine = cmd; + } + QMetaObject::invokeMethod( d->stubCreator, [stubSetupData, this] { d->stubCreator->startStubProcess(stubSetupData); }, diff --git a/src/libs/utils/terminalinterface.h b/src/libs/utils/terminalinterface.h index a1960e7b966..7c1d402e7e6 100644 --- a/src/libs/utils/terminalinterface.h +++ b/src/libs/utils/terminalinterface.h @@ -11,6 +11,8 @@ namespace Utils { class TerminalInterfacePrivate; +const char TERMINAL_SHELL_NAME[] = "Terminal.ShellName"; + class StubCreator : public QObject { public: diff --git a/src/libs/utils/transientscroll.h b/src/libs/utils/transientscroll.h index 2042bbf0fb0..c48880caa78 100644 --- a/src/libs/utils/transientscroll.h +++ b/src/libs/utils/transientscroll.h @@ -7,9 +7,12 @@ #include +QT_BEGIN_NAMESPACE class QAbstractScrollArea; +QT_END_NAMESPACE namespace Utils { + class ScrollAreaPrivate; class ScrollBarPrivate; diff --git a/src/plugins/android/androidavdmanager.cpp b/src/plugins/android/androidavdmanager.cpp index 67403703025..e5568ef541f 100644 --- a/src/plugins/android/androidavdmanager.cpp +++ b/src/plugins/android/androidavdmanager.cpp @@ -91,7 +91,7 @@ static CreateAvdInfo createAvdCommand(const AndroidConfig &config, const CreateA proc.setCommand(avdManager); proc.start(); if (!proc.waitForStarted()) { - result.error = Tr::tr("Could not start process \"%1\"").arg(avdManager.toUserOutput()); + result.error = Tr::tr("Could not start process \"%1\".").arg(avdManager.toUserOutput()); return result; } QTC_CHECK(proc.isRunning()); diff --git a/src/plugins/android/androidconfigurations.cpp b/src/plugins/android/androidconfigurations.cpp index 9c444f6e756..82410682c60 100644 --- a/src/plugins/android/androidconfigurations.cpp +++ b/src/plugins/android/androidconfigurations.cpp @@ -191,7 +191,7 @@ void AndroidConfig::load(const QSettings &settings) { // user settings QVariant emulatorArgs = settings.value(EmulatorArgsKey, QString("-netdelay none -netspeed full")); - if (emulatorArgs.type() == QVariant::StringList) // Changed in 8.0 from QStringList to QString. + if (emulatorArgs.typeId() == QVariant::StringList) // Changed in 8.0 from QStringList to QString. emulatorArgs = ProcessArgs::joinArgs(emulatorArgs.toStringList()); m_emulatorArgs = emulatorArgs.toString(); m_sdkLocation = FilePath::fromUserInput(settings.value(SDKLocationKey).toString()).cleanPath(); diff --git a/src/plugins/android/androidmanifestdocument.cpp b/src/plugins/android/androidmanifestdocument.cpp index 294379cddeb..8f759d717e6 100644 --- a/src/plugins/android/androidmanifestdocument.cpp +++ b/src/plugins/android/androidmanifestdocument.cpp @@ -25,10 +25,12 @@ AndroidManifestDocument::AndroidManifestDocument(AndroidManifestEditorWidget *ed this, &Core::IDocument::changed); } -bool AndroidManifestDocument::save(QString *errorString, const Utils::FilePath &filePath, bool autoSave) +bool AndroidManifestDocument::saveImpl(QString *errorString, + const Utils::FilePath &filePath, + bool autoSave) { m_editorWidget->preSave(); - bool result = TextDocument::save(errorString, filePath, autoSave); + bool result = TextDocument::saveImpl(errorString, filePath, autoSave); m_editorWidget->postSave(); return result; } diff --git a/src/plugins/android/androidmanifestdocument.h b/src/plugins/android/androidmanifestdocument.h index 9903c348f62..2e5735da858 100644 --- a/src/plugins/android/androidmanifestdocument.h +++ b/src/plugins/android/androidmanifestdocument.h @@ -14,12 +14,15 @@ class AndroidManifestDocument : public TextEditor::TextDocument { public: explicit AndroidManifestDocument(AndroidManifestEditorWidget *editorWidget); - bool save(QString *errorString, const Utils::FilePath &filePath, - bool autoSave = false) override; bool isModified() const override; bool isSaveAsAllowed() const override; +protected: + bool saveImpl(QString *errorString, + const Utils::FilePath &filePath, + bool autoSave = false) override; + private: AndroidManifestEditorWidget *m_editorWidget; }; diff --git a/src/plugins/android/androidqmlpreviewworker.cpp b/src/plugins/android/androidqmlpreviewworker.cpp index d296dfe677e..af238e00d0d 100644 --- a/src/plugins/android/androidqmlpreviewworker.cpp +++ b/src/plugins/android/androidqmlpreviewworker.cpp @@ -389,7 +389,7 @@ FilePath AndroidQmlPreviewWorker::createQmlrcFile(const FilePath &workFolder, rccProcess.setCommand({rccBinary, args}); rccProcess.start(); if (!rccProcess.waitForStarted()) { - appendMessage(Tr::tr("Could not create file for %1 \"%2\""). + appendMessage(Tr::tr("Could not create file for %1 \"%2\"."). arg(apkInfo()->name, rccProcess.commandLine().toUserOutput()), StdErrFormat); qrcPath.removeFile(); @@ -400,7 +400,7 @@ FilePath AndroidQmlPreviewWorker::createQmlrcFile(const FilePath &workFolder, if (!rccProcess.readDataFromProcess(&stdOut, &stdErr)) { rccProcess.stop(); rccProcess.waitForFinished(); - appendMessage(Tr::tr("A timeout occurred running \"%1\""). + appendMessage(Tr::tr("A timeout occurred running \"%1\"."). arg(rccProcess.commandLine().toUserOutput()), StdErrFormat); qrcPath.removeFile(); return {}; @@ -412,7 +412,7 @@ FilePath AndroidQmlPreviewWorker::createQmlrcFile(const FilePath &workFolder, appendMessage(QString::fromLocal8Bit(stdErr), StdErrFormat); if (rccProcess.exitStatus() != QProcess::NormalExit) { - appendMessage(Tr::tr("Crash while creating file for %1 \"%2\""). + appendMessage(Tr::tr("Crash while creating file for %1 \"%2\"."). arg(apkInfo()->name, rccProcess.commandLine().toUserOutput()), StdErrFormat); qrcPath.removeFile(); diff --git a/src/plugins/android/androidrunnerworker.cpp b/src/plugins/android/androidrunnerworker.cpp index b9a09629fb2..2f52d694f3c 100644 --- a/src/plugins/android/androidrunnerworker.cpp +++ b/src/plugins/android/androidrunnerworker.cpp @@ -264,13 +264,13 @@ AndroidRunnerWorker::AndroidRunnerWorker(RunWorker *runner, const QString &packa m_extraAppParams = runControl->commandLine().arguments(); if (auto aspect = runControl->aspect(Constants::ANDROID_AM_START_ARGS)) { - QTC_CHECK(aspect->value.type() == QVariant::String); + QTC_CHECK(aspect->value.typeId() == QVariant::String); const QString startArgs = aspect->value.toString(); m_amStartExtraArgs = ProcessArgs::splitArgs(startArgs, OsTypeOtherUnix); } if (auto aspect = runControl->aspect(Constants::ANDROID_PRESTARTSHELLCMDLIST)) { - QTC_CHECK(aspect->value.type() == QVariant::String); + QTC_CHECK(aspect->value.typeId() == QVariant::String); const QStringList commands = aspect->value.toString().split('\n', Qt::SkipEmptyParts); for (const QString &shellCmd : commands) m_beforeStartAdbCommands.append(QString("shell %1").arg(shellCmd)); @@ -280,7 +280,7 @@ AndroidRunnerWorker::AndroidRunnerWorker(RunWorker *runner, const QString &packa m_beforeStartAdbCommands.append(QString("shell %1").arg(shellCmd)); if (auto aspect = runControl->aspect(Constants::ANDROID_POSTFINISHSHELLCMDLIST)) { - QTC_CHECK(aspect->value.type() == QVariant::String); + QTC_CHECK(aspect->value.typeId() == QVariant::String); const QStringList commands = aspect->value.toString().split('\n', Qt::SkipEmptyParts); for (const QString &shellCmd : commands) m_afterFinishAdbCommands.append(QString("shell %1").arg(shellCmd)); diff --git a/src/plugins/autotest/autotestplugin.cpp b/src/plugins/autotest/autotestplugin.cpp index d6bfc6f9a88..91c058cec77 100644 --- a/src/plugins/autotest/autotestplugin.cpp +++ b/src/plugins/autotest/autotestplugin.cpp @@ -195,7 +195,7 @@ void AutotestPluginPrivate::initializeMenuEntries() QAction *action = new QAction(Tr::tr("Run &All Tests"), this); action->setIcon(Utils::Icons::RUN_SMALL.icon()); - action->setToolTip(Tr::tr("Run all tests")); + action->setToolTip(Tr::tr("Run All Tests")); Command *command = ActionManager::registerAction(action, Constants::ACTION_RUN_ALL_ID); command->setDefaultKeySequence( QKeySequence(useMacShortcuts ? Tr::tr("Ctrl+Meta+T, Ctrl+Meta+A") : Tr::tr("Alt+Shift+T,Alt+A"))); @@ -206,7 +206,7 @@ void AutotestPluginPrivate::initializeMenuEntries() action = new QAction(Tr::tr("Run All Tests Without Deployment"), this); action->setIcon(Utils::Icons::RUN_SMALL.icon()); - action->setToolTip(Tr::tr("Run all tests without deployment")); + action->setToolTip(Tr::tr("Run All Tests Without Deployment")); command = ActionManager::registerAction(action, Constants::ACTION_RUN_ALL_NODEPLOY_ID); command->setDefaultKeySequence( QKeySequence(useMacShortcuts ? Tr::tr("Ctrl+Meta+T, Ctrl+Meta+E") : Tr::tr("Alt+Shift+T,Alt+E"))); @@ -217,7 +217,7 @@ void AutotestPluginPrivate::initializeMenuEntries() action = new QAction(Tr::tr("&Run Selected Tests"), this); action->setIcon(Utils::Icons::RUN_SELECTED.icon()); - action->setToolTip(Tr::tr("Run selected tests")); + action->setToolTip(Tr::tr("Run Selected Tests")); command = ActionManager::registerAction(action, Constants::ACTION_RUN_SELECTED_ID); command->setDefaultKeySequence( QKeySequence(useMacShortcuts ? Tr::tr("Ctrl+Meta+T, Ctrl+Meta+R") : Tr::tr("Alt+Shift+T,Alt+R"))); @@ -228,7 +228,7 @@ void AutotestPluginPrivate::initializeMenuEntries() action = new QAction(Tr::tr("&Run Selected Tests Without Deployment"), this); action->setIcon(Utils::Icons::RUN_SELECTED.icon()); - action->setToolTip(Tr::tr("Run selected tests")); + action->setToolTip(Tr::tr("Run Selected Tests Without Deployment")); command = ActionManager::registerAction(action, Constants::ACTION_RUN_SELECTED_NODEPLOY_ID); command->setDefaultKeySequence( QKeySequence(useMacShortcuts ? Tr::tr("Ctrl+Meta+T, Ctrl+Meta+W") : Tr::tr("Alt+Shift+T,Alt+W"))); @@ -239,7 +239,7 @@ void AutotestPluginPrivate::initializeMenuEntries() action = new QAction(Tr::tr("Run &Failed Tests"), this); action->setIcon(Icons::RUN_FAILED.icon()); - action->setToolTip(Tr::tr("Run failed tests")); + action->setToolTip(Tr::tr("Run Failed Tests")); command = ActionManager::registerAction(action, Constants::ACTION_RUN_FAILED_ID); command->setDefaultKeySequence( useMacShortcuts ? Tr::tr("Ctrl+Meta+T, Ctrl+Meta+F") : Tr::tr("Alt+Shift+T,Alt+F")); @@ -249,7 +249,7 @@ void AutotestPluginPrivate::initializeMenuEntries() action = new QAction(Tr::tr("Run Tests for &Current File"), this); action->setIcon(Utils::Icons::RUN_FILE.icon()); - action->setToolTip(Tr::tr("Run tests for current file")); + action->setToolTip(Tr::tr("Run Tests for Current File")); command = ActionManager::registerAction(action, Constants::ACTION_RUN_FILE_ID); command->setDefaultKeySequence( QKeySequence(useMacShortcuts ? Tr::tr("Ctrl+Meta+T, Ctrl+Meta+C") : Tr::tr("Alt+Shift+T,Alt+C"))); diff --git a/src/plugins/autotest/boost/boosttestconfiguration.cpp b/src/plugins/autotest/boost/boosttestconfiguration.cpp index e581cff50ee..768161a5c0c 100644 --- a/src/plugins/autotest/boost/boosttestconfiguration.cpp +++ b/src/plugins/autotest/boost/boosttestconfiguration.cpp @@ -102,8 +102,13 @@ QStringList BoostTestConfiguration::argumentsForTestRunner(QStringList *omitted) arguments << "--detect_memory_leaks=0"; // TODO improve the test case gathering and arguments building to avoid too long command lines - for (const QString &test : testCases()) - arguments << "-t" << test; + if (isDebugRunMode()) { // debugger has its own quoting + for (const QString &test : testCases()) + arguments << "-t" << test; + } else { + for (const QString &test : testCases()) + arguments << "-t" << "\"" + test + "\""; + } if (TestSettings::instance()->processArgs()) { arguments << filterInterfering(runnable().command.arguments().split( diff --git a/src/plugins/autotest/boost/boosttestoutputreader.cpp b/src/plugins/autotest/boost/boosttestoutputreader.cpp index 96b0dffd61b..c206b3b498a 100644 --- a/src/plugins/autotest/boost/boosttestoutputreader.cpp +++ b/src/plugins/autotest/boost/boosttestoutputreader.cpp @@ -31,12 +31,6 @@ BoostTestOutputReader::BoostTestOutputReader(Process *testApplication, , m_logLevel(log) , m_reportLevel(report) { - if (!testApplication) - return; - - connect(testApplication, &Process::done, this, [this, testApplication] { - onDone(testApplication->exitCode()); - }); } // content of "error:..." / "info:..." / ... messages @@ -147,7 +141,7 @@ void BoostTestOutputReader::handleMessageMatch(const QRegularExpressionMatch &ma if (m_currentTest != match.captured(11) && m_currentTest.isEmpty()) m_currentTest = match.captured(11); m_result = ResultType::TestEnd; - m_description = Tr::tr("Test execution took %1").arg(match.captured(12)); + m_description = Tr::tr("Test execution took %1.").arg(match.captured(12)); } else if (type == "suite") { if (!m_currentSuite.isEmpty()) { int index = m_currentSuite.lastIndexOf('/'); @@ -163,7 +157,7 @@ void BoostTestOutputReader::handleMessageMatch(const QRegularExpressionMatch &ma } m_currentTest.clear(); m_result = ResultType::TestEnd; - m_description = Tr::tr("Test suite execution took %1").arg(match.captured(12)); + m_description = Tr::tr("Test suite execution took %1.").arg(match.captured(12)); } } else if (content.startsWith("Test case ")) { m_currentTest = match.captured(4); @@ -247,7 +241,7 @@ void BoostTestOutputReader::processOutputLine(const QByteArray &outputLine) } else { QTC_CHECK(m_currentModule == match.captured(3)); BoostTestResult result(id(), m_currentModule, m_projectFile); - result.setDescription(Tr::tr("Test module execution took %1").arg(match.captured(4))); + result.setDescription(Tr::tr("Test module execution took %1.").arg(match.captured(4))); result.setResult(ResultType::TestEnd); reportResult(result); @@ -394,17 +388,17 @@ void BoostTestOutputReader::onDone(int exitCode) if (m_logLevel == LogLevel::Nothing && m_reportLevel == ReportLevel::No) { switch (exitCode) { case 0: - reportNoOutputFinish(Tr::tr("Running tests exited with %1").arg("boost::exit_success."), + reportNoOutputFinish(Tr::tr("Running tests exited with %1.").arg("boost::exit_success"), ResultType::Pass); break; case 200: reportNoOutputFinish( - Tr::tr("Running tests exited with %1").arg("boost::exit_test_exception."), + Tr::tr("Running tests exited with %1.").arg("boost::exit_test_exception"), ResultType::MessageFatal); break; case 201: - reportNoOutputFinish(Tr::tr("Running tests exited with %1") - .arg("boost::exit_test_failure."), ResultType::Fail); + reportNoOutputFinish(Tr::tr("Running tests exited with %1.") + .arg("boost::exit_test_failure"), ResultType::Fail); break; } } else if (exitCode != 0 && exitCode != 201 && !m_description.isEmpty()) { diff --git a/src/plugins/autotest/boost/boosttestoutputreader.h b/src/plugins/autotest/boost/boosttestoutputreader.h index 648b55547f0..15564cc91f0 100644 --- a/src/plugins/autotest/boost/boosttestoutputreader.h +++ b/src/plugins/autotest/boost/boosttestoutputreader.h @@ -23,7 +23,7 @@ protected: TestResult createDefaultResult() const override; private: - void onDone(int exitCode); + void onDone(int exitCode) override; void sendCompleteInformation(); void handleMessageMatch(const QRegularExpressionMatch &match); void reportNoOutputFinish(const QString &description, ResultType type); diff --git a/src/plugins/autotest/catch/catchoutputreader.cpp b/src/plugins/autotest/catch/catchoutputreader.cpp index 7a3aa277d4b..6c730c30455 100644 --- a/src/plugins/autotest/catch/catchoutputreader.cpp +++ b/src/plugins/autotest/catch/catchoutputreader.cpp @@ -240,17 +240,17 @@ void CatchOutputReader::sendResult(const ResultType result) catchResult.setResult(result); if (result == ResultType::TestStart && m_testCaseInfo.size() > 0) { - catchResult.setDescription(Tr::tr("Executing %1 \"%2\"") + catchResult.setDescription(Tr::tr("Executing %1 \"%2\"...") .arg(testOutputNodeToString().toLower(), catchResult.description())); } else if (result == ResultType::Pass || result == ResultType::UnexpectedPass) { if (result == ResultType::UnexpectedPass) ++m_xpassCount; if (m_currentExpression.isEmpty()) { - catchResult.setDescription(Tr::tr("%1 \"%2\" passed") + catchResult.setDescription(Tr::tr("%1 \"%2\" passed.") .arg(testOutputNodeToString(), catchResult.description())); } else { - catchResult.setDescription(Tr::tr("Expression passed") + catchResult.setDescription(Tr::tr("Expression passed.") .append('\n').append(m_currentExpression)); } m_reportedSectionResult = true; @@ -262,7 +262,7 @@ void CatchOutputReader::sendResult(const ResultType result) m_reportedSectionResult = true; m_reportedResult = true; } else if (result == ResultType::TestEnd) { - catchResult.setDescription(Tr::tr("Finished executing %1 \"%2\"") + catchResult.setDescription(Tr::tr("Finished executing %1 \"%2\".") .arg(testOutputNodeToString().toLower(), catchResult.description())); } else if (result == ResultType::Benchmark || result == ResultType::MessageFatal) { catchResult.setDescription(m_currentExpression); diff --git a/src/plugins/autotest/ctest/ctestoutputreader.cpp b/src/plugins/autotest/ctest/ctestoutputreader.cpp index 10f48d9d46c..f53dd873b00 100644 --- a/src/plugins/autotest/ctest/ctestoutputreader.cpp +++ b/src/plugins/autotest/ctest/ctestoutputreader.cpp @@ -90,7 +90,7 @@ void CTestOutputReader::processOutputLine(const QByteArray &outputLine) m_project = match.captured(1); TestResult testResult = createDefaultResult(); testResult.setResult(ResultType::TestStart); - testResult.setDescription(Tr::tr("Running tests for %1").arg(m_project)); + testResult.setDescription(Tr::tr("Running tests for \"%1\".").arg(m_project)); reportResult(testResult); } else if (ExactMatch match = testCase1.match(line)) { int current = match.captured("current").toInt(); diff --git a/src/plugins/autotest/gtest/gtestoutputreader.cpp b/src/plugins/autotest/gtest/gtestoutputreader.cpp index 1e980c06d00..b24291caeb5 100644 --- a/src/plugins/autotest/gtest/gtestoutputreader.cpp +++ b/src/plugins/autotest/gtest/gtestoutputreader.cpp @@ -23,18 +23,6 @@ GTestOutputReader::GTestOutputReader(Process *testApplication, : TestOutputReader(testApplication, buildDirectory) , m_projectFile(projectFile) { - if (testApplication) { - connect(testApplication, &Process::done, this, [this, testApplication] { - const int exitCode = testApplication->exitCode(); - if (exitCode == 1 && !m_description.isEmpty()) { - createAndReportResult(Tr::tr("Running tests failed.\n %1\nExecutable: %2") - .arg(m_description).arg(id()), ResultType::MessageFatal); - } - // on Windows abort() will result in normal termination, but exit code will be set to 3 - if (HostOsInfo::isWindowsHost() && exitCode == 3) - reportCrash(); - }); - } } void GTestOutputReader::processOutputLine(const QByteArray &outputLine) @@ -81,7 +69,7 @@ void GTestOutputReader::processOutputLine(const QByteArray &outputLine) if (ExactMatch match = testEnds.match(line)) { TestResult testResult = createDefaultResult(); testResult.setResult(ResultType::TestEnd); - testResult.setDescription(Tr::tr("Test execution took %1").arg(match.captured(2))); + testResult.setDescription(Tr::tr("Test execution took %1.").arg(match.captured(2))); reportResult(testResult); m_currentTestSuite.clear(); m_currentTestCase.clear(); @@ -182,6 +170,17 @@ TestResult GTestOutputReader::createDefaultResult() const return result; } +void GTestOutputReader::onDone(int exitCode) +{ + if (exitCode == 1 && !m_description.isEmpty()) { + createAndReportResult(Tr::tr("Running tests failed.\n %1\nExecutable: %2") + .arg(m_description).arg(id()), ResultType::MessageFatal); + } + // on Windows abort() will result in normal termination, but exit code will be set to 3 + if (HostOsInfo::isWindowsHost() && exitCode == 3) + reportCrash(); +} + void GTestOutputReader::setCurrentTestCase(const QString &testCase) { m_currentTestCase = testCase; diff --git a/src/plugins/autotest/gtest/gtestoutputreader.h b/src/plugins/autotest/gtest/gtestoutputreader.h index a63c5668295..e098f3870a5 100644 --- a/src/plugins/autotest/gtest/gtestoutputreader.h +++ b/src/plugins/autotest/gtest/gtestoutputreader.h @@ -19,6 +19,7 @@ protected: TestResult createDefaultResult() const override; private: + void onDone(int exitCode) override; void setCurrentTestCase(const QString &testCase); void setCurrentTestSuite(const QString &testSuite); void handleDescriptionAndReportResult(const TestResult &testResult); diff --git a/src/plugins/autotest/testconfiguration.cpp b/src/plugins/autotest/testconfiguration.cpp index c095e9e0f6a..5f225db1972 100644 --- a/src/plugins/autotest/testconfiguration.cpp +++ b/src/plugins/autotest/testconfiguration.cpp @@ -57,9 +57,10 @@ FilePath ITestConfiguration::executableFilePath() const if (!hasExecutable()) return {}; - const Environment env = m_runnable.environment.hasChanges() - ? m_runnable.environment : Environment::systemEnvironment(); - return env.searchInPath(m_runnable.command.executable().path()); + const Environment env = m_runnable.environment.appliedToEnvironment( + m_runnable.command.executable().deviceEnvironment()); + + return m_runnable.command.executable().searchInDirectories(env.path()); } Environment ITestConfiguration::filteredEnvironment(const Environment &original) const diff --git a/src/plugins/autotest/testoutputreader.h b/src/plugins/autotest/testoutputreader.h index e87c463735a..247d036831a 100644 --- a/src/plugins/autotest/testoutputreader.h +++ b/src/plugins/autotest/testoutputreader.h @@ -28,6 +28,8 @@ public: void setId(const QString &id) { m_id = id; } QString id() const { return m_id; } + virtual void onDone(int exitCode) { Q_UNUSED(exitCode) } + void resetCommandlineColor(); signals: void newResult(const TestResult &result); diff --git a/src/plugins/autotest/testrunner.cpp b/src/plugins/autotest/testrunner.cpp index 23912f15d98..d355317b5eb 100644 --- a/src/plugins/autotest/testrunner.cpp +++ b/src/plugins/autotest/testrunner.cpp @@ -355,13 +355,13 @@ void TestRunner::runTestsHelper() const auto onSetup = [this, config] { if (!config->project()) - return TaskAction::StopWithDone; + return SetupResult::StopWithDone; if (config->testExecutable().isEmpty()) { reportResult(ResultType::MessageFatal, Tr::tr("Executable path is empty. (%1)").arg(config->displayName())); - return TaskAction::StopWithDone; + return SetupResult::StopWithDone; } - return TaskAction::Continue; + return SetupResult::Continue; }; const auto onProcessSetup = [this, config, storage](Process &process) { TestStorage *testStorage = storage.activeStorage(); @@ -419,6 +419,9 @@ void TestRunner::runTestsHelper() + processInformation(&process) + rcInfo(config)); } + if (testStorage->m_outputReader) + testStorage->m_outputReader->onDone(process.exitCode()); + if (process.exitStatus() == QProcess::CrashExit) { if (testStorage->m_outputReader) testStorage->m_outputReader->reportCrash(); @@ -455,7 +458,7 @@ void TestRunner::runTestsHelper() connect(m_taskTree.get(), &TaskTree::errorOccurred, this, &TestRunner::onFinished); auto progress = new TaskProgress(m_taskTree.get()); - progress->setDisplayName(tr("Running Tests")); + progress->setDisplayName(Tr::tr("Running Tests")); progress->setAutoStopOnCancel(false); progress->setHalfLifeTimePerTask(10000); // 10 seconds connect(progress, &TaskProgress::canceled, this, [this, progress] { diff --git a/src/plugins/axivion/axivionoutputpane.cpp b/src/plugins/axivion/axivionoutputpane.cpp index ae3aed35488..f36df8d0f17 100644 --- a/src/plugins/axivion/axivionoutputpane.cpp +++ b/src/plugins/axivion/axivionoutputpane.cpp @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -28,7 +29,8 @@ public: private: QLabel *m_project = nullptr; QLabel *m_loc = nullptr; - QFormLayout *m_formLayout = nullptr; + QLabel *m_timestamp = nullptr; + QGridLayout *m_gridLayout = nullptr; }; DashboardWidget::DashboardWidget(QWidget *parent) @@ -40,59 +42,99 @@ DashboardWidget::DashboardWidget(QWidget *parent) m_project = new QLabel(this); projectLayout->addRow(Tr::tr("Project:"), m_project); m_loc = new QLabel(this); - projectLayout->addRow(Tr::tr("Lines of Code:"), m_loc); + projectLayout->addRow(Tr::tr("Lines of code:"), m_loc); + m_timestamp = new QLabel(this); + projectLayout->addRow(Tr::tr("Analysis timestamp:"), m_timestamp); layout->addLayout(projectLayout); - m_formLayout = new QFormLayout; - layout->addLayout(m_formLayout); + layout->addSpacing(10); + auto row = new QHBoxLayout; + m_gridLayout = new QGridLayout; + row->addLayout(m_gridLayout); + row->addStretch(1); + layout->addLayout(row); + layout->addStretch(1); setWidget(widget); setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); setWidgetResizable(true); } +static QPixmap trendIcon(int added, int removed) +{ + static const QPixmap unchanged = Utils::Icons::NEXT.pixmap(); + static const QPixmap increased = Utils::Icon( + { {":/utils/images/arrowup.png", Utils::Theme::IconsErrorColor} }).pixmap(); + static const QPixmap decreased = Utils::Icon( + { {":/utils/images/arrowdown.png", Utils::Theme::IconsRunColor} }).pixmap(); + if (added == removed) + return unchanged; + return added < removed ? decreased : increased; +} + void DashboardWidget::updateUi() { const ProjectInfo &info = AxivionPlugin::projectInfo(); m_project->setText(info.name); m_loc->setText({}); - while (m_formLayout->rowCount()) - m_formLayout->removeRow(0); + m_timestamp->setText({}); + QLayoutItem *child; + while ((child = m_gridLayout->takeAt(0)) != nullptr) { + delete child->widget(); + delete child; + } if (info.versions.isEmpty()) 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") + : Tr::tr("unknown")); - const QString tmpl("%1 %2 +%3 / -%4"); - auto apply = [&tmpl](int t, int a, int r){ - QChar tr = (a == r ? '=' : (a < r ? '^' : 'v')); - return tmpl.arg(t, 10, 10, QLatin1Char(' ')).arg(tr).arg(a, 5, 10, QLatin1Char(' ')) - .arg(r, 5, 10, QLatin1Char(' ')); - }; const QList &issueKinds = info.issueKinds; auto toolTip = [issueKinds](const QString &prefix){ for (const IssueKind &kind : issueKinds) { if (kind.prefix == prefix) return kind.nicePlural; } - return QString(); + return prefix; }; - int allTotal = 0, allAdded = 0, allRemoved = 0; + auto addValuesWidgets = [this, &toolTip](const IssueCount &issueCount, int row){ + const QString currentToolTip = toolTip(issueCount.issueKind); + QLabel *label = new QLabel(issueCount.issueKind, this); + label->setToolTip(currentToolTip); + m_gridLayout->addWidget(label, row, 0); + label = new QLabel(QString::number(issueCount.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->setToolTip(currentToolTip); + m_gridLayout->addWidget(label, row, 2); + label = new QLabel('+' + QString::number(issueCount.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->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; - const QString txt = apply(issueCount.total, issueCount.added, issueCount.removed); - const QString currentToolTip = toolTip(issueCount.issueKind); - QLabel *label = new QLabel(issueCount.issueKind, this); - label->setToolTip(currentToolTip); - QLabel *values = new QLabel(txt, this); - values->setToolTip(currentToolTip); - m_formLayout->addRow(label, values); + addValuesWidgets(issueCount, row); + ++row; } - QLabel *label = new QLabel(apply(allTotal, allAdded, allRemoved), this); - m_formLayout->addRow(Tr::tr("Total:"), label); + const IssueCount total{{}, Tr::tr("Total:"), allTotal, allAdded, allRemoved}; + addValuesWidgets(total, row); } AxivionOutputPane::AxivionOutputPane(QObject *parent) diff --git a/src/plugins/beautifier/abstractsettings.cpp b/src/plugins/beautifier/abstractsettings.cpp index 1b4ff3aa10e..362a2481538 100644 --- a/src/plugins/beautifier/abstractsettings.cpp +++ b/src/plugins/beautifier/abstractsettings.cpp @@ -88,9 +88,10 @@ AbstractSettings::AbstractSettings(const QString &name, const QString &ending) setSettingsGroups(Utils::Constants::BEAUTIFIER_SETTINGS_GROUP, name); command.setSettingsKey("command"); - command.setExpectedKind(Utils::PathChooser::ExistingCommand); + command.setExpectedKind(PathChooser::ExistingCommand); command.setCommandVersionArguments({"--version"}); command.setPromptDialogTitle(BeautifierPlugin::msgCommandPromptDialogTitle("Clang Format")); + command.setValidatePlaceHolder(true); supportedMimeTypes.setDisplayStyle(StringAspect::LineEditDisplay); supportedMimeTypes.setSettingsKey("supportedMime"); diff --git a/src/plugins/beautifier/artisticstyle/artisticstylesettings.cpp b/src/plugins/beautifier/artisticstyle/artisticstylesettings.cpp index 415d4ab20d6..3117269f0fa 100644 --- a/src/plugins/beautifier/artisticstyle/artisticstylesettings.cpp +++ b/src/plugins/beautifier/artisticstyle/artisticstylesettings.cpp @@ -77,10 +77,10 @@ void ArtisticStyleSettings::createDocumentationFile() const if (process.result() != ProcessResult::FinishedWithSuccess) return; + if (!documentationFilePath.exists()) + documentationFilePath.parentDir().ensureWritableDir(); + 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; diff --git a/src/plugins/beautifier/uncrustify/uncrustify.cpp b/src/plugins/beautifier/uncrustify/uncrustify.cpp index 1439dc315f2..7079d4665ba 100644 --- a/src/plugins/beautifier/uncrustify/uncrustify.cpp +++ b/src/plugins/beautifier/uncrustify/uncrustify.cpp @@ -33,6 +33,7 @@ #include using namespace TextEditor; +using namespace Utils; namespace Beautifier::Internal { @@ -72,7 +73,7 @@ void Uncrustify::updateActions(Core::IEditor *editor) void Uncrustify::formatFile() { - const QString cfgFileName = configurationFile(); + const FilePath cfgFileName = configurationFile(); if (cfgFileName.isEmpty()) { BeautifierPlugin::showError(BeautifierPlugin::msgCannotGetConfigurationFile( Tr::tr(Constants::UNCRUSTIFY_DISPLAY_NAME))); @@ -83,7 +84,7 @@ void Uncrustify::formatFile() void Uncrustify::formatSelectedText() { - const QString cfgFileName = configurationFile(); + const FilePath cfgFileName = configurationFile(); if (cfgFileName.isEmpty()) { BeautifierPlugin::showError(BeautifierPlugin::msgCannotGetConfigurationFile( Tr::tr(Constants::UNCRUSTIFY_DISPLAY_NAME))); @@ -112,42 +113,41 @@ void Uncrustify::formatSelectedText() } } -QString Uncrustify::configurationFile() const +FilePath Uncrustify::configurationFile() const { if (m_settings.useCustomStyle()) - return m_settings.styleFileName(m_settings.customStyle()); + return FilePath::fromUserInput(m_settings.styleFileName(m_settings.customStyle())); if (m_settings.useOtherFiles()) { - if (const ProjectExplorer::Project *project - = ProjectExplorer::ProjectTree::currentProject()) { - const Utils::FilePaths files = project->files( - [](const ProjectExplorer::Node *n) { return n->filePath().endsWith("cfg"); }); - for (const Utils::FilePath &file : files) { - const QFileInfo fi = file.toFileInfo(); - if (fi.isReadable() && fi.fileName() == "uncrustify.cfg") - return file.toString(); - } + using namespace ProjectExplorer; + if (const Project *project = ProjectTree::currentProject()) { + const FilePaths files = project->files([](const Node *n) { + const FilePath fp = n->filePath(); + return fp.fileName() == "uncrustify.cfg" && fp.isReadableFile(); + }); + if (!files.isEmpty()) + return files.first(); } } if (m_settings.useSpecificConfigFile()) { - const Utils::FilePath file = m_settings.specificConfigFile(); + const FilePath file = m_settings.specificConfigFile(); if (file.exists()) - return file.toString(); - } - - if (m_settings.useHomeFile()) { - const QString file = QDir::home().filePath("uncrustify.cfg"); - if (QFile::exists(file)) return file; } - return QString(); + if (m_settings.useHomeFile()) { + const FilePath file = FileUtils::homePath() / "uncrustify.cfg"; + if (file.exists()) + return file; + } + + return {}; } Command Uncrustify::command() const { - const QString cfgFile = configurationFile(); + const FilePath cfgFile = configurationFile(); return cfgFile.isEmpty() ? Command() : command(cfgFile, false); } @@ -156,7 +156,7 @@ bool Uncrustify::isApplicable(const Core::IDocument *document) const return m_settings.isApplicable(document); } -Command Uncrustify::command(const QString &cfgFile, bool fragment) const +Command Uncrustify::command(const FilePath &cfgFile, bool fragment) const { Command command; command.setExecutable(m_settings.command()); @@ -173,7 +173,7 @@ Command Uncrustify::command(const QString &cfgFile, bool fragment) const if (fragment) command.addOption("--frag"); command.addOption("-c"); - command.addOption(cfgFile); + command.addOption(cfgFile.path()); return command; } diff --git a/src/plugins/beautifier/uncrustify/uncrustify.h b/src/plugins/beautifier/uncrustify/uncrustify.h index 685a29c25a1..774a400017e 100644 --- a/src/plugins/beautifier/uncrustify/uncrustify.h +++ b/src/plugins/beautifier/uncrustify/uncrustify.h @@ -22,8 +22,8 @@ public: private: void formatFile(); void formatSelectedText(); - QString configurationFile() const; - TextEditor::Command command(const QString &cfgFile, bool fragment = false) const; + Utils::FilePath configurationFile() const; + TextEditor::Command command(const Utils::FilePath &cfgFile, bool fragment = false) const; QAction *m_formatFile = nullptr; QAction *m_formatRange = nullptr; diff --git a/src/plugins/bineditor/bineditorplugin.cpp b/src/plugins/bineditor/bineditorplugin.cpp index 0d66bc10ffc..5133d57c9dc 100644 --- a/src/plugins/bineditor/bineditorplugin.cpp +++ b/src/plugins/bineditor/bineditorplugin.cpp @@ -213,17 +213,6 @@ public: return type == TypeRemoved ? BehaviorSilent : IDocument::reloadBehavior(state, type); } - bool save(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); - return true; - } - return false; - } - OpenResult open(QString *errorString, const FilePath &filePath, const FilePath &realFilePath) override { @@ -316,6 +305,18 @@ public: return success; } +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); + return true; + } + return false; + } + private: BinEditorWidget *m_widget; }; diff --git a/src/plugins/boot2qt/qdbstopapplicationstep.cpp b/src/plugins/boot2qt/qdbstopapplicationstep.cpp index 5fef60268f9..1949aac9580 100644 --- a/src/plugins/boot2qt/qdbstopapplicationstep.cpp +++ b/src/plugins/boot2qt/qdbstopapplicationstep.cpp @@ -43,7 +43,7 @@ Group QdbStopApplicationStep::deployRecipe() const auto device = DeviceKitAspect::device(target()->kit()); if (!device) { addErrorMessage(Tr::tr("No device to stop the application on.")); - return TaskAction::StopWithError; + return SetupResult::StopWithError; } QTC_CHECK(device); process.setCommand({device->filePath(Constants::AppcontrollerFilepath), {"--stop"}}); @@ -52,7 +52,7 @@ Group QdbStopApplicationStep::deployRecipe() connect(proc, &Process::readyReadStandardOutput, this, [this, proc] { handleStdOutData(proc->readAllStandardOutput()); }); - return TaskAction::Continue; + return SetupResult::Continue; }; const auto doneHandler = [this](const Process &) { addProgressMessage(Tr::tr("Stopped the running application.")); diff --git a/src/plugins/clangcodemodel/clangdclient.cpp b/src/plugins/clangcodemodel/clangdclient.cpp index 87a059c15cd..24e1c2d18f4 100644 --- a/src/plugins/clangcodemodel/clangdclient.cpp +++ b/src/plugins/clangcodemodel/clangdclient.cpp @@ -710,11 +710,11 @@ class ClangdDiagnosticManager : public LanguageClient::DiagnosticManager }); } - TextMark *createTextMark(const Utils::FilePath &filePath, + TextMark *createTextMark(TextDocument *doc, const Diagnostic &diagnostic, bool isProjectFile) const override { - return new ClangdTextMark(filePath, diagnostic, isProjectFile, getClient()); + return new ClangdTextMark(doc, diagnostic, isProjectFile, getClient()); } }; diff --git a/src/plugins/clangcodemodel/clangdsemantichighlighting.cpp b/src/plugins/clangcodemodel/clangdsemantichighlighting.cpp index f93d8302bd3..378e0f9346f 100644 --- a/src/plugins/clangcodemodel/clangdsemantichighlighting.cpp +++ b/src/plugins/clangcodemodel/clangdsemantichighlighting.cpp @@ -326,8 +326,10 @@ void doSemanticHighlighting( styles.mainStyle = C_PARAMETER; } else if (token.type == "macro") { styles.mainStyle = C_MACRO; - } else if (token.type == "type" || token.type == "concept") { + } else if (token.type == "type") { styles.mainStyle = C_TYPE; + } else if (token.type == "concept") { + styles.mainStyle = C_CONCEPT; } else if (token.type == "modifier") { styles.mainStyle = C_KEYWORD; } else if (token.type == "label") { diff --git a/src/plugins/clangcodemodel/clangtextmark.cpp b/src/plugins/clangcodemodel/clangtextmark.cpp index 570e6a2e956..1a886c248ed 100644 --- a/src/plugins/clangcodemodel/clangtextmark.cpp +++ b/src/plugins/clangcodemodel/clangtextmark.cpp @@ -263,16 +263,16 @@ Task createTask(const ClangDiagnostic &diagnostic) } // anonymous namespace -ClangdTextMark::ClangdTextMark(const FilePath &filePath, +ClangdTextMark::ClangdTextMark(TextEditor::TextDocument *doc, const Diagnostic &diagnostic, bool isProjectFile, ClangdClient *client) - : TextEditor::TextMark(filePath, + : TextEditor::TextMark(doc, int(diagnostic.range().start().line() + 1), {client->name(), client->id()}) , m_lspDiagnostic(diagnostic) , m_diagnostic( - convertDiagnostic(ClangdDiagnostic(diagnostic), filePath, client->hostPathMapper())) + convertDiagnostic(ClangdDiagnostic(diagnostic), doc->filePath(), client->hostPathMapper())) , m_client(client) { setSettingsPage(CppEditor::Constants::CPP_CLANGD_SETTINGS_ID); diff --git a/src/plugins/clangcodemodel/clangtextmark.h b/src/plugins/clangcodemodel/clangtextmark.h index 3f254789607..de7dc7857cb 100644 --- a/src/plugins/clangcodemodel/clangtextmark.h +++ b/src/plugins/clangcodemodel/clangtextmark.h @@ -23,7 +23,7 @@ class ClangdClient; class ClangdTextMark : public TextEditor::TextMark { public: - ClangdTextMark(const ::Utils::FilePath &filePath, + ClangdTextMark(TextEditor::TextDocument *doc, const LanguageServerProtocol::Diagnostic &diagnostic, bool isProjectFile, ClangdClient *client); diff --git a/src/plugins/clangcodemodel/test/clangdtests.cpp b/src/plugins/clangcodemodel/test/clangdtests.cpp index a102f1d3527..5a52a207853 100644 --- a/src/plugins/clangcodemodel/test/clangdtests.cpp +++ b/src/plugins/clangcodemodel/test/clangdtests.cpp @@ -1302,8 +1302,8 @@ void ClangdTestHighlighting::test_data() QTest::newRow("fake operator method call") << 1050 << 8 << 1050 << 22 << QList{C_FUNCTION} << 0; QTest::newRow("concept definition") << 1053 << 30 << 1053 << 42 - << QList{C_TYPE, C_DECLARATION} << 0; - QTest::newRow("concept use") << 1054 << 29 << 1054 << 41 << QList{C_TYPE} << 0; + << QList{C_CONCEPT, C_DECLARATION} << 0; + QTest::newRow("concept use") << 1054 << 29 << 1054 << 41 << QList{C_CONCEPT} << 0; QTest::newRow("label declaration") << 242 << 1 << 242 << 11 << QList{C_LABEL, C_DECLARATION} << 0; QTest::newRow("label use") << 244 << 10 << 244 << 20 << QList{C_LABEL} << 0; diff --git a/src/plugins/clangformat/clangformatbaseindenter.cpp b/src/plugins/clangformat/clangformatbaseindenter.cpp index 5138e03510f..4f572726826 100644 --- a/src/plugins/clangformat/clangformatbaseindenter.cpp +++ b/src/plugins/clangformat/clangformatbaseindenter.cpp @@ -7,15 +7,16 @@ #include +#include + #include #include #include #include +#include #include -#include - #include #include #include @@ -24,6 +25,8 @@ #include #include +#include + namespace ClangFormat { Internal::LlvmFileSystemAdapter llvmFileSystemAdapter = {}; @@ -760,7 +763,7 @@ clang::format::FormatStyle overrideStyle(const Utils::FilePath &fileName) Utils::FilePath filePath = filePathToCurrentSettings(preferences); if (!filePath.exists()) - return qtcStyle(); + return currentQtStyle(preferences); clang::format::FormatStyle currentSettingsStyle; currentSettingsStyle.Language = clang::format::FormatStyle::LK_Cpp; @@ -769,7 +772,7 @@ clang::format::FormatStyle overrideStyle(const Utils::FilePath &fileName) .toStdString(), ¤tSettingsStyle); QTC_ASSERT(error.value() == static_cast(clang::format::ParseError::Success), - return qtcStyle()); + return currentQtStyle(preferences)); return currentSettingsStyle; } diff --git a/src/plugins/clangformat/clangformatconfigwidget.cpp b/src/plugins/clangformat/clangformatconfigwidget.cpp index e2f9272e146..7c5b8ac3369 100644 --- a/src/plugins/clangformat/clangformatconfigwidget.cpp +++ b/src/plugins/clangformat/clangformatconfigwidget.cpp @@ -80,7 +80,7 @@ ClangFormatConfigWidget::ClangFormatConfigWidget(TextEditor::ICodeStylePreferenc : CppCodeStyleWidget(parent), d(new Private) { d->project = project; - d->config = std::make_unique(filePathToCurrentSettings(codeStyle->currentPreferences())); + d->config = std::make_unique(codeStyle->currentPreferences()); d->fallbackConfig = new QLabel(Tr::tr("Clang-Format Style")); d->checksScrollArea = new QScrollArea(); @@ -136,7 +136,7 @@ void ClangFormatConfigWidget::slotCodeStyleChanged( { if (!codeStyle) return; - d->config.reset(new ClangFormatFile(filePathToCurrentSettings(codeStyle))); + d->config.reset(new ClangFormatFile(codeStyle)); d->config->setIsReadOnly(codeStyle->isReadOnly()); d->style = d->config->style(); @@ -207,7 +207,7 @@ void ClangFormatConfigWidget::createStyleFileIfNeeded(bool isGlobal) if (configFile.exists()) return; - QDir().mkpath(path.toString()); + path.ensureWritableDir(); if (!isGlobal) { FilePath possibleProjectConfig = d->project->rootProjectDirectory() / Constants::SETTINGS_FILE_NAME; @@ -218,11 +218,8 @@ void ClangFormatConfigWidget::createStyleFileIfNeeded(bool isGlobal) } } - std::fstream newStyleFile(configFile.toString().toStdString(), std::fstream::out); - if (newStyleFile.is_open()) { - newStyleFile << clang::format::configurationAsText(constructStyle()); - newStyleFile.close(); - } + const std::string config = clang::format::configurationAsText(constructStyle()); + configFile.writeFileContents(QByteArray::fromStdString(config)); } void ClangFormatConfigWidget::showOrHideWidgets() diff --git a/src/plugins/clangformat/clangformatfile.cpp b/src/plugins/clangformat/clangformatfile.cpp index fc1659b976c..16209c30943 100644 --- a/src/plugins/clangformat/clangformatfile.cpp +++ b/src/plugins/clangformat/clangformatfile.cpp @@ -4,17 +4,23 @@ #include "clangformatfile.h" #include "clangformatsettings.h" #include "clangformatutils.h" + +#include #include + #include + +#include #include + #include #include using namespace ClangFormat; -ClangFormatFile::ClangFormatFile(Utils::FilePath filePath) - : m_filePath(filePath) +ClangFormatFile::ClangFormatFile(const TextEditor::ICodeStylePreferences *preferences) + : m_filePath(filePathToCurrentSettings(preferences)) { if (!m_filePath.exists()) { // create file and folder @@ -23,7 +29,7 @@ ClangFormatFile::ClangFormatFile(Utils::FilePath filePath) if (newStyleFile.is_open()) { newStyleFile.close(); } - resetStyleToQtC(); + resetStyleToQtC(preferences); return; } @@ -33,7 +39,7 @@ ClangFormatFile::ClangFormatFile(Utils::FilePath filePath) .toStdString(), &m_style); if (error.value() != static_cast(clang::format::ParseError::Success)) { - resetStyleToQtC(); + resetStyleToQtC(preferences); } } @@ -62,9 +68,9 @@ void ClangFormatFile::setIsReadOnly(bool isReadOnly) m_isReadOnly = isReadOnly; } -void ClangFormatFile::resetStyleToQtC() +void ClangFormatFile::resetStyleToQtC(const TextEditor::ICodeStylePreferences *preferences) { - m_style = qtcStyle(); + m_style = currentQtStyle(preferences); saveStyleToFile(m_style, m_filePath); } @@ -178,48 +184,7 @@ CppEditor::CppCodeStyleSettings ClangFormatFile::toCppCodeStyleSettings( void ClangFormatFile::fromCppCodeStyleSettings(const CppEditor::CppCodeStyleSettings &settings) { - using namespace clang::format; - if (settings.indentAccessSpecifiers) - m_style.AccessModifierOffset = 0; - else - m_style.AccessModifierOffset = -1 * m_style.IndentWidth; - - if (settings.indentNamespaceBody || settings.indentNamespaceBraces) - m_style.NamespaceIndentation = FormatStyle::NamespaceIndentationKind::NI_All; - else - m_style.NamespaceIndentation = FormatStyle::NamespaceIndentationKind::NI_None; - - if (settings.indentClassBraces || settings.indentEnumBraces || settings.indentBlockBraces - || settings.indentFunctionBraces) - m_style.BreakBeforeBraces = FormatStyle::BS_Whitesmiths; - else - m_style.BreakBeforeBraces = FormatStyle::BS_Custom; - - - m_style.IndentCaseLabels = settings.indentSwitchLabels; -#if LLVM_VERSION_MAJOR >= 11 - m_style.IndentCaseBlocks = settings.indentBlocksRelativeToSwitchLabels; -#endif - - if (settings.extraPaddingForConditionsIfConfusingAlign) - m_style.BreakBeforeBinaryOperators = FormatStyle::BOS_All; - else if (settings.alignAssignments) - m_style.BreakBeforeBinaryOperators = FormatStyle::BOS_NonAssignment; - else - m_style.BreakBeforeBinaryOperators = FormatStyle::BOS_None; - - m_style.DerivePointerAlignment = settings.bindStarToIdentifier || settings.bindStarToTypeName - || settings.bindStarToLeftSpecifier - || settings.bindStarToRightSpecifier; - - if ((settings.bindStarToIdentifier || settings.bindStarToRightSpecifier) - && ClangFormatSettings::instance().mode() == ClangFormatSettings::Mode::Formatting) - m_style.PointerAlignment = FormatStyle::PAS_Right; - - if ((settings.bindStarToTypeName || settings.bindStarToLeftSpecifier) - && ClangFormatSettings::instance().mode() == ClangFormatSettings::Mode::Formatting) - m_style.PointerAlignment = FormatStyle::PAS_Left; - + ::fromCppCodeStyleSettings(m_style, settings); saveNewFormat(); } @@ -258,22 +223,6 @@ TextEditor::TabSettings ClangFormatFile::toTabSettings(ProjectExplorer::Project void ClangFormatFile::fromTabSettings(const TextEditor::TabSettings &settings) { - using namespace clang::format; - - m_style.IndentWidth = settings.m_indentSize; - m_style.TabWidth = settings.m_tabSize; - - switch (settings.m_tabPolicy) { - case TextEditor::TabSettings::TabPolicy::MixedTabPolicy: - m_style.UseTab = FormatStyle::UT_ForContinuationAndIndentation; - break; - case TextEditor::TabSettings::TabPolicy::SpacesOnlyTabPolicy: - m_style.UseTab = FormatStyle::UT_Never; - break; - case TextEditor::TabSettings::TabPolicy::TabsOnlyTabPolicy: - m_style.UseTab = FormatStyle::UT_Always; - break; - } - + ::fromTabSettings(m_style, settings); saveNewFormat(); } diff --git a/src/plugins/clangformat/clangformatfile.h b/src/plugins/clangformat/clangformatfile.h index dcd0e0d6c10..ad32952b0f0 100644 --- a/src/plugins/clangformat/clangformatfile.h +++ b/src/plugins/clangformat/clangformatfile.h @@ -9,18 +9,21 @@ namespace CppEditor { class CppCodeStyleSettings; } namespace ProjectExplorer { class Project; } -namespace TextEditor { class TabSettings; } +namespace TextEditor { +class ICodeStylePreferences; +class TabSettings; +} namespace ClangFormat { class ClangFormatFile { public: - explicit ClangFormatFile(Utils::FilePath file); + explicit ClangFormatFile(const TextEditor::ICodeStylePreferences *preferences); clang::format::FormatStyle style(); Utils::FilePath filePath(); - void resetStyleToQtC(); + void resetStyleToQtC(const TextEditor::ICodeStylePreferences *codeStyle); void setBasedOnStyle(QString styleName); void setStyle(clang::format::FormatStyle style); QString setStyle(QString style); diff --git a/src/plugins/clangformat/clangformatglobalconfigwidget.cpp b/src/plugins/clangformat/clangformatglobalconfigwidget.cpp index af8a0846b15..64aa2c7c0fa 100644 --- a/src/plugins/clangformat/clangformatglobalconfigwidget.cpp +++ b/src/plugins/clangformat/clangformatglobalconfigwidget.cpp @@ -177,15 +177,15 @@ void ClangFormatGlobalConfigWidget::initOverrideCheckBox() connect(m_indentingOrFormatting, &QComboBox::currentIndexChanged, this, setEnableOverrideCheckBox); - m_overrideDefault->setToolTip(Tr::tr( - "When this option is enabled, ClangFormat will use a\n" - "user-specified configuration from the widget below,\n" - "instead of the project .clang-format file. You can\n" - "customize the formatting options for your code by\n" - "adjusting the settings in the widget. Note that any\n" - "changes made there will only affect the current\n" - "configuration, and will not modify the project\n" - ".clang-format file.")); + m_overrideDefault->setToolTip("" + + Tr::tr("When this option is enabled, ClangFormat will use a " + "user-specified configuration from the widget below, " + "instead of the project .clang-format file. You can " + "customize the formatting options for your code by " + "adjusting the settings in the widget. Note that any " + "changes made there will only affect the current " + "configuration, and will not modify the project " + ".clang-format file.")); m_overrideDefault->setChecked(getProjectOverriddenSettings(m_project)); setTemporarilyReadOnly(); diff --git a/src/plugins/clangformat/clangformatindenter.cpp b/src/plugins/clangformat/clangformatindenter.cpp index 67d5171b010..2e9781de322 100644 --- a/src/plugins/clangformat/clangformatindenter.cpp +++ b/src/plugins/clangformat/clangformatindenter.cpp @@ -32,7 +32,7 @@ static bool isBeautifierPluginActivated() return std::find_if(specs.begin(), specs.end(), [](ExtensionSystem::PluginSpec *spec) { - return spec->name() == "Beautifier"; + return spec->name() == "Beautifier" && spec->isEffectivelyEnabled(); }) != specs.end(); } diff --git a/src/plugins/clangformat/clangformatutils.cpp b/src/plugins/clangformat/clangformatutils.cpp index 9304599d999..735c0a03548 100644 --- a/src/plugins/clangformat/clangformatutils.cpp +++ b/src/plugins/clangformat/clangformatutils.cpp @@ -7,6 +7,7 @@ #include +#include #include #include @@ -179,6 +180,83 @@ clang::format::FormatStyle qtcStyle() return style; } +clang::format::FormatStyle currentQtStyle(const TextEditor::ICodeStylePreferences *preferences) +{ + clang::format::FormatStyle style = qtcStyle(); + if (!preferences) + return style; + + fromTabSettings(style, preferences->tabSettings()); + if (auto ccpPreferences = dynamic_cast(preferences)) + fromCppCodeStyleSettings(style, ccpPreferences->codeStyleSettings()); + return style; +} + +void fromCppCodeStyleSettings(clang::format::FormatStyle &style, + const CppEditor::CppCodeStyleSettings &settings) +{ + using namespace clang::format; + if (settings.indentAccessSpecifiers) + style.AccessModifierOffset = 0; + else + style.AccessModifierOffset = -1 * style.IndentWidth; + + if (settings.indentNamespaceBody || settings.indentNamespaceBraces) + style.NamespaceIndentation = FormatStyle::NamespaceIndentationKind::NI_All; + else + style.NamespaceIndentation = FormatStyle::NamespaceIndentationKind::NI_None; + + if (settings.indentClassBraces || settings.indentEnumBraces || settings.indentBlockBraces + || settings.indentFunctionBraces) + style.BreakBeforeBraces = FormatStyle::BS_Whitesmiths; + else + style.BreakBeforeBraces = FormatStyle::BS_Custom; + + style.IndentCaseLabels = settings.indentSwitchLabels; +#if LLVM_VERSION_MAJOR >= 11 + style.IndentCaseBlocks = settings.indentBlocksRelativeToSwitchLabels; +#endif + + if (settings.extraPaddingForConditionsIfConfusingAlign) + style.BreakBeforeBinaryOperators = FormatStyle::BOS_All; + else if (settings.alignAssignments) + style.BreakBeforeBinaryOperators = FormatStyle::BOS_NonAssignment; + else + style.BreakBeforeBinaryOperators = FormatStyle::BOS_None; + + style.DerivePointerAlignment = settings.bindStarToIdentifier || settings.bindStarToTypeName + || settings.bindStarToLeftSpecifier + || settings.bindStarToRightSpecifier; + + if ((settings.bindStarToIdentifier || settings.bindStarToRightSpecifier) + && ClangFormatSettings::instance().mode() == ClangFormatSettings::Mode::Formatting) + style.PointerAlignment = FormatStyle::PAS_Right; + + if ((settings.bindStarToTypeName || settings.bindStarToLeftSpecifier) + && ClangFormatSettings::instance().mode() == ClangFormatSettings::Mode::Formatting) + style.PointerAlignment = FormatStyle::PAS_Left; +} + +void fromTabSettings(clang::format::FormatStyle &style, const TextEditor::TabSettings &settings) +{ + using namespace clang::format; + + style.IndentWidth = settings.m_indentSize; + style.TabWidth = settings.m_tabSize; + + switch (settings.m_tabPolicy) { + case TextEditor::TabSettings::TabPolicy::MixedTabPolicy: + style.UseTab = FormatStyle::UT_ForContinuationAndIndentation; + break; + case TextEditor::TabSettings::TabPolicy::SpacesOnlyTabPolicy: + style.UseTab = FormatStyle::UT_Never; + break; + case TextEditor::TabSettings::TabPolicy::TabsOnlyTabPolicy: + style.UseTab = FormatStyle::UT_Always; + break; + } +} + QString projectUniqueId(ProjectExplorer::Project *project) { if (!project) diff --git a/src/plugins/clangformat/clangformatutils.h b/src/plugins/clangformat/clangformatutils.h index f7d67e7dc83..931ca087fae 100644 --- a/src/plugins/clangformat/clangformatutils.h +++ b/src/plugins/clangformat/clangformatutils.h @@ -14,8 +14,12 @@ #include -namespace TextEditor { class ICodeStylePreferences; } +namespace TextEditor { +class ICodeStylePreferences; +class TabSettings; +} namespace ProjectExplorer { class Project; } +namespace CppEditor { class CppCodeStyleSettings; } namespace ClangFormat { QString projectUniqueId(ProjectExplorer::Project *project); @@ -32,10 +36,15 @@ ClangFormatSettings::Mode getCurrentIndentationOrFormattingSettings(const Utils: Utils::FilePath configForFile(const Utils::FilePath &fileName); Utils::FilePath findConfig(const Utils::FilePath &fileName); +void fromTabSettings(clang::format::FormatStyle &style, const TextEditor::TabSettings &settings); +void fromCppCodeStyleSettings(clang::format::FormatStyle &style, + const CppEditor::CppCodeStyleSettings &settings); + bool getProjectOverriddenSettings(const ProjectExplorer::Project *project); void addQtcStatementMacros(clang::format::FormatStyle &style); clang::format::FormatStyle qtcStyle(); +clang::format::FormatStyle currentQtStyle(const TextEditor::ICodeStylePreferences *codeStyle); Utils::FilePath filePathToCurrentSettings(const TextEditor::ICodeStylePreferences *codeStyle); } diff --git a/src/plugins/clangformat/llvmfilesystem.h b/src/plugins/clangformat/llvmfilesystem.h index 675791f349e..fface01fc08 100644 --- a/src/plugins/clangformat/llvmfilesystem.h +++ b/src/plugins/clangformat/llvmfilesystem.h @@ -50,7 +50,7 @@ public: Q_UNUSED(RequiresNullTerminator); Q_UNUSED(IsVolatile); - const FilePath path = FilePath::fromString(QString::fromStdString(Name.str())); + const FilePath path = FilePath::fromUserInput(QString::fromStdString(Name.str())); const expected_str contents = path.fileContents(FileSize, 0); QTC_ASSERT_EXPECTED(contents, return std::error_code()); @@ -72,7 +72,7 @@ public: ErrorOr status(const Twine &Path) override { - const Utils::FilePath path = FilePath::fromString(QString::fromStdString(Path.str())); + const FilePath path = FilePath::fromUserInput(QString::fromStdString(Path.str())); QFileInfo fInfo(QString::fromStdString(Path.str())); if (!fInfo.exists()) diff --git a/src/plugins/clangtools/clangtool.cpp b/src/plugins/clangtools/clangtool.cpp index 00da4bb880e..2ce4373b7f7 100644 --- a/src/plugins/clangtools/clangtool.cpp +++ b/src/plugins/clangtools/clangtool.cpp @@ -677,16 +677,6 @@ void ClangTool::startTool(ClangTool::FileSelection fileSelection, ProjectExplorerPlugin::startRunControl(m_runControl); } -Diagnostics ClangTool::read(const FilePath &logFilePath, - const QSet &projectFiles, - QString *errorMessage) const -{ - const auto acceptFromFilePath = [projectFiles](const FilePath &filePath) { - return projectFiles.contains(filePath); - }; - return readExportedDiagnostics(logFilePath, acceptFromFilePath, errorMessage); -} - FileInfos ClangTool::collectFileInfos(Project *project, FileSelection fileSelection) { FileSelectionType *selectionType = std::get_if(&fileSelection); @@ -763,21 +753,17 @@ void ClangTool::loadDiagnosticsFromFiles() // Load files Diagnostics diagnostics; - QString errors; + QStringList errors; for (const FilePath &filePath : filePaths) { - QString currentError; - diagnostics << readExportedDiagnostics(filePath, {}, ¤tError); - - if (!currentError.isEmpty()) { - if (!errors.isEmpty()) - errors.append("\n"); - errors.append(currentError); - } + if (expected_str expectedDiagnostics = readExportedDiagnostics(filePath)) + diagnostics << *expectedDiagnostics; + else + errors.append(expectedDiagnostics.error()); } // Show errors if (!errors.isEmpty()) { - AsynchronousMessageBox::critical(Tr::tr("Error Loading Diagnostics"), errors); + AsynchronousMessageBox::critical(Tr::tr("Error Loading Diagnostics"), errors.join('\n')); return; } diff --git a/src/plugins/clangtools/clangtool.h b/src/plugins/clangtools/clangtool.h index 98d219ac923..fad280a17f1 100644 --- a/src/plugins/clangtools/clangtool.h +++ b/src/plugins/clangtools/clangtool.h @@ -65,10 +65,6 @@ public: const RunSettings &runSettings, const CppEditor::ClangDiagnosticConfig &diagnosticConfig); - Diagnostics read(const Utils::FilePath &logFilePath, - const QSet &projectFiles, - QString *errorMessage) const; - FileInfos collectFileInfos(ProjectExplorer::Project *project, FileSelection fileSelection); diff --git a/src/plugins/clangtools/clangtoolruncontrol.cpp b/src/plugins/clangtools/clangtoolruncontrol.cpp index 39de004ac89..aaf20741a7f 100644 --- a/src/plugins/clangtools/clangtoolruncontrol.cpp +++ b/src/plugins/clangtools/clangtoolruncontrol.cpp @@ -242,26 +242,15 @@ void ClangToolRunWorker::onDone(const AnalyzeOutputData &output) qCDebug(LOG) << "onRunnerFinishedWithSuccess:" << output.outputFilePath; - QString errorMessage; - const Diagnostics diagnostics = m_tool->read(output.outputFilePath, m_projectFiles, - &errorMessage); + const Diagnostics diagnostics = output.diagnostics; - if (!errorMessage.isEmpty()) { - m_filesAnalyzed.remove(output.fileToAnalyze); - m_filesNotAnalyzed.insert(output.fileToAnalyze); - qCDebug(LOG) << "onRunnerFinishedWithSuccess: Error reading log file:" << errorMessage; - appendMessage(Tr::tr("Failed to analyze \"%1\": %2") - .arg(output.fileToAnalyze.toUserOutput(), errorMessage), - Utils::StdErrFormat); - } else { - 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); - } + 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); } } diff --git a/src/plugins/clangtools/clangtoolrunner.cpp b/src/plugins/clangtools/clangtoolrunner.cpp index 6681a9133dd..666f07963b2 100644 --- a/src/plugins/clangtools/clangtoolrunner.cpp +++ b/src/plugins/clangtools/clangtoolrunner.cpp @@ -3,6 +3,7 @@ #include "clangtoolrunner.h" +#include "clangtoolslogfilereader.h" #include "clangtoolstr.h" #include "clangtoolsutils.h" @@ -11,6 +12,9 @@ #include #include +#include + +#include #include #include #include @@ -20,6 +24,7 @@ #include #include + static Q_LOGGING_CATEGORY(LOG, "qtc.clangtools.runner", QtWarningMsg) using namespace CppEditor; @@ -123,23 +128,23 @@ GroupItem clangToolTask(const AnalyzeInputData &input, const auto onSetup = [=] { if (setupHandler && !setupHandler()) - return TaskAction::StopWithError; + return SetupResult::StopWithError; ClangToolStorage *data = storage.activeStorage(); data->name = clangToolName(input.tool); data->executable = toolExecutable(input.tool); if (!data->executable.isExecutableFile()) { qWarning() << "Can't start:" << data->executable << "as" << data->name; - return TaskAction::StopWithError; + return SetupResult::StopWithError; } QTC_CHECK(!input.unit.arguments.contains(QLatin1String("-o"))); QTC_CHECK(!input.unit.arguments.contains(input.unit.file.nativePath())); - QTC_ASSERT(input.unit.file.exists(), return TaskAction::StopWithError); + QTC_ASSERT(input.unit.file.exists(), return SetupResult::StopWithError); data->outputFilePath = createOutputFilePath(input.outputDirPath, input.unit.file); - QTC_ASSERT(!data->outputFilePath.isEmpty(), return TaskAction::StopWithError); + QTC_ASSERT(!data->outputFilePath.isEmpty(), return SetupResult::StopWithError); - return TaskAction::Continue; + return SetupResult::Continue; }; const auto onProcessSetup = [=](Process &process) { process.setEnvironment(input.environment); @@ -160,9 +165,6 @@ GroupItem clangToolTask(const AnalyzeInputData &input, }; const auto onProcessDone = [=](const Process &process) { qCDebug(LOG).noquote() << "Output:\n" << process.cleanedStdOut(); - if (!outputHandler) - return; - outputHandler({true, input.unit.file, storage->outputFilePath, input.tool}); }; const auto onProcessError = [=](const Process &process) { if (!outputHandler) @@ -179,15 +181,50 @@ GroupItem clangToolTask(const AnalyzeInputData &input, message = Tr::tr("%1 finished with exit code: %2.").arg(data.name).arg(process.exitCode()); else message = Tr::tr("%1 crashed.").arg(data.name); - outputHandler({false, input.unit.file, data.outputFilePath, input.tool, message, details}); + outputHandler( + {false, input.unit.file, data.outputFilePath, {}, input.tool, message, details}); + }; + + const auto onReadSetup = [=](Async> &data) { + data.setConcurrentCallData(&parseDiagnostics, + storage->outputFilePath, + input.diagnosticsFilter); + data.setFutureSynchronizer(ExtensionSystem::PluginManager::futureSynchronizer()); + }; + const auto onReadDone = [=](const Async> &data) { + if (!outputHandler) + return; + const expected_str result = data.result(); + const bool success = result.has_value(); + Diagnostics diagnostics; + QString error; + if (success) + diagnostics = *result; + else + error = result.error(); + outputHandler({success, + input.unit.file, + storage->outputFilePath, + diagnostics, + input.tool, + error}); + }; + const auto onReadError = [=](const Async> &data) { + if (!outputHandler) + return; + const expected_str result = data.result(); + outputHandler( + {false, input.unit.file, storage->outputFilePath, {}, input.tool, result.error()}); }; const Group group { Storage(storage), onGroupSetup(onSetup), Group { + sequential, finishAllAndDone, - ProcessTask(onProcessSetup, onProcessDone, onProcessError) + ProcessTask(onProcessSetup, onProcessDone, onProcessError), + AsyncTask>(onReadSetup, onReadDone, onReadError) } }; return group; diff --git a/src/plugins/clangtools/clangtoolrunner.h b/src/plugins/clangtools/clangtoolrunner.h index a8d1204c224..ab4039c92c8 100644 --- a/src/plugins/clangtools/clangtoolrunner.h +++ b/src/plugins/clangtools/clangtoolrunner.h @@ -4,6 +4,8 @@ #pragma once #include "clangfileinfo.h" +#include "clangtoolsdiagnostic.h" +#include "clangtoolslogfilereader.h" #include "clangtoolssettings.h" #include @@ -35,6 +37,7 @@ struct AnalyzeInputData Utils::Environment environment; AnalyzeUnit unit; QString overlayFilePath = {}; + AcceptDiagsFromFilePath diagnosticsFilter = {}; }; struct AnalyzeOutputData @@ -42,6 +45,7 @@ struct AnalyzeOutputData bool success = true; Utils::FilePath fileToAnalyze; Utils::FilePath outputFilePath; + Diagnostics diagnostics; CppEditor::ClangToolType toolType; QString errorMessage = {}; QString errorDetails = {}; diff --git a/src/plugins/clangtools/clangtoolslogfilereader.cpp b/src/plugins/clangtools/clangtoolslogfilereader.cpp index d7ccb5579ee..a68b3c4d82a 100644 --- a/src/plugins/clangtools/clangtoolslogfilereader.cpp +++ b/src/plugins/clangtools/clangtoolslogfilereader.cpp @@ -11,26 +11,13 @@ #include #include +#include + #include namespace ClangTools { namespace Internal { -static bool checkFilePath(const Utils::FilePath &filePath, QString *errorMessage) -{ - QFileInfo fi(filePath.toFileInfo()); - if (!fi.exists() || !fi.isReadable()) { - if (errorMessage) { - *errorMessage - = QString(QT_TRANSLATE_NOOP("QtC::ClangTools", - "File \"%1\" does not exist or is not readable.")) - .arg(filePath.toUserOutput()); - } - return false; - } - return true; -} - std::optional byteOffsetInUtf8TextToLineColumn(const char *text, int offset, int startLine) @@ -190,19 +177,25 @@ private: } // namespace -Diagnostics readExportedDiagnostics(const Utils::FilePath &logFilePath, - const AcceptDiagsFromFilePath &acceptFromFilePath, - QString *errorMessage) +void parseDiagnostics(QPromise> &promise, + const Utils::FilePath &logFilePath, + const AcceptDiagsFromFilePath &acceptFromFilePath) { - if (!checkFilePath(logFilePath, errorMessage)) - return {}; + const Utils::expected_str localFileContents = logFilePath.fileContents(); + if (!localFileContents.has_value()) { + promise.addResult(Utils::make_unexpected(localFileContents.error())); + promise.future().cancel(); + return; + } FileCache fileCache; Diagnostics diagnostics; try { - YAML::Node document = YAML::LoadFile(logFilePath.toString().toStdString()); + YAML::Node document = YAML::Load(*localFileContents); for (const auto &diagNode : document["Diagnostics"]) { + if (promise.isCanceled()) + return; // Since llvm/clang 9.0 the diagnostic items are wrapped in a "DiagnosticMessage" node. const auto msgNode = diagNode["DiagnosticMessage"]; const YAML::Node &node = msgNode ? msgNode : diagNode; @@ -252,16 +245,24 @@ Diagnostics readExportedDiagnostics(const Utils::FilePath &logFilePath, diagnostics.append(diag); } + promise.addResult(diagnostics); } catch (std::exception &e) { - if (errorMessage) { - *errorMessage = QString( - QT_TRANSLATE_NOOP("QtC::ClangTools", - "Error: Failed to parse YAML file \"%1\": %2.")) - .arg(logFilePath.toUserOutput(), QString::fromUtf8(e.what())); - } + const QString errorMessage + = QString(QT_TRANSLATE_NOOP("QtC::ClangTools", + "Error: Failed to parse YAML file \"%1\": %2.")) + .arg(logFilePath.toUserOutput(), QString::fromUtf8(e.what())); + promise.addResult(Utils::make_unexpected(errorMessage)); + promise.future().cancel(); } +} - return diagnostics; +Utils::expected_str readExportedDiagnostics( + const Utils::FilePath &logFilePath, const AcceptDiagsFromFilePath &acceptFromFilePath) +{ + QPromise> promise; + promise.start(); + parseDiagnostics(promise, logFilePath, acceptFromFilePath); + return promise.future().result(); } } // namespace Internal diff --git a/src/plugins/clangtools/clangtoolslogfilereader.h b/src/plugins/clangtools/clangtoolslogfilereader.h index 4b8f3edcc1e..09c2b79f021 100644 --- a/src/plugins/clangtools/clangtoolslogfilereader.h +++ b/src/plugins/clangtools/clangtoolslogfilereader.h @@ -6,6 +6,7 @@ #include "clangtoolsdiagnostic.h" +#include #include namespace Utils { class FilePath; } @@ -16,9 +17,13 @@ namespace Internal { using AcceptDiagsFromFilePath = std::function; // Reads diagnostics generated by "clang-tidy/clazy-standalone -export-fixes=path/to/file" -Diagnostics readExportedDiagnostics(const Utils::FilePath &logFilePath, - const AcceptDiagsFromFilePath &acceptFromFilePath, - QString *errorMessage = nullptr); +void parseDiagnostics(QPromise> &promise, + const Utils::FilePath &logFilePath, + const AcceptDiagsFromFilePath &acceptFromFilePath = {}); + +Utils::expected_str readExportedDiagnostics( + const Utils::FilePath &logFilePath, + const AcceptDiagsFromFilePath &acceptFromFilePath = {}); // Exposed for tests struct LineColumnInfo { diff --git a/src/plugins/clangtools/diagnosticmark.cpp b/src/plugins/clangtools/diagnosticmark.cpp index 4184ce35350..bc92e1d17e3 100644 --- a/src/plugins/clangtools/diagnosticmark.cpp +++ b/src/plugins/clangtools/diagnosticmark.cpp @@ -8,18 +8,21 @@ #include "clangtoolsutils.h" #include "diagnosticconfigswidget.h" +#include #include #include #include +using namespace TextEditor; + namespace ClangTools { namespace Internal { -DiagnosticMark::DiagnosticMark(const Diagnostic &diagnostic) - : TextEditor::TextMark(diagnostic.location.filePath, - diagnostic.location.line, - {Tr::tr("Clang Tools"), Utils::Id(Constants::DIAGNOSTIC_MARK_ID)}) +DiagnosticMark::DiagnosticMark(const Diagnostic &diagnostic, TextDocument *document) + : TextMark(document, + diagnostic.location.line, + {Tr::tr("Clang Tools"), Utils::Id(Constants::DIAGNOSTIC_MARK_ID)}) , m_diagnostic(diagnostic) { setSettingsPage(Constants::SETTINGS_PAGE_ID); @@ -57,6 +60,10 @@ DiagnosticMark::DiagnosticMark(const Diagnostic &diagnostic) }); } +DiagnosticMark::DiagnosticMark(const Diagnostic &diagnostic) + : DiagnosticMark(diagnostic, TextDocument::textDocumentForFilePath(diagnostic.location.filePath)) +{} + void DiagnosticMark::disable() { if (!m_enabled) diff --git a/src/plugins/clangtools/diagnosticmark.h b/src/plugins/clangtools/diagnosticmark.h index 7218d89ebd5..6d566739d42 100644 --- a/src/plugins/clangtools/diagnosticmark.h +++ b/src/plugins/clangtools/diagnosticmark.h @@ -14,6 +14,7 @@ namespace Internal { class DiagnosticMark : public TextEditor::TextMark { public: + DiagnosticMark(const Diagnostic &diagnostic, TextEditor::TextDocument *document); explicit DiagnosticMark(const Diagnostic &diagnostic); void disable(); diff --git a/src/plugins/clangtools/documentclangtoolrunner.cpp b/src/plugins/clangtools/documentclangtoolrunner.cpp index 8dab531dcb6..28ea234f9ad 100644 --- a/src/plugins/clangtools/documentclangtoolrunner.cpp +++ b/src/plugins/clangtools/documentclangtoolrunner.cpp @@ -83,9 +83,7 @@ static void removeClangToolRefactorMarkers(TextEditor::TextEditorWidget *editor) { if (!editor) return; - editor->setRefactorMarkers( - TextEditor::RefactorMarker::filterOutType(editor->refactorMarkers(), - Constants::CLANG_TOOL_FIXIT_AVAILABLE_MARKER_ID)); + editor->clearRefactorMarkers(Constants::CLANG_TOOL_FIXIT_AVAILABLE_MARKER_ID); } void DocumentClangToolRunner::scheduleRun() @@ -203,8 +201,16 @@ void DocumentClangToolRunner::run() if (includeDir.isEmpty() || clangVersion.isEmpty()) return; const AnalyzeUnit unit(m_fileInfo, includeDir, clangVersion); - const AnalyzeInputData input{tool, runSettings, config, m_temporaryDir.path(), env, unit, - vfso().overlayFilePath().toString()}; + auto diagnosticFilter = [mappedPath = vfso().autoSavedFilePath(m_document)]( + const FilePath &path) { return path == mappedPath; }; + const AnalyzeInputData input{tool, + runSettings, + config, + m_temporaryDir.path(), + env, + unit, + vfso().overlayFilePath().toString(), + diagnosticFilter}; const auto setupHandler = [this, executable] { return !m_document->isModified() || isVFSOverlaySupported(executable); }; @@ -236,11 +242,7 @@ void DocumentClangToolRunner::onDone(const AnalyzeOutputData &output) return; } - const FilePath mappedPath = vfso().autoSavedFilePath(m_document); - Diagnostics diagnostics = readExportedDiagnostics( - output.outputFilePath, - [&](const FilePath &path) { return path == mappedPath; }); - + Diagnostics diagnostics = output.diagnostics; for (Diagnostic &diag : diagnostics) { updateLocation(diag.location); for (ExplainingStep &explainingStep : diag.explainingSteps) { @@ -266,7 +268,7 @@ void DocumentClangToolRunner::onDone(const AnalyzeOutputData &output) if (isSuppressed(diagnostic)) continue; - auto mark = new DiagnosticMark(diagnostic); + auto mark = new DiagnosticMark(diagnostic, doc); mark->toolType = toolType; if (doc && Utils::anyOf(diagnostic.explainingSteps, &ExplainingStep::isFixIt)) { @@ -291,7 +293,7 @@ void DocumentClangToolRunner::onDone(const AnalyzeOutputData &output) for (auto editor : TextEditor::BaseTextEditor::textEditorsForDocument(doc)) { if (TextEditor::TextEditorWidget *widget = editor->editorWidget()) { - widget->setRefactorMarkers(markers + widget->refactorMarkers()); + widget->setRefactorMarkers(markers, Constants::CLANG_TOOL_FIXIT_AVAILABLE_MARKER_ID); if (!m_editorsWithMarkers.contains(widget)) m_editorsWithMarkers << widget; } diff --git a/src/plugins/clangtools/readexporteddiagnosticstest.cpp b/src/plugins/clangtools/readexporteddiagnosticstest.cpp index 0b318b64d78..d839108451b 100644 --- a/src/plugins/clangtools/readexporteddiagnosticstest.cpp +++ b/src/plugins/clangtools/readexporteddiagnosticstest.cpp @@ -31,41 +31,40 @@ ReadExportedDiagnosticsTest::ReadExportedDiagnosticsTest() ReadExportedDiagnosticsTest::~ReadExportedDiagnosticsTest() { delete m_baseDir; } void ReadExportedDiagnosticsTest::initTestCase() { QVERIFY(m_baseDir->isValid()); } -void ReadExportedDiagnosticsTest::init() { m_message.clear(); } +void ReadExportedDiagnosticsTest::init() { } void ReadExportedDiagnosticsTest::testNonExistingFile() { - const Diagnostics diags = readExportedDiagnostics("nonExistingFile.yaml", {}, &m_message); - QVERIFY(diags.isEmpty()); - QVERIFY(!m_message.isEmpty()); + const expected_str diags = readExportedDiagnostics("nonExistingFile.yaml"); + QVERIFY(!diags.has_value()); + QVERIFY(!diags.error().isEmpty()); } void ReadExportedDiagnosticsTest::testEmptyFile() { - const Diagnostics diags = readExportedDiagnostics(filePath("empty.yaml"), {}, &m_message); - QVERIFY(diags.isEmpty()); - QVERIFY2(m_message.isEmpty(), qPrintable(m_message)); + const expected_str diags = readExportedDiagnostics(filePath("empty.yaml")); + QVERIFY(diags.has_value()); + QVERIFY(diags->isEmpty()); } void ReadExportedDiagnosticsTest::testUnexpectedFileContents() { - const Diagnostics diags = readExportedDiagnostics(filePath("tidy.modernize-use-nullptr.cpp"), - {}, &m_message); - QVERIFY(!m_message.isEmpty()); - QVERIFY(diags.isEmpty()); + const expected_str diags = readExportedDiagnostics( + filePath("tidy.modernize-use-nullptr.cpp")); + QVERIFY(!diags.has_value()); + QVERIFY(!diags.error().isEmpty()); } static QString appendYamlSuffix(const char *filePathFragment) { - const QString yamlSuffix = QLatin1String(Utils::HostOsInfo::isWindowsHost() - ? "_win.yaml" : ".yaml"); + const QString yamlSuffix = QLatin1String(HostOsInfo::isWindowsHost() ? "_win.yaml" : ".yaml"); return filePathFragment + yamlSuffix; } void ReadExportedDiagnosticsTest::testTidy() { const FilePath sourceFile = filePath("tidy.modernize-use-nullptr.cpp"); - const QString exportedFile = createFile( + const FilePath exportedFile = createFile( filePath(appendYamlSuffix("tidy.modernize-use-nullptr")).toString(), sourceFile.toString()); Diagnostic expectedDiag; @@ -79,32 +78,31 @@ void ReadExportedDiagnosticsTest::testTidy() expectedDiag.location, {expectedDiag.location, {sourceFile, 2, 26}}, true}}; - const Diagnostics diags = readExportedDiagnostics(Utils::FilePath::fromString(exportedFile), - {}, &m_message); + const expected_str diags = readExportedDiagnostics(exportedFile); - QVERIFY2(m_message.isEmpty(), qPrintable(m_message)); - QCOMPARE(diags, {expectedDiag}); + QVERIFY(diags.has_value()); + QCOMPARE(*diags, {expectedDiag}); } void ReadExportedDiagnosticsTest::testAcceptDiagsFromFilePaths_None() { const QString sourceFile = filePath("tidy.modernize-use-nullptr.cpp").toString(); - const QString exportedFile = createFile(filePath("tidy.modernize-use-nullptr.yaml").toString(), + const FilePath exportedFile = createFile(filePath("tidy.modernize-use-nullptr.yaml").toString(), sourceFile); - const auto acceptNone = [](const Utils::FilePath &) { return false; }; - const Diagnostics diags = readExportedDiagnostics(FilePath::fromString(exportedFile), - acceptNone, &m_message); - QVERIFY2(m_message.isEmpty(), qPrintable(m_message)); - QVERIFY(diags.isEmpty()); + const auto acceptNone = [](const FilePath &) { return false; }; + const expected_str diags + = readExportedDiagnostics(exportedFile, acceptNone); + QVERIFY(diags.has_value()); + QVERIFY(diags->isEmpty()); } // Diagnostics from clang (static) analyzer passed through via clang-tidy void ReadExportedDiagnosticsTest::testTidy_ClangAnalyzer() { const FilePath sourceFile = filePath("clang-analyzer.dividezero.cpp"); - const QString exportedFile = createFile( - filePath(appendYamlSuffix("clang-analyzer.dividezero")).toString(), - sourceFile.toString()); + const FilePath exportedFile + = createFile(filePath(appendYamlSuffix("clang-analyzer.dividezero")).toString(), + sourceFile.toString()); Diagnostic expectedDiag; expectedDiag.name = "clang-analyzer-core.DivideZero"; expectedDiag.location = {sourceFile, 4, 15}; @@ -128,16 +126,15 @@ void ReadExportedDiagnosticsTest::testTidy_ClangAnalyzer() false, }, }; - const Diagnostics diags = readExportedDiagnostics(Utils::FilePath::fromString(exportedFile), - {}, &m_message); - QVERIFY2(m_message.isEmpty(), qPrintable(m_message)); - QCOMPARE(diags, {expectedDiag}); + const expected_str diags = readExportedDiagnostics(exportedFile); + QVERIFY(diags.has_value()); + QCOMPARE(*diags, {expectedDiag}); } void ReadExportedDiagnosticsTest::testClazy() { const FilePath sourceFile = filePath("clazy.qgetenv.cpp"); - const QString exportedFile = createFile(filePath(appendYamlSuffix("clazy.qgetenv")).toString(), + const FilePath exportedFile = createFile(filePath(appendYamlSuffix("clazy.qgetenv")).toString(), sourceFile.toString()); Diagnostic expectedDiag; expectedDiag.name = "clazy-qgetenv"; @@ -156,10 +153,9 @@ void ReadExportedDiagnosticsTest::testClazy() {{sourceFile, 7, 18}, {sourceFile, 7, 29}}, true}, }; - const Diagnostics diags = readExportedDiagnostics(Utils::FilePath::fromString(exportedFile), - {}, &m_message); - QVERIFY2(m_message.isEmpty(), qPrintable(m_message)); - QCOMPARE(diags, {expectedDiag}); + const expected_str diags = readExportedDiagnostics(exportedFile); + QVERIFY(diags.has_value()); + QCOMPARE(*diags, {expectedDiag}); } void ReadExportedDiagnosticsTest::testOffsetInvalidText() @@ -263,25 +259,24 @@ void ReadExportedDiagnosticsTest::testOffsetMultiByteCodePoint2() } // Replace FILE_PATH with a real absolute file path in the *.yaml files. -QString ReadExportedDiagnosticsTest::createFile(const QString &yamlFilePath, - const QString &filePathToInject) const +FilePath ReadExportedDiagnosticsTest::createFile(const QString &yamlFilePath, + const QString &filePathToInject) const { - QTC_ASSERT(QDir::isAbsolutePath(filePathToInject), return QString()); - const Utils::FilePath newFileName = m_baseDir->absolutePath( - QFileInfo(yamlFilePath).fileName()); + QTC_ASSERT(QDir::isAbsolutePath(filePathToInject), return {}); + const FilePath newFileName = m_baseDir->absolutePath(QFileInfo(yamlFilePath).fileName()); - Utils::FileReader reader; - if (QTC_GUARD(reader.fetch(Utils::FilePath::fromString(yamlFilePath), + FileReader reader; + if (QTC_GUARD(reader.fetch(FilePath::fromString(yamlFilePath), QIODevice::ReadOnly | QIODevice::Text))) { QByteArray contents = reader.data(); contents.replace("FILE_PATH", filePathToInject.toLocal8Bit()); - Utils::FileSaver fileSaver(newFileName, QIODevice::WriteOnly | QIODevice::Text); + FileSaver fileSaver(newFileName, QIODevice::WriteOnly | QIODevice::Text); QTC_CHECK(fileSaver.write(contents)); QTC_CHECK(fileSaver.finalize()); } - return newFileName.toString(); + return newFileName; } FilePath ReadExportedDiagnosticsTest::filePath(const QString &fileName) const diff --git a/src/plugins/clangtools/readexporteddiagnosticstest.h b/src/plugins/clangtools/readexporteddiagnosticstest.h index ce290dedec6..54bde6b75c1 100644 --- a/src/plugins/clangtools/readexporteddiagnosticstest.h +++ b/src/plugins/clangtools/readexporteddiagnosticstest.h @@ -44,11 +44,10 @@ private slots: void testOffsetMultiByteCodePoint2(); private: - QString createFile(const QString &yamlFilePath, const QString &filePathToInject) const; + Utils::FilePath createFile(const QString &yamlFilePath, const QString &filePathToInject) const; Utils::FilePath filePath(const QString &fileName) const; CppEditor::Tests::TemporaryCopiedDir * const m_baseDir; - QString m_message; }; } // namespace ClangTools::Internal diff --git a/src/plugins/cmakeprojectmanager/cmakeautocompleter.cpp b/src/plugins/cmakeprojectmanager/cmakeautocompleter.cpp index 6e5b482a70a..1f520a86bf5 100644 --- a/src/plugins/cmakeprojectmanager/cmakeautocompleter.cpp +++ b/src/plugins/cmakeprojectmanager/cmakeautocompleter.cpp @@ -89,7 +89,7 @@ QString CMakeAutoCompleter::insertMatchingQuote(const QTextCursor &cursor, int CMakeAutoCompleter::paragraphSeparatorAboutToBeInserted(QTextCursor &cursor) { const QString line = cursor.block().text().trimmed(); - if (line.contains(QRegularExpression(QStringLiteral("^(endfunction|endmacro|endif|endforeach|endwhile)\\w*\\(")))) + if (line.contains(QRegularExpression(QStringLiteral("^(endfunction|endmacro|endif|endforeach|endwhile|endblock)\\w*\\(")))) tabSettings().indentLine(cursor.block(), tabSettings().indentationColumn(cursor.block().text())); return 0; } diff --git a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp index 5ddc68c1c23..48acbe50c88 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp +++ b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp @@ -1462,10 +1462,18 @@ CMakeBuildConfiguration::CMakeBuildConfiguration(Target *target, Id id) // Android magic: if (DeviceTypeKitAspect::deviceTypeId(k) == Android::Constants::ANDROID_DEVICE_TYPE) { + auto addUniqueKeyToCmd = [&cmd] (const QString &prefix, const QString &value) -> bool { + const bool isUnique = + !Utils::contains(cmd.splitArguments(), [&prefix] (const QString &arg) { + return arg.startsWith(prefix); }); + if (isUnique) + cmd.addArg(prefix + value); + return isUnique; + }; buildSteps()->appendStep(Android::Constants::ANDROID_BUILD_APK_ID); const auto bs = buildSteps()->steps().constLast(); - cmd.addArg("-DANDROID_PLATFORM:STRING=" - + bs->data(Android::Constants::AndroidNdkPlatform).toString()); + addUniqueKeyToCmd("-DANDROID_PLATFORM:STRING=", + bs->data(Android::Constants::AndroidNdkPlatform).toString()); auto ndkLocation = bs->data(Android::Constants::NdkLocation).value(); cmd.addArg("-DANDROID_NDK:PATH=" + ndkLocation.path()); @@ -2087,7 +2095,10 @@ void CMakeBuildConfiguration::addToEnvironment(Utils::Environment &env) const Environment CMakeBuildConfiguration::configureEnvironment() const { - return aspect()->environment(); + Environment env = aspect()->environment(); + addToEnvironment(env); + + return env; } QString CMakeBuildSystem::cmakeBuildType() const diff --git a/src/plugins/cmakeprojectmanager/cmakebuildstep.cpp b/src/plugins/cmakeprojectmanager/cmakebuildstep.cpp index 2d2501ab03b..9e4a705fb54 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildstep.cpp +++ b/src/plugins/cmakeprojectmanager/cmakebuildstep.cpp @@ -12,6 +12,10 @@ #include "cmakeprojectmanagertr.h" #include "cmaketool.h" +#include + +#include + #include #include #include @@ -159,7 +163,7 @@ Qt::ItemFlags CMakeTargetItem::flags(int) const // CMakeBuildStep -static QString initialStagingDir() +static QString initialStagingDir(Kit *kit) { // Avoid actual file accesses. auto rg = QRandomGenerator::global(); @@ -167,16 +171,21 @@ static QString initialStagingDir() char buf[sizeof(rand)]; memcpy(&buf, &rand, sizeof(rand)); const QByteArray ba = QByteArray(buf, sizeof(buf)).toHex(); + IDeviceConstPtr buildDevice = BuildDeviceKitAspect::device(kit); + if (buildDevice && buildDevice->type() == ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE) + return TemporaryDirectory::masterDirectoryPath() + "/staging-" + ba; return QString::fromUtf8("/tmp/Qt-Creator-staging-" + ba); } -static bool buildAndRunOnSameDevice(Kit *kit) +static bool supportsStageForInstallation(const Kit *kit) { IDeviceConstPtr runDevice = DeviceKitAspect::device(kit); IDeviceConstPtr buildDevice = BuildDeviceKitAspect::device(kit); QTC_ASSERT(runDevice, return false); QTC_ASSERT(buildDevice, return false); - return runDevice->id() == buildDevice->id(); + return runDevice->id() != buildDevice->id() + && runDevice->type() != Android::Constants::ANDROID_DEVICE_TYPE + && runDevice->type() != Ios::Constants::IOS_DEVICE_TYPE; } CMakeBuildStep::CMakeBuildStep(BuildStepList *bsl, Id id) : @@ -194,13 +203,13 @@ CMakeBuildStep::CMakeBuildStep(BuildStepList *bsl, Id id) : m_useStaging = addAspect(); m_useStaging->setSettingsKey(USE_STAGING_KEY); - m_useStaging->setLabelPlacement(BoolAspect::LabelPlacement::AtCheckBox); - m_useStaging->setDefaultValue(!buildAndRunOnSameDevice(kit())); + m_useStaging->setLabel(Tr::tr("Stage for installation"), BoolAspect::LabelPlacement::AtCheckBox); + m_useStaging->setDefaultValue(supportsStageForInstallation(kit())); m_stagingDir = addAspect(); m_stagingDir->setSettingsKey(STAGING_DIR_KEY); m_stagingDir->setLabelText(Tr::tr("Staging directory:")); - m_stagingDir->setDefaultValue(initialStagingDir()); + m_stagingDir->setDefaultValue(initialStagingDir(kit())); Kit *kit = buildConfiguration()->kit(); if (CMakeBuildConfiguration::isIos(kit)) { @@ -426,8 +435,10 @@ CommandLine CMakeBuildStep::cmakeCommand() const } return s; })); + if (m_useStaging->value()) + cmd.addArg("install"); - auto bs = qobject_cast(buildSystem()); + auto bs = qobject_cast(buildSystem()); if (bs && bs->isMultiConfigReader()) { cmd.addArg("--config"); if (m_configuration) @@ -439,9 +450,6 @@ CommandLine CMakeBuildStep::cmakeCommand() const if (!m_cmakeArguments->value().isEmpty()) cmd.addArgs(m_cmakeArguments->value(), CommandLine::Raw); - if (m_useStaging->value()) - cmd.addArg("install"); - bool toolArgumentsSpecified = false; if (!m_toolArguments->value().isEmpty()) { cmd.addArg("--"); @@ -504,8 +512,10 @@ QWidget *CMakeBuildStep::createConfigWidget() m_stagingDir->setEnabled(m_useStaging->value()); if (m_useStaging->value()) { - summaryText.append(" " + Tr::tr("and stage at %2 for %3") - .arg(currentStagingDir(), currentInstallPrefix())); + //: Stage (for installation) at for + summaryText.append( + "; " + + Tr::tr("Stage at %2 for %3").arg(currentStagingDir(), currentInstallPrefix())); } if (!m_buildPreset.isEmpty()) { @@ -568,7 +578,8 @@ QWidget *CMakeBuildStep::createConfigWidget() Layouting::Form builder; builder.addRow({m_cmakeArguments}); builder.addRow({m_toolArguments}); - builder.addRow({Tr::tr("Stage for installation:"), Layouting::Row{m_useStaging, m_stagingDir}}); + builder.addRow({m_useStaging}); + builder.addRow({m_stagingDir}); if (m_useiOSAutomaticProvisioningUpdates) builder.addRow({m_useiOSAutomaticProvisioningUpdates}); diff --git a/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp b/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp index 1bd19dffeb1..9abb3597d15 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp +++ b/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp @@ -175,8 +175,8 @@ void CMakeBuildSystem::triggerParsing() qCDebug(cmakeBuildSystemLog) << "Parse called with flags:" << reparseParametersString(reparseParameters); - const QString cache = m_parameters.buildDirectory.pathAppended("CMakeCache.txt").toString(); - if (!QFileInfo::exists(cache)) { + const FilePath cache = m_parameters.buildDirectory.pathAppended("CMakeCache.txt"); + if (!cache.exists()) { reparseParameters |= REPARSE_FORCE_INITIAL_CONFIGURATION | REPARSE_FORCE_CMAKE_RUN; qCDebug(cmakeBuildSystemLog) << "No" << cache diff --git a/src/plugins/cmakeprojectmanager/cmakeindenter.cpp b/src/plugins/cmakeprojectmanager/cmakeindenter.cpp index 68157aea5bf..e3e88212c57 100644 --- a/src/plugins/cmakeprojectmanager/cmakeindenter.cpp +++ b/src/plugins/cmakeprojectmanager/cmakeindenter.cpp @@ -52,7 +52,8 @@ static bool lineStartsBlock(const QString &line) lineContainsFunction(line, QStringLiteral("while")) || lineContainsFunction(line, QStringLiteral("if")) || lineContainsFunction(line, QStringLiteral("elseif")) || - lineContainsFunction(line, QStringLiteral("else")); + lineContainsFunction(line, QStringLiteral("else")) || + lineContainsFunction(line, QStringLiteral("block")); } static bool lineEndsBlock(const QString &line) { @@ -62,7 +63,8 @@ static bool lineEndsBlock(const QString &line) lineContainsFunction(line, QStringLiteral("endwhile")) || lineContainsFunction(line, QStringLiteral("endif")) || lineContainsFunction(line, QStringLiteral("elseif")) || - lineContainsFunction(line, QStringLiteral("else")); + lineContainsFunction(line, QStringLiteral("else")) || + lineContainsFunction(line, QStringLiteral("endblock")); } static bool lineIsEmpty(const QString &line) { diff --git a/src/plugins/cmakeprojectmanager/cmakeproject.cpp b/src/plugins/cmakeprojectmanager/cmakeproject.cpp index 78b6449a7a9..44ebaff555e 100644 --- a/src/plugins/cmakeprojectmanager/cmakeproject.cpp +++ b/src/plugins/cmakeprojectmanager/cmakeproject.cpp @@ -115,7 +115,16 @@ Internal::PresetsData CMakeProject::combinePresets(Internal::PresetsData &cmakeP && left.inherits.value() == right.inherits.value(); const bool leftInheritsRight = left.inherits && left.inherits.value().contains(right.name); - if ((left.inherits && !right.inherits) || leftInheritsRight || sameInheritance) + + const bool inheritsGreater = left.inherits && right.inherits + && left.inherits.value().first() + > right.inherits.value().first(); + + const bool noInheritsGreater = !left.inherits && !right.inherits + && left.name > right.name; + + if ((left.inherits && !right.inherits) || leftInheritsRight || sameInheritance + || inheritsGreater || noInheritsGreater) return false; return true; }); diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectmanager.cpp b/src/plugins/cmakeprojectmanager/cmakeprojectmanager.cpp index c43c7978a61..96d54518c33 100644 --- a/src/plugins/cmakeprojectmanager/cmakeprojectmanager.cpp +++ b/src/plugins/cmakeprojectmanager/cmakeprojectmanager.cpp @@ -233,18 +233,18 @@ 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 CMake presets kits. The manual " - "CMake project modifications will be lost."), - settings->askBeforePresetsReload.checkableDecider(), - QMessageBox::Yes | QMessageBox::Cancel, - QMessageBox::Yes, - QMessageBox::Yes, - { - {QMessageBox::Yes, Tr::tr("Reload")}, - }); + 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.checkableDecider(), + QMessageBox::Yes | QMessageBox::Cancel, + QMessageBox::Yes, + QMessageBox::Yes, + { + {QMessageBox::Yes, Tr::tr("Reload")}, + }); if (clickedButton == QMessageBox::Cancel) return; diff --git a/src/plugins/coco/cocolanguageclient.cpp b/src/plugins/coco/cocolanguageclient.cpp index 8d99e17154e..56f3d20ee2c 100644 --- a/src/plugins/coco/cocolanguageclient.cpp +++ b/src/plugins/coco/cocolanguageclient.cpp @@ -130,8 +130,8 @@ public: class CocoTextMark : public TextEditor::TextMark { public: - CocoTextMark(const FilePath &fileName, const CocoDiagnostic &diag, const Id &clientId) - : TextEditor::TextMark(fileName, diag.range().start().line() + 1, {"Coco", clientId}) + CocoTextMark(TextEditor::TextDocument *doc, const CocoDiagnostic &diag, const Id &clientId) + : TextEditor::TextMark(doc, diag.range().start().line() + 1, {"Coco", clientId}) , m_severity(diag.cocoSeverity()) { setLineAnnotation(diag.message()); @@ -180,13 +180,13 @@ private: }); } - TextEditor::TextMark *createTextMark(const FilePath &filePath, + TextEditor::TextMark *createTextMark(TextEditor::TextDocument *doc, const Diagnostic &diagnostic, bool /*isProjectFile*/) const override { const CocoDiagnostic cocoDiagnostic(diagnostic); if (std::optional severity = cocoDiagnostic.cocoSeverity()) - return new CocoTextMark(filePath, cocoDiagnostic, client()->id()); + return new CocoTextMark(doc, cocoDiagnostic, client()->id()); return nullptr; } diff --git a/src/plugins/copilot/authwidget.cpp b/src/plugins/copilot/authwidget.cpp index f29e3d3a150..559dee30cf9 100644 --- a/src/plugins/copilot/authwidget.cpp +++ b/src/plugins/copilot/authwidget.cpp @@ -24,12 +24,14 @@ AuthWidget::AuthWidget(QWidget *parent) { using namespace Layouting; - m_button = new QPushButton(Tr::tr("Sign in")); + m_button = new QPushButton(Tr::tr("Sign In")); m_button->setEnabled(false); m_progressIndicator = new Utils::ProgressIndicator(Utils::ProgressIndicatorSize::Small); m_progressIndicator->setVisible(false); m_statusLabel = new QLabel(); m_statusLabel->setVisible(false); + m_statusLabel->setTextInteractionFlags(Qt::TextInteractionFlag::TextSelectableByMouse + | Qt::TextInteractionFlag::TextSelectableByKeyboard); // clang-format off Column { @@ -91,13 +93,13 @@ void AuthWidget::updateClient(const Utils::FilePath &nodeJs, const Utils::FilePa { 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); @@ -117,7 +119,7 @@ void AuthWidget::signIn() QDesktopServices::openUrl(QUrl(response.result()->verificationUri())); - m_statusLabel->setText(Tr::tr("A browser window will open, enter the code %1 when " + m_statusLabel->setText(Tr::tr("A browser window will open. Enter the code %1 when " "asked.\nThe code has been copied to your clipboard.") .arg(response.result()->userCode())); m_statusLabel->setVisible(true); @@ -129,7 +131,7 @@ void AuthWidget::signIn() if (response.error()) { QMessageBox::critical(this, - Tr::tr("Login failed"), + Tr::tr("Login Failed"), Tr::tr( "The login request failed: ") + response.error()->message()); diff --git a/src/plugins/copilot/copilotclient.cpp b/src/plugins/copilot/copilotclient.cpp index f935210b53d..061d5d4a459 100644 --- a/src/plugins/copilot/copilotclient.cpp +++ b/src/plugins/copilot/copilotclient.cpp @@ -180,7 +180,27 @@ void CopilotClient::handleCompletions(const GetCompletionRequest::Response &resp return; if (const std::optional result = response.result()) { - QList completions = result->completions().toListOrEmpty(); + auto isValidCompletion = [](const Completion &completion) { + return completion.isValid() && !completion.text().trimmed().isEmpty(); + }; + QList completions = Utils::filtered(result->completions().toListOrEmpty(), + isValidCompletion); + + // remove trailing whitespaces from the end of the completions + for (Completion &completion : completions) { + const LanguageServerProtocol::Range range = completion.range(); + if (range.start().line() != range.end().line()) + continue; // do not remove trailing whitespaces for multi-line replacements + + const QString completionText = completion.text(); + const int end = int(completionText.size()) - 1; // empty strings have been removed above + int delta = 0; + while (delta <= end && completionText[end - delta].isSpace()) + ++delta; + + if (delta > 0) + completion.setText(completionText.chopped(delta)); + } if (completions.isEmpty()) return; editor->insertSuggestion( diff --git a/src/plugins/copilot/copilotconstants.h b/src/plugins/copilot/copilotconstants.h index 6ace5f2f6f9..1b557556ad4 100644 --- a/src/plugins/copilot/copilotconstants.h +++ b/src/plugins/copilot/copilotconstants.h @@ -12,6 +12,8 @@ const char COPILOT_TOGGLE[] = "Copilot.Toggle"; const char COPILOT_ENABLE[] = "Copilot.Enable"; const char COPILOT_DISABLE[] = "Copilot.Disable"; const char COPILOT_REQUEST_SUGGESTION[] = "Copilot.RequestSuggestion"; +const char COPILOT_NEXT_SUGGESTION[] = "Copilot.NextSuggestion"; +const char COPILOT_PREVIOUS_SUGGESTION[] = "Copilot.PreviousSuggestion"; const char COPILOT_GENERAL_OPTIONS_ID[] = "Copilot.General"; const char COPILOT_GENERAL_OPTIONS_CATEGORY[] = "ZY.Copilot"; diff --git a/src/plugins/copilot/copilotoptionspage.cpp b/src/plugins/copilot/copilotoptionspage.cpp index 001db87834d..f37042b2d12 100644 --- a/src/plugins/copilot/copilotoptionspage.cpp +++ b/src/plugins/copilot/copilotoptionspage.cpp @@ -13,6 +13,8 @@ #include #include +#include + using namespace Utils; using namespace LanguageClient; @@ -31,20 +33,23 @@ public: helpLabel->setTextFormat(Qt::MarkdownText); helpLabel->setWordWrap(true); helpLabel->setTextInteractionFlags(Qt::LinksAccessibleByMouse - | Qt::LinksAccessibleByKeyboard); + | 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(R"( -The Copilot plugin requires node.js and the Copilot neovim plugin. -If you install the neovim plugin as described in the -[README.md](https://github.com/github/copilot.vim), -the plugin will find the agent.js file automatically. - -Otherwise you need to specify the path to the -[agent.js](https://github.com/github/copilot.vim/tree/release/copilot/dist) -file from the Copilot neovim plugin. - )", "Markdown text for the copilot instruction label")); + 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)", + "[agent.js](https://github.com/github/copilot.vim/tree/release/copilot/dist)")); Column { authWidget, br, diff --git a/src/plugins/copilot/copilotplugin.cpp b/src/plugins/copilot/copilotplugin.cpp index 526a6061cbe..1a554e13977 100644 --- a/src/plugins/copilot/copilotplugin.cpp +++ b/src/plugins/copilot/copilotplugin.cpp @@ -9,6 +9,7 @@ #include "copilotoptionspage.h" #include "copilotprojectpanel.h" #include "copilotsettings.h" +#include "copilotsuggestion.h" #include "copilottr.h" #include @@ -20,6 +21,7 @@ #include +#include #include #include @@ -32,6 +34,28 @@ using namespace ProjectExplorer; namespace Copilot { namespace Internal { +enum Direction { Previous, Next }; +void cycleSuggestion(TextEditor::TextEditorWidget *editor, Direction direction) +{ + QTextBlock block = editor->textCursor().block(); + if (auto *suggestion = dynamic_cast( + TextEditor::TextDocumentLayout::suggestion(block))) { + int index = suggestion->currentCompletion(); + if (direction == Previous) + --index; + else + ++index; + if (index < 0) + index = suggestion->completions().count() - 1; + else if (index >= suggestion->completions().count()) + index = 0; + suggestion->reset(); + editor->insertSuggestion(std::make_unique(suggestion->completions(), + editor->document(), + index)); + } +} + void CopilotPlugin::initialize() { CopilotSettings::instance().readSettings(ICore::settings()); @@ -57,6 +81,30 @@ void CopilotPlugin::initialize() ActionManager::registerAction(requestAction, Constants::COPILOT_REQUEST_SUGGESTION); + QAction *nextSuggestionAction = new QAction(this); + nextSuggestionAction->setText(Tr::tr("Show next Copilot Suggestion")); + nextSuggestionAction->setToolTip(Tr::tr( + "Cycles through the received Copilot Suggestions showing the next available Suggestion.")); + + connect(nextSuggestionAction, &QAction::triggered, this, [] { + if (auto editor = TextEditor::TextEditorWidget::currentTextEditorWidget()) + cycleSuggestion(editor, Next); + }); + + ActionManager::registerAction(nextSuggestionAction, Constants::COPILOT_NEXT_SUGGESTION); + + QAction *previousSuggestionAction = new QAction(this); + previousSuggestionAction->setText(Tr::tr("Show previos Copilot Suggestion")); + previousSuggestionAction->setToolTip(Tr::tr("Cycles through the received Copilot Suggestions " + "showing the previous available Suggestion.")); + + connect(previousSuggestionAction, &QAction::triggered, this, [] { + if (auto editor = TextEditor::TextEditorWidget::currentTextEditorWidget()) + cycleSuggestion(editor, Previous); + }); + + ActionManager::registerAction(previousSuggestionAction, Constants::COPILOT_PREVIOUS_SUGGESTION); + QAction *disableAction = new QAction(this); disableAction->setText(Tr::tr("Disable Copilot")); disableAction->setToolTip(Tr::tr("Disable Copilot.")); diff --git a/src/plugins/copilot/requests/getcompletions.h b/src/plugins/copilot/requests/getcompletions.h index d602fea97d3..2280cfc73df 100644 --- a/src/plugins/copilot/requests/getcompletions.h +++ b/src/plugins/copilot/requests/getcompletions.h @@ -27,6 +27,7 @@ public: return typedValue(LanguageServerProtocol::rangeKey); } QString text() const { return typedValue(LanguageServerProtocol::textKey); } + void setText(const QString &text) { insert(LanguageServerProtocol::textKey, text); } QString uuid() const { return typedValue(uuidKey); } bool isValid() const override diff --git a/src/plugins/coreplugin/actionsfilter.cpp b/src/plugins/coreplugin/actionsfilter.cpp index 01772ac2520..3b805f76f72 100644 --- a/src/plugins/coreplugin/actionsfilter.cpp +++ b/src/plugins/coreplugin/actionsfilter.cpp @@ -190,11 +190,11 @@ LocatorMatcherTasks ActionsFilter::matchers() collectEntriesForCommands(); if (storage->input().simplified().isEmpty()) { storage->reportOutput(m_entries); - return TaskAction::StopWithDone; + return SetupResult::StopWithDone; } async.setFutureSynchronizer(ExtensionSystem::PluginManager::futureSynchronizer()); async.setConcurrentCallData(matches, *storage, m_entries); - return TaskAction::Continue; + return SetupResult::Continue; }; return {{AsyncTask(onSetup), storage}}; diff --git a/src/plugins/coreplugin/find/findtoolwindow.cpp b/src/plugins/coreplugin/find/findtoolwindow.cpp index 0125671d34e..d1e3a9fd235 100644 --- a/src/plugins/coreplugin/find/findtoolwindow.cpp +++ b/src/plugins/coreplugin/find/findtoolwindow.cpp @@ -65,6 +65,7 @@ FindToolWindow::FindToolWindow(QWidget *parent) m_searchTerm = new FancyLineEdit(this); m_searchTerm->setFiltering(true); + m_searchTerm->setPlaceholderText({}); m_searchLabel = new QLabel(this); m_searchLabel->setText(Tr::tr("Search f&or:", nullptr)); diff --git a/src/plugins/coreplugin/idocument.cpp b/src/plugins/coreplugin/idocument.cpp index 1e8d6ca1f18..5dcff9d3fc1 100644 --- a/src/plugins/coreplugin/idocument.cpp +++ b/src/plugins/coreplugin/idocument.cpp @@ -181,6 +181,26 @@ \sa reload() */ +/*! + \fn Core::IDocument::aboutToSave(const Utils::FilePath &filePath, bool autoSave) + + This signal is emitted before the document is saved to \a filePath. + + \a autoSave indicates whether this save was triggered by the auto save timer. + + \sa save() +*/ + +/*! + \fn Core::IDocument::saved(const Utils::FilePath &filePath, bool autoSave) + + This signal is emitted after the document was saved to \a filePath. + + \a autoSave indicates whether this save was triggered by the auto save timer. + + \sa save() +*/ + /*! \fn Core::IDocument::filePathChanged(const Utils::FilePath &oldName, const Utils::FilePath &newName) @@ -315,11 +335,35 @@ IDocument::OpenResult IDocument::open(QString *errorString, const Utils::FilePat Returns whether saving was successful. - The default implementation does nothing and returns \c false. + If saving was successful saved is emitted. \sa shouldAutoSave() + \sa aboutToSave() + \sa saved() */ bool IDocument::save(QString *errorString, const Utils::FilePath &filePath, bool autoSave) +{ + emit aboutToSave(filePath, autoSave); + const bool success = saveImpl(errorString, filePath, autoSave); + if (success) + emit saved(filePath, autoSave); + return success; +} + +/*! + Implementation of saving the contents of the document to the \a filePath on disk. + + 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 + user-requested saves. + + Use \a errorString to return an error message if saving failed. + + Returns whether saving was successful. + + The default implementation does nothing and returns \c false. +*/ +bool IDocument::saveImpl(QString *errorString, const Utils::FilePath &filePath, bool autoSave) { Q_UNUSED(errorString) Q_UNUSED(filePath) diff --git a/src/plugins/coreplugin/idocument.h b/src/plugins/coreplugin/idocument.h index 30c187924e5..b771843056b 100644 --- a/src/plugins/coreplugin/idocument.h +++ b/src/plugins/coreplugin/idocument.h @@ -68,7 +68,7 @@ public: virtual OpenResult open(QString *errorString, const Utils::FilePath &filePath, const Utils::FilePath &realFilePath); - virtual bool save(QString *errorString, const Utils::FilePath &filePath = Utils::FilePath(), bool autoSave = false); + bool save(QString *errorString, const Utils::FilePath &filePath = Utils::FilePath(), bool autoSave = false); virtual QByteArray contents() const; virtual bool setContents(const QByteArray &contents); @@ -124,9 +124,16 @@ signals: void aboutToReload(); void reloadFinished(bool success); + void aboutToSave(const Utils::FilePath &filePath, bool autoSave); + void saved(const Utils::FilePath &filePath, bool autoSave); void filePathChanged(const Utils::FilePath &oldName, const Utils::FilePath &newName); +protected: + virtual bool saveImpl(QString *errorString, + const Utils::FilePath &filePath = Utils::FilePath(), + bool autoSave = false); + private: Internal::IDocumentPrivate *d; }; diff --git a/src/plugins/coreplugin/locator/directoryfilter.cpp b/src/plugins/coreplugin/locator/directoryfilter.cpp index 42cf51addf7..6acdb74720a 100644 --- a/src/plugins/coreplugin/locator/directoryfilter.cpp +++ b/src/plugins/coreplugin/locator/directoryfilter.cpp @@ -82,9 +82,9 @@ DirectoryFilter::DirectoryFilter(Id id) using namespace Tasking; const auto groupSetup = [this] { if (!m_directories.isEmpty()) - return TaskAction::Continue; // Async task will run + return SetupResult::Continue; // Async task will run m_cache.setFilePaths({}); - return TaskAction::StopWithDone; // Group stops, skips async task + return SetupResult::StopWithDone; // Group stops, skips async task }; const auto asyncSetup = [this](Async &async) { async.setConcurrentCallData(&refresh, m_directories, m_filters, m_exclusionFilters, diff --git a/src/plugins/coreplugin/locator/ilocatorfilter.cpp b/src/plugins/coreplugin/locator/ilocatorfilter.cpp index 74ad227e977..7f272fd108d 100644 --- a/src/plugins/coreplugin/locator/ilocatorfilter.cpp +++ b/src/plugins/coreplugin/locator/ilocatorfilter.cpp @@ -1503,16 +1503,16 @@ LocatorMatcherTask LocatorFileCache::matcher() const const auto onSetup = [storage, weak](Async &async) { auto that = weak.lock(); if (!that) // LocatorMatcher is running after *this LocatorFileCache was destructed. - return TaskAction::StopWithDone; + return SetupResult::StopWithDone; if (!that->ensureValidated()) - return TaskAction::StopWithDone; // The cache is invalid and + return SetupResult::StopWithDone; // The cache is invalid and // no provider is set or it returned empty generator that->bumpExecutionId(); async.setFutureSynchronizer(ExtensionSystem::PluginManager::futureSynchronizer()); async.setConcurrentCallData(&filter, *storage, *that); - return TaskAction::Continue; + return SetupResult::Continue; }; const auto onDone = [weak](const Async &async) { auto that = weak.lock(); diff --git a/src/plugins/coreplugin/locator/javascriptfilter.cpp b/src/plugins/coreplugin/locator/javascriptfilter.cpp index b179b32316d..a15cabd8d9d 100644 --- a/src/plugins/coreplugin/locator/javascriptfilter.cpp +++ b/src/plugins/coreplugin/locator/javascriptfilter.cpp @@ -372,7 +372,7 @@ LocatorMatcherTasks JavaScriptFilter::matchers() const auto onSetup = [storage, engine] { if (!engine) - return TaskAction::StopWithError; + return SetupResult::StopWithError; if (storage->input().trimmed().isEmpty()) { LocatorFilterEntry entry; entry.displayName = Tr::tr("Reset Engine"); @@ -385,9 +385,9 @@ LocatorMatcherTasks JavaScriptFilter::matchers() return AcceptResult(); }; storage->reportOutput({entry}); - return TaskAction::StopWithDone; + return SetupResult::StopWithDone; } - return TaskAction::Continue; + return SetupResult::Continue; }; const auto onJavaScriptSetup = [storage, engine](JavaScriptRequest &request) { diff --git a/src/plugins/coreplugin/locator/spotlightlocatorfilter.cpp b/src/plugins/coreplugin/locator/spotlightlocatorfilter.cpp index 64ea71998b7..d89bec2263b 100644 --- a/src/plugins/coreplugin/locator/spotlightlocatorfilter.cpp +++ b/src/plugins/coreplugin/locator/spotlightlocatorfilter.cpp @@ -179,7 +179,7 @@ LocatorMatcherTasks SpotlightLocatorFilter::matchers() const Link link = Link::fromString(storage->input(), true); const FilePath input = link.targetFilePath; if (input.isEmpty()) - return TaskAction::StopWithDone; + return SetupResult::StopWithDone; // only pass the file name part to allow searches like "somepath/*foo" const std::unique_ptr expander(createMacroExpander(input.fileName())); @@ -189,7 +189,7 @@ LocatorMatcherTasks SpotlightLocatorFilter::matchers() CommandLine::Raw); async.setFutureSynchronizer(ExtensionSystem::PluginManager::futureSynchronizer()); async.setConcurrentCallData(matches, *storage, cmd); - return TaskAction::Continue; + return SetupResult::Continue; }; return {{AsyncTask(onSetup), storage}}; diff --git a/src/plugins/coreplugin/mainwindow.cpp b/src/plugins/coreplugin/mainwindow.cpp index c93710ff580..846b5d9dea2 100644 --- a/src/plugins/coreplugin/mainwindow.cpp +++ b/src/plugins/coreplugin/mainwindow.cpp @@ -1465,8 +1465,10 @@ void MainWindow::changeLog() return; const FilePath file = versionedFiles.at(index).second; QString contents = QString::fromUtf8(file.fileContents().value_or(QByteArray())); - static const QRegularExpression bugexpr("(QT(CREATOR)?BUG-[0-9]+)"); - contents.replace(bugexpr, "[\\1](https://bugreports.qt.io/browse/\\1)"); + // (? matches; for (const QRegularExpressionMatch &m : docexpr.globalMatch(contents)) diff --git a/src/plugins/coreplugin/mimetypesettings.cpp b/src/plugins/coreplugin/mimetypesettings.cpp index 116fcf4a06e..e3dac8ded96 100644 --- a/src/plugins/coreplugin/mimetypesettings.cpp +++ b/src/plugins/coreplugin/mimetypesettings.cpp @@ -83,9 +83,7 @@ public: class MimeTypeSettingsModel : public QAbstractTableModel { public: - enum class Role { - DefaultHandler = Qt::UserRole - }; + enum class Role { DefaultHandler = Qt::UserRole, MimeType }; MimeTypeSettingsModel(QObject *parent = nullptr) : QAbstractTableModel(parent) {} @@ -158,6 +156,8 @@ QVariant MimeTypeSettingsModel::data(const QModelIndex &modelIndex, int role) co } } return QVariant(); + } else if (role == int(Role::MimeType)) { + return QVariant::fromValue(m_mimeTypes.at(modelIndex.row())); } return QVariant(); } @@ -222,6 +222,37 @@ void MimeTypeSettingsModel::resetUserDefaults() endResetModel(); } +class MimeFilterModel : public QSortFilterProxyModel +{ +public: + explicit MimeFilterModel(QObject *parent = nullptr); + +protected: + bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override; +}; + +MimeFilterModel::MimeFilterModel(QObject *parent) + : QSortFilterProxyModel(parent) +{ +} + +bool MimeFilterModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const +{ + const QModelIndex &index = sourceModel()->index(source_row, 0, source_parent); + const MimeType mt + = sourceModel()->data(index, int(MimeTypeSettingsModel::Role::MimeType)).value(); + const QModelIndex &handlerIndex = sourceModel()->index(source_row, 1, source_parent); + const QString handlerText = sourceModel()->data(handlerIndex, Qt::DisplayRole).toString(); + + const QStringList matchStrings = mt.globPatterns() << mt.name() << handlerText; + const QRegularExpression regex = filterRegularExpression(); + for (const QString &str : matchStrings) { + if (regex.match(str).hasMatch()) + return true; + } + return false; +} + // MimeTypeSettingsPrivate class MimeTypeSettingsPrivate : public QObject { @@ -256,7 +287,7 @@ public: static UserMimeTypeHash m_userModifiedMimeTypes; // these are already in mime database MimeTypeSettingsModel *m_model; - QSortFilterProxyModel *m_filterModel; + MimeFilterModel *m_filterModel; UserMimeTypeHash m_pendingModifiedMimeTypes; // currently edited in the options page QString m_filterPattern; QPointer m_widget; @@ -277,10 +308,11 @@ MimeTypeSettingsPrivate::UserMimeTypeHash MimeTypeSettingsPrivate::m_userModifie MimeTypeSettingsPrivate::MimeTypeSettingsPrivate() : m_model(new MimeTypeSettingsModel(this)) - , m_filterModel(new QSortFilterProxyModel(this)) + , m_filterModel(new MimeFilterModel(this)) { m_filterModel->setSourceModel(m_model); m_filterModel->setFilterKeyColumn(-1); + m_filterModel->setFilterCaseSensitivity(Qt::CaseInsensitive); connect(ICore::instance(), &ICore::saveSettingsRequested, this, &MimeTypeSettingsPrivate::writeUserModifiedMimeTypes); } @@ -317,6 +349,7 @@ void MimeTypeSettingsPrivate::configureUi(QWidget *w) auto filterLineEdit = new FancyLineEdit; filterLineEdit->setObjectName("filterLineEdit"); filterLineEdit->setFiltering(true); + m_filterModel->setFilterWildcard({}); m_mimeTypesTreeView = new QTreeView; m_mimeTypesTreeView->setObjectName("mimeTypesTreeView"); diff --git a/src/plugins/cppeditor/cppcompletionassist.cpp b/src/plugins/cppeditor/cppcompletionassist.cpp index 5466f675eaf..4865f3dfedc 100644 --- a/src/plugins/cppeditor/cppcompletionassist.cpp +++ b/src/plugins/cppeditor/cppcompletionassist.cpp @@ -37,6 +37,8 @@ #include #include +#include + using namespace CPlusPlus; using namespace CppEditor; using namespace TextEditor; @@ -847,27 +849,27 @@ bool InternalCppCompletionAssistProcessor::accepts() const IAssistProposal *InternalCppCompletionAssistProcessor::createContentProposal() { // Duplicates are kept only if they are snippets. - QSet processed; - auto it = m_completions.begin(); - while (it != m_completions.end()) { - auto item = static_cast(*it); - if (!processed.contains(item->text()) || item->isSnippet()) { + std::set processed; + for (auto it = m_completions.begin(); it != m_completions.end();) { + if ((*it)->isSnippet()) { ++it; - if (!item->isSnippet()) { - processed.insert(item->text()); - if (!item->isOverloaded()) { - if (auto symbol = qvariant_cast(item->data())) { - if (Function *funTy = symbol->type()->asFunctionType()) { - if (funTy->hasArguments()) - item->markAsOverloaded(); - } - } - } - } - } else { + continue; + } + if (!processed.insert((*it)->text()).second) { delete *it; it = m_completions.erase(it); + continue; } + auto item = static_cast(*it); + if (!item->isOverloaded()) { + if (auto symbol = qvariant_cast(item->data())) { + if (Function *funTy = symbol->type()->asFunctionType()) { + if (funTy->hasArguments()) + item->markAsOverloaded(); + } + } + } + ++it; } m_model->loadContent(m_completions); diff --git a/src/plugins/cppeditor/cppeditordocument.cpp b/src/plugins/cppeditor/cppeditordocument.cpp index a1d01575c58..0398674941c 100644 --- a/src/plugins/cppeditor/cppeditordocument.cpp +++ b/src/plugins/cppeditor/cppeditordocument.cpp @@ -435,10 +435,10 @@ TextEditor::TabSettings CppEditorDocument::tabSettings() const return indenter()->tabSettings().value_or(TextEditor::TextDocument::tabSettings()); } -bool CppEditorDocument::save(QString *errorString, const FilePath &filePath, bool autoSave) +bool CppEditorDocument::saveImpl(QString *errorString, const FilePath &filePath, bool autoSave) { if (!indenter()->formatOnSave() || autoSave) - return TextEditor::TextDocument::save(errorString, filePath, autoSave); + return TextEditor::TextDocument::saveImpl(errorString, filePath, autoSave); auto *layout = qobject_cast(document()->documentLayout()); const int documentRevision = layout->lastSaveRevision; @@ -476,7 +476,7 @@ bool CppEditorDocument::save(QString *errorString, const FilePath &filePath, boo settings.m_cleanWhitespace = false; setStorageSettings(settings); - return TextEditor::TextDocument::save(errorString, filePath, autoSave); + return TextEditor::TextDocument::saveImpl(errorString, filePath, autoSave); } bool CppEditorDocument::usesClangd() const diff --git a/src/plugins/cppeditor/cppeditordocument.h b/src/plugins/cppeditor/cppeditordocument.h index 18b3deb56f3..734c64b665f 100644 --- a/src/plugins/cppeditor/cppeditordocument.h +++ b/src/plugins/cppeditor/cppeditordocument.h @@ -47,10 +47,6 @@ public: QFuture cursorInfo(const CursorInfoParams ¶ms); TextEditor::TabSettings tabSettings() const override; - bool save(QString *errorString, - const Utils::FilePath &filePath = Utils::FilePath(), - bool autoSave = false) override; - bool usesClangd() const; signals: @@ -68,8 +64,12 @@ signals: protected: void applyFontSettings() override; + bool saveImpl(QString *errorString, + const Utils::FilePath &filePath = Utils::FilePath(), + bool autoSave = false) override; private: + void invalidateFormatterCache(); void onFilePathChanged(const Utils::FilePath &oldPath, const Utils::FilePath &newPath); void onMimeTypeChanged(); diff --git a/src/plugins/cppeditor/cppeditorwidget.cpp b/src/plugins/cppeditor/cppeditorwidget.cpp index edc65f76526..a6daf681db5 100644 --- a/src/plugins/cppeditor/cppeditorwidget.cpp +++ b/src/plugins/cppeditor/cppeditorwidget.cpp @@ -590,8 +590,7 @@ void CppEditorWidget::onCodeWarningsUpdated(unsigned revision, setExtraSelections(TextEditorWidget::CodeWarningsSelection, unselectLeadingWhitespace(selections)); - setRefactorMarkers(refactorMarkers + RefactorMarker::filterOutType( - this->refactorMarkers(), Constants::CPP_CLANG_FIXIT_AVAILABLE_MARKER_ID)); + setRefactorMarkers(refactorMarkers, Constants::CPP_CLANG_FIXIT_AVAILABLE_MARKER_ID); } void CppEditorWidget::onIfdefedOutBlocksUpdated(unsigned revision, diff --git a/src/plugins/cppeditor/cppfunctiondecldeflink.cpp b/src/plugins/cppeditor/cppfunctiondecldeflink.cpp index b43e87758e2..aa17721ea37 100644 --- a/src/plugins/cppeditor/cppfunctiondecldeflink.cpp +++ b/src/plugins/cppeditor/cppfunctiondecldeflink.cpp @@ -279,8 +279,7 @@ void FunctionDeclDefLink::hideMarker(CppEditorWidget *editor) { if (!hasMarker) return; - editor->setRefactorMarkers(RefactorMarker::filterOutType( - editor->refactorMarkers(), Constants::CPP_FUNCTION_DECL_DEF_LINK_MARKER_ID)); + editor->clearRefactorMarkers(Constants::CPP_FUNCTION_DECL_DEF_LINK_MARKER_ID); hasMarker = false; } @@ -289,8 +288,7 @@ void FunctionDeclDefLink::showMarker(CppEditorWidget *editor) if (hasMarker) return; - QList markers = RefactorMarker::filterOutType( - editor->refactorMarkers(), Constants::CPP_FUNCTION_DECL_DEF_LINK_MARKER_ID); + RefactorMarkers markers; RefactorMarker marker; // show the marker at the end of the linked area, with a special case @@ -321,7 +319,7 @@ void FunctionDeclDefLink::showMarker(CppEditorWidget *editor) cppEditor->applyDeclDefLinkChanges(true); }; markers += marker; - editor->setRefactorMarkers(markers); + editor->setRefactorMarkers(markers, Constants::CPP_FUNCTION_DECL_DEF_LINK_MARKER_ID); hasMarker = true; } diff --git a/src/plugins/cppeditor/cpphighlighter.cpp b/src/plugins/cppeditor/cpphighlighter.cpp index f5ef94efdee..656173feee2 100644 --- a/src/plugins/cppeditor/cpphighlighter.cpp +++ b/src/plugins/cppeditor/cpphighlighter.cpp @@ -621,8 +621,9 @@ void CppHighlighterTest::test() const QChar c = m_doc.characterAt(pos); if (c == QChar::ParagraphSeparator) continue; - const QTextCharFormat expectedFormat = c.isSpace() - ? whitespacified(formatForStyle) : formatForStyle; + const QTextCharFormat expectedFormat = asSyntaxHighlight( + c.isSpace() ? whitespacified(formatForStyle) : formatForStyle); + const QTextCharFormat actualFormat = getActualFormat(pos); if (actualFormat != expectedFormat) { int posLine; diff --git a/src/plugins/cppeditor/cppprojectinfogenerator.cpp b/src/plugins/cppeditor/cppprojectinfogenerator.cpp index 5c4c03de4cc..2f1f4b7cd7f 100644 --- a/src/plugins/cppeditor/cppprojectinfogenerator.cpp +++ b/src/plugins/cppeditor/cppprojectinfogenerator.cpp @@ -42,14 +42,14 @@ ProjectInfo::ConstPtr ProjectInfoGenerator::generate(const QPromise QString { if (call) @@ -353,6 +353,7 @@ QString declFromExpr(const TypeOrExpr &typeOrExpr, const CallAST *call, const Na return type.isValid() ? oo.prettyType(type, varName->name) : getTypeFromUser(); Function func(file->cppDocument()->translationUnit(), 0, varName->name); + func.setConst(makeConst); for (ExpressionListAST *it = call->expression_list; it; it = it->next) { Argument * const arg = new Argument(nullptr, 0, nullptr); arg->setType(getTypeOfExpr(it->value)); @@ -1671,7 +1672,7 @@ private: if (currentFile->cppDocument()->languageFeatures().cxx11Enabled && settings->useAuto) return "auto " + oo.prettyName(simpleNameAST->name); return declFromExpr(binaryAST->right_expression, nullptr, simpleNameAST, snapshot(), - context(), currentFile); + context(), currentFile, false); } const BinaryExpressionAST *binaryAST; @@ -2939,10 +2940,11 @@ public: const TypeOrExpr &typeOrExpr, const CallAST *call, InsertionPointLocator::AccessSpec accessSpec, - bool makeStatic) + bool makeStatic, + bool makeConst) : CppQuickFixOperation(interface), m_class(theClass), m_memberName(memberName), m_typeOrExpr(typeOrExpr), m_call(call), - m_accessSpec(accessSpec), m_makeStatic(makeStatic) + m_accessSpec(accessSpec), m_makeStatic(makeStatic), m_makeConst(makeConst) { if (call) setDescription(Tr::tr("Add Member Function \"%1\"").arg(nameString(memberName))); @@ -2954,7 +2956,7 @@ private: void perform() override { QString decl = declFromExpr(m_typeOrExpr, m_call, m_memberName, snapshot(), context(), - currentFile()); + currentFile(), m_makeConst); if (decl.isEmpty()) return; if (m_makeStatic) @@ -2983,6 +2985,7 @@ private: const CallAST * m_call; const InsertionPointLocator::AccessSpec m_accessSpec; const bool m_makeStatic; + const bool m_makeConst; }; void AddDeclarationForUndeclaredIdentifier::match(const CppQuickFixInterface &interface, @@ -3198,7 +3201,7 @@ bool AddDeclarationForUndeclaredIdentifier::checkForMemberInitializer( result << new InsertMemberFromInitializationOp( interface, theClass, memInitializer->name->asSimpleName(), memInitializer->expression, - nullptr, InsertionPointLocator::Private, false); + nullptr, InsertionPointLocator::Private, false, false); return false; } @@ -3268,7 +3271,8 @@ void AddDeclarationForUndeclaredIdentifier::maybeAddMember( } } result << new InsertMemberFromInitializationOp(interface, theClass, path.last()->asName(), - typeOrExpr, call, accessSpec, needsStatic); + typeOrExpr, call, accessSpec, needsStatic, + func->symbol->isConst()); } void AddDeclarationForUndeclaredIdentifier::maybeAddStaticMember( @@ -3311,7 +3315,7 @@ void AddDeclarationForUndeclaredIdentifier::maybeAddStaticMember( if (theClass) { result << new InsertMemberFromInitializationOp( interface, theClass, path.last()->asName(), typeOrExpr, call, - InsertionPointLocator::Public, true); + InsertionPointLocator::Public, true, false); } } diff --git a/src/plugins/debugger/breakhandler.cpp b/src/plugins/debugger/breakhandler.cpp index 180b2c0a07d..963aced456c 100644 --- a/src/plugins/debugger/breakhandler.cpp +++ b/src/plugins/debugger/breakhandler.cpp @@ -1107,7 +1107,7 @@ QVariant BreakpointItem::data(int column, int role) const return QVariant(); } -void BreakpointItem::addToCommand(DebuggerCommand *cmd) const +void BreakpointItem::addToCommand(DebuggerCommand *cmd, BreakpointPathUsage defaultPathUsage) const { QTC_ASSERT(m_globalBreakpoint, return); const BreakpointParameters &requested = requestedParameters(); @@ -1120,10 +1120,19 @@ void BreakpointItem::addToCommand(DebuggerCommand *cmd) const cmd->arg("function", requested.functionName); cmd->arg("oneshot", requested.oneShot); cmd->arg("enabled", requested.enabled); - cmd->arg("file", requested.fileName.path()); cmd->arg("line", requested.textPosition.line); cmd->arg("address", requested.address); cmd->arg("expression", requested.expression); + + BreakpointPathUsage pathUsage = (requested.pathUsage + == BreakpointPathUsage::BreakpointPathUsageEngineDefault) + ? defaultPathUsage + : requested.pathUsage; + + cmd->arg("file", + pathUsage == BreakpointPathUsage::BreakpointUseFullPath + ? requested.fileName.path() + : requested.fileName.fileName()); } void BreakpointItem::updateFromGdbOutput(const GdbMi &bkpt, const FilePath &fileRoot) diff --git a/src/plugins/debugger/breakhandler.h b/src/plugins/debugger/breakhandler.h index e850151c3be..0132e2bcaf6 100644 --- a/src/plugins/debugger/breakhandler.h +++ b/src/plugins/debugger/breakhandler.h @@ -107,7 +107,9 @@ public: int markerLineNumber() const; const BreakpointParameters &requestedParameters() const; - void addToCommand(DebuggerCommand *cmd) const; + void addToCommand(DebuggerCommand *cmd, + BreakpointPathUsage defaultPathUsage + = BreakpointPathUsage::BreakpointUseFullPath) const; void updateFromGdbOutput(const GdbMi &bkpt, const Utils::FilePath &fileRoot); int modelId() const; diff --git a/src/plugins/debugger/cdb/cdbengine.cpp b/src/plugins/debugger/cdb/cdbengine.cpp index c60052452a8..28d90c747cf 100644 --- a/src/plugins/debugger/cdb/cdbengine.cpp +++ b/src/plugins/debugger/cdb/cdbengine.cpp @@ -2289,9 +2289,9 @@ void CdbEngine::parseOutputLine(QString line) CheckableMessageBox::information( Core::ICore::dialogParent(), Tr::tr("Debugger Start Failed"), - Tr::tr("The system prevents loading of %1, which is required for debugging. " + Tr::tr("The system prevents loading of \"%1\", which is required for debugging. " "Make sure that your antivirus solution is up to date and if that does not work " - "consider adding an exception for %1.") + "consider adding an exception for \"%1\".") .arg(m_extensionFileName), QString("SecureInfoCdbextCannotBeLoaded")); notifyEngineSetupFailed(); @@ -2778,7 +2778,7 @@ void CdbEngine::setupScripting(const DebuggerResponse &response) if (!toLoad) { Core::AsynchronousMessageBox::critical( Tr::tr("Cannot Find Debugger Initialization Script"), - Tr::tr("Cannot read %1: %2").arg(loadOrderFile.toUserOutput(), toLoad.error())); + Tr::tr("Cannot read \"%1\": %2").arg(loadOrderFile.toUserOutput(), toLoad.error())); notifyEngineSetupFailed(); return; } @@ -2795,7 +2795,8 @@ void CdbEngine::setupScripting(const DebuggerResponse &response) const FilePath codeFile = dumperPath / (module + ".py"); const expected_str code = codeFile.fileContents(); if (!code) { - qDebug() << Tr::tr("Cannot read %1: %2").arg(codeFile.toUserOutput(), code.error()); + qDebug() << Tr::tr("Cannot read \"%1\": %2") + .arg(codeFile.toUserOutput(), code.error()); continue; } diff --git a/src/plugins/debugger/debuggerengine.h b/src/plugins/debugger/debuggerengine.h index 783fa9b9f17..cc6baf32d28 100644 --- a/src/plugins/debugger/debuggerengine.h +++ b/src/plugins/debugger/debuggerengine.h @@ -157,6 +157,7 @@ public: Utils::FilePath debugInfoLocation; // Gdb "set-debug-file-directory". QStringList debugSourceLocation; // Gdb "directory" QString qtPackageSourceLocation; + Utils::FilePath qtSourceLocation; bool isSnapshot = false; // Set if created internally. ProjectExplorer::Abi toolChainAbi; diff --git a/src/plugins/debugger/debuggeritemmanager.cpp b/src/plugins/debugger/debuggeritemmanager.cpp index aca5a976359..2c97bdc8456 100644 --- a/src/plugins/debugger/debuggeritemmanager.cpp +++ b/src/plugins/debugger/debuggeritemmanager.cpp @@ -221,7 +221,7 @@ DebuggerItemModel::DebuggerItemModel() genericGdb.setEngineType(GdbEngineType); genericGdb.setAbi(Abi()); genericGdb.setCommand("gdb"); - genericGdb.setUnexpandedDisplayName(Tr::tr("%1 from PATH on Build Device").arg("GDB")); + genericGdb.setUnexpandedDisplayName(Tr::tr("GDB from PATH on Build Device")); generic->appendChild(new DebuggerTreeItem(genericGdb, false)); DebuggerItem genericLldb(QVariant("lldb")); @@ -230,7 +230,7 @@ DebuggerItemModel::DebuggerItemModel() genericLldb.setGeneric(true); genericLldb.setAbi(Abi()); genericLldb.setCommand("lldb"); - genericLldb.setUnexpandedDisplayName(Tr::tr("%1 from PATH on Build Device").arg("LLDB")); + genericLldb.setUnexpandedDisplayName(Tr::tr("LLDB from PATH on Build Device")); generic->appendChild(new DebuggerTreeItem(genericLldb, false)); } diff --git a/src/plugins/debugger/debuggerrunconfigurationaspect.cpp b/src/plugins/debugger/debuggerrunconfigurationaspect.cpp index 8c118f553b6..8cf92fe1da8 100644 --- a/src/plugins/debugger/debuggerrunconfigurationaspect.cpp +++ b/src/plugins/debugger/debuggerrunconfigurationaspect.cpp @@ -73,19 +73,19 @@ DebuggerRunConfigurationAspect::DebuggerRunConfigurationAspect(Target *target) const auto setSummaryText = [this, details] { QStringList items; if (m_cppAspect->value() == TriState::Enabled) - items.append(Tr::tr("Enable C++ debugger")); + items.append(Tr::tr("Enable C++ debugger.")); else if (m_cppAspect->value() == TriState::Default) - items.append(Tr::tr("Try to determine need for C++ debugger")); + items.append(Tr::tr("Try to determine need for C++ debugger.")); if (m_qmlAspect->value() == TriState::Enabled) - items.append(Tr::tr("Enable QML debugger")); + items.append(Tr::tr("Enable QML debugger.")); else if (m_qmlAspect->value() == TriState::Default) - items.append(Tr::tr("Try to determine need for QML debugger")); + items.append(Tr::tr("Try to determine need for QML debugger.")); items.append(m_overrideStartupAspect->value().isEmpty() - ? Tr::tr("Without additional startup commands") - : Tr::tr("With additional startup commands")); - details->setSummaryText(items.join(". ")); + ? Tr::tr("Without additional startup commands.") + : Tr::tr("With additional startup commands.")); + details->setSummaryText(items.join(" ")); }; setSummaryText(); diff --git a/src/plugins/debugger/debuggerruncontrol.cpp b/src/plugins/debugger/debuggerruncontrol.cpp index e3feb04e393..ed4a34f3067 100644 --- a/src/plugins/debugger/debuggerruncontrol.cpp +++ b/src/plugins/debugger/debuggerruncontrol.cpp @@ -863,8 +863,10 @@ DebuggerRunTool::DebuggerRunTool(RunControl *runControl, AllowTerminal allowTerm m_runParameters.debugger = DebuggerKitAspect::runnable(kit); m_runParameters.cppEngineType = DebuggerKitAspect::engineType(kit); - if (QtSupport::QtVersion *qtVersion = QtSupport::QtKitAspect::qtVersion(kit)) + if (QtSupport::QtVersion *qtVersion = QtSupport::QtKitAspect::qtVersion(kit)) { m_runParameters.qtPackageSourceLocation = qtVersion->qtPackageSourcePath().toString(); + m_runParameters.qtSourceLocation = qtVersion->sourcePath(); + } if (auto aspect = runControl->aspect()) { if (!aspect->useCppDebugger) diff --git a/src/plugins/debugger/debuggersourcepathmappingwidget.cpp b/src/plugins/debugger/debuggersourcepathmappingwidget.cpp index 9d3af533bcc..0c98ba174b0 100644 --- a/src/plugins/debugger/debuggersourcepathmappingwidget.cpp +++ b/src/plugins/debugger/debuggersourcepathmappingwidget.cpp @@ -415,47 +415,18 @@ void DebuggerSourcePathMappingWidget::slotEditTargetFieldChanged() } } -// Find Qt installation by running qmake -static QString findQtInstallPath(const FilePath &qmakePath) -{ - if (qmakePath.isEmpty()) - return QString(); - Process proc; - proc.setCommand({qmakePath, {"-query", "QT_INSTALL_HEADERS"}}); - proc.start(); - if (!proc.waitForFinished()) { - qWarning("%s: Timeout running '%s'.", Q_FUNC_INFO, qPrintable(qmakePath.toString())); - return QString(); - } - if (proc.exitStatus() != QProcess::NormalExit) { - qWarning("%s: '%s' crashed.", Q_FUNC_INFO, qPrintable(qmakePath.toString())); - return QString(); - } - const QByteArray ba = proc.readAllRawStandardOutput().trimmed(); - QDir dir(QString::fromLocal8Bit(ba)); - if (dir.exists() && dir.cdUp()) - return dir.absolutePath(); - return QString(); -} - /* Merge settings for an installed Qt (unless another setting is already in the map. */ SourcePathMap mergePlatformQtPath(const DebuggerRunParameters &sp, const SourcePathMap &in) { - const FilePath qmake = BuildableHelperLibrary::findSystemQt(sp.inferior.environment); - // FIXME: Get this from the profile? - // We could query the QtVersion for this information directly, but then we - // will need to add a dependency on QtSupport to the debugger. - // - // The profile could also get a function to extract the required information from - // its information to avoid this dependency (as we do for the environment). - const QString qtInstallPath = findQtInstallPath(qmake); - if (qtInstallPath.isEmpty()) + static const QString qglobal = "qtbase/src/corelib/global/qglobal.h"; + const FilePath sourceLocation = sp.qtSourceLocation; + if (!(sourceLocation / qglobal).exists()) return in; SourcePathMap rc = in; for (const QString &buildPath : qtBuildPaths()) { if (!rc.contains(buildPath)) // Do not overwrite user settings. - rc.insert(buildPath, qtInstallPath + "/../Src"); + rc.insert(buildPath, sourceLocation.path()); } return rc; } diff --git a/src/plugins/debugger/gdb/gdbengine.cpp b/src/plugins/debugger/gdb/gdbengine.cpp index e5052d1b89d..d52e2bc7104 100644 --- a/src/plugins/debugger/gdb/gdbengine.cpp +++ b/src/plugins/debugger/gdb/gdbengine.cpp @@ -3976,9 +3976,9 @@ void GdbEngine::handleGdbStarted() const FilePath loadOrderFile = dumperPath / "loadorder.txt"; const expected_str toLoad = loadOrderFile.fileContents(); if (!toLoad) { - AsynchronousMessageBox::critical( - Tr::tr("Cannot Find Debugger Initialization Script"), - Tr::tr("Cannot read %1: %2").arg(loadOrderFile.toUserOutput(), toLoad.error())); + AsynchronousMessageBox::critical(Tr::tr("Cannot Find Debugger Initialization Script"), + Tr::tr("Cannot read \"%1\": %2") + .arg(loadOrderFile.toUserOutput(), toLoad.error())); notifyEngineSetupFailed(); return; } @@ -3995,7 +3995,8 @@ void GdbEngine::handleGdbStarted() const FilePath codeFile = dumperPath / (module + ".py"); const expected_str code = codeFile.fileContents(); if (!code) { - qDebug() << Tr::tr("Cannot read %1: %2").arg(codeFile.toUserOutput(), code.error()); + qDebug() << Tr::tr("Cannot read \"%1\": %2") + .arg(codeFile.toUserOutput(), code.error()); continue; } diff --git a/src/plugins/debugger/qml/qmlengine.cpp b/src/plugins/debugger/qml/qmlengine.cpp index c0ecaf4849f..6c7962088a7 100644 --- a/src/plugins/debugger/qml/qmlengine.cpp +++ b/src/plugins/debugger/qml/qmlengine.cpp @@ -879,7 +879,7 @@ static ConsoleItem *constructLogItemTree(const QVariant &result, item->appendChild(child); } - } else if (result.type() == QVariant::List) { + } else if (result.typeId() == QVariant::List) { if (key.isEmpty()) text = "List"; else @@ -1354,9 +1354,9 @@ void QmlEnginePrivate::scripts(int types, const QList ids, bool includeSour if (includeSource) cmd.arg(INCLUDESOURCE, includeSource); - if (filter.type() == QVariant::String) + if (filter.typeId() == QVariant::String) cmd.arg(FILTER, filter.toString()); - else if (filter.type() == QVariant::Int) + else if (filter.typeId() == QVariant::Int) cmd.arg(FILTER, filter.toInt()); else QTC_CHECK(!filter.isValid()); @@ -2039,7 +2039,7 @@ StackFrame QmlEnginePrivate::extractStackFrame(const QVariant &bodyVal) } auto extractString = [this](const QVariant &item) { - return ((item.type() == QVariant::String) ? item : extractData(item).value).toString(); + return (item.typeId() == QVariant::String ? item : extractData(item).value).toString(); }; stackFrame.function = extractString(body.value("func")); diff --git a/src/plugins/debugger/terminal.cpp b/src/plugins/debugger/terminal.cpp index ac8a043df86..86531d0e0ad 100644 --- a/src/plugins/debugger/terminal.cpp +++ b/src/plugins/debugger/terminal.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include @@ -172,9 +173,18 @@ void TerminalRunner::start() QTC_ASSERT(!m_stubProc, reportFailure({}); return); Runnable stub = m_stubRunnable(); + bool runAsRoot = false; + if (auto runAsRootAspect = runControl()->aspect()) + runAsRoot = runAsRootAspect->value; + m_stubProc = new Process(this); m_stubProc->setTerminalMode(TerminalMode::Debug); + if (runAsRoot) { + m_stubProc->setRunAsRoot(runAsRoot); + RunControl::provideAskPassEntry(stub.environment); + } + connect(m_stubProc, &Process::started, this, &TerminalRunner::stubStarted); connect(m_stubProc, &Process::done, diff --git a/src/plugins/debugger/watchhandler.cpp b/src/plugins/debugger/watchhandler.cpp index 5324c7a8dcb..45a3277f702 100644 --- a/src/plugins/debugger/watchhandler.cpp +++ b/src/plugins/debugger/watchhandler.cpp @@ -2517,9 +2517,9 @@ void WatchModel::showEditValue(const WatchItem *item) else if (format == DisplayUtf8String) str = QString::fromUtf8(ba.constData(), ba.size()); else if (format == DisplayUtf16String) - str = QString::fromUtf16((ushort *)ba.constData(), ba.size() / 2); + str = QString::fromUtf16(reinterpret_cast(ba.constData()), ba.size() / 2); else if (format == DisplayUcs4String) - str = QString::fromUcs4((uint *)ba.constData(), ba.size() / 4); + str = QString::fromUcs4(reinterpret_cast(ba.constData()), ba.size() / 4); m_separatedView->prepareObject(item)->setPlainText(str); } else if (format == DisplayPlotData) { // Plots diff --git a/src/plugins/designer/formeditor.cpp b/src/plugins/designer/formeditor.cpp index 96e65796500..ab2dd3ddd1e 100644 --- a/src/plugins/designer/formeditor.cpp +++ b/src/plugins/designer/formeditor.cpp @@ -647,7 +647,7 @@ ActionContainer *FormEditorData::createPreviewStyleMenu(QActionGroup *actionGrou QString name = menuId; name += dot; const QVariant data = a->data(); - const bool isDeviceProfile = data.type() == QVariant::Int; + const bool isDeviceProfile = data.typeId() == QVariant::Int; if (isDeviceProfile) { name += deviceProfilePrefix; name += dot; diff --git a/src/plugins/designer/formwindowfile.cpp b/src/plugins/designer/formwindowfile.cpp index caad3c1c7e1..7ab08540933 100644 --- a/src/plugins/designer/formwindowfile.cpp +++ b/src/plugins/designer/formwindowfile.cpp @@ -82,7 +82,7 @@ Core::IDocument::OpenResult FormWindowFile::open(QString *errorString, return OpenResult::Success; } -bool FormWindowFile::save(QString *errorString, const FilePath &filePath, bool autoSave) +bool FormWindowFile::saveImpl(QString *errorString, const FilePath &filePath, bool autoSave) { const FilePath &actualName = filePath.isEmpty() ? this->filePath() : filePath; diff --git a/src/plugins/designer/formwindowfile.h b/src/plugins/designer/formwindowfile.h index 8110d72b991..a392e1ee59a 100644 --- a/src/plugins/designer/formwindowfile.h +++ b/src/plugins/designer/formwindowfile.h @@ -28,7 +28,6 @@ public: // IDocument OpenResult open(QString *errorString, const Utils::FilePath &filePath, const Utils::FilePath &realFilePath) override; - bool save(QString *errorString, const Utils::FilePath &filePath, bool autoSave) override; QByteArray contents() const override; bool setContents(const QByteArray &contents) override; bool shouldAutoSave() const override; @@ -52,6 +51,9 @@ public: void setShouldAutoSave(bool sad = true) { m_shouldAutoSave = sad; } void updateIsModified(); +protected: + bool saveImpl(QString *errorString, const Utils::FilePath &filePath, bool autoSave) override; + private: void slotFormWindowRemoved(QDesignerFormWindowInterface *w); diff --git a/src/plugins/diffeditor/diffeditor.cpp b/src/plugins/diffeditor/diffeditor.cpp index 929c30c3398..ddf91b4e1ec 100644 --- a/src/plugins/diffeditor/diffeditor.cpp +++ b/src/plugins/diffeditor/diffeditor.cpp @@ -51,8 +51,7 @@ using namespace Core; using namespace TextEditor; using namespace Utils; -namespace DiffEditor { -namespace Internal { +namespace DiffEditor::Internal { class DescriptionEditorWidget : public TextEditorWidget { @@ -618,7 +617,6 @@ void DiffEditor::showDiffView(IDiffView *view) setupView(view); } -} // namespace Internal -} // namespace DiffEditor +} // namespace DiffEditor::Internal #include "diffeditor.moc" diff --git a/src/plugins/diffeditor/diffeditor.h b/src/plugins/diffeditor/diffeditor.h index 49922d323eb..24f89d55030 100644 --- a/src/plugins/diffeditor/diffeditor.h +++ b/src/plugins/diffeditor/diffeditor.h @@ -18,9 +18,8 @@ QT_END_NAMESPACE namespace TextEditor { class TextEditorWidget; } -namespace DiffEditor { +namespace DiffEditor::Internal { -namespace Internal { class DescriptionEditorWidget; class DiffEditorDocument; class IDiffView; @@ -95,5 +94,4 @@ private: bool m_showDescription = true; }; -} // namespace Internal -} // namespace DiffEditor +} // namespace DiffEditor::Internal diff --git a/src/plugins/diffeditor/diffeditorcontroller.cpp b/src/plugins/diffeditor/diffeditorcontroller.cpp index 1ab7bfccb2d..4756afc7828 100644 --- a/src/plugins/diffeditor/diffeditorcontroller.cpp +++ b/src/plugins/diffeditor/diffeditorcontroller.cpp @@ -124,17 +124,20 @@ void DiffEditorController::reloadFinished(bool success) m_document->endReload(success); } +void DiffEditorController::addExtraActions(QMenu *menu, int fileIndex, int chunkIndex, + const ChunkSelection &selection) +{ + Q_UNUSED(menu) + Q_UNUSED(fileIndex) + Q_UNUSED(chunkIndex) + Q_UNUSED(selection) +} + void DiffEditorController::setStartupFile(const QString &startupFile) { m_document->setStartupFile(startupFile); } -void DiffEditorController::requestChunkActions(QMenu *menu, int fileIndex, int chunkIndex, - const ChunkSelection &selection) -{ - emit chunkActionsRequested(menu, fileIndex, chunkIndex, selection); -} - bool DiffEditorController::chunkExists(int fileIndex, int chunkIndex) const { if (!m_document) diff --git a/src/plugins/diffeditor/diffeditorcontroller.h b/src/plugins/diffeditor/diffeditorcontroller.h index 1f791b2dcb3..1fddc31c4dd 100644 --- a/src/plugins/diffeditor/diffeditorcontroller.h +++ b/src/plugins/diffeditor/diffeditorcontroller.h @@ -19,7 +19,10 @@ namespace Utils { class FilePath; } namespace DiffEditor { -namespace Internal { class DiffEditorDocument; } +namespace Internal { +class DiffEditorDocument; +class DiffEditorWidgetController; +} class ChunkSelection; @@ -30,12 +33,9 @@ public: explicit DiffEditorController(Core::IDocument *document); void requestReload(); - bool isReloading() const; Utils::FilePath workingDirectory() const; void setWorkingDirectory(const Utils::FilePath &directory); - int contextLineCount() const; - bool ignoreWhitespace() const; enum PatchOption { NoOption = 0, @@ -43,23 +43,19 @@ public: AddPrefix = 2 }; Q_DECLARE_FLAGS(PatchOptions, PatchOption) + + static Core::IDocument *findOrCreateDocument(const QString &vcsId, const QString &displayName); + static DiffEditorController *controller(Core::IDocument *document); + +protected: + bool isReloading() const; + int contextLineCount() const; + bool ignoreWhitespace() const; + bool chunkExists(int fileIndex, int chunkIndex) const; + Core::IDocument *document() const; QString makePatch(int fileIndex, int chunkIndex, const ChunkSelection &selection, PatchOptions options) const; - static Core::IDocument *findOrCreateDocument(const QString &vcsId, - const QString &displayName); - static DiffEditorController *controller(Core::IDocument *document); - - void requestChunkActions(QMenu *menu, int fileIndex, int chunkIndex, - const ChunkSelection &selection); - bool chunkExists(int fileIndex, int chunkIndex) const; - Core::IDocument *document() const; - -signals: - void chunkActionsRequested(QMenu *menu, int fileIndex, int chunkIndex, - const ChunkSelection &selection); - -protected: // Core functions: void setReloadRecipe(const Tasking::Group &recipe) { m_reloadRecipe = recipe; } void setDiffFiles(const QList &diffFileList); @@ -71,6 +67,9 @@ protected: private: void reloadFinished(bool success); + friend class Internal::DiffEditorWidgetController; + virtual void addExtraActions(QMenu *menu, int fileIndex, int chunkIndex, + const ChunkSelection &selection); Internal::DiffEditorDocument *const m_document; QString m_displayName; diff --git a/src/plugins/diffeditor/diffeditordocument.cpp b/src/plugins/diffeditor/diffeditordocument.cpp index 1d43064d60f..39b467a3dcc 100644 --- a/src/plugins/diffeditor/diffeditordocument.cpp +++ b/src/plugins/diffeditor/diffeditordocument.cpp @@ -24,8 +24,7 @@ using namespace Core; using namespace Utils; -namespace DiffEditor { -namespace Internal { +namespace DiffEditor::Internal { DiffEditorDocument::DiffEditorDocument() : Core::BaseTextDocument() @@ -236,7 +235,7 @@ bool DiffEditorDocument::isSaveAsAllowed() const return state() == LoadOK; } -bool DiffEditorDocument::save(QString *errorString, const FilePath &filePath, bool autoSave) +bool DiffEditorDocument::saveImpl(QString *errorString, const FilePath &filePath, bool autoSave) { Q_UNUSED(errorString) Q_UNUSED(autoSave) @@ -388,5 +387,4 @@ void DiffEditorDocument::endReload(bool success) emit reloadFinished(success); } -} // namespace Internal -} // namespace DiffEditor +} // namespace DiffEditor::Internal diff --git a/src/plugins/diffeditor/diffeditordocument.h b/src/plugins/diffeditor/diffeditordocument.h index 9bf8573a785..6a7e72ae373 100644 --- a/src/plugins/diffeditor/diffeditordocument.h +++ b/src/plugins/diffeditor/diffeditordocument.h @@ -60,7 +60,6 @@ public: QString fallbackSaveAsFileName() const override; bool isSaveAsAllowed() const override; - bool save(QString *errorString, const Utils::FilePath &filePath, bool autoSave) override; void reload(); bool reload(QString *errorString, ReloadFlag flag, ChangeType type) override; OpenResult open(QString *errorString, const Utils::FilePath &filePath, @@ -75,6 +74,9 @@ signals: void documentChanged(); void descriptionChanged(); +protected: + bool saveImpl(QString *errorString, const Utils::FilePath &filePath, bool autoSave) override; + private: void beginReload(); void endReload(bool success); diff --git a/src/plugins/diffeditor/diffeditorfactory.cpp b/src/plugins/diffeditor/diffeditorfactory.cpp index a771fda53a1..70537dd3f73 100644 --- a/src/plugins/diffeditor/diffeditorfactory.cpp +++ b/src/plugins/diffeditor/diffeditorfactory.cpp @@ -15,8 +15,7 @@ using namespace Core; using namespace TextEditor; using namespace Utils; -namespace DiffEditor { -namespace Internal { +namespace DiffEditor::Internal { DiffEditorFactory::DiffEditorFactory() : descriptionHandler { @@ -50,5 +49,4 @@ DiffEditorFactory::DiffEditorFactory() : setEditorCreator([] { return new DiffEditor(new DiffEditorDocument); }); } -} // namespace Internal -} // namespace DiffEditor +} // namespace DiffEditor::Internal diff --git a/src/plugins/diffeditor/diffeditorfactory.h b/src/plugins/diffeditor/diffeditorfactory.h index bebabb208aa..e39f0fc46fc 100644 --- a/src/plugins/diffeditor/diffeditorfactory.h +++ b/src/plugins/diffeditor/diffeditorfactory.h @@ -7,8 +7,7 @@ #include -namespace DiffEditor { -namespace Internal { +namespace DiffEditor::Internal { class DiffEditorFactory : public Core::IEditorFactory { @@ -22,5 +21,4 @@ private: TextEditor::TextEditorActionHandler rightHandler; }; -} // namespace Internal -} // namespace DiffEditor +} // namespace DiffEditor::Internal diff --git a/src/plugins/diffeditor/diffeditorplugin.cpp b/src/plugins/diffeditor/diffeditorplugin.cpp index 10ae3b76a26..3112170bc41 100644 --- a/src/plugins/diffeditor/diffeditorplugin.cpp +++ b/src/plugins/diffeditor/diffeditorplugin.cpp @@ -29,8 +29,7 @@ using namespace Core; using namespace TextEditor; using namespace Utils; -namespace DiffEditor { -namespace Internal { +namespace DiffEditor::Internal { class ReloadInput { public: @@ -535,8 +534,7 @@ void DiffEditorPlugin::initialize() d = new DiffEditorPluginPrivate; } -} // namespace Internal -} // namespace DiffEditor +} // namespace DiffEditor::Internal #ifdef WITH_TESTS diff --git a/src/plugins/diffeditor/diffeditorplugin.h b/src/plugins/diffeditor/diffeditorplugin.h index 17b6a2d430d..9888628daac 100644 --- a/src/plugins/diffeditor/diffeditorplugin.h +++ b/src/plugins/diffeditor/diffeditorplugin.h @@ -6,8 +6,7 @@ #include #include -namespace DiffEditor { -namespace Internal { +namespace DiffEditor::Internal { class DiffEditorServiceImpl : public QObject, public Core::DiffService { @@ -46,5 +45,4 @@ private slots: #endif // WITH_TESTS }; -} // namespace Internal -} // namespace DiffEditor +} // namespace DiffEditor::Internal diff --git a/src/plugins/diffeditor/diffeditorwidgetcontroller.cpp b/src/plugins/diffeditor/diffeditorwidgetcontroller.cpp index e3f321c7c44..0f3a57ee086 100644 --- a/src/plugins/diffeditor/diffeditorwidgetcontroller.cpp +++ b/src/plugins/diffeditor/diffeditorwidgetcontroller.cpp @@ -29,8 +29,7 @@ using namespace Core; using namespace TextEditor; using namespace Utils; -namespace DiffEditor { -namespace Internal { +namespace DiffEditor::Internal { DiffEditorWidgetController::DiffEditorWidgetController(QWidget *diffEditorWidget) : QObject(diffEditorWidget) @@ -280,7 +279,7 @@ void DiffEditorWidgetController::addExtraActions(QMenu *menu, int fileIndex, int const ChunkSelection &selection) { if (DiffEditorController *controller = m_document->controller()) - controller->requestChunkActions(menu, fileIndex, chunkIndex, selection); + controller->addExtraActions(menu, fileIndex, chunkIndex, selection); } void DiffEditorWidgetController::updateCannotDecodeInfo() @@ -330,6 +329,4 @@ DiffEditorInput::DiffEditorInput(DiffEditorWidgetController *controller) , m_charFormat{&controller->m_charFormat[LeftSide], &controller->m_charFormat[RightSide]} { } - -} // namespace Internal -} // namespace DiffEditor +} // namespace DiffEditor::Internal diff --git a/src/plugins/diffeditor/diffview.cpp b/src/plugins/diffeditor/diffview.cpp index 686f9798b23..1b513aefc33 100644 --- a/src/plugins/diffeditor/diffview.cpp +++ b/src/plugins/diffeditor/diffview.cpp @@ -14,8 +14,7 @@ #include -namespace DiffEditor { -namespace Internal { +namespace DiffEditor::Internal { IDiffView::IDiffView(QObject *parent) : QObject(parent) { } @@ -230,5 +229,4 @@ void SideBySideView::setSync(bool sync) m_widget->setHorizontalSync(sync); } -} // namespace Internal -} // namespace DiffEditor +} // namespace DiffEditor::Internal diff --git a/src/plugins/diffeditor/selectabletexteditorwidget.cpp b/src/plugins/diffeditor/selectabletexteditorwidget.cpp index bbf0480a6bb..e0c95f6abd3 100644 --- a/src/plugins/diffeditor/selectabletexteditorwidget.cpp +++ b/src/plugins/diffeditor/selectabletexteditorwidget.cpp @@ -13,8 +13,7 @@ using namespace TextEditor; -namespace DiffEditor { -namespace Internal { +namespace DiffEditor::Internal { SelectableTextEditorWidget::SelectableTextEditorWidget(Utils::Id id, QWidget *parent) : TextEditorWidget(parent) @@ -142,5 +141,4 @@ void SelectableTextEditorWidget::paintBlock(QPainter *painter, TextEditorWidget::paintBlock(painter, block, offset, newSelections, clipRect); } -} // namespace Internal -} // namespace DiffEditor +} // namespace DiffEditor::Internal diff --git a/src/plugins/diffeditor/selectabletexteditorwidget.h b/src/plugins/diffeditor/selectabletexteditorwidget.h index ca128aa28e5..4b0d465204f 100644 --- a/src/plugins/diffeditor/selectabletexteditorwidget.h +++ b/src/plugins/diffeditor/selectabletexteditorwidget.h @@ -7,8 +7,7 @@ namespace TextEditor { class DisplaySettings; } -namespace DiffEditor { -namespace Internal { +namespace DiffEditor::Internal { class DiffSelection { @@ -46,5 +45,4 @@ private: DiffSelections m_diffSelections; }; -} // namespace Internal -} // namespace DiffEditor +} // namespace DiffEditor::Internal diff --git a/src/plugins/diffeditor/sidebysidediffeditorwidget.cpp b/src/plugins/diffeditor/sidebysidediffeditorwidget.cpp index ef8e2504972..341edce7334 100644 --- a/src/plugins/diffeditor/sidebysidediffeditorwidget.cpp +++ b/src/plugins/diffeditor/sidebysidediffeditorwidget.cpp @@ -35,8 +35,7 @@ using namespace Utils; using namespace std::placeholders; -namespace DiffEditor { -namespace Internal { +namespace DiffEditor::Internal { static DiffSide oppositeSide(DiffSide side) { @@ -1104,7 +1103,6 @@ void SideBySideDiffEditorWidget::syncCursor(SideDiffEditorWidget *source, SideDi dest->horizontalScrollBar()->setValue(oldHSliderPos); } -} // namespace Internal -} // namespace DiffEditor +} // namespace DiffEditor::Internal #include "sidebysidediffeditorwidget.moc" diff --git a/src/plugins/diffeditor/unifieddiffeditorwidget.cpp b/src/plugins/diffeditor/unifieddiffeditorwidget.cpp index 0c06c85bae0..d5633d50ec0 100644 --- a/src/plugins/diffeditor/unifieddiffeditorwidget.cpp +++ b/src/plugins/diffeditor/unifieddiffeditorwidget.cpp @@ -28,8 +28,7 @@ using namespace Core; using namespace TextEditor; using namespace Utils; -namespace DiffEditor { -namespace Internal { +namespace DiffEditor::Internal { UnifiedDiffEditorWidget::UnifiedDiffEditorWidget(QWidget *parent) : SelectableTextEditorWidget("DiffEditor.UnifiedDiffEditor", parent) @@ -589,5 +588,4 @@ void UnifiedDiffEditorWidget::setCurrentDiffFileIndex(int diffFileIndex) verticalScrollBar()->setValue(blockNumber); } -} // namespace Internal -} // namespace DiffEditor +} // namespace DiffEditor::Internal diff --git a/src/plugins/docker/dockerdevice.cpp b/src/plugins/docker/dockerdevice.cpp index 2dd2a589f72..b4cd5d3ca53 100644 --- a/src/plugins/docker/dockerdevice.cpp +++ b/src/plugins/docker/dockerdevice.cpp @@ -749,7 +749,7 @@ bool DockerDevicePrivate::startContainer() DockerApi::recheckDockerDaemon(); MessageManager::writeFlashing(Tr::tr("Docker daemon appears to be not running. " "Verify daemon is up and running and reset the " - "docker daemon on the docker device settings page " + "Docker daemon in Docker device preferences " "or restart Qt Creator.")); }); diff --git a/src/plugins/docker/dockerdevicewidget.cpp b/src/plugins/docker/dockerdevicewidget.cpp index cd32fead6fb..259325274c0 100644 --- a/src/plugins/docker/dockerdevicewidget.cpp +++ b/src/plugins/docker/dockerdevicewidget.cpp @@ -80,9 +80,9 @@ DockerDeviceWidget::DockerDeviceWidget(const IDevice::Ptr &device) }); m_enableLldbFlags = new QCheckBox(Tr::tr("Enable flags needed for LLDB")); - m_enableLldbFlags->setToolTip(Tr::tr("Adds the following flags to the container: " - "--cap-add=SYS_PTRACE --security-opt seccomp=unconfined, " - "this is necessary to allow lldb to run")); + 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); @@ -159,7 +159,7 @@ DockerDeviceWidget::DockerDeviceWidget(const IDevice::Ptr &device) searchDirsLineEdit->setPlaceholderText(Tr::tr("Semicolon-separated list of directories")); searchDirsLineEdit->setToolTip( - Tr::tr("Select the paths in the docker image that should be scanned for kit entries.")); + Tr::tr("Select the paths in the Docker image that should be scanned for kit entries.")); searchDirsLineEdit->setHistoryCompleter("DockerMounts", true); auto searchPaths = [searchDirsComboBox, searchDirsLineEdit, dockerDevice] { @@ -195,7 +195,7 @@ DockerDeviceWidget::DockerDeviceWidget(const IDevice::Ptr &device) m_kitItemDetector.autoDetect(dockerDevice->id().toString(), searchPaths()); if (DockerApi::instance()->dockerDaemonAvailable().value_or(false) == false) - logView->append(Tr::tr("Docker daemon appears to be not running.")); + logView->append(Tr::tr("Docker daemon appears to be stopped.")); else logView->append(Tr::tr("Docker daemon appears to be running.")); diff --git a/src/plugins/genericprojectmanager/genericbuildconfiguration.cpp b/src/plugins/genericprojectmanager/genericbuildconfiguration.cpp index 6bf4914840a..607a200dc90 100644 --- a/src/plugins/genericprojectmanager/genericbuildconfiguration.cpp +++ b/src/plugins/genericprojectmanager/genericbuildconfiguration.cpp @@ -3,45 +3,48 @@ #include "genericbuildconfiguration.h" -#include "genericmakestep.h" #include "genericproject.h" #include "genericprojectconstants.h" #include "genericprojectmanagertr.h" #include #include -#include #include #include -#include #include #include -#include #include - using namespace ProjectExplorer; using namespace Utils; -namespace GenericProjectManager { -namespace Internal { +namespace GenericProjectManager::Internal { -GenericBuildConfiguration::GenericBuildConfiguration(Target *parent, Utils::Id id) - : BuildConfiguration(parent, id) +class GenericBuildConfiguration final : public BuildConfiguration { - setConfigWidgetDisplayName(GenericProjectManager::Tr::tr("Generic Manager")); - setBuildDirectoryHistoryCompleter("Generic.BuildDir.History"); +public: + GenericBuildConfiguration(Target *target, Id id) + : BuildConfiguration(target, id) + { + setConfigWidgetDisplayName(GenericProjectManager::Tr::tr("Generic Manager")); + setBuildDirectoryHistoryCompleter("Generic.BuildDir.History"); + + setInitializer([this](const BuildInfo &) { + buildSteps()->appendStep(Constants::GENERIC_MS_ID); + cleanSteps()->appendStep(Constants::GENERIC_MS_ID); + updateCacheAndEmitEnvironmentChanged(); + }); - setInitializer([this](const BuildInfo &) { - buildSteps()->appendStep(Constants::GENERIC_MS_ID); - cleanSteps()->appendStep(Constants::GENERIC_MS_ID); updateCacheAndEmitEnvironmentChanged(); - }); + } - updateCacheAndEmitEnvironmentChanged(); -} + void addToEnvironment(Environment &env) const final + { + QtSupport::QtKitAspect::addHostBinariesToPath(kit(), env); + } +}; // GenericBuildConfigurationFactory @@ -68,10 +71,4 @@ GenericBuildConfigurationFactory::GenericBuildConfigurationFactory() }); } -void GenericBuildConfiguration::addToEnvironment(Utils::Environment &env) const -{ - QtSupport::QtKitAspect::addHostBinariesToPath(kit(), env); -} - -} // namespace Internal -} // namespace GenericProjectManager +} // GenericProjectManager::Internal diff --git a/src/plugins/genericprojectmanager/genericbuildconfiguration.h b/src/plugins/genericprojectmanager/genericbuildconfiguration.h index 16d19a719ec..6fb8a3807b1 100644 --- a/src/plugins/genericprojectmanager/genericbuildconfiguration.h +++ b/src/plugins/genericprojectmanager/genericbuildconfiguration.h @@ -5,18 +5,7 @@ #include -namespace GenericProjectManager { -namespace Internal { - -class GenericBuildConfiguration : public ProjectExplorer::BuildConfiguration -{ - Q_OBJECT - - friend class ProjectExplorer::BuildConfigurationFactory; - GenericBuildConfiguration(ProjectExplorer::Target *target, Utils::Id id); - - void addToEnvironment(Utils::Environment &env) const final; -}; +namespace GenericProjectManager::Internal { class GenericBuildConfigurationFactory final : public ProjectExplorer::BuildConfigurationFactory { @@ -24,5 +13,4 @@ public: GenericBuildConfigurationFactory(); }; -} // namespace Internal -} // namespace GenericProjectManager +} // GenericProjectManager::Internal diff --git a/src/plugins/git/gitclient.cpp b/src/plugins/git/gitclient.cpp index 845531ed825..70954ff4214 100644 --- a/src/plugins/git/gitclient.cpp +++ b/src/plugins/git/gitclient.cpp @@ -112,6 +112,38 @@ static QString branchesDisplay(const QString &prefix, QStringList *branches, boo /////////////////////////////// +static void stage(DiffEditorController *diffController, const QString &patch, bool revert) +{ + TemporaryFile patchFile("git-patchfile"); + if (!patchFile.open()) + return; + + const FilePath baseDir = diffController->workingDirectory(); + QTextCodec *codec = EditorManager::defaultTextCodec(); + const QByteArray patchData = codec ? codec->fromUnicode(patch) : patch.toLocal8Bit(); + patchFile.write(patchData); + patchFile.close(); + + QStringList args = {"--cached"}; + if (revert) + args << "--reverse"; + QString errorMessage; + if (GitClient::instance()->synchronousApplyPatch(baseDir, patchFile.fileName(), + &errorMessage, args)) { + if (errorMessage.isEmpty()) { + if (revert) + VcsOutputWindow::appendSilently(Tr::tr("Chunk successfully unstaged")); + else + VcsOutputWindow::appendSilently(Tr::tr("Chunk successfully staged")); + } else { + VcsOutputWindow::appendError(errorMessage); + } + diffController->requestReload(); + } else { + VcsOutputWindow::appendError(errorMessage); + } +} + class GitBaseDiffEditorController : public VcsBaseDiffEditorController { Q_OBJECT @@ -120,6 +152,50 @@ protected: explicit GitBaseDiffEditorController(IDocument *document); QStringList addConfigurationArguments(const QStringList &args) const; + +private: + void addExtraActions(QMenu *menu, int fileIndex, int chunkIndex, + const ChunkSelection &selection) final + { + menu->addSeparator(); + + auto stageChunk = [this, fileIndex, chunkIndex](DiffEditorController::PatchOptions options, + const DiffEditor::ChunkSelection &selection) { + options |= DiffEditorController::AddPrefix; + const QString patch = makePatch(fileIndex, chunkIndex, selection, options); + stage(this, patch, options & Revert); + }; + + QAction *stageChunkAction = menu->addAction(Tr::tr("Stage Chunk")); + connect(stageChunkAction, &QAction::triggered, this, [stageChunk] { + stageChunk(DiffEditorController::NoOption, {}); + }); + QAction *stageLinesAction = menu->addAction(Tr::tr("Stage Selection (%n Lines)", "", + selection.selectedRowsCount())); + connect(stageLinesAction, &QAction::triggered, this, [stageChunk, selection] { + stageChunk(DiffEditorController::NoOption, selection); + }); + QAction *unstageChunkAction = menu->addAction(Tr::tr("Unstage Chunk")); + connect(unstageChunkAction, &QAction::triggered, this, [stageChunk] { + stageChunk(DiffEditorController::Revert, {}); + }); + QAction *unstageLinesAction = menu->addAction(Tr::tr("Unstage Selection (%n Lines)", "", + selection.selectedRowsCount())); + connect(unstageLinesAction, &QAction::triggered, this, [stageChunk, selection] { + stageChunk(DiffEditorController::Revert, selection); + }); + + if (selection.isNull()) { + stageLinesAction->setVisible(false); + unstageLinesAction->setVisible(false); + } + if (!chunkExists(fileIndex, chunkIndex)) { + stageChunkAction->setEnabled(false); + stageLinesAction->setEnabled(false); + unstageChunkAction->setEnabled(false); + unstageLinesAction->setEnabled(false); + } + } }; class GitDiffEditorController : public GitBaseDiffEditorController @@ -234,12 +310,12 @@ FileListDiffController::FileListDiffController(IDocument *document, const QStrin const auto setupStaged = [this, stagedFiles](Process &process) { if (stagedFiles.isEmpty()) - return TaskAction::StopWithError; + return SetupResult::StopWithError; process.setCodec(VcsBaseEditor::getCodec(workingDirectory(), stagedFiles)); setupCommand(process, addConfigurationArguments( QStringList({"diff", "--cached", "--"}) + stagedFiles)); VcsOutputWindow::appendCommand(process.workingDirectory(), process.commandLine()); - return TaskAction::Continue; + return SetupResult::Continue; }; const auto onStagedDone = [storage](const Process &process) { storage->m_stagedOutput = process.cleanedStdOut(); @@ -247,12 +323,12 @@ FileListDiffController::FileListDiffController(IDocument *document, const QStrin const auto setupUnstaged = [this, unstagedFiles](Process &process) { if (unstagedFiles.isEmpty()) - return TaskAction::StopWithError; + return SetupResult::StopWithError; process.setCodec(VcsBaseEditor::getCodec(workingDirectory(), unstagedFiles)); setupCommand(process, addConfigurationArguments( QStringList({"diff", "--"}) + unstagedFiles)); VcsOutputWindow::appendCommand(process.workingDirectory(), process.commandLine()); - return TaskAction::Continue; + return SetupResult::Continue; }; const auto onUnstagedDone = [storage](const Process &process) { storage->m_unstagedOutput = process.cleanedStdOut(); @@ -345,8 +421,8 @@ ShowController::ShowController(IDocument *document, const QString &id) const auto desciptionDetailsSetup = [storage] { if (!storage->m_postProcessDescription) - return TaskAction::StopWithDone; - return TaskAction::Continue; + return SetupResult::StopWithDone; + return SetupResult::Continue; }; const auto setupBranches = [this, storage](Process &process) { @@ -850,95 +926,6 @@ QTextCodec *GitClient::encoding(GitClient::EncodingType encodingType, const File } } -void GitClient::chunkActionsRequested(DiffEditor::DiffEditorController *controller, - QMenu *menu, int fileIndex, int chunkIndex, - const DiffEditor::ChunkSelection &selection) const -{ - QPointer diffController(controller); - - auto stageChunk = [this](QPointer diffController, - int fileIndex, int chunkIndex, DiffEditorController::PatchOptions options, - const DiffEditor::ChunkSelection &selection) { - if (diffController.isNull()) - return; - - options |= DiffEditorController::AddPrefix; - const QString patch = diffController->makePatch(fileIndex, chunkIndex, selection, options); - stage(diffController, patch, options & Revert); - }; - - menu->addSeparator(); - QAction *stageChunkAction = menu->addAction(Tr::tr("Stage Chunk")); - connect(stageChunkAction, &QAction::triggered, this, - [stageChunk, diffController, fileIndex, chunkIndex] { - stageChunk(diffController, fileIndex, chunkIndex, - DiffEditorController::NoOption, DiffEditor::ChunkSelection()); - }); - QAction *stageLinesAction = menu->addAction(Tr::tr("Stage Selection (%n Lines)", "", selection.selectedRowsCount())); - connect(stageLinesAction, &QAction::triggered, this, - [stageChunk, diffController, fileIndex, chunkIndex, selection] { - stageChunk(diffController, fileIndex, chunkIndex, - DiffEditorController::NoOption, selection); - }); - QAction *unstageChunkAction = menu->addAction(Tr::tr("Unstage Chunk")); - connect(unstageChunkAction, &QAction::triggered, this, - [stageChunk, diffController, fileIndex, chunkIndex] { - stageChunk(diffController, fileIndex, chunkIndex, - DiffEditorController::Revert, DiffEditor::ChunkSelection()); - }); - QAction *unstageLinesAction = menu->addAction(Tr::tr("Unstage Selection (%n Lines)", "", selection.selectedRowsCount())); - connect(unstageLinesAction, &QAction::triggered, this, - [stageChunk, diffController, fileIndex, chunkIndex, selection] { - stageChunk(diffController, fileIndex, chunkIndex, - DiffEditorController::Revert, - selection); - }); - if (selection.isNull()) { - stageLinesAction->setVisible(false); - unstageLinesAction->setVisible(false); - } - if (!diffController || !diffController->chunkExists(fileIndex, chunkIndex)) { - stageChunkAction->setEnabled(false); - stageLinesAction->setEnabled(false); - unstageChunkAction->setEnabled(false); - unstageLinesAction->setEnabled(false); - } -} - -void GitClient::stage(DiffEditor::DiffEditorController *diffController, - const QString &patch, bool revert) const -{ - TemporaryFile patchFile("git-patchfile"); - if (!patchFile.open()) - return; - - const FilePath baseDir = diffController->workingDirectory(); - QTextCodec *codec = EditorManager::defaultTextCodec(); - const QByteArray patchData = codec - ? codec->fromUnicode(patch) : patch.toLocal8Bit(); - patchFile.write(patchData); - patchFile.close(); - - QStringList args = {"--cached"}; - if (revert) - args << "--reverse"; - QString errorMessage; - if (synchronousApplyPatch(baseDir, patchFile.fileName(), - &errorMessage, args)) { - if (errorMessage.isEmpty()) { - if (revert) - VcsOutputWindow::appendSilently(Tr::tr("Chunk successfully unstaged")); - else - VcsOutputWindow::appendSilently(Tr::tr("Chunk successfully staged")); - } else { - VcsOutputWindow::appendError(errorMessage); - } - diffController->requestReload(); - } else { - VcsOutputWindow::appendError(errorMessage); - } -} - void GitClient::requestReload(const QString &documentId, const FilePath &source, const QString &title, const FilePath &workingDirectory, std::function factory) const @@ -956,10 +943,6 @@ void GitClient::requestReload(const QString &documentId, const FilePath &source, using namespace std::placeholders; - connect(controller, &DiffEditorController::chunkActionsRequested, this, - std::bind(&GitClient::chunkActionsRequested, this, controller, _1, _2, _3, _4), - Qt::DirectConnection); - VcsBase::setSource(document, sourceCopy); EditorManager::activateEditorForDocument(document); controller->requestReload(); @@ -2873,7 +2856,7 @@ bool GitClient::addAndCommit(const FilePath &repositoryDirectory, GitPlugin::updateCurrentBranch(); return true; } - VcsOutputWindow::appendError(Tr::tr("Cannot commit %n file(s)", nullptr, commitCount) + "\n"); + VcsOutputWindow::appendError(Tr::tr("Cannot commit %n file(s).", nullptr, commitCount) + "\n"); return false; } diff --git a/src/plugins/git/gitclient.h b/src/plugins/git/gitclient.h index 398b1c1cc86..3604eb9f3cf 100644 --- a/src/plugins/git/gitclient.h +++ b/src/plugins/git/gitclient.h @@ -21,7 +21,6 @@ #include QT_BEGIN_NAMESPACE -class QProcessEnvironment; class QMenu; QT_END_NAMESPACE @@ -363,12 +362,6 @@ private: static GitSettings &settings(); void finishSubmoduleUpdate(); - void chunkActionsRequested(DiffEditor::DiffEditorController *controller, - QMenu *menu, int fileIndex, int chunkIndex, - const DiffEditor::ChunkSelection &selection) const; - - void stage(DiffEditor::DiffEditorController *diffController, - const QString &patch, bool revert) const; void requestReload(const QString &documentId, const Utils::FilePath &source, const QString &title, const Utils::FilePath &workingDirectory, diff --git a/src/plugins/incredibuild/buildconsolebuildstep.cpp b/src/plugins/incredibuild/buildconsolebuildstep.cpp index c28c1cd6769..19ac73dd0cf 100644 --- a/src/plugins/incredibuild/buildconsolebuildstep.cpp +++ b/src/plugins/incredibuild/buildconsolebuildstep.cpp @@ -50,6 +50,39 @@ public: BuildConsoleBuildStep(BuildStepList *buildStepList, Id id); void setupOutputFormatter(OutputFormatter *formatter) final; + + TextDisplay t1{this, "" + Tr::tr("Target and Configuration")}; + CommandBuilderAspect commandBuilder{this}; + + TextDisplay t2{this, "" + Tr::tr("Enter the appropriate arguments to your build command.")}; + TextDisplay t3{this, "" + Tr::tr("Make sure the build command's multi-job " + "parameter value is large enough " + "(such as -j200 for the JOM or Make build tools)")}; + BoolAspect keepJobNum{this}; + + TextDisplay t4{this, "" + Tr::tr("IncrediBuild Distribution Control")}; + FilePathAspect profileXml{this}; + BoolAspect avoidLocal{this}; + IntegerAspect maxCpu{this}; + SelectionAspect maxWinVer{this}; + SelectionAspect minWinVer{this}; + + TextDisplay t5{this, "" + Tr::tr("Output and Logging")}; + StringAspect title{this}; + FilePathAspect monFile{this}; + BoolAspect suppressStdOut{this}; + FilePathAspect logFile{this}; + BoolAspect showCmd{this}; + BoolAspect showAgents{this}; + BoolAspect showTime{this}; + BoolAspect hideHeader{this}; + SelectionAspect logLevel{this}; + + TextDisplay t6{this, "" + Tr::tr("Miscellaneous")}; + StringAspect setEnv{this}; + BoolAspect stopOnError{this}; + StringAspect additionalArguments{this}; + BoolAspect openMonitor{this}; }; BuildConsoleBuildStep::BuildConsoleBuildStep(BuildStepList *buildStepList, Id id) @@ -57,233 +90,198 @@ BuildConsoleBuildStep::BuildConsoleBuildStep(BuildStepList *buildStepList, Id id { setDisplayName(Tr::tr("IncrediBuild for Windows")); - addAspect("" + Tr::tr("Target and Configuration")); + commandBuilder.setSettingsKey("IncrediBuild.BuildConsole.CommandBuilder"); - auto commandBuilder = addAspect(this); - commandBuilder->setSettingsKey("IncrediBuild.BuildConsole.CommandBuilder"); + keepJobNum.setSettingsKey("IncrediBuild.BuildConsole.KeepJobNum"); + keepJobNum.setLabel(Tr::tr("Keep original jobs number:")); + keepJobNum.setToolTip(Tr::tr("Forces IncrediBuild to not override the -j command line switch, " + "that controls the number of parallel spawned tasks. The default " + "IncrediBuild behavior is to set it to 200.")); - addAspect("" + Tr::tr("Enter the appropriate arguments to your build command.")); - addAspect("" + Tr::tr("Make sure the build command's multi-job " - "parameter value is large enough " - "(such as -j200 for the JOM or Make build tools)")); + profileXml.setSettingsKey("IncrediBuild.BuildConsole.ProfileXml"); + profileXml.setLabelText(Tr::tr("Profile.xml:")); + profileXml.setExpectedKind(PathChooser::Kind::File); + profileXml.setBaseFileName(PathChooser::homePath()); + profileXml.setHistoryCompleter("IncrediBuild.BuildConsole.ProfileXml.History"); + profileXml.setToolTip(Tr::tr("Defines how Automatic " + "Interception Interface should handle the various processes " + "involved in a distributed job. It is not necessary for " + "\"Visual Studio\" or \"Make and Build tools\" builds, " + "but can be used to provide configuration options if those " + "builds use additional processes that are not included in " + "those packages. It is required to configure distributable " + "processes in \"Dev Tools\" builds.")); - auto keepJobNum = addAspect(); - keepJobNum->setSettingsKey("IncrediBuild.BuildConsole.KeepJobNum"); - keepJobNum->setLabel(Tr::tr("Keep original jobs number:")); - keepJobNum->setToolTip(Tr::tr("Forces IncrediBuild to not override the -j command line switch, " - "that controls the number of parallel spawned tasks. The default " - "IncrediBuild behavior is to set it to 200.")); + avoidLocal.setSettingsKey("IncrediBuild.BuildConsole.AvoidLocal"); + avoidLocal.setLabel(Tr::tr("Avoid local task execution:")); + avoidLocal.setToolTip(Tr::tr("Overrides the Agent Settings dialog Avoid task execution on local " + "machine when possible option. This allows to free more resources " + "on the initiator machine and could be beneficial to distribution " + "in scenarios where the initiating machine is bottlenecking the " + "build with High CPU usage.")); - addAspect("" + Tr::tr("IncrediBuild Distribution Control")); + maxCpu.setSettingsKey("IncrediBuild.BuildConsole.MaxCpu"); + maxCpu.setToolTip(Tr::tr("Determines the maximum number of CPU cores that can be used in a " + "build, regardless of the number of available Agents. " + "It takes into account both local and remote cores, even if the " + "Avoid Task Execution on Local Machine option is selected.")); + maxCpu.setLabel(Tr::tr("Maximum CPUs to utilize in the build:")); + maxCpu.setRange(0, 65536); - auto profileXml = addAspect(); - profileXml->setSettingsKey("IncrediBuild.BuildConsole.ProfileXml"); - profileXml->setLabelText(Tr::tr("Profile.xml:")); - profileXml->setExpectedKind(PathChooser::Kind::File); - profileXml->setBaseFileName(PathChooser::homePath()); - profileXml->setHistoryCompleter("IncrediBuild.BuildConsole.ProfileXml.History"); - profileXml->setToolTip(Tr::tr("Defines how Automatic " - "Interception Interface should handle the various processes " - "involved in a distributed job. It is not necessary for " - "\"Visual Studio\" or \"Make and Build tools\" builds, " - "but can be used to provide configuration options if those " - "builds use additional processes that are not included in " - "those packages. It is required to configure distributable " - "processes in \"Dev Tools\" builds.")); - - auto avoidLocal = addAspect(); - avoidLocal->setSettingsKey("IncrediBuild.BuildConsole.AvoidLocal"); - avoidLocal->setLabel(Tr::tr("Avoid local task execution:")); - avoidLocal->setToolTip(Tr::tr("Overrides the Agent Settings dialog Avoid task execution on local " - "machine when possible option. This allows to free more resources " - "on the initiator machine and could be beneficial to distribution " - "in scenarios where the initiating machine is bottlenecking the " - "build with High CPU usage.")); - - auto maxCpu = addAspect(); - maxCpu->setSettingsKey("IncrediBuild.BuildConsole.MaxCpu"); - maxCpu->setToolTip(Tr::tr("Determines the maximum number of CPU cores that can be used in a " - "build, regardless of the number of available Agents. " - "It takes into account both local and remote cores, even if the " - "Avoid Task Execution on Local Machine option is selected.")); - maxCpu->setLabel(Tr::tr("Maximum CPUs to utilize in the build:")); - maxCpu->setRange(0, 65536); - - auto maxWinVer = addAspect(); - maxWinVer->setSettingsKey("IncrediBuild.BuildConsole.MaxWinVer"); - maxWinVer->setDisplayName(Tr::tr("Newest allowed helper machine OS:")); - maxWinVer->setDisplayStyle(SelectionAspect::DisplayStyle::ComboBox); - maxWinVer->setToolTip(Tr::tr("Specifies the newest operating system installed on a helper " - "machine to be allowed to participate as helper in the build.")); + maxWinVer.setSettingsKey("IncrediBuild.BuildConsole.MaxWinVer"); + maxWinVer.setDisplayName(Tr::tr("Newest allowed helper machine OS:")); + maxWinVer.setDisplayStyle(SelectionAspect::DisplayStyle::ComboBox); + maxWinVer.setToolTip(Tr::tr("Specifies the newest operating system installed on a helper " + "machine to be allowed to participate as helper in the build.")); for (const QString &version : supportedWindowsVersions()) - maxWinVer->addOption(version); + maxWinVer.addOption(version); - auto minWinVer = addAspect(); - minWinVer->setSettingsKey("IncrediBuild.BuildConsole.MinWinVer"); - minWinVer->setDisplayName(Tr::tr("Oldest allowed helper machine OS:")); - minWinVer->setDisplayStyle(SelectionAspect::DisplayStyle::ComboBox); - minWinVer->setToolTip(Tr::tr("Specifies the oldest operating system installed on a helper " - "machine to be allowed to participate as helper in the build.")); + minWinVer.setSettingsKey("IncrediBuild.BuildConsole.MinWinVer"); + minWinVer.setDisplayName(Tr::tr("Oldest allowed helper machine OS:")); + minWinVer.setDisplayStyle(SelectionAspect::DisplayStyle::ComboBox); + minWinVer.setToolTip(Tr::tr("Specifies the oldest operating system installed on a helper " + "machine to be allowed to participate as helper in the build.")); for (const QString &version : supportedWindowsVersions()) - minWinVer->addOption(version); + minWinVer.addOption(version); - addAspect("" + Tr::tr("Output and Logging")); + title.setSettingsKey("IncrediBuild.BuildConsole.Title"); + title.setLabelText(Tr::tr("Build title:")); + title.setDisplayStyle(StringAspect::LineEditDisplay); + title.setToolTip(Tr::tr("Specifies a custom header line which will be displayed in the " + "beginning of the build output text. This title will also be used " + "for the Build History and Build Monitor displays.")); - auto title = addAspect(); - title->setSettingsKey("IncrediBuild.BuildConsole.Title"); - title->setLabelText(Tr::tr("Build title:")); - title->setDisplayStyle(StringAspect::LineEditDisplay); - title->setToolTip(Tr::tr("Specifies a custom header line which will be displayed in the " - "beginning of the build output text. This title will also be used " - "for the Build History and Build Monitor displays.")); + monFile.setSettingsKey("IncrediBuild.BuildConsole.MonFile"); + 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.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 " + "written to the end of the build output.")); - auto monFile = addAspect(); - monFile->setSettingsKey("IncrediBuild.BuildConsole.MonFile"); - 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->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 " - "written to the end of the build output.")); + suppressStdOut.setSettingsKey("IncrediBuild.BuildConsole.SuppressStdOut"); + suppressStdOut.setLabel(Tr::tr("Suppress STDOUT:")); + suppressStdOut.setToolTip(Tr::tr("Does not write anything to the standard output.")); - auto suppressStdOut = addAspect(); - suppressStdOut->setSettingsKey("IncrediBuild.BuildConsole.SuppressStdOut"); - suppressStdOut->setLabel(Tr::tr("Suppress STDOUT:")); - suppressStdOut->setToolTip(Tr::tr("Does not write anything to the standard output.")); + logFile.setSettingsKey("IncrediBuild.BuildConsole.LogFile"); + logFile.setLabelText(Tr::tr("Output Log file:")); + logFile.setExpectedKind(PathChooser::Kind::SaveFile); + logFile.setBaseFileName(PathChooser::homePath()); + logFile.setHistoryCompleter(QLatin1String("IncrediBuild.BuildConsole.LogFile.History")); + logFile.setToolTip(Tr::tr("Writes build output to a file.")); - auto logFile = addAspect(); - logFile->setSettingsKey("IncrediBuild.BuildConsole.LogFile"); - logFile->setLabelText(Tr::tr("Output Log file:")); - logFile->setExpectedKind(PathChooser::Kind::SaveFile); - logFile->setBaseFileName(PathChooser::homePath()); - logFile->setHistoryCompleter(QLatin1String("IncrediBuild.BuildConsole.LogFile.History")); - logFile->setToolTip(Tr::tr("Writes build output to a file.")); + showCmd.setSettingsKey("IncrediBuild.BuildConsole.ShowCmd"); + showCmd.setLabel(Tr::tr("Show Commands in output:")); + showCmd.setToolTip(Tr::tr("Shows, for each file built, the command-line used by IncrediBuild " + "to build the file.")); - auto showCmd = addAspect(); - showCmd->setSettingsKey("IncrediBuild.BuildConsole.ShowCmd"); - showCmd->setLabel(Tr::tr("Show Commands in output:")); - showCmd->setToolTip(Tr::tr("Shows, for each file built, the command-line used by IncrediBuild " - "to build the file.")); + showAgents.setSettingsKey("IncrediBuild.BuildConsole.ShowAgents"); + showAgents.setLabel(Tr::tr("Show Agents in output:")); + showAgents.setToolTip(Tr::tr("Shows the Agent used to build each file.")); - auto showAgents = addAspect(); - showAgents->setSettingsKey("IncrediBuild.BuildConsole.ShowAgents"); - showAgents->setLabel(Tr::tr("Show Agents in output:")); - showAgents->setToolTip(Tr::tr("Shows the Agent used to build each file.")); + showTime.setSettingsKey("IncrediBuild.BuildConsole.ShowTime"); + showTime.setLabel(Tr::tr("Show Time in output:")); + showTime.setToolTip(Tr::tr("Shows the Start and Finish time for each file built.")); - auto showTime = addAspect(); - showTime->setSettingsKey("IncrediBuild.BuildConsole.ShowTime"); - showTime->setLabel(Tr::tr("Show Time in output:")); - showTime->setToolTip(Tr::tr("Shows the Start and Finish time for each file built.")); + hideHeader.setSettingsKey("IncrediBuild.BuildConsole.HideHeader"); + hideHeader.setLabel(Tr::tr("Hide IncrediBuild Header in output:")); + hideHeader.setToolTip(Tr::tr("Suppresses IncrediBuild's header in the build output")); - auto hideHeader = addAspect(); - hideHeader->setSettingsKey("IncrediBuild.BuildConsole.HideHeader"); - hideHeader->setLabel(Tr::tr("Hide IncrediBuild Header in output:")); - hideHeader->setToolTip(Tr::tr("Suppresses IncrediBuild's header in the build output")); + logLevel.setSettingsKey("IncrediBuild.BuildConsole.LogLevel"); + logLevel.setDisplayName(Tr::tr("Internal IncrediBuild logging level:")); + logLevel.setDisplayStyle(SelectionAspect::DisplayStyle::ComboBox); + logLevel.addOption(QString()); + logLevel.addOption("Minimal"); + logLevel.addOption("Extended"); + logLevel.addOption("Detailed"); + logLevel.setToolTip(Tr::tr("Overrides the internal Incredibuild logging level for this build. " + "Does not affect output or any user accessible logging. Used mainly " + "to troubleshoot issues with the help of IncrediBuild support")); - auto logLevel = addAspect(); - logLevel->setSettingsKey("IncrediBuild.BuildConsole.LogLevel"); - logLevel->setDisplayName(Tr::tr("Internal IncrediBuild logging level:")); - logLevel->setDisplayStyle(SelectionAspect::DisplayStyle::ComboBox); - logLevel->addOption(QString()); - logLevel->addOption("Minimal"); - logLevel->addOption("Extended"); - logLevel->addOption("Detailed"); - logLevel->setToolTip(Tr::tr("Overrides the internal Incredibuild logging level for this build. " - "Does not affect output or any user accessible logging. Used mainly " - "to troubleshoot issues with the help of IncrediBuild support")); + setEnv.setSettingsKey("IncrediBuild.BuildConsole.SetEnv"); + setEnv.setLabelText(Tr::tr("Set an Environment Variable:")); + setEnv.setDisplayStyle(StringAspect::LineEditDisplay); + setEnv.setToolTip(Tr::tr("Sets or overrides environment variables for the context of the build.")); - addAspect("" + Tr::tr("Miscellaneous")); + stopOnError.setSettingsKey("IncrediBuild.BuildConsole.StopOnError"); + stopOnError.setLabel(Tr::tr("Stop on errors:")); + stopOnError.setToolTip(Tr::tr("When specified, the execution will stop as soon as an error " + "is encountered. This is the default behavior in " + "\"Visual Studio\" builds, but not the default for " + "\"Make and Build tools\" or \"Dev Tools\" builds")); - auto setEnv = addAspect(); - setEnv->setSettingsKey("IncrediBuild.BuildConsole.SetEnv"); - setEnv->setLabelText(Tr::tr("Set an Environment Variable:")); - setEnv->setDisplayStyle(StringAspect::LineEditDisplay); - setEnv->setToolTip(Tr::tr("Sets or overrides environment variables for the context of the build.")); + additionalArguments.setSettingsKey("IncrediBuild.BuildConsole.AdditionalArguments"); + additionalArguments.setLabelText(Tr::tr("Additional Arguments:")); + additionalArguments.setDisplayStyle(StringAspect::LineEditDisplay); + additionalArguments.setToolTip(Tr::tr("Add additional buildconsole arguments manually. " + "The value of this field will be concatenated to the " + "final buildconsole command line")); - auto stopOnError = addAspect(); - stopOnError->setSettingsKey("IncrediBuild.BuildConsole.StopOnError"); - stopOnError->setLabel(Tr::tr("Stop on errors:")); - stopOnError->setToolTip(Tr::tr("When specified, the execution will stop as soon as an error " - "is encountered. This is the default behavior in " - "\"Visual Studio\" builds, but not the default for " - "\"Make and Build tools\" or \"Dev Tools\" builds")); + openMonitor.setSettingsKey("IncrediBuild.BuildConsole.OpenMonitor"); + openMonitor.setLabel(Tr::tr("Open Build Monitor:")); + openMonitor.setToolTip(Tr::tr("Opens Build Monitor once the build starts.")); - auto additionalArguments = addAspect(); - additionalArguments->setSettingsKey("IncrediBuild.BuildConsole.AdditionalArguments"); - additionalArguments->setLabelText(Tr::tr("Additional Arguments:")); - additionalArguments->setDisplayStyle(StringAspect::LineEditDisplay); - additionalArguments->setToolTip(Tr::tr("Add additional buildconsole arguments manually. " - "The value of this field will be concatenated to the " - "final buildconsole command line")); + setCommandLineProvider([this] { + CommandLine cmd("BuildConsole.exe"); - auto openMonitor = addAspect(); - openMonitor->setSettingsKey("IncrediBuild.BuildConsole.OpenMonitor"); - openMonitor->setLabel(Tr::tr("Open Build Monitor:")); - openMonitor->setToolTip(Tr::tr("Opens Build Monitor once the build starts.")); + cmd.addArgs(QString("/Command=%1").arg(commandBuilder.fullCommandFlag(keepJobNum())), CommandLine::Raw); - setCommandLineProvider([=] { - QStringList args; + if (!profileXml().isEmpty()) + cmd.addArg(QString("/Profile=%1").arg(profileXml().path())); - QString cmd("/Command= %1"); - cmd = cmd.arg(commandBuilder->fullCommandFlag(keepJobNum->value())); - args.append(cmd); + cmd.addArg(QString("/AvoidLocal=%1").arg(avoidLocal() ? QString("ON") : QString("OFF"))); - if (!profileXml->value().isEmpty()) - args.append("/Profile=" + profileXml->value()); + if (maxCpu() > 0) + cmd.addArg(QString("/MaxCPUs=%1").arg(maxCpu())); - args.append(QString("/AvoidLocal=%1").arg(avoidLocal->value() ? QString("ON") : QString("OFF"))); + if (!maxWinVer.stringValue().isEmpty()) + cmd.addArg(QString("/MaxWinVer=%1").arg(normalizeWinVerArgument(maxWinVer.stringValue()))); - if (maxCpu->value() > 0) - args.append(QString("/MaxCPUs=%1").arg(maxCpu->value())); + if (!minWinVer.stringValue().isEmpty()) + cmd.addArg(QString("/MinWinVer=%1").arg(normalizeWinVerArgument(minWinVer.stringValue()))); - if (!maxWinVer->stringValue().isEmpty()) - args.append(QString("/MaxWinVer=%1").arg(normalizeWinVerArgument(maxWinVer->stringValue()))); + if (!title().isEmpty()) + cmd.addArg("/Title=" + title()); - if (!minWinVer->stringValue().isEmpty()) - args.append(QString("/MinWinVer=%1").arg(normalizeWinVerArgument(minWinVer->stringValue()))); + if (!monFile().isEmpty()) + cmd.addArg("/Mon=" + monFile().path()); - if (!title->value().isEmpty()) - args.append(QString("/Title=" + title->value())); + if (suppressStdOut()) + cmd.addArg("/Silent"); - if (!monFile->value().isEmpty()) - args.append(QString("/Mon=" + monFile->value())); + if (!logFile().isEmpty()) + cmd.addArg("/Log=" + logFile().path()); - if (suppressStdOut->value()) - args.append("/Silent"); + if (showCmd()) + cmd.addArg("/ShowCmd"); - if (!logFile->value().isEmpty()) - args.append(QString("/Log=" + logFile->value())); + if (showAgents()) + cmd.addArg("/ShowAgent"); - if (showCmd->value()) - args.append("/ShowCmd"); + if (showAgents()) + cmd.addArg("/ShowTime"); - if (showAgents->value()) - args.append("/ShowAgent"); + if (hideHeader()) + cmd.addArg("/NoLogo"); - if (showAgents->value()) - args.append("/ShowTime"); + if (!logLevel.stringValue().isEmpty()) + cmd.addArg("/LogLevel=" + logLevel.stringValue()); - if (hideHeader->value()) - args.append("/NoLogo"); + if (!setEnv().isEmpty()) + cmd.addArg("/SetEnv=" + setEnv()); - if (!logLevel->stringValue().isEmpty()) - args.append(QString("/LogLevel=" + logLevel->stringValue())); + if (stopOnError()) + cmd.addArg("/StopOnErrors"); - if (!setEnv->value().isEmpty()) - args.append(QString("/SetEnv=" + setEnv->value())); + if (!additionalArguments().isEmpty()) + cmd.addArgs(additionalArguments(), CommandLine::Raw); - if (stopOnError->value()) - args.append("/StopOnErrors"); + if (openMonitor()) + cmd.addArg("/OpenMonitor"); - if (!additionalArguments->value().isEmpty()) - args.append(additionalArguments->value()); - - if (openMonitor->value()) - args.append("/OpenMonitor"); - - return CommandLine("BuildConsole.exe", args); + return cmd; }); } diff --git a/src/plugins/incredibuild/ibconsolebuildstep.cpp b/src/plugins/incredibuild/ibconsolebuildstep.cpp index 3bd453fdb8c..c53ffad4552 100644 --- a/src/plugins/incredibuild/ibconsolebuildstep.cpp +++ b/src/plugins/incredibuild/ibconsolebuildstep.cpp @@ -28,6 +28,21 @@ public: IBConsoleBuildStep(BuildStepList *buildStepList, Id id); void setupOutputFormatter(OutputFormatter *formatter) final; + + TextDisplay t1{this, "" + Tr::tr("Target and Configuration")}; + CommandBuilderAspect commandBuilder{this}; + BoolAspect keepJobNum{this}; + + TextDisplay t2{this, "" + Tr::tr("Enter the appropriate arguments to your build command.")}; + TextDisplay t3{this, "" + Tr::tr("Make sure the build command's " + "multi-job parameter value is large enough (such as " + "-j200 for the JOM or Make build tools)")}; + + TextDisplay t4{this, "" + Tr::tr("IncrediBuild Distribution Control")}; + + IntegerAspect nice{this}; + BoolAspect forceRemote{this}; + BoolAspect alternate{this}; }; IBConsoleBuildStep::IBConsoleBuildStep(BuildStepList *buildStepList, Id id) @@ -35,52 +50,38 @@ IBConsoleBuildStep::IBConsoleBuildStep(BuildStepList *buildStepList, Id id) { setDisplayName(Tr::tr("IncrediBuild for Linux")); - addAspect("" + Tr::tr("Target and Configuration")); + commandBuilder.setSettingsKey("IncrediBuild.IBConsole.CommandBuilder"); - auto commandBuilder = addAspect(this); - commandBuilder->setSettingsKey("IncrediBuild.IBConsole.CommandBuilder"); + keepJobNum.setSettingsKey("IncrediBuild.IBConsole.KeepJobNum"); + keepJobNum.setLabel(Tr::tr("Keep original jobs number:")); + keepJobNum.setToolTip(Tr::tr("Forces IncrediBuild to not override the -j command line switch, " + "that controls the number of parallel spawned tasks. The default " + "IncrediBuild behavior is to set it to 200.")); - addAspect("" + Tr::tr("Enter the appropriate arguments to your build command.")); - addAspect("" + Tr::tr("Make sure the build command's " - "multi-job parameter value is large enough (such as " - "-j200 for the JOM or Make build tools)")); + nice.setSettingsKey("IncrediBuild.IBConsole.Nice"); + nice.setToolTip(Tr::tr("Specify nice value. Nice Value should be numeric and between -20 and 19")); + nice.setLabel(Tr::tr("Nice value:")); + nice.setRange(-20, 19); - auto keepJobNum = addAspect(); - keepJobNum->setSettingsKey("IncrediBuild.IBConsole.KeepJobNum"); - keepJobNum->setLabel(Tr::tr("Keep original jobs number:")); - keepJobNum->setToolTip(Tr::tr("Forces IncrediBuild to not override the -j command line switch, " - "that controls the number of parallel spawned tasks. The default " - "IncrediBuild behavior is to set it to 200.")); + forceRemote.setSettingsKey("IncrediBuild.IBConsole.Alternate"); + forceRemote.setLabel(Tr::tr("Force remote:")); - addAspect("" + Tr::tr("IncrediBuild Distribution Control")); + alternate.setSettingsKey("IncrediBuild.IBConsole.ForceRemote"); + alternate.setLabel(Tr::tr("Alternate tasks preference:")); - auto nice = addAspect(); - nice->setSettingsKey("IncrediBuild.IBConsole.Nice"); - nice->setToolTip(Tr::tr("Specify nice value. Nice Value should be numeric and between -20 and 19")); - nice->setLabel(Tr::tr("Nice value:")); - nice->setRange(-20, 19); - - auto forceRemote = addAspect(); - forceRemote->setSettingsKey("IncrediBuild.IBConsole.Alternate"); - forceRemote->setLabel(Tr::tr("Force remote:")); - - auto alternate = addAspect(); - alternate->setSettingsKey("IncrediBuild.IBConsole.ForceRemote"); - alternate->setLabel(Tr::tr("Alternate tasks preference:")); - - setCommandLineProvider([=] { + setCommandLineProvider([this] { QStringList args; - if (nice->value() != 0) - args.append(QString("--nice %1 ").arg(nice->value())); + if (nice() != 0) + args.append(QString("--nice %1 ").arg(nice())); - if (alternate->value()) + if (alternate()) args.append("--alternate"); - if (forceRemote->value()) + if (forceRemote()) args.append("--force-remote"); - args.append(commandBuilder->fullCommandFlag(keepJobNum->value())); + args.append(commandBuilder.fullCommandFlag(keepJobNum())); return CommandLine("ib_console", args); }); diff --git a/src/plugins/insight/insightwidget.cpp b/src/plugins/insight/insightwidget.cpp index aaf152bea31..03a46b03443 100644 --- a/src/plugins/insight/insightwidget.cpp +++ b/src/plugins/insight/insightwidget.cpp @@ -51,7 +51,7 @@ InsightWidget::InsightWidget(InsightView *insightView, InsightModel *insightMode engine()->addImportPath(propertyEditorResourcesPath() + "/imports"); engine()->addImportPath(qmlSourcesPath() + "/imports"); - m_qmlSourceUpdateShortcut = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_F11), this); + m_qmlSourceUpdateShortcut = new QShortcut(QKeySequence(Qt::CTRL | Qt::Key_F11), this); connect(m_qmlSourceUpdateShortcut, &QShortcut::activated, this, diff --git a/src/plugins/languageclient/client.cpp b/src/plugins/languageclient/client.cpp index bb5a4594dc5..79e6b9a738c 100644 --- a/src/plugins/languageclient/client.cpp +++ b/src/plugins/languageclient/client.cpp @@ -202,8 +202,7 @@ public: for (Core::IEditor *editor : editors) { if (auto textEditor = qobject_cast(editor)) { TextEditorWidget *widget = textEditor->editorWidget(); - widget->setRefactorMarkers( - RefactorMarker::filterOutType(widget->refactorMarkers(), m_id)); + widget->clearRefactorMarkers(m_id); widget->removeHoverHandler(&m_hoverHandler); } } @@ -278,10 +277,14 @@ public: { QObject::disconnect(contentsChangedConnection); QObject::disconnect(filePathChangedConnection); + QObject::disconnect(aboutToSaveConnection); + QObject::disconnect(savedConnection); delete document; } QMetaObject::Connection contentsChangedConnection; QMetaObject::Connection filePathChangedConnection; + QMetaObject::Connection aboutToSaveConnection; + QMetaObject::Connection savedConnection; QTextDocument *document = nullptr; }; QMap m_openedDocument; @@ -649,6 +652,22 @@ void Client::openDocument(TextEditor::TextDocument *document) if (isSupportedDocument(document)) openDocument(document); }); + d->m_openedDocument[document].savedConnection + = connect(document, + &TextDocument::saved, + this, + [this, document](const FilePath &saveFilePath) { + if (saveFilePath == document->filePath()) + documentContentsSaved(document); + }); + d->m_openedDocument[document].aboutToSaveConnection + = connect(document, + &TextDocument::aboutToSave, + this, + [this, document](const FilePath &saveFilePath) { + if (saveFilePath == document->filePath()) + documentWillSave(document); + }); if (!d->m_documentVersions.contains(filePath)) d->m_documentVersions[filePath] = 0; d->sendOpenNotification(filePath, document->mimeType(), document->plainText(), @@ -910,8 +929,7 @@ void Client::deactivateDocument(TextEditor::TextDocument *document) TextEditor::TextEditorWidget *widget = textEditor->editorWidget(); widget->removeHoverHandler(&d->m_hoverHandler); widget->setExtraSelections(TextEditor::TextEditorWidget::CodeSemanticsSelection, {}); - widget->setRefactorMarkers( - TextEditor::RefactorMarker::filterOutType(widget->refactorMarkers(), id())); + widget->clearRefactorMarkers(id()); updateEditorToolBar(editor); } } @@ -1158,7 +1176,7 @@ void Client::documentContentsChanged(TextEditor::TextDocument *document, TextEditorWidget *widget = editor->editorWidget(); QTC_ASSERT(widget, continue); delete d->m_documentHighlightsTimer.take(widget); - widget->setRefactorMarkers(RefactorMarker::filterOutType(widget->refactorMarkers(), id())); + widget->clearRefactorMarkers(id()); } d->m_documentUpdateTimer.start(); } @@ -1680,27 +1698,35 @@ void ClientPrivate::log(const ShowMessageParams &message) LanguageClientValue ClientPrivate::showMessageBox( const ShowMessageRequestParams &message) { - auto box = new QMessageBox(); - box->setText(message.toString()); - box->setAttribute(Qt::WA_DeleteOnClose); + QMessageBox box; + box.setText(message.toString()); switch (message.type()) { - case Error: box->setIcon(QMessageBox::Critical); break; - case Warning: box->setIcon(QMessageBox::Warning); break; - case Info: box->setIcon(QMessageBox::Information); break; - case Log: box->setIcon(QMessageBox::NoIcon); break; + case Error: + box.setIcon(QMessageBox::Critical); + break; + case Warning: + box.setIcon(QMessageBox::Warning); + break; + case Info: + box.setIcon(QMessageBox::Information); + break; + case Log: + box.setIcon(QMessageBox::NoIcon); + break; } + QHash itemForButton; if (const std::optional> actions = message.actions()) { - auto button = box->addButton(QMessageBox::Close); - connect(button, &QPushButton::clicked, box, &QMessageBox::reject); for (const MessageActionItem &action : *actions) { - connect(button, &QPushButton::clicked, box, &QMessageBox::accept); + auto button = box.addButton(action.title(), QMessageBox::ActionRole); + connect(button, &QPushButton::clicked, &box, &QMessageBox::accept); itemForButton.insert(button, action); } } - if (box->exec() == QDialog::Rejected) + + if (box.exec() == QDialog::Rejected || itemForButton.isEmpty()) return {}; - const MessageActionItem &item = itemForButton.value(box->clickedButton()); + const MessageActionItem &item = itemForButton.value(box.clickedButton()); return item.isValid() ? LanguageClientValue(item) : LanguageClientValue(); } diff --git a/src/plugins/languageclient/diagnosticmanager.cpp b/src/plugins/languageclient/diagnosticmanager.cpp index 403f38fb0f5..2e8e2d87396 100644 --- a/src/plugins/languageclient/diagnosticmanager.cpp +++ b/src/plugins/languageclient/diagnosticmanager.cpp @@ -31,10 +31,8 @@ namespace LanguageClient { class TextMark : public TextEditor::TextMark { public: - TextMark(const FilePath &fileName, const Diagnostic &diag, const Client *client) - : TextEditor::TextMark(fileName, - diag.range().start().line() + 1, - {client->name(), client->id()}) + TextMark(TextDocument *doc, const Diagnostic &diag, const Client *client) + : TextEditor::TextMark(doc, diag.range().start().line() + 1, {client->name(), client->id()}) { setLineAnnotation(diag.message()); setToolTip(diag.message()); @@ -106,7 +104,7 @@ void DiagnosticManager::showDiagnostics(const FilePath &filePath, int version) = createDiagnosticSelection(diagnostic, doc->document()); if (!selection.cursor.isNull()) extraSelections << selection; - if (TextEditor::TextMark *mark = createTextMark(filePath, diagnostic, isProjectFile)) + if (TextEditor::TextMark *mark = createTextMark(doc, diagnostic, isProjectFile)) marks.marks.append(mark); } if (!marks.marks.isEmpty()) @@ -118,13 +116,13 @@ void DiagnosticManager::showDiagnostics(const FilePath &filePath, int version) } } -TextEditor::TextMark *DiagnosticManager::createTextMark(const FilePath &filePath, +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 QString tooltip = Tr::tr("Copy to Clipboard"); - auto mark = new TextMark(filePath, diagnostic, m_client); + auto mark = new TextMark(doc, diagnostic, m_client); mark->setActionsProvider([text = diagnostic.message()] { QAction *action = new QAction(); action->setIcon(icon); diff --git a/src/plugins/languageclient/diagnosticmanager.h b/src/plugins/languageclient/diagnosticmanager.h index 0f333d71f06..a4ad23e1b98 100644 --- a/src/plugins/languageclient/diagnosticmanager.h +++ b/src/plugins/languageclient/diagnosticmanager.h @@ -55,7 +55,7 @@ signals: protected: Client *client() const { return m_client; } - virtual TextEditor::TextMark *createTextMark(const Utils::FilePath &filePath, + virtual TextEditor::TextMark *createTextMark(TextEditor::TextDocument *doc, const LanguageServerProtocol::Diagnostic &diagnostic, bool isProjectFile) const; virtual QTextEdit::ExtraSelection createDiagnosticSelection( diff --git a/src/plugins/languageclient/languageclientmanager.cpp b/src/plugins/languageclient/languageclientmanager.cpp index 86485ff8aaf..3129bccf30c 100644 --- a/src/plugins/languageclient/languageclientmanager.cpp +++ b/src/plugins/languageclient/languageclientmanager.cpp @@ -63,10 +63,6 @@ LanguageClientManager::LanguageClientManager(QObject *parent) this, &LanguageClientManager::documentOpened); connect(EditorManager::instance(), &EditorManager::documentClosed, this, &LanguageClientManager::documentClosed); - connect(EditorManager::instance(), &EditorManager::saved, - this, &LanguageClientManager::documentContentsSaved); - connect(EditorManager::instance(), &EditorManager::aboutToSave, - this, &LanguageClientManager::documentWillSave); connect(ProjectManager::instance(), &ProjectManager::projectAdded, this, &LanguageClientManager::projectAdded); connect(ProjectManager::instance(), &ProjectManager::projectRemoved, @@ -550,24 +546,6 @@ void LanguageClientManager::documentClosed(Core::IDocument *document) m_clientForDocument.remove(textDocument); } -void LanguageClientManager::documentContentsSaved(Core::IDocument *document) -{ - if (auto textDocument = qobject_cast(document)) { - const QList &clients = reachableClients(); - for (Client *client : clients) - client->documentContentsSaved(textDocument); - } -} - -void LanguageClientManager::documentWillSave(Core::IDocument *document) -{ - if (auto textDocument = qobject_cast(document)) { - const QList &clients = reachableClients(); - for (Client *client : clients) - client->documentWillSave(textDocument); - } -} - void LanguageClientManager::updateProject(ProjectExplorer::Project *project) { for (BaseSettings *setting : std::as_const(m_currentSettings)) { diff --git a/src/plugins/languageclient/languageclientmanager.h b/src/plugins/languageclient/languageclientmanager.h index 0a38b3d3872..301cb4c5264 100644 --- a/src/plugins/languageclient/languageclientmanager.h +++ b/src/plugins/languageclient/languageclientmanager.h @@ -91,8 +91,6 @@ private: void editorOpened(Core::IEditor *editor); void documentOpened(Core::IDocument *document); void documentClosed(Core::IDocument *document); - void documentContentsSaved(Core::IDocument *document); - void documentWillSave(Core::IDocument *document); void updateProject(ProjectExplorer::Project *project); void projectAdded(ProjectExplorer::Project *project); diff --git a/src/plugins/languageclient/languageclientsymbolsupport.cpp b/src/plugins/languageclient/languageclientsymbolsupport.cpp index 7069360b021..89cc9f44e2b 100644 --- a/src/plugins/languageclient/languageclientsymbolsupport.cpp +++ b/src/plugins/languageclient/languageclientsymbolsupport.cpp @@ -551,7 +551,7 @@ void SymbolSupport::handleRenameResponse(Core::SearchResult *search, if (error.has_value()) { errorMessage = error->toString(); if (errorMessage.contains("Cannot rename symbol: new name is the same as the old name")) - errorMessage = Tr::tr("Start typing to see replacements"); // clangd optimization + errorMessage = Tr::tr("Start typing to see replacements."); // clangd optimization else m_client->log(*error); } diff --git a/src/plugins/languageclient/languageclientutils.cpp b/src/plugins/languageclient/languageclientutils.cpp index c421b2a9376..06ed8dcae65 100644 --- a/src/plugins/languageclient/languageclientutils.cpp +++ b/src/plugins/languageclient/languageclientutils.cpp @@ -200,7 +200,7 @@ void updateCodeActionRefactoringMarker(Client *client, const RefactorMarkers markers = markersAtBlock.values(); for (BaseTextEditor *editor : editors) { if (TextEditorWidget *editorWidget = editor->editorWidget()) - editorWidget->setRefactorMarkers(markers + editorWidget->refactorMarkers()); + editorWidget->setRefactorMarkers(markers, client->id()); } } diff --git a/src/plugins/languageclient/locatorfilter.cpp b/src/plugins/languageclient/locatorfilter.cpp index da03b2e7c50..61a6d187daa 100644 --- a/src/plugins/languageclient/locatorfilter.cpp +++ b/src/plugins/languageclient/locatorfilter.cpp @@ -69,10 +69,10 @@ LocatorMatcherTask locatorMatcher(Client *client, int maxResultCount, const auto onFilterSetup = [storage, resultStorage, client, filter](Async &async) { const QList results = *resultStorage; if (results.isEmpty()) - return TaskAction::StopWithDone; + return SetupResult::StopWithDone; async.setFutureSynchronizer(ExtensionSystem::PluginManager::futureSynchronizer()); async.setConcurrentCallData(filterResults, *storage, client, results, filter); - return TaskAction::Continue; + return SetupResult::Continue; }; const Group root { diff --git a/src/plugins/languageclient/lspinspector.cpp b/src/plugins/languageclient/lspinspector.cpp index 09307a06c3e..31a353c2787 100644 --- a/src/plugins/languageclient/lspinspector.cpp +++ b/src/plugins/languageclient/lspinspector.cpp @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -334,7 +335,7 @@ private: QTabWidget * const m_tabWidget; enum class TabIndex { Log, Capabilities, Custom }; - QListWidget *m_clients = nullptr; + QComboBox *m_clients = nullptr; }; void LspInspector::show(const QString &defaultClient) @@ -401,34 +402,34 @@ LspInspectorWidget::LspInspectorWidget(LspInspector *inspector) this, &LspInspectorWidget::updateCapabilities); connect(Core::ICore::instance(), &Core::ICore::coreAboutToClose, this, &QWidget::close); - m_clients = new QListWidget; - m_clients->addItems(inspector->clients()); - m_clients->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::MinimumExpanding); - auto mainLayout = new QVBoxLayout; - auto mainSplitter = new Core::MiniSplitter; - mainSplitter->setOrientation(Qt::Horizontal); - mainSplitter->addWidget(m_clients); - mainSplitter->addWidget(m_tabWidget); - mainSplitter->setStretchFactor(0, 0); - mainSplitter->setStretchFactor(1, 1); + + m_clients = new QComboBox; + m_clients->addItem(Tr::tr("