forked from qt-creator/qt-creator
Merge remote-tracking branch 'origin/11.0'
Change-Id: I8d1c9720a868da02b3157a48954eb4e262539c84
This commit is contained in:
76
dist/changelog/changes-10.0.2.md
vendored
Normal file
76
dist/changelog/changes-10.0.2.md
vendored
Normal file
@@ -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
|
||||
@@ -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 <sourceDir>/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
|
||||
|
||||
227
doc/qtcreator/src/cmake/creator-projects-cmake-presets.qdoc
Normal file
227
doc/qtcreator/src/cmake/creator-projects-cmake-presets.qdoc
Normal file
@@ -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 <sourceDir>/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
|
||||
*/
|
||||
@@ -181,6 +181,18 @@
|
||||
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.
|
||||
|
||||
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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -433,15 +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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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}.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -1284,7 +1284,7 @@ void TaskNode::invokeEndHandler(bool success)
|
||||
\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
|
||||
@@ -1513,7 +1513,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.
|
||||
|
||||
@@ -1745,16 +1745,50 @@ 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 "
|
||||
@@ -1763,6 +1797,15 @@ TaskTree::~TaskTree()
|
||||
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 +1815,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 TaskAction::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 +1849,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 +1915,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<void> dummy;
|
||||
@@ -1799,6 +1942,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<void> &future)
|
||||
{
|
||||
if (future.isCanceled())
|
||||
@@ -1830,6 +1979,19 @@ bool TaskTree::runBlocking(const QFuture<void> &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<void> dummy;
|
||||
@@ -1837,6 +1999,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<void> &future, milliseconds timeout)
|
||||
{
|
||||
const Group root = timeout == milliseconds::max() ? recipe
|
||||
@@ -1845,16 +2013,145 @@ bool TaskTree::runBlocking(const Group &recipe, const QFuture<void> &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 <typename StorageStruct, typename StorageHandler> void TaskTree::onStorageSetup(const TreeStorage<StorageStruct> &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<QByteArray> storage;
|
||||
|
||||
const auto onSaverSetup = [storage](ConcurrentCall<void> &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 <typename StorageStruct, typename StorageHandler> void TaskTree::onStorageDone(const TreeStorage<StorageStruct> &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<QByteArray> storage;
|
||||
|
||||
const auto onLoaderSetup = [storage](ConcurrentCall<void> &concurrent) {
|
||||
concurrent.setConcurrentCallData(&load, "foo.txt");
|
||||
};
|
||||
const auto onLoaderDone = [storage](const ConcurrentCall<void> &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)
|
||||
|
||||
@@ -108,6 +108,8 @@ expected_str<qint64> 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);
|
||||
|
||||
|
||||
@@ -384,9 +384,13 @@ 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(FilePath("sudo").searchInPath(), {"-A"});
|
||||
rootCommand.addCommandLineAsArgs(cmd);
|
||||
|
||||
@@ -11,6 +11,8 @@ namespace Utils {
|
||||
|
||||
class TerminalInterfacePrivate;
|
||||
|
||||
const char TERMINAL_SHELL_NAME[] = "Terminal.ShellName";
|
||||
|
||||
class StubCreator : public QObject
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -217,17 +217,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
|
||||
{
|
||||
@@ -320,6 +309,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;
|
||||
};
|
||||
|
||||
@@ -711,11 +711,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());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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<QByteArray> contents = path.fileContents(FileSize, 0);
|
||||
QTC_ASSERT_EXPECTED(contents, return std::error_code());
|
||||
|
||||
@@ -72,7 +72,7 @@ public:
|
||||
|
||||
ErrorOr<Status> 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())
|
||||
|
||||
@@ -426,8 +426,10 @@ CommandLine CMakeBuildStep::cmakeCommand() const
|
||||
}
|
||||
return s;
|
||||
}));
|
||||
if (m_useStaging->value())
|
||||
cmd.addArg("install");
|
||||
|
||||
auto bs = qobject_cast<CMakeBuildSystem*>(buildSystem());
|
||||
auto bs = qobject_cast<CMakeBuildSystem *>(buildSystem());
|
||||
if (bs && bs->isMultiConfigReader()) {
|
||||
cmd.addArg("--config");
|
||||
if (m_configuration)
|
||||
@@ -439,9 +441,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("--");
|
||||
|
||||
@@ -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<CocoDiagnosticSeverity> severity = cocoDiagnostic.cocoSeverity())
|
||||
return new CocoTextMark(filePath, cocoDiagnostic, client()->id());
|
||||
return new CocoTextMark(doc, cocoDiagnostic, client()->id());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
@@ -183,8 +183,24 @@ void CopilotClient::handleCompletions(const GetCompletionRequest::Response &resp
|
||||
auto isValidCompletion = [](const Completion &completion) {
|
||||
return completion.isValid() && !completion.text().trimmed().isEmpty();
|
||||
};
|
||||
const QList<Completion> completions = Utils::filtered(result->completions().toListOrEmpty(),
|
||||
QList<Completion> 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(
|
||||
|
||||
@@ -27,6 +27,7 @@ public:
|
||||
return typedValue<LanguageServerProtocol::Range>(LanguageServerProtocol::rangeKey);
|
||||
}
|
||||
QString text() const { return typedValue<QString>(LanguageServerProtocol::textKey); }
|
||||
void setText(const QString &text) { insert(LanguageServerProtocol::textKey, text); }
|
||||
QString uuid() const { return typedValue<QString>(uuidKey); }
|
||||
|
||||
bool isValid() const override
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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<TextEditor::TextDocumentLayout *>(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
|
||||
|
||||
@@ -47,10 +47,6 @@ public:
|
||||
QFuture<CursorInfo> 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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -235,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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -277,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<TextEditor::TextDocument *, OpenedDocument> m_openedDocument;
|
||||
@@ -648,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(),
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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<TextEditor::TextDocument *>(document)) {
|
||||
const QList<Client *> &clients = reachableClients();
|
||||
for (Client *client : clients)
|
||||
client->documentContentsSaved(textDocument);
|
||||
}
|
||||
}
|
||||
|
||||
void LanguageClientManager::documentWillSave(Core::IDocument *document)
|
||||
{
|
||||
if (auto textDocument = qobject_cast<TextEditor::TextDocument *>(document)) {
|
||||
const QList<Client *> &clients = reachableClients();
|
||||
for (Client *client : clients)
|
||||
client->documentWillSave(textDocument);
|
||||
}
|
||||
}
|
||||
|
||||
void LanguageClientManager::updateProject(ProjectExplorer::Project *project)
|
||||
{
|
||||
for (BaseSettings *setting : std::as_const(m_currentSettings)) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -146,9 +146,9 @@ const QString stmCubeProgrammerDetectionPath{HostOsInfo::isWindowsHost()
|
||||
? QString("bin/STM32_Programmer_CLI.exe")
|
||||
: QString("bin/STM32_Programmer.sh")};
|
||||
|
||||
const char renesasProgrammerSetting[]{"FlashProgrammerPath"};
|
||||
const char renesasProgrammerSetting[]{"RenesasFlashProgrammer"};
|
||||
const char renesasProgrammerCmakeVar[]{"RENESAS_FLASH_PROGRAMMER_PATH"};
|
||||
const QString renesasProgrammerEnvVar{"RenesasFlashProgrammer_PATH"};
|
||||
const char renesasProgrammerEnvVar[]{"RENESAS_FLASH_PROGRAMMER_PATH"};
|
||||
const char renesasProgrammerLabel[]{"Renesas Flash Programmer"};
|
||||
const QString renesasProgrammerDetectionPath{HostOsInfo::withExecutableSuffix("rfp-cli")};
|
||||
|
||||
@@ -1539,9 +1539,9 @@ void McuSupportTest::test_legacy_createThirdPartyPackage_data()
|
||||
<< PackageCreator{[this]() {
|
||||
return Legacy::createRenesasProgrammerPackage(settingsMockPtr);
|
||||
}}
|
||||
<< ghs_rh850_d1m1a_baremetal_json << defaultToolPath << defaultToolPath
|
||||
<< renesasProgrammerSetting << renesasProgrammerCmakeVar << renesasProgrammerEnvVar
|
||||
<< renesasProgrammerLabel << renesasProgrammerDetectionPath;
|
||||
<< ghs_rh850_d1m1a_baremetal_json << empty << empty << renesasProgrammerSetting
|
||||
<< renesasProgrammerCmakeVar << renesasProgrammerEnvVar << renesasProgrammerLabel
|
||||
<< renesasProgrammerDetectionPath;
|
||||
}
|
||||
|
||||
void McuSupportTest::test_legacy_createThirdPartyPackage()
|
||||
@@ -1574,7 +1574,53 @@ void McuSupportTest::test_legacy_createThirdPartyPackage()
|
||||
|
||||
void McuSupportTest::test_createThirdPartyPackage_data()
|
||||
{
|
||||
test_legacy_createThirdPartyPackage_data();
|
||||
QTest::addColumn<QString>("json");
|
||||
QTest::addColumn<QString>("path");
|
||||
QTest::addColumn<QString>("defaultPath");
|
||||
QTest::addColumn<QString>("setting");
|
||||
QTest::addColumn<QString>("cmakeVar");
|
||||
QTest::addColumn<QString>("envVar");
|
||||
QTest::addColumn<QString>("label");
|
||||
QTest::addColumn<QString>("detectionPath");
|
||||
|
||||
// Sometimes the jsons have different values than the legacy packages
|
||||
// Enter the expected values from the jsons here when they diverge from legacy values
|
||||
QString programFiles = qtcEnvironmentVariable("Env:PROGRAMFILES(x86)");
|
||||
const QString renesasProgrammerDefaultPath = {
|
||||
HostOsInfo::isWindowsHost()
|
||||
? QString("%1/Renesas Electronics/Programming Tools/Renesas "
|
||||
"Flash Programmer V3.09").arg(programFiles)
|
||||
: QString("")};
|
||||
|
||||
QTest::newRow("armgcc_mimxrt1050_evk_freertos_json mcuXpresso")
|
||||
<< armgcc_mimxrt1050_evk_freertos_json << xpressoIdePath << xpressoIdePath
|
||||
<< xpressoIdeSetting << xpressoIdeCmakeVar << xpressoIdeEnvVar << xpressoIdeLabel
|
||||
<< xpressoIdeDetectionPath;
|
||||
|
||||
QTest::newRow("armgcc_mimxrt1064_evk_freertos_json mcuXpresso")
|
||||
<< armgcc_mimxrt1064_evk_freertos_json << xpressoIdePath << xpressoIdePath
|
||||
<< xpressoIdeSetting << xpressoIdeCmakeVar << xpressoIdeEnvVar << xpressoIdeLabel
|
||||
<< xpressoIdeDetectionPath;
|
||||
|
||||
QTest::newRow("armgcc_mimxrt1170_evk_freertos_json mcuXpresso")
|
||||
<< armgcc_mimxrt1170_evk_freertos_json << xpressoIdePath << xpressoIdePath
|
||||
<< xpressoIdeSetting << xpressoIdeCmakeVar << xpressoIdeEnvVar << xpressoIdeLabel
|
||||
<< xpressoIdeDetectionPath;
|
||||
|
||||
QTest::newRow("armgcc_stm32h750b_discovery_baremetal_json stmCubeProgrammer")
|
||||
<< armgcc_stm32h750b_discovery_baremetal_json << stmCubeProgrammerPath
|
||||
<< stmCubeProgrammerPath << stmCubeProgrammerSetting << empty << empty
|
||||
<< stmCubeProgrammerLabel << stmCubeProgrammerDetectionPath;
|
||||
|
||||
QTest::newRow("armgcc_stm32f769i_discovery_freertos_json stmCubeProgrammer")
|
||||
<< armgcc_stm32f769i_discovery_freertos_json << stmCubeProgrammerPath
|
||||
<< stmCubeProgrammerPath << stmCubeProgrammerSetting << empty << empty
|
||||
<< stmCubeProgrammerLabel << stmCubeProgrammerDetectionPath;
|
||||
|
||||
QTest::newRow("ghs_rh850_d1m1a_baremetal_json renesasProgrammer")
|
||||
<< ghs_rh850_d1m1a_baremetal_json << renesasProgrammerDefaultPath << empty
|
||||
<< "FlashProgrammerPath" << renesasProgrammerCmakeVar << "RenesasFlashProgrammer_PATH"
|
||||
<< renesasProgrammerLabel << renesasProgrammerDetectionPath;
|
||||
}
|
||||
|
||||
void McuSupportTest::test_createThirdPartyPackage()
|
||||
|
||||
@@ -55,7 +55,7 @@ Core::IDocument::OpenResult ModelDocument::open(QString *errorString,
|
||||
return result;
|
||||
}
|
||||
|
||||
bool ModelDocument::save(QString *errorString, const Utils::FilePath &filePath, bool autoSave)
|
||||
bool ModelDocument::saveImpl(QString *errorString, const Utils::FilePath &filePath, bool autoSave)
|
||||
{
|
||||
if (!d->documentController) {
|
||||
*errorString = Tr::tr("No model loaded. Cannot save.");
|
||||
|
||||
@@ -29,7 +29,6 @@ public:
|
||||
OpenResult open(QString *errorString,
|
||||
const Utils::FilePath &filePath,
|
||||
const Utils::FilePath &realFilePath) override;
|
||||
bool save(QString *errorString, const Utils::FilePath &filePath, bool autoSave) override;
|
||||
bool shouldAutoSave() const override;
|
||||
bool isModified() const override;
|
||||
bool isSaveAsAllowed() const override;
|
||||
@@ -39,6 +38,9 @@ public:
|
||||
|
||||
OpenResult load(QString *errorString, const QString &fileName);
|
||||
|
||||
protected:
|
||||
bool saveImpl(QString *errorString, const Utils::FilePath &filePath, bool autoSave) override;
|
||||
|
||||
private:
|
||||
ModelDocumentPrivate *d;
|
||||
};
|
||||
|
||||
@@ -61,7 +61,9 @@ static const QList<Target *> targetsForSelection(const Project *project,
|
||||
{
|
||||
if (targetSelection == ConfigSelection::All)
|
||||
return project->targets();
|
||||
return {project->activeTarget()};
|
||||
if (project->activeTarget())
|
||||
return {project->activeTarget()};
|
||||
return {};
|
||||
}
|
||||
|
||||
static const QList<BuildConfiguration *> buildConfigsForSelection(const Target *target,
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include <utils/process.h>
|
||||
#include <utils/processinterface.h>
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/terminalinterface.h>
|
||||
#include <utils/utilsicons.h>
|
||||
|
||||
#include <coreplugin/icontext.h>
|
||||
@@ -1348,9 +1349,12 @@ void SimpleTargetRunnerPrivate::start()
|
||||
|
||||
m_stopRequested = false;
|
||||
|
||||
QVariantHash extraData = m_extraData;
|
||||
extraData[TERMINAL_SHELL_NAME] = m_command.executable().fileName();
|
||||
|
||||
m_process.setCommand(cmdLine);
|
||||
m_process.setEnvironment(env);
|
||||
m_process.setExtraData(m_extraData);
|
||||
m_process.setExtraData(extraData);
|
||||
|
||||
m_state = Run;
|
||||
m_process.setWorkingDirectory(m_workingDirectory);
|
||||
|
||||
@@ -120,9 +120,8 @@ static QJsonObject readObjJson(const FilePath &projectFile, QString *errorMessag
|
||||
|
||||
static QStringList readLines(const FilePath &projectFile)
|
||||
{
|
||||
const QString projectFileName = projectFile.fileName();
|
||||
QSet<QString> visited = { projectFileName };
|
||||
QStringList lines = { projectFileName };
|
||||
QSet<QString> visited;
|
||||
QStringList lines;
|
||||
|
||||
const expected_str<QByteArray> contents = projectFile.fileContents();
|
||||
if (contents) {
|
||||
@@ -144,9 +143,8 @@ static QStringList readLines(const FilePath &projectFile)
|
||||
|
||||
static QStringList readLinesJson(const FilePath &projectFile, QString *errorMessage)
|
||||
{
|
||||
const QString projectFileName = projectFile.fileName();
|
||||
QSet<QString> visited = { projectFileName };
|
||||
QStringList lines = { projectFileName };
|
||||
QSet<QString> visited;
|
||||
QStringList lines;
|
||||
|
||||
const QJsonObject obj = readObjJson(projectFile, errorMessage);
|
||||
for (const QJsonValue &file : obj.value("files").toArray()) {
|
||||
@@ -205,8 +203,6 @@ static FileType getFileType(const FilePath &f)
|
||||
{
|
||||
if (f.endsWith(".py"))
|
||||
return FileType::Source;
|
||||
if (f.endsWith(".pyproject") || f.endsWith(".pyqtc"))
|
||||
return FileType::Project;
|
||||
if (f.endsWith(".qrc"))
|
||||
return FileType::Resource;
|
||||
if (f.endsWith(".ui"))
|
||||
@@ -221,11 +217,16 @@ void PythonBuildSystem::triggerParsing()
|
||||
ParseGuard guard = guardParsingRun();
|
||||
parse();
|
||||
|
||||
const QDir baseDir(projectDirectory().toString());
|
||||
QList<BuildTargetInfo> appTargets;
|
||||
|
||||
auto newRoot = std::make_unique<PythonProjectNode>(projectDirectory());
|
||||
for (const FileEntry &entry: std::as_const(m_files)) {
|
||||
|
||||
const FilePath projectFile = projectFilePath();
|
||||
const QString displayName = projectFile.relativePathFrom(projectDirectory()).toUserOutput();
|
||||
newRoot->addNestedNode(
|
||||
std::make_unique<PythonFileNode>(projectFile, displayName, FileType::Project));
|
||||
|
||||
for (const FileEntry &entry : std::as_const(m_files)) {
|
||||
const QString displayName = entry.filePath.relativePathFrom(projectDirectory()).toUserOutput();
|
||||
const FileType fileType = getFileType(entry.filePath);
|
||||
|
||||
@@ -236,7 +237,7 @@ void PythonBuildSystem::triggerParsing()
|
||||
bti.displayName = displayName;
|
||||
bti.buildKey = entry.filePath.toString();
|
||||
bti.targetFilePath = entry.filePath;
|
||||
bti.projectFilePath = projectFilePath();
|
||||
bti.projectFilePath = projectFile;
|
||||
bti.isQtcRunnable = entry.filePath.fileName() == "main.py";
|
||||
appTargets.append(bti);
|
||||
}
|
||||
@@ -301,12 +302,21 @@ bool PythonBuildSystem::addFiles(Node *, const FilePaths &filePaths, FilePaths *
|
||||
{
|
||||
const Utils::FilePath projectDir = projectDirectory();
|
||||
|
||||
auto comp = [](const FileEntry &left, const FileEntry &right) {
|
||||
return left.rawEntry < right.rawEntry;
|
||||
};
|
||||
|
||||
const bool isSorted = std::is_sorted(m_files.begin(), m_files.end(), comp);
|
||||
|
||||
for (const FilePath &filePath : filePaths) {
|
||||
if (!projectDir.isSameDevice(filePath))
|
||||
return false;
|
||||
m_files.append(FileEntry{filePath.relativePathFrom(projectDir).toString(), filePath});
|
||||
}
|
||||
|
||||
if (isSorted)
|
||||
std::sort(m_files.begin(), m_files.end(), comp);
|
||||
|
||||
return save();
|
||||
}
|
||||
|
||||
|
||||
@@ -120,10 +120,10 @@ Core::IDocument::OpenResult ResourceEditorDocument::open(QString *errorString,
|
||||
return OpenResult::Success;
|
||||
}
|
||||
|
||||
bool ResourceEditorDocument::save(QString *errorString, const FilePath &filePath, bool autoSave)
|
||||
bool ResourceEditorDocument::saveImpl(QString *errorString, const FilePath &filePath, bool autoSave)
|
||||
{
|
||||
if (debugResourceEditorW)
|
||||
qDebug() << ">ResourceEditorW::save: " << filePath;
|
||||
qDebug() << ">ResourceEditorW::saveImpl: " << filePath;
|
||||
|
||||
const FilePath &actualName = filePath.isEmpty() ? this->filePath() : filePath;
|
||||
if (actualName.isEmpty())
|
||||
|
||||
@@ -30,7 +30,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;
|
||||
QString plainText() const;
|
||||
QByteArray contents() const override;
|
||||
bool setContents(const QByteArray &contents) override;
|
||||
@@ -47,6 +46,9 @@ public:
|
||||
signals:
|
||||
void loaded(bool success);
|
||||
|
||||
protected:
|
||||
bool saveImpl(QString *errorString, const Utils::FilePath &filePath, bool autoSave) override;
|
||||
|
||||
private:
|
||||
void dirtyChanged(bool);
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ Core::IDocument::OpenResult ScxmlEditorDocument::open(QString *errorString,
|
||||
return OpenResult::Success;
|
||||
}
|
||||
|
||||
bool ScxmlEditorDocument::save(QString *errorString, const FilePath &filePath, bool autoSave)
|
||||
bool ScxmlEditorDocument::saveImpl(QString *errorString, const FilePath &filePath, bool autoSave)
|
||||
{
|
||||
const FilePath oldFileName = this->filePath();
|
||||
const FilePath actualName = filePath.isEmpty() ? oldFileName : filePath;
|
||||
|
||||
@@ -30,7 +30,6 @@ public:
|
||||
OpenResult open(QString *errorString,
|
||||
const Utils::FilePath &filePath,
|
||||
const Utils::FilePath &realFilePath) override;
|
||||
bool save(QString *errorString, const Utils::FilePath &filePath, bool autoSave) override;
|
||||
bool shouldAutoSave() const override;
|
||||
bool isSaveAsAllowed() const override;
|
||||
bool isModified() const override;
|
||||
@@ -46,6 +45,9 @@ public:
|
||||
signals:
|
||||
void reloadRequested(QString *errorString, const QString &);
|
||||
|
||||
protected:
|
||||
bool saveImpl(QString *errorString, const Utils::FilePath &filePath, bool autoSave) override;
|
||||
|
||||
private:
|
||||
QPointer<Common::MainWidget> m_designWidget;
|
||||
};
|
||||
|
||||
@@ -41,7 +41,9 @@ Core::IDocument::OpenResult ObjectsMapDocument::open(QString *errorString,
|
||||
return result;
|
||||
}
|
||||
|
||||
bool ObjectsMapDocument::save(QString *errorString, const Utils::FilePath &fileName, bool autoSave)
|
||||
bool ObjectsMapDocument::saveImpl(QString *errorString,
|
||||
const Utils::FilePath &fileName,
|
||||
bool autoSave)
|
||||
{
|
||||
const Utils::FilePath actual = fileName.isEmpty() ? filePath() : fileName;
|
||||
if (actual.isEmpty())
|
||||
|
||||
@@ -21,7 +21,6 @@ public:
|
||||
OpenResult open(QString *errorString,
|
||||
const Utils::FilePath &fileName,
|
||||
const Utils::FilePath &realFileName) override;
|
||||
bool save(QString *errorString, const Utils::FilePath &fileName, bool autoSave) override;
|
||||
Utils::FilePath fallbackSaveAsPath() const override;
|
||||
QString fallbackSaveAsFileName() const override;
|
||||
bool isModified() const override { return m_isModified; }
|
||||
@@ -34,6 +33,9 @@ public:
|
||||
QByteArray contents() const override;
|
||||
ObjectsMapModel *model() const { return m_contentModel; }
|
||||
|
||||
protected:
|
||||
bool saveImpl(QString *errorString, const Utils::FilePath &fileName, bool autoSave) override;
|
||||
|
||||
private:
|
||||
OpenResult openImpl(QString *error,
|
||||
const Utils::FilePath &fileName,
|
||||
|
||||
@@ -49,7 +49,11 @@ public:
|
||||
if (!terminal) {
|
||||
terminal = new TerminalWidget(nullptr, openParameters);
|
||||
|
||||
terminal->setShellName(setup.m_commandLine.executable().fileName());
|
||||
terminal->setShellName(
|
||||
setup.m_extraData
|
||||
.value(TERMINAL_SHELL_NAME, setup.m_commandLine.executable().fileName())
|
||||
.toString());
|
||||
|
||||
m_terminalPane->addTerminal(terminal, "App");
|
||||
} else {
|
||||
terminal->restart(openParameters);
|
||||
|
||||
@@ -146,8 +146,8 @@ void TerminalWidget::setupPty()
|
||||
TerminalSettings::instance().shellArguments.value(),
|
||||
CommandLine::Raw});
|
||||
|
||||
Environment env = m_openParameters.environment.value_or(
|
||||
shellCommand.executable().deviceEnvironment());
|
||||
Environment env = m_openParameters.environment.value_or(Environment{})
|
||||
.appliedToEnvironment(shellCommand.executable().deviceEnvironment());
|
||||
|
||||
// For git bash on Windows
|
||||
env.prependOrSetPath(shellCommand.executable().parentDir());
|
||||
|
||||
@@ -185,6 +185,28 @@ void GenerigHighlighterTests::testChange()
|
||||
compareFormats(actualFormats.at(i), formatRanges.at(i));
|
||||
}
|
||||
|
||||
void GenerigHighlighterTests::testPreeditText()
|
||||
{
|
||||
QTextBlock block = m_editor->textDocument()->document()->findBlockByNumber(2);
|
||||
QVERIFY(block.isValid());
|
||||
|
||||
block.layout()->setPreeditArea(7, "uaf");
|
||||
m_editor->textDocument()->syntaxHighlighter()->rehighlight();
|
||||
|
||||
const FormatRanges formatRanges = {{0, 4, toFormat(C_VISUAL_WHITESPACE)},
|
||||
{4, 3, toFormat(C_TYPE)},
|
||||
{10, 3, toFormat(C_TYPE)},
|
||||
{13, 1, toFormat(C_FUNCTION)},
|
||||
{14, 1, toFormat(C_VISUAL_WHITESPACE)},
|
||||
{15, 6, toFormat(C_STRING)},
|
||||
{21, 1, toFormat(C_FUNCTION)}};
|
||||
const QList<QTextLayout::FormatRange> actualFormats = block.layout()->formats();
|
||||
// full hash calculation for QTextCharFormat fails so just check the important entries of format
|
||||
QCOMPARE(actualFormats.size(), formatRanges.size());
|
||||
for (int i = 0; i < formatRanges.size(); ++i)
|
||||
compareFormats(actualFormats.at(i), formatRanges.at(i));
|
||||
}
|
||||
|
||||
void GenerigHighlighterTests::cleanupTestCase()
|
||||
{
|
||||
if (m_editor)
|
||||
|
||||
@@ -17,6 +17,7 @@ private slots:
|
||||
void testHighlight_data();
|
||||
void testHighlight();
|
||||
void testChange();
|
||||
void testPreeditText();
|
||||
void cleanupTestCase();
|
||||
|
||||
private:
|
||||
|
||||
@@ -17,6 +17,12 @@
|
||||
|
||||
namespace TextEditor {
|
||||
|
||||
enum HighlighterTypeProperty
|
||||
{
|
||||
SyntaxHighlight = QTextFormat::UserProperty + 1,
|
||||
SemanticHighlight = QTextFormat::UserProperty + 2
|
||||
};
|
||||
|
||||
class SyntaxHighlighterPrivate
|
||||
{
|
||||
SyntaxHighlighter *q_ptr = nullptr;
|
||||
@@ -98,9 +104,9 @@ void SyntaxHighlighterPrivate::applyFormatChanges(int from, int charsRemoved, in
|
||||
|
||||
QVector<QTextLayout::FormatRange> ranges;
|
||||
QVector<QTextLayout::FormatRange> oldRanges;
|
||||
std::tie(ranges, oldRanges)
|
||||
std::tie(oldRanges, ranges)
|
||||
= Utils::partition(layout->formats(), [](const QTextLayout::FormatRange &range) {
|
||||
return range.format.property(QTextFormat::UserProperty).toBool();
|
||||
return range.format.property(SyntaxHighlight).toBool();
|
||||
});
|
||||
|
||||
if (currentBlock.contains(from)) {
|
||||
@@ -129,8 +135,23 @@ void SyntaxHighlighterPrivate::applyFormatChanges(int from, int charsRemoved, in
|
||||
while (i < formatChanges.count() && formatChanges.at(i) == r.format)
|
||||
++i;
|
||||
|
||||
r.format.setProperty(SyntaxHighlight, true);
|
||||
r.length = i - r.start;
|
||||
|
||||
const QString preeditText = currentBlock.layout()->preeditAreaText();
|
||||
if (!preeditText.isEmpty()) {
|
||||
const int preeditPosition = currentBlock.layout()->preeditAreaPosition();
|
||||
if (r.start >= preeditPosition) {
|
||||
r.start += preeditText.length();
|
||||
} else if (r.start + r.length > preeditPosition) {
|
||||
QTextLayout::FormatRange beforePreeditRange = r;
|
||||
r.start = preeditPosition + preeditText.length();
|
||||
r.length = r.length - (r.start - preeditPosition);
|
||||
beforePreeditRange.length = preeditPosition - beforePreeditRange.start;
|
||||
newRanges << beforePreeditRange;
|
||||
}
|
||||
}
|
||||
|
||||
newRanges << r;
|
||||
}
|
||||
|
||||
@@ -656,6 +677,24 @@ void SyntaxHighlighter::setExtraFormats(const QTextBlock &block,
|
||||
if (block.layout() == nullptr || blockLength == 0)
|
||||
return;
|
||||
|
||||
const QString preeditText = block.layout()->preeditAreaText();
|
||||
if (!preeditText.isEmpty()) {
|
||||
QVector<QTextLayout::FormatRange> additionalRanges;
|
||||
const int preeditPosition = block.layout()->preeditAreaPosition();
|
||||
for (QTextLayout::FormatRange &r : formats) {
|
||||
if (r.start >= preeditPosition) {
|
||||
r.start += preeditText.length();
|
||||
} else if (r.start + r.length > preeditPosition) {
|
||||
QTextLayout::FormatRange afterPreeditRange = r;
|
||||
afterPreeditRange.start = preeditPosition + preeditText.length();
|
||||
afterPreeditRange.length = r.length - (preeditPosition - r.start);
|
||||
additionalRanges << afterPreeditRange;
|
||||
r.length = preeditPosition - r.start;
|
||||
}
|
||||
}
|
||||
formats << additionalRanges;
|
||||
}
|
||||
|
||||
Utils::sort(formats, byStartOfRange);
|
||||
|
||||
const QVector<QTextLayout::FormatRange> all = block.layout()->formats();
|
||||
@@ -663,11 +702,11 @@ void SyntaxHighlighter::setExtraFormats(const QTextBlock &block,
|
||||
QVector<QTextLayout::FormatRange> formatsToApply;
|
||||
std::tie(previousSemanticFormats, formatsToApply)
|
||||
= Utils::partition(all, [](const QTextLayout::FormatRange &r) {
|
||||
return r.format.hasProperty(QTextFormat::UserProperty);
|
||||
return r.format.property(SemanticHighlight).toBool();
|
||||
});
|
||||
|
||||
for (auto &format : formats)
|
||||
format.format.setProperty(QTextFormat::UserProperty, true);
|
||||
format.format.setProperty(SemanticHighlight, true);
|
||||
|
||||
if (formats.size() == previousSemanticFormats.size()) {
|
||||
Utils::sort(previousSemanticFormats, byStartOfRange);
|
||||
@@ -694,7 +733,7 @@ void SyntaxHighlighter::clearExtraFormats(const QTextBlock &block)
|
||||
|
||||
const QVector<QTextLayout::FormatRange> formatsToApply
|
||||
= Utils::filtered(block.layout()->formats(), [](const QTextLayout::FormatRange &r) {
|
||||
return !r.format.hasProperty(QTextFormat::UserProperty);
|
||||
return !r.format.property(SemanticHighlight).toBool();
|
||||
});
|
||||
|
||||
bool wasInReformatBlocks = d->inReformatBlocks;
|
||||
|
||||
@@ -622,7 +622,7 @@ SyntaxHighlighter *TextDocument::syntaxHighlighter() const
|
||||
* If \a autoSave is true, the cursor will be restored and some signals suppressed
|
||||
* and we do not clean up the text file (cleanWhitespace(), ensureFinalNewLine()).
|
||||
*/
|
||||
bool TextDocument::save(QString *errorString, const FilePath &filePath, bool autoSave)
|
||||
bool TextDocument::saveImpl(QString *errorString, const FilePath &filePath, bool autoSave)
|
||||
{
|
||||
QTextCursor cursor(&d->m_document);
|
||||
|
||||
|
||||
@@ -101,7 +101,6 @@ public:
|
||||
static bool marksAnnotationHidden(const Utils::Id &category);
|
||||
|
||||
// IDocument implementation.
|
||||
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;
|
||||
@@ -166,6 +165,7 @@ signals:
|
||||
|
||||
protected:
|
||||
virtual void applyFontSettings();
|
||||
bool saveImpl(QString *errorString, const Utils::FilePath &filePath, bool autoSave) override;
|
||||
|
||||
private:
|
||||
OpenResult openImpl(QString *errorString,
|
||||
|
||||
@@ -4947,15 +4947,15 @@ void TextEditorWidget::paintEvent(QPaintEvent *e)
|
||||
|
||||
paintBlock(&painter, data.block, data.offset, blockData.selections, data.eventRect);
|
||||
|
||||
if (data.isEditable && data.context.cursorPosition < -1
|
||||
&& !blockData.layout->preeditAreaText().isEmpty()) {
|
||||
const int cursorPos = blockData.layout->preeditAreaPosition()
|
||||
- (data.context.cursorPosition + 2);
|
||||
data.cursors.append(generateCursorData(cursorPos, data, blockData, painter));
|
||||
}
|
||||
|
||||
if (drawCursor && !drawCursorAsBlock)
|
||||
if (data.isEditable && !blockData.layout->preeditAreaText().isEmpty()) {
|
||||
if (data.context.cursorPosition < -1) {
|
||||
const int cursorPos = blockData.layout->preeditAreaPosition()
|
||||
- (data.context.cursorPosition + 2);
|
||||
data.cursors = {generateCursorData(cursorPos, data, blockData, painter)};
|
||||
}
|
||||
} else if (drawCursor && !drawCursorAsBlock) {
|
||||
d->addCursorsPosition(data, painter, blockData);
|
||||
}
|
||||
d->paintIndentDepth(data, painter, blockData);
|
||||
d->paintAdditionalVisualWhitespaces(data, painter, blockData.boundingRect.top());
|
||||
d->paintReplacement(data, painter, blockData.boundingRect.top());
|
||||
|
||||
@@ -159,6 +159,9 @@ QPainterPath TextEditorOverlay::createSelectionPath(const QTextCursor &begin, co
|
||||
int beginChar = 0;
|
||||
if (block == begin.block()) {
|
||||
beginChar = begin.positionInBlock();
|
||||
const QString preeditAreaText = begin.block().layout()->preeditAreaText();
|
||||
if (!preeditAreaText.isEmpty() && beginChar >= begin.block().layout()->preeditAreaPosition())
|
||||
beginChar += preeditAreaText.length();
|
||||
QTextLine line = blockLayout->lineForTextPosition(beginChar);
|
||||
QTC_ASSERT(line.isValid(), return {});
|
||||
firstLine = line.lineNumber();
|
||||
@@ -171,6 +174,9 @@ QPainterPath TextEditorOverlay::createSelectionPath(const QTextCursor &begin, co
|
||||
int endChar = -1;
|
||||
if (block == end.block()) {
|
||||
endChar = end.positionInBlock();
|
||||
const QString preeditAreaText = end.block().layout()->preeditAreaText();
|
||||
if (!preeditAreaText.isEmpty() && endChar >= end.block().layout()->preeditAreaPosition())
|
||||
endChar += preeditAreaText.length();
|
||||
QTextLine line = blockLayout->lineForTextPosition(endChar);
|
||||
QTC_ASSERT(line.isValid(), return {});
|
||||
lastLine = line.lineNumber();
|
||||
|
||||
@@ -34,6 +34,7 @@ class TextMarkRegistry : public QObject
|
||||
Q_OBJECT
|
||||
public:
|
||||
static void add(TextMark *mark);
|
||||
static void add(TextMark *mark, TextDocument *document);
|
||||
static bool remove(TextMark *mark);
|
||||
|
||||
private:
|
||||
@@ -75,6 +76,16 @@ TextMark::TextMark(const FilePath &filePath, int lineNumber, TextMarkCategory ca
|
||||
TextMarkRegistry::add(this);
|
||||
}
|
||||
|
||||
TextMark::TextMark(TextDocument *document, int lineNumber, TextMarkCategory category)
|
||||
: m_fileName(QTC_GUARD(document) ? document->filePath() : FilePath())
|
||||
, m_lineNumber(lineNumber)
|
||||
, m_visible(true)
|
||||
, m_category(category)
|
||||
{
|
||||
if (!m_fileName.isEmpty())
|
||||
TextMarkRegistry::add(this, document);
|
||||
}
|
||||
|
||||
TextMark::~TextMark()
|
||||
{
|
||||
if (!m_fileName.isEmpty())
|
||||
@@ -461,9 +472,14 @@ TextMarkRegistry::TextMarkRegistry(QObject *parent)
|
||||
}
|
||||
|
||||
void TextMarkRegistry::add(TextMark *mark)
|
||||
{
|
||||
add(mark, TextDocument::textDocumentForFilePath(mark->filePath()));
|
||||
}
|
||||
|
||||
void TextMarkRegistry::add(TextMark *mark, TextDocument *document)
|
||||
{
|
||||
instance()->m_marks[mark->filePath()].insert(mark);
|
||||
if (TextDocument *document = TextDocument::textDocumentForFilePath(mark->filePath()))
|
||||
if (document)
|
||||
document->addMark(mark);
|
||||
}
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@ class TEXTEDITOR_EXPORT TextMark
|
||||
public:
|
||||
TextMark() = delete;
|
||||
TextMark(const Utils::FilePath &filePath, int lineNumber, TextMarkCategory category);
|
||||
TextMark(TextDocument *document, int lineNumber, TextMarkCategory category);
|
||||
virtual ~TextMark();
|
||||
|
||||
// determine order on markers on the same line.
|
||||
|
||||
@@ -67,7 +67,7 @@ void SubmitEditorFile::setModified(bool modified)
|
||||
emit changed();
|
||||
}
|
||||
|
||||
bool SubmitEditorFile::save(QString *errorString, const FilePath &filePath, bool autoSave)
|
||||
bool SubmitEditorFile::saveImpl(QString *errorString, const FilePath &filePath, bool autoSave)
|
||||
{
|
||||
const FilePath &fName = filePath.isEmpty() ? this->filePath() : filePath;
|
||||
FileSaver saver(fName, QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text);
|
||||
|
||||
@@ -23,11 +23,13 @@ public:
|
||||
bool setContents(const QByteArray &contents) override;
|
||||
|
||||
bool isModified() const override { return m_modified; }
|
||||
bool save(QString *errorString, const Utils::FilePath &filePath, bool autoSave) override;
|
||||
ReloadBehavior reloadBehavior(ChangeTrigger state, ChangeType type) const override;
|
||||
|
||||
void setModified(bool modified = true);
|
||||
|
||||
protected:
|
||||
bool saveImpl(QString *errorString, const Utils::FilePath &filePath, bool autoSave) override;
|
||||
|
||||
private:
|
||||
bool m_modified;
|
||||
VcsBaseSubmitEditor *m_editor;
|
||||
|
||||
@@ -26,6 +26,7 @@ private slots:
|
||||
void test_setExtraAdditionalFormats();
|
||||
void test_clearExtraFormats();
|
||||
void test_incrementalApplyAdditionalFormats();
|
||||
void test_preeditText();
|
||||
void cleanup();
|
||||
|
||||
private:
|
||||
@@ -235,6 +236,7 @@ void tst_highlighter::test_incrementalApplyAdditionalFormats()
|
||||
formatHash);
|
||||
|
||||
formats = firstBlock.layout()->formats();
|
||||
QCOMPARE(formats.size(), 1);
|
||||
QCOMPARE(formats.at(0).format.fontItalic(), true);
|
||||
QCOMPARE(formats.at(0).format.fontOverline(), false);
|
||||
QCOMPARE(formats.at(0).start, 0);
|
||||
@@ -338,6 +340,28 @@ void tst_highlighter::test_incrementalApplyAdditionalFormats()
|
||||
QCOMPARE(formats.at(1).length, 2);
|
||||
}
|
||||
|
||||
void tst_highlighter::test_preeditText()
|
||||
{
|
||||
QCOMPARE(doc->blockCount(), 4);
|
||||
|
||||
QTextBlock firstBlock = doc->findBlockByNumber(0);
|
||||
firstBlock.layout()->setPreeditArea(2, "uaf");
|
||||
|
||||
SemanticHighlighter::setExtraAdditionalFormats(highlighter, highlightingResults(), formatHash);
|
||||
|
||||
auto formats = firstBlock.layout()->formats();
|
||||
QCOMPARE(formats.size(), 2);
|
||||
QCOMPARE(formats.at(0).format.fontItalic(), true);
|
||||
QCOMPARE(formats.at(0).format.fontOverline(), false);
|
||||
QCOMPARE(formats.at(0).start, 0);
|
||||
QCOMPARE(formats.at(0).length, 2);
|
||||
|
||||
QCOMPARE(formats.at(1).format.fontItalic(), true);
|
||||
QCOMPARE(formats.at(1).format.fontOverline(), false);
|
||||
QCOMPARE(formats.at(1).start, 5);
|
||||
QCOMPARE(formats.at(1).length, 3);
|
||||
}
|
||||
|
||||
void tst_highlighter::cleanup()
|
||||
{
|
||||
delete doc;
|
||||
|
||||
@@ -25,8 +25,8 @@ def main():
|
||||
test.warning("Parsing project timed out")
|
||||
compareProjectTree(rootNodeTemplate % "Qt Creator", "projecttree_creator.tsv")
|
||||
buildIssuesTexts = map(lambda i: str(i[0]), getBuildIssues())
|
||||
deprecationWarnings = filter(lambda s: "deprecated" in s, buildIssuesTexts)
|
||||
deprecationWarnings = "\n".join(set(filter(lambda s: "deprecated" in s, buildIssuesTexts)))
|
||||
if deprecationWarnings:
|
||||
test.warning("Creator claims that the .qbs file uses deprecated features.",
|
||||
"\n".join(set(deprecationWarnings)))
|
||||
deprecationWarnings)
|
||||
invokeMenuItem("File", "Exit")
|
||||
|
||||
Reference in New Issue
Block a user