diff --git a/dist/changes-8.0.0.md b/dist/changes-8.0.0.md new file mode 100644 index 00000000000..339a4af41c8 --- /dev/null +++ b/dist/changes-8.0.0.md @@ -0,0 +1,197 @@ +Qt Creator 8 +============ + +Qt Creator version 8 contains bug fixes and new features. + +The most important changes are listed in this document. For a complete list of +changes, see the Git log for the Qt Creator sources that you can check out from +the public Git repository. For example: + + git clone git://code.qt.io/qt-creator/qt-creator.git + git log --cherry-pick --pretty=oneline origin/7.0..v8.0.0 + +Editing +------- + +* Added shortcut for adding next search match to multi-selection +* Added warning when editing generated file (QTCREATORBUG-27173) +* Fixed updating of annotations (QTCREATORBUG-26812) +* Fixed that whitespace was not selected on double-click (QTCREATORBUG-24607) + +### C++ + +* Removed `libclang` based code model +* Clangd + * Increased minimum `Clangd` version to 14 + * Improved performance of `compile_commands.json` creation + * Replaced some refactoring actions by the ones from `Clangd` + * Added desugaring of types and warning about unused includes + * Added option for ignoring big files + * Added information on parent function to `Find References With Access Type` + (QTCREATORBUG-27550) + * Worked around `Clangd` highlighting issue (QTCREATORBUG-27601) + * Fixed `Follow Symbol Under Cursor` for template types (QTCREATORBUG-27524) + * Fixed that issues from other files could be shown in `Issues` + (QTCREATORBUG-27260) + * Fixed position of diagnostics lightbulb + * Fixed function return types in `Outline` (QTCREATORBUG-27587) + * Fixed that UI files with same name could confuse code model + (QTCREATORBUG-27584) +* clang-format + * Simplified options dialog + +### QML + +* Added option for maximum line length to code style options + (QTCREATORBUG-23411) +* Fixed handling of JavaScript string templates (QTCREATORBUG-21869) +* Fixed formatting issue with nullish coalescing operator (QTCREATORBUG-27344) + +### Python + +* Switched to `python-lsp-server` by default (QTCREATORBUG-26230) +* Added configuration options for `python-lsp-server` +* Added check and installation help for `PySide` (PYSIDE-1742) +* Added clean up of outdated interpreters +* Added UIC based project wizard +* Improved code style in wizard generated files +* Fixed that unsaved and uncompiled UI files where outdated in code model +* Fixed performance issues (QTCREATORBUG-24140, QTCREATORBUG-24704) + +### Language Server Protocol + +* Improved performance for large server responses +* Fixed semantic highlighting after server reset +* Fixed that semantic update was delayed by `Document update threshold` even + after saving + +### Image Viewer + +* Added button for copying image as data URL + +Projects +-------- + +* Added locator filter for starting run configurations + +### CMake + +* Added `Profile` build configuration type that is `RelWithDebInfo` with `QML + debugging and profiling` +* Turned `QML debugging and profiling` option on by default for `Debug` + configurations +* Removed hardcoded `QT_QML_DEBUG` from wizard created project files +* Fixed issue when reconfiguring with `QML debugging and profiling` option + enabled + +Debugging +--------- + +* Switched fallback Qt version for debug information to Qt 6.2 +* Added pretty printer for `QAnyStringView` + +Analyzer +-------- + +### Coco + +* Added experimental `Coco` integration + +### CppCheck + +* Added `Copy to Clipboard` to text marks (QTCREATORBUG-27092) +* Fixed quoting of command line arguments (QTCREATORBUG-27284) + +Version Control Systems +----------------------- + +* Changed output pane to use text editor font (QTCREATORBUG-27164) + +### Git + +* Fixed that fetching tags when showing changes blocked UI + +### Gerrit + +* Fixed that non-Gerrit remote could be selected in `Push to Gerrit` + +### GitLab + +* Added experimental `GitLab` integration +* Added support for browsing and cloning projects + +Platforms +--------- + +### Windows + +* Removed support for Universal Windows Platform (UWP) +* Added auto-detection for MSVC ARM toolchain and debugger +* Fixed ABI detection on ARM Windows + +### Android + +* Added option to connect physical device over WiFi +* Moved SDK manager to separate dialog +* Aligned platform names with Android Studio (QTCREATORBUG-27161) +* Fixed issues with newer SDK tools (QTCREATORBUG-27174) + +### Remote Linux + +* Switched to `echo` for testing connection + +### Docker + +* Added default mount point set to projects directory +* Fixed state detection of docker daemon +* Fixed issues with kit initialization + +### MCU + +* Added support for QUL 2.0 and removed support for earlier versions + +### QNX + +* Fixed debugger detection + +Credits for these changes go to: +-------------------------------- +Aaron Barany +Adam Treat +Alesandro Portale +Alessandro Portale +Alexander Drozdov +Alexandru Croitor +Andre Hartmann +André Pönitz +Artem Sokolovskii +Assam Boudjelthia +Christiaan Janssen +Christian Kandeler +Christian Stenger +Christian Strømme +Cristian Adam +Cristián Maureira-Fredes +David Schulz +Dmitry Shachnev +Eike Ziller +Erik Verbruggen +Fawzi Mohamed +Henning Gruendl +Ihor Ivlev +Jaroslaw Kobus +Knud Dollereder +Leena Miettinen +Marcus Tillmanns +Maximilian Goldstein +Miikka Heikkinen +Orgad Shaneh +Piotr Mućko +Rafael Roquetto +Robert Löhning +Sergey Morozov +Tapani Mattila +Tasuku Suzuki +Thiago Macieira +Thomas Hartmann +Xavier Besson diff --git a/doc/qtcreator/src/linux-mobile/creator-deployment-embedded-linux.qdoc b/doc/qtcreator/src/linux-mobile/creator-deployment-embedded-linux.qdoc index d1880588a00..c0b67b7f28e 100644 --- a/doc/qtcreator/src/linux-mobile/creator-deployment-embedded-linux.qdoc +++ b/doc/qtcreator/src/linux-mobile/creator-deployment-embedded-linux.qdoc @@ -47,9 +47,9 @@ \image qtcreator-embedded-linux-deployment-details.png "Deploy to embedded Linux" The files to be installed are listed in the \uicontrol {Deployment} step, - the \uicontrol {Files to deploy} field. The \uicontrol {Local File Path} + the \uicontrol {Files to deploy} field. The \uicontrol {Source File Path} field displays the location of the file on the development PC. The - \uicontrol {Remote Directory} field displays the directory where the file is + \uicontrol {Target Directory} field displays the directory where the file is installed on the device. Text in red color indicates that the information is missing. diff --git a/doc/qtcreator/src/python/creator-python-project.qdocinc b/doc/qtcreator/src/python/creator-python-project.qdocinc index 075d6b80962..1409b7d840b 100644 --- a/doc/qtcreator/src/python/creator-python-project.qdocinc +++ b/doc/qtcreator/src/python/creator-python-project.qdocinc @@ -44,7 +44,7 @@ use \c {.pyqtc} files, but we recommend that you choose \c{.pyproject} files for new projects. - The \uicontrol {Qt for Python - Window (UI file)} wizard enables you to + The \uicontrol {Window UI} wizard enables you to create a Python project that contains the source file for a class. Specify the PySide version, class name, base class, and and source file for the class. @@ -56,13 +56,23 @@ Widgets module, and Qt UI tools: \badcode - import os - from pathlib import Path import sys + from pathlib import Path from PySide6.QtWidgets import QApplication, QWidget - from PySide6.QtCore import QFile - from PySide6.QtUiTools import QUiLoader + \endcode + + \note It is important that you first create the Python code + from your UI form. In PySide6, you can do this by executing + \c{pyside6-uic form.ui -o ui_form.py} on a terminal. This + enables you to import the class that represents your UI + from that Python file. + + Once you generate the Python code from the UI file, + you can import the class: + + \badcode + from ui_form import Ui_Widget \endcode The wizard also adds a main class with the specified name that @@ -70,25 +80,22 @@ \badcode class Widget(QWidget): - def __init__(self): - super(Widget, self).__init__() - self.load_ui() - ... + def __init__(self, parent=None): + super().__init__(parent) \endcode - The following lines in the main class load the generated Python class from - the UI file: + The following lines in the main class instantiate the generated Python class from + your UI file, and set up the interface for the current class. \badcode - def load_ui(self): - loader = QUiLoader() - path = os.fspath(Path(__file__).resolve().parent / "form.ui") - ui_file = QFile(path) - ui_file.open(QFile.ReadOnly) - loader.load(ui_file, self) - ui_file.close() + self.ui = Ui_Widget() + self.ui.setupUi(self) \endcode + \note UI elements of the new class can be accessed as member variables. + For example, if you have a button called \e{button1}, you + can interact with it using \c{self.ui.button1}. + Next, the wizard adds a main function, where it creates a QApplication instance. As Qt can receive arguments from the command line, you can pass any arguments to the QApplication object. Usually, you do not @@ -96,7 +103,7 @@ \badcode if __name__ == "__main__": - app = QApplication([]) + app = QApplication(sys.argv) \endcode Next, the wizard instantiates the \c MainWindow class and shows it: @@ -107,11 +114,11 @@ ... \endcode - Finally, the wizard calls the \c app.exec_() method to enter the Qt + Finally, the wizard calls the \c app.exec() method to enter the Qt main loop and start executing the Qt code: \badcode - sys.exit(app.exec_()) + sys.exit(app.exec()) \endcode You can now modify the boilerplate code in the Edit mode to develop your @@ -121,6 +128,8 @@ select \uicontrol {REPL Import File}. To also import all functions from the file, select \uicontrol {REPL Import *}. + Always regenerate the Python code after modifying a UI file. + Open the .ui file in the \uicontrol Design mode to create a widget-based UI in \QD. @@ -182,13 +191,13 @@ Finally, the wizard adds code that checks whether the file was successfully loaded. If loading the file fails, the application exits with an error code. - If loading succeeds, the wizard calls the \c app.exec_() method to enter the + If loading succeeds, the wizard calls the \c app.exec() method to enter the Qt main loop and start executing the Qt code: \badcode if not engine.rootObjects(): sys.exit(-1) - sys.exit(app.exec_()) + sys.exit(app.exec()) \endcode Open the .qml file in the \uicontrol Edit mode to design a Qt Quick UI, or diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.cpp index dd21622b5dc..74c2eb270ef 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.cpp @@ -826,8 +826,15 @@ bool GeneralHelper::getBounds(QQuick3DViewport *view3D, QQuick3DNode *node, QVec auto renderNode = static_cast(nodePriv->spatialNode); if (recursive && renderNode) { +#if QT_VERSION < QT_VERSION_CHECK(6, 4, 0) if (renderNode->flags.testFlag(QSSGRenderNode::Flag::TransformDirty)) renderNode->calculateLocalTransform(); +#else + if (renderNode->isDirty(QSSGRenderNode::DirtyFlag::TransformDirty)) { + renderNode->localTransform = QSSGRenderNode::calculateTransformMatrix( + node->position(), node->scale(), node->pivot(), node->rotation()); + } +#endif localTransform = renderNode->localTransform; } diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/selectionboxgeometry.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/selectionboxgeometry.cpp index 8296c9155d7..dc4484de9d9 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/selectionboxgeometry.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/selectionboxgeometry.cpp @@ -192,7 +192,11 @@ void SelectionBoxGeometry::doUpdateGeometry() m = targetRN->parent->globalTransform; } rootRN->localTransform = m; +#if QT_VERSION < QT_VERSION_CHECK(6, 4, 0) rootRN->markDirty(QSSGRenderNode::TransformDirtyFlag::TransformNotDirty); +#else + rootRN->markDirty(QSSGRenderNode::DirtyFlag::TransformDirty); +#endif rootRN->calculateGlobalVariables(); } else if (!m_spatialNodeUpdatePending) { // Necessary spatial nodes do not yet exist. Defer selection box creation one frame. @@ -236,8 +240,15 @@ void SelectionBoxGeometry::getBounds( if (node != m_targetNode) { if (renderNode) { +#if QT_VERSION < QT_VERSION_CHECK(6, 4, 0) if (renderNode->flags.testFlag(QSSGRenderNode::Flag::TransformDirty)) renderNode->calculateLocalTransform(); +#else + if (renderNode->isDirty(QSSGRenderNode::DirtyFlag::TransformDirty)) { + renderNode->localTransform = QSSGRenderNode::calculateTransformMatrix( + node->position(), node->scale(), node->pivot(), node->rotation()); + } +#endif localTransform = renderNode->localTransform; } trackNodeChanges(node); diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/import3d/import3d.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/import3d/import3d.cpp index 1f24aaca8f8..2bc6135ce9b 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/import3d/import3d.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/import3d/import3d.cpp @@ -52,7 +52,12 @@ void import3D(const QString &sourceAsset, const QString &outDir, const QString & if (!optDoc.isNull() && optDoc.isObject()) { QJsonObject optObj = optDoc.object(); - if (importer->importFile(sourceAsset, outDir, optObj.toVariantMap(), &errorStr) +#if (QT_VERSION >= QT_VERSION_CHECK(6, 4, 0)) + const auto &optionsMap = optObj; +#else + const auto optionsMap = optObj.toVariantMap(); +#endif // QT_VERSION >= 6.4.0 + if (importer->importFile(sourceAsset, outDir, optionsMap, &errorStr) != QSSGAssetImportManager::ImportState::Success) { } } else { diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp index c6acc5e5f9d..5183b5cd8fc 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp @@ -118,6 +118,7 @@ #endif #ifdef IMPORT_QUICK3D_ASSETS +#include #include #endif @@ -303,7 +304,15 @@ void Qt5InformationNodeInstanceServer::resolveImportSupport() #ifdef IMPORT_QUICK3D_ASSETS QSSGAssetImportManager importManager; const QHash supportedExtensions = importManager.getSupportedExtensions(); - const QHash supportedOptions = importManager.getAllOptions(); +#if (QT_VERSION >= QT_VERSION_CHECK(6, 4, 0)) +#define AS_VARIANT_MAP(IT) IT.value().toVariantMap() + using PluginOptionMaps = QSSGAssetImportManager::PluginOptionMaps; +#else +#define AS_VARIANT_MAP(IT) IT.value() + using PluginOptionMaps = QHash; +#endif // QT_VERSION >= 6.4.0 + + const PluginOptionMaps supportedOptions = importManager.getAllOptions(); QVariantMap supportMap; @@ -317,7 +326,7 @@ void Qt5InformationNodeInstanceServer::resolveImportSupport() QVariantMap optMap; auto itOpt = supportedOptions.constBegin(); while (itOpt != supportedOptions.constEnd()) { - optMap.insert(itOpt.key(), itOpt.value()); + optMap.insert(itOpt.key(), AS_VARIANT_MAP(itOpt)); ++itOpt; } diff --git a/share/qtcreator/qmldesigner/qt4mcu/metadata.qml b/share/qtcreator/qmldesigner/qt4mcu/metadata.qml index 5e5c515199d..97f058f2c70 100644 --- a/share/qtcreator/qmldesigner/qt4mcu/metadata.qml +++ b/share/qtcreator/qmldesigner/qt4mcu/metadata.qml @@ -27,7 +27,7 @@ Metadata { id: metadataFile - defaultVersion: v21 + defaultVersion: v22 VersionData { id: v14 @@ -64,4 +64,10 @@ Metadata { name: "Qt for MCUs 2.1" path: "qul-21.qml" } + + VersionData { + id: v22 + name: "Qt for MCUs 2.2" + path: "qul-22.qml" + } } diff --git a/share/qtcreator/qmldesigner/qt4mcu/qul-22.qml b/share/qtcreator/qmldesigner/qt4mcu/qul-22.qml new file mode 100644 index 00000000000..a539a0aeb25 --- /dev/null +++ b/share/qtcreator/qmldesigner/qt4mcu/qul-22.qml @@ -0,0 +1,225 @@ +/**************************************************************************** +** +** Copyright (C) 2022 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +//differences from 2.0: +//2.1: + text.elide +//2.2: + text.wrapMode + +VersionData { + name: "Qt for MCUs 2.2" + + bannedItems: ["QtQuick.AnimatedImage", + "QtQuick.FocusScope", + "QtQuick.TextInput", + "QtQuick.TextEdit", + "QtQuick.Flow", + "QtQuick.Grid", + "QtQuick.GridView", + "QtQuick.PathView", + "QtQuick.Loader", + "QtQuick.Controls", + "QtQuick.Controls.BusyIndicator", + "QtQuick.Controls.ButtonGroup", + "QtQuick.Controls.CheckDelegate", + "QtQuick.Controls.Container", + "QtQuick.Controls.ComboBox", + "QtQuick.Controls.DelayButton", + "QtQuick.Controls.Frame", + "QtQuick.Controls.GroupBox", + "QtQuick.Controls.ItemDelegate", + "QtQuick.Controls.Label", + "QtQuick.Controls.Page", + "QtQuick.Controls.PageIndicator", + "QtQuick.Controls.Pane", + "QtQuick.Controls.RadioDelegate", + "QtQuick.Controls.RangeSlider", + "QtQuick.Controls.RoundButton", + "QtQuick.Controls.ScrollView", + "QtQuick.Controls.SpinBox", + "QtQuick.Controls.StackView", + "QtQuick.Controls.SwipeDelegate", + "QtQuick.Controls.SwitchDelegate", + "QtQuick.Controls.ToolBar", + "QtQuick.Controls.ToolButton", + "QtQuick.Controls.TabBar", + "QtQuick.Controls.TabButton", + "QtQuick.Controls.TextArea", + "QtQuick.Controls.TextField", + "QtQuick.Controls.ToolSeparator", + "QtQuick.Controls.Tumbler", + "QtQuick.Shapes.ConicalGradient", + "QtQuick.Shapes.LinearGradient", + "QtQuick.Shapes.RadialGradient", + "QtQuick.Shapes.ShapeGradient"] + + allowedImports: ["QtQuick", + "QtQuick.Shapes", + "QtQuick.Controls", + "QtQuick.Timeline", + "QtQuickUltralite.Extras", + "QtQuickUltralite.Layers"] + + bannedImports: ["FlowView"] + + //ComplexProperty is not a type, it's just a way to handle bigger props + ComplexProperty { + prefix: "font" + bannedProperties: ["wordSpacing", "letterSpacing", "hintingPreference", + "kerning", "preferShaping", "capitalization", + "strikeout", "underline", "styleName"] + } + + QtQuick.Item { + bannedProperties: ["layer", "opacity", "smooth", "antialiasing", + "baselineOffset", "focus", "activeFocusOnTab", + "rotation", "scale", "transformOrigin"] + } + + QtQuick.Rectangle { + bannedProperties: ["gradient", "border"] + } + + QtQuick.Flickable { + bannedProperties: ["boundsBehavior", "boundsMovement", "flickDeceleration", + "flickableDirection", "leftMargin", "rightMargin", "bottomMargin", "topMargin", + "originX", "originY", "pixelAligned", "pressDelay", "synchronousDrag"] + } + + QtQuick.MouseArea { + bannedProperties: ["propagateComposedEvents", "preventStealing", "cursorShape", + "scrollGestureEnabled", "drag", "acceptedButtons", "hoverEnabled"] + } + + QtQuick.Image { + allowChildren: false + allowedProperties: ["rotation", "scale", "transformOrigin"] + bannedProperties: ["mirror", "mipmap", "cache", "autoTransform", "asynchronous", + "sourceSize", "smooth"] + } + + QtQuick.BorderImage { + bannedProperties: ["asynchronous", "cache", "currentFrame", "frameCount", + "horizontalTileMode", "mirror", "progress", "smooth", "sourceSize", + "status", "verticalTileMode"] + } + + QtQuick.Text { + allowChildren: false + allowedProperties: ["rotation", "scale", "transformOrigin"] + bannedProperties: ["lineHeight", "lineHeightMode", "style", + "styleColor", "minimumPointSize", "minimumPixelSize", + "fontSizeMode", "renderType", "renderTypeQuality", "textFormat", "maximumLineCount"] + } + + //Padding is not an actual item, but rather set of properties in Text + Padding { + bannedProperties: ["bottomPadding", "topPadding", "leftPadding", "rightPadding"] + } + + QtQuick.Column { + bannedProperties: ["bottomPadding", "leftPadding", "rightPadding", "topPadding"] + } + + QtQuick.Row { + bannedProperties: ["bottomPadding", "leftPadding", "rightPadding", "topPadding", + "effectiveLayoutDirection", "layoutDirection"] + } + + QtQuick.ListView { + bannedProperties: ["cacheBuffer", "highlightRangeMode", "highlightMoveDuration", + "highlightResizeDuration", "preferredHighlightBegin", "layoutDirection", + "preferredHighlightEnd", "highlightFollowsCurrentItem", "keyNavigationWraps", + "snapMode", "highlightMoveVelocity", "highlightResizeVelocity"] + } + + QtQuick.Animation { + bannedProperties: ["paused"] + } + + //Quick Controls2 Items and properties: + + QtQuick.Controls.Control { + bannedProperties: ["focusPolicy", "hoverEnabled", "wheelEnabled"] + } + + QtQuick.Controls.AbstractButton { + bannedProperties: ["display", "autoExclusive"] + } + + QtQuick.Controls.ProgressBar { + bannedProperties: ["indeterminate"] + } + + QtQuick.Controls.Slider { + bannedProperties: ["live", "snapMode", "touchDragThreshold"] + } + + //Path and Shapes related: + + QtQuick.Path { + bannedProperties: ["scale", "pathElements"] + } + + QtQuick.PathArc { + bannedProperties: ["relativeX", "relativeY"] + } + + QtQuick.PathLine { + bannedProperties: ["relativeX", "relativeY"] + } + + QtQuick.PathMove { + bannedProperties: ["relativeX", "relativeY"] + } + + QtQuick.PathQuad { + bannedProperties: ["relativeX", "relativeY", + "relativeControlX", "relativeControlY"] + } + + QtQuick.PathCubic { + bannedProperties: ["relativeX", "relativeY", + "relativeControl1X", "relativeControl1Y", + "relativeControl2X", "relativeControl2Y"] + } + + QtQuick.PathElement { + //nothing + } + + QtQuick.PathSvg { + //nothing + } + + QtQuick.Shapes.Shape { + bannedProperties: ["asynchronous", "containsMode", "data", + "renderType", "status", "vendorExtensionsEnabled"] + } + + QtQuick.Shapes.ShapePath { + bannedProperties: ["dashOffset", "dashPattern", + "fillGradient", "strokeStyle"] + } +} diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/application-3d/Screen01.ui.qml.tpl b/share/qtcreator/qmldesigner/studio_templates/projects/application-3d/Screen01.ui.qml.tpl index 1da5c8844ba..338621b72e0 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/application-3d/Screen01.ui.qml.tpl +++ b/share/qtcreator/qmldesigner/studio_templates/projects/application-3d/Screen01.ui.qml.tpl @@ -49,13 +49,18 @@ Rectangle { id: cubeModel eulerRotation.y: 45 eulerRotation.x: 30 - materials: cubeMaterial + materials: defaultMaterial source: "#Cube" - DefaultMaterial { - id: cubeMaterial - diffuseColor: "#4aee45" - } } } } + + Item { + id: __materialLibrary__ + DefaultMaterial { + id: defaultMaterial + objectName: "Default Material" + diffuseColor: "#4aee45" + } + } } diff --git a/src/libs/qmljs/qmljsscanner.cpp b/src/libs/qmljs/qmljsscanner.cpp index aff9c7c70dc..771fb4042fd 100644 --- a/src/libs/qmljs/qmljsscanner.cpp +++ b/src/libs/qmljs/qmljsscanner.cpp @@ -510,8 +510,10 @@ QList Scanner::operator()(const QString &text, int startState) case '.': tokens.append(Token(index, 2, Token::Delimiter)); index += 2; + break; default: tokens.append(Token(index++, 1, Token::Delimiter)); + break; } setRegexpMayFollow(&_state, true); break; diff --git a/src/libs/utils/commandline.cpp b/src/libs/utils/commandline.cpp index 503f0c90776..78f003f1979 100644 --- a/src/libs/utils/commandline.cpp +++ b/src/libs/utils/commandline.cpp @@ -1508,6 +1508,11 @@ QString CommandLine::toUserOutput() const return res; } +QString CommandLine::displayName() const +{ + return m_executable.displayName(m_arguments); +} + QStringList CommandLine::splitArguments() const { return ProcessArgs::splitArgs(m_arguments, m_executable.osType()); diff --git a/src/libs/utils/commandline.h b/src/libs/utils/commandline.h index 3bffd8d7865..c8476ab95ed 100644 --- a/src/libs/utils/commandline.h +++ b/src/libs/utils/commandline.h @@ -152,6 +152,7 @@ public: void addCommandLineAsArgs(const CommandLine &cmd, RawType); QString toUserOutput() const; + QString displayName() const; FilePath executable() const { return m_executable; } void setExecutable(const FilePath &executable) { m_executable = executable; } diff --git a/src/libs/utils/deviceshell.cpp b/src/libs/utils/deviceshell.cpp index 91ada89a6bd..d46be147096 100644 --- a/src/libs/utils/deviceshell.cpp +++ b/src/libs/utils/deviceshell.cpp @@ -25,8 +25,9 @@ #include "deviceshell.h" -#include -#include +#include "processinterface.h" +#include "qtcassert.h" +#include "qtcprocess.h" #include #include @@ -35,6 +36,123 @@ Q_LOGGING_CATEGORY(deviceShellLog, "qtc.utils.deviceshell", QtWarningMsg) namespace Utils { +namespace { + +/*! + * The multiplex script waits for input via stdin. + * + * To start a command, a message is send with the format " "" \n" + * To stop the script, simply send "exit\n" via stdin + * + * Once a message is received, two new streams are created that the new process redirects its output to ( $stdoutraw and $stderrraw ). + * + * These streams are piped through base64 into the two streams stdoutenc and stderrenc. + * + * Two subshells read from these base64 encoded streams, and prepend the command-id, as well as either "O:" or "E:" depending on whether its the stdout or stderr stream. + * + * Once the process exits its exit code is send to stdout with the command-id and the type "R". + * + */ +const QLatin1String r_execScript = QLatin1String(R"( +#!/bin/sh + +readAndMark() { + local buffer + while read buffer + do + printf '%s:%s:%s\n' "$1" "$2" "$buffer" + done +} + +base64decode() +{ + base64 -d 2>/dev/null +} + +base64encode() +{ + base64 2>/dev/null +} + +executeAndMark() +{ + PID="$1" + INDATA="$2" + shift + shift + CMD="$@" + + # Output Streams + stdoutenc=$(mktemp -u) + stderrenc=$(mktemp -u) + mkfifo "$stdoutenc" "$stderrenc" + + # app output streams + stdoutraw=$(mktemp -u) + stderrraw=$(mktemp -u) + mkfifo "$stdoutraw" "$stderrraw" + + # Cleanup + trap 'rm -f "$stdoutenc" "$stderrenc" "$stdoutraw" "$stderrraw" ' EXIT + + # Pipe all app output through base64, and then into the output streams + cat $stdoutraw | base64encode > "$stdoutenc" & + cat $stderrraw | base64encode > "$stderrenc" & + + # Mark the app's output streams + readAndMark $PID 'O' < "$stdoutenc" >&1 & + readAndMark $PID 'E' < "$stderrenc" >&1 & + + # Start the app ... + if [ -z "$INDATA" ] + then + eval $CMD 1> "$stdoutraw" 2> "$stderrraw" + else + echo $INDATA | base64decode | eval "$CMD" 1> "$stdoutraw" 2> "$stderrraw" + fi + + exitcode=$(echo $? | base64encode) + + wait + echo "$PID:R:$exitcode" +} + +execute() +{ + PID="$1" + INDATA=$(eval echo "$2") + shift + shift + CMD=$@ + executeAndMark $PID "$INDATA" "$CMD" +} + +cleanup() +{ + kill -- -$$ + exit 1 +} + +if ! command -v base64 &> /dev/null +then + echo "base64 command could not be found" >&2 + exit 1 +fi + +trap cleanup 1 2 3 6 + +echo SCRIPT_INSTALLED >&2 + +while read -r id inData cmd; do + if [ "$id" = "exit" ]; then + exit + fi + execute $id $inData $cmd & +done +)"); + +} // namespace + DeviceShell::DeviceShell() { m_thread.setObjectName("Shell Thread"); @@ -43,26 +161,14 @@ DeviceShell::DeviceShell() DeviceShell::~DeviceShell() { + m_shellProcess->deleteLater(); + if (m_thread.isRunning()) { m_thread.quit(); m_thread.wait(); } } -bool DeviceShell::waitForStarted() -{ - QTC_ASSERT(m_shellProcess, return false); - Q_ASSERT(QThread::currentThread() != &m_thread); - - bool result; - QMetaObject::invokeMethod( - m_shellProcess, - [this] { return m_shellProcess->waitForStarted(); }, - Qt::BlockingQueuedConnection, - &result); - return result; -} - /*! * \brief DeviceShell::runInShell * \param cmd The command to run @@ -78,48 +184,8 @@ bool DeviceShell::runInShell(const CommandLine &cmd, const QByteArray &stdInData QTC_ASSERT(m_shellProcess, return false); Q_ASSERT(QThread::currentThread() != &m_thread); - bool result = false; - QMetaObject::invokeMethod( - m_shellProcess, - [this, &cmd, &stdInData] { return runInShellImpl(cmd, stdInData); }, - Qt::BlockingQueuedConnection, - &result); - return result; -} - -bool DeviceShell::runInShellImpl(const CommandLine &cmd, const QByteArray &stdInData) -{ - QTC_ASSERT(QThread::currentThread() == &m_thread, return false); - - QTC_ASSERT(m_shellProcess->isRunning(), return false); - QTC_ASSERT(m_shellProcess, return false); - QTC_CHECK(m_shellProcess->readAllStandardOutput().isNull()); // clean possible left-overs - QTC_CHECK(m_shellProcess->readAllStandardError().isNull()); // clean possible left-overs - auto cleanup = qScopeGuard( - [this] { m_shellProcess->readAllStandardOutput(); }); // clean on assert - - QString prefix; - if (!stdInData.isEmpty()) - prefix = "echo '" + QString::fromUtf8(stdInData.toBase64()) + "' | base64 -d | "; - - const QString suffix = " > /dev/null 2>&1\necho $?\n"; - const QString command = prefix + cmd.toUserOutput() + suffix; - - qCDebug(deviceShellLog) << "Running:" << command; - - m_shellProcess->write(command); - m_shellProcess->waitForReadyRead(); - - const QByteArray output = m_shellProcess->readAllStandardOutput(); - - bool ok = false; - const int result = output.toInt(&ok); - - qCInfo(deviceShellLog) << "Run command in shell:" << cmd.toUserOutput() << "result: " << output - << " ==>" << result; - QTC_ASSERT(ok, return false); - - return result == EXIT_SUCCESS; + const RunResult result = run(cmd, stdInData); + return result.exitCode == 0; } /*! @@ -138,59 +204,37 @@ DeviceShell::RunResult DeviceShell::outputForRunInShell(const CommandLine &cmd, QTC_ASSERT(m_shellProcess, return {}); Q_ASSERT(QThread::currentThread() != &m_thread); - RunResult result; - QMetaObject::invokeMethod( - m_shellProcess, - [this, &cmd, &stdInData] { return outputForRunInShellImpl(cmd, stdInData); }, - Qt::BlockingQueuedConnection, - &result); - return result; + return run(cmd, stdInData); } -DeviceShell::RunResult DeviceShell::outputForRunInShellImpl(const CommandLine &cmd, - const QByteArray &stdInData) +DeviceShell::State DeviceShell::state() const { return m_shellScriptState; } + +DeviceShell::RunResult DeviceShell::run(const CommandLine &cmd, const QByteArray &stdInData) { - QTC_ASSERT(QThread::currentThread() == &m_thread, return {}); + const RunResult errorResult{-1, {}, {}}; + QTC_ASSERT(m_shellProcess, return errorResult); + QTC_ASSERT(m_shellScriptState == State::Succeeded, return errorResult); - QTC_ASSERT(m_shellProcess, return {}); - QTC_CHECK(m_shellProcess->readAllStandardOutput().isNull()); // clean possible left-overs - QTC_CHECK(m_shellProcess->readAllStandardError().isNull()); // clean possible left-overs - auto cleanup = qScopeGuard( - [this] { m_shellProcess->readAllStandardOutput(); }); // clean on assert + QMutexLocker lk(&m_commandMutex); - QString prefix; - if (!stdInData.isEmpty()) - prefix = "echo '" + QString::fromUtf8(stdInData.toBase64()) + "' | base64 -d | "; + QWaitCondition waiter; + const int id = ++m_currentId; + const auto it = m_commandOutput.insert(id, CommandRun{-1, {}, {}, &waiter}); - const QString markerCmd = "echo __qtc$?qtc__ 1>&2\n"; - const QString suffix = "\n" + markerCmd; - const QString command = prefix + cmd.toUserOutput() + suffix; + QMetaObject::invokeMethod(m_shellProcess, [this, id, &cmd, &stdInData]() { + const QString command = QString("%1 \"%2\" %3\n") + .arg(id) + .arg(QString::fromLatin1(stdInData.toBase64())) + .arg(cmd.toUserOutput()); + qCDebug(deviceShellLog) << "Running:" << command; + m_shellProcess->writeRaw(command.toUtf8()); + }); - qCDebug(deviceShellLog) << "Running:" << command; - m_shellProcess->write(command); + waiter.wait(&m_commandMutex); - RunResult result; + const RunResult result = *it; + m_commandOutput.erase(it); - while (true) { - m_shellProcess->waitForReadyRead(); - QByteArray stdErr = m_shellProcess->readAllStandardError(); - if (stdErr.endsWith("qtc__\n")) { - QByteArray marker = stdErr.right(stdErr.length() - stdErr.lastIndexOf("__qtc")); - QByteArray exitCodeStr = marker.mid(5, marker.length() - 11); - bool ok = false; - const int exitCode = exitCodeStr.toInt(&ok); - - result.stdOutput = m_shellProcess->readAllStandardOutput(); - result.exitCode = ok ? exitCode : -1; - break; - } - } - - //const QByteArray output = m_shellProcess->readAllStandardOutput(); - qCDebug(deviceShellLog) << "Received output:" << result.stdOutput; - qCInfo(deviceShellLog) << "Run command in shell:" << cmd.toUserOutput() - << "output size:" << result.stdOutput.size() - << "exit code:" << result.exitCode; return result; } @@ -221,7 +265,7 @@ void DeviceShell::setupShellProcess(QtcProcess *shellProcess) */ void DeviceShell::startupFailed(const CommandLine &cmdLine) { - qCDebug(deviceShellLog) << "Failed to start shell via:" << cmdLine.toUserOutput(); + qCWarning(deviceShellLog) << "Failed to start shell via:" << cmdLine.toUserOutput(); } /*! @@ -241,7 +285,6 @@ bool DeviceShell::start() setupShellProcess(m_shellProcess); m_shellProcess->setProcessMode(ProcessMode::Writer); - m_shellProcess->setWriteData("echo\n"); // Moving the process into its own thread ... m_shellProcess->moveToThread(&m_thread); @@ -252,12 +295,46 @@ bool DeviceShell::start() [this] { m_shellProcess->start(); - if (!m_shellProcess->waitForStarted() || !m_shellProcess->waitForReadyRead() - || m_shellProcess->readAllStandardOutput() != "\n") { + if (!m_shellProcess->waitForStarted()) { closeShellProcess(); return false; } - // TODO: Check if necessary tools are available ( e.g. base64, /bin/sh etc. ) + + connect(m_shellProcess, &QtcProcess::readyReadStandardOutput, m_shellProcess, [this] { + onReadyRead(); + }); + connect(m_shellProcess, &QtcProcess::readyReadStandardError, m_shellProcess, [this] { + const QByteArray stdErr = m_shellProcess->readAllStandardError(); + + if (m_shellScriptState == State::Unknown) { + if (stdErr.contains("SCRIPT_INSTALLED")) { + m_shellScriptState = State::Succeeded; + return; + } + if (stdErr.contains("ERROR_INSTALL_SCRIPT")) { + m_shellScriptState = State::FailedToStart; + qCWarning(deviceShellLog) << "Failed installing device shell script"; + return; + } + } + + qCWarning(deviceShellLog) << "Received unexpected output on stderr:" << stdErr; + }); + + connect(m_shellProcess, &QtcProcess::done, m_shellProcess, [this]() { + if (m_shellProcess->resultData().m_exitCode != EXIT_SUCCESS + || m_shellProcess->resultData().m_exitStatus != QProcess::NormalExit) { + qCWarning(deviceShellLog) << "Shell exited with error code:" + << m_shellProcess->resultData().m_exitCode << "(" + << m_shellProcess->exitMessage() << ")"; + } + }); + + if (!installShellScript()) { + closeShellProcess(); + return false; + } + return true; }, Qt::BlockingQueuedConnection, @@ -270,16 +347,158 @@ bool DeviceShell::start() return result; } +bool DeviceShell::installShellScript() +{ + const QString installCmd + = QString("echo %1 | base64 -d > /tmp/shell.sh 2>/dev/null && " + "chmod +x /tmp/shell.sh && " + "/tmp/shell.sh || echo ERROR_INSTALL_SCRIPT >&2\n") + .arg(QString::fromLatin1( + QByteArray(r_execScript.begin(), r_execScript.size()).toBase64())); + + qCDebug(deviceShellLog) << "Install shell script command:" << installCmd; + m_shellProcess->write(installCmd); + + while (m_shellScriptState == State::Unknown) { + if (!m_shellProcess->waitForReadyRead()) { + qCWarning(deviceShellLog) << "Timeout while waiting for device shell script to install"; + m_shellScriptState = State::FailedToStart; + return false; + } + } + return m_shellScriptState == State::Succeeded; +} + void DeviceShell::closeShellProcess() { if (m_shellProcess) { if (m_shellProcess->isRunning()) { - m_shellProcess->write("exit\n"); + m_shellProcess->write("exit\nexit\n"); if (!m_shellProcess->waitForFinished(2000)) m_shellProcess->terminate(); } - m_shellProcess->deleteLater(); } } +QByteArray::const_iterator next(const QByteArray::const_iterator &bufferEnd, + const QByteArray::const_iterator &itCurrent) +{ + for (QByteArray::const_iterator it = itCurrent; it != bufferEnd; ++it) { + if (*it == '\n') + return it; + } + return bufferEnd; +} + +QByteArray byteArrayFromRange(QByteArray::const_iterator itStart, QByteArray::const_iterator itEnd) +{ + return QByteArray(itStart, std::distance(itStart, itEnd)); +} + +QList> parseShellOutput(const QByteArray &data) +{ + auto itStart = data.cbegin(); + const auto itEnd = data.cend(); + + QList> result; + + for (auto it = next(itEnd, itStart); it != itEnd; ++it, itStart = it, it = next(itEnd, it)) { + const QByteArray lineView = byteArrayFromRange(itStart, it); + QTC_ASSERT(lineView.size() > 0, continue); + + const auto pidEnd = lineView.indexOf(':'); + const auto typeEnd = lineView.indexOf(':', pidEnd + 1); + + QTC_ASSERT(pidEnd != -1 && typeEnd != -1, continue); + + bool ok = false; + const QLatin1String sId(lineView.begin(), pidEnd); + const quint64 id = QString(sId).toInt(&ok); + QTC_ASSERT(ok, continue); + + const QByteArray data = byteArrayFromRange(lineView.begin() + typeEnd + 1, lineView.end()); + const QByteArray decoded = QByteArray::fromBase64(data); + + DeviceShell::ParseType t; + char type = lineView.at(typeEnd - 1); + switch (type) { + case 'O': + t = DeviceShell::ParseType::StdOut; + break; + case 'E': + t = DeviceShell::ParseType::StdErr; + break; + case 'R': + t = DeviceShell::ParseType::ExitCode; + break; + default: + QTC_CHECK(false); + continue; + } + + result.append(std::make_tuple(id, t, decoded)); + } + + return result; +} + +/*! + * \brief DeviceShell::onReadyRead + * + * Reads lines coming from the multiplex script. + * + * The format is: "::base64-encoded-text-or-returnvalue" + * The possible 's are: + * O for stdout + * E for stderr + * R for exit code + * + * Multiple O/E messages may be received for a process. Once + * a single "R" is received, the exit code is reported back + * and no further messages from that process are expected. + */ +void DeviceShell::onReadyRead() +{ + m_commandBuffer += m_shellProcess->readAllStandardOutput(); + const qsizetype lastLineEndIndex = m_commandBuffer.lastIndexOf('\n') + 1; + + if (lastLineEndIndex == 0) + return; + + const QByteArray input(m_commandBuffer.cbegin(), lastLineEndIndex); + + const auto result = parseShellOutput(input); + + QMutexLocker lk(&m_commandMutex); + for (const auto &line : result) { + const auto &[cmdId, type, data] = line; + + const auto itCmd = m_commandOutput.find(cmdId); + QTC_ASSERT(itCmd != m_commandOutput.end(), continue); + + switch (type) { + case Utils::DeviceShell::ParseType::StdOut: + itCmd->stdOut.append(data); + break; + case Utils::DeviceShell::ParseType::StdErr: + itCmd->stdErr.append(data); + break; + case Utils::DeviceShell::ParseType::ExitCode: { + bool ok = false; + int exitCode; + exitCode = QString::fromUtf8(data.begin(), data.size()).toInt(&ok); + QTC_ASSERT(ok, exitCode = -1); + itCmd->exitCode = exitCode; + itCmd->waiter->wakeOne(); + break; + } + } + }; + + if (lastLineEndIndex == m_commandBuffer.size()) + m_commandBuffer.clear(); + else + m_commandBuffer = m_commandBuffer.mid(lastLineEndIndex); +} + } // namespace Utils diff --git a/src/libs/utils/deviceshell.h b/src/libs/utils/deviceshell.h index b800712a0b6..8def78f6d3c 100644 --- a/src/libs/utils/deviceshell.h +++ b/src/libs/utils/deviceshell.h @@ -25,8 +25,11 @@ #include "utils_global.h" +#include +#include #include #include +#include #include @@ -42,10 +45,19 @@ class QTCREATOR_UTILS_EXPORT DeviceShell : public QObject Q_OBJECT public: + enum class State { FailedToStart = -1, Unknown = 0, Succeeded = 1 }; + struct RunResult { - int exitCode; - QByteArray stdOutput; + int exitCode = 0; + QByteArray stdOut; + QByteArray stdErr; + }; + + enum class ParseType { + StdOut, + StdErr, + ExitCode, }; DeviceShell(); @@ -54,28 +66,43 @@ public: bool runInShell(const CommandLine &cmd, const QByteArray &stdInData = {}); RunResult outputForRunInShell(const CommandLine &cmd, const QByteArray &stdInData = {}); - bool waitForStarted(); + State state() const; signals: void done(); void errorOccurred(QProcess::ProcessError error); protected: - bool runInShellImpl(const CommandLine &cmd, const QByteArray &stdInData = {}); - RunResult outputForRunInShellImpl(const CommandLine &cmd, const QByteArray &stdInData = {}); + virtual void startupFailed(const CommandLine &cmdLine); + RunResult run(const CommandLine &cmd, const QByteArray &stdInData = {}); bool start(); void close(); private: virtual void setupShellProcess(QtcProcess *shellProcess); - virtual void startupFailed(const CommandLine &cmdLine); + bool installShellScript(); void closeShellProcess(); + void onReadyRead(); + private: + struct CommandRun : public RunResult + { + QWaitCondition *waiter; + }; + QtcProcess *m_shellProcess = nullptr; QThread m_thread; + int m_currentId{0}; + + QMutex m_commandMutex; + // QMap is used here to preserve iterators + QMap m_commandOutput; + QByteArray m_commandBuffer; + + State m_shellScriptState = State::Unknown; }; } // namespace Utils diff --git a/src/libs/utils/environment.cpp b/src/libs/utils/environment.cpp index fa61313144f..3220fe4a83b 100644 --- a/src/libs/utils/environment.cpp +++ b/src/libs/utils/environment.cpp @@ -45,6 +45,11 @@ NameValueItems Environment::diff(const Environment &other, bool checkAppendPrepe return m_dict.diff(other.m_dict, checkAppendPrepend); } +int Environment::isValid() const +{ + return m_dict.size() != 0; +} + QProcessEnvironment Environment::toProcessEnvironment() const { QProcessEnvironment result; @@ -433,44 +438,61 @@ optional EnvironmentProvider::provider(const QByteArray &id void EnvironmentChange::addSetValue(const QString &key, const QString &value) { - m_changeItems.append([key, value](Environment &env) { env.set(key, value); }); + m_changeItems.append({Item::SetValue, QVariant::fromValue(QPair(key, value))}); } void EnvironmentChange::addUnsetValue(const QString &key) { - m_changeItems.append([key](Environment &env) { env.unset(key); }); + m_changeItems.append({Item::UnsetValue, key}); } void EnvironmentChange::addPrependToPath(const FilePaths &values) { for (int i = values.size(); --i >= 0; ) { const FilePath value = values.at(i); - m_changeItems.append([value](Environment &env) { env.prependOrSetPath(value); }); + m_changeItems.append({Item::PrependToPath, value.toVariant()}); } } void EnvironmentChange::addAppendToPath(const FilePaths &values) { for (const FilePath &value : values) - m_changeItems.append([value](Environment &env) { env.appendOrSetPath(value); }); -} - -void EnvironmentChange::addModify(const NameValueItems &items) -{ - m_changeItems.append([items](Environment &env) { env.modify(items); }); + m_changeItems.append({Item::AppendToPath, value.toVariant()}); } EnvironmentChange EnvironmentChange::fromFixedEnvironment(const Environment &fixedEnv) { EnvironmentChange change; - change.m_changeItems.append([fixedEnv](Environment &env) { env = fixedEnv; }); + change.m_changeItems.append({Item::SetFixedEnvironment, QVariant::fromValue(fixedEnv)}); return change; } void EnvironmentChange::applyToEnvironment(Environment &env) const { - for (const Item &item : m_changeItems) - item(env); + for (const Item &item : m_changeItems) { + switch (item.type) { + case Item::SetSystemEnvironment: + env = Environment::systemEnvironment(); + break; + case Item::SetFixedEnvironment: + env = item.data.value(); + break; + case Item::SetValue: { + auto data = item.data.value>(); + env.set(data.first, data.second); + break; + } + case Item::UnsetValue: + env.unset(item.data.toString()); + break; + case Item::PrependToPath: + env.prependOrSetPath(FilePath::fromVariant(item.data)); + break; + case Item::AppendToPath: + env.appendOrSetPath(FilePath::fromVariant(item.data)); + break; + } + } } } // namespace Utils diff --git a/src/libs/utils/environment.h b/src/libs/utils/environment.h index 7aa42ab2ad9..13fab9605f2 100644 --- a/src/libs/utils/environment.h +++ b/src/libs/utils/environment.h @@ -57,7 +57,7 @@ public: void unset(const QString &key) { m_dict.unset(key); } void modify(const NameValueItems &items) { m_dict.modify(items); } - int size() const { return m_dict.size(); } + int isValid() const; void clear() { return m_dict.clear(); } QStringList toStringList() const { return m_dict.toStringList(); } @@ -132,10 +132,24 @@ private: class QTCREATOR_UTILS_EXPORT EnvironmentChange final { public: - using Item = std::function; - EnvironmentChange() = default; + class Item final + { + public: + enum Type { + SetSystemEnvironment, + SetFixedEnvironment, + SetValue, + UnsetValue, + PrependToPath, + AppendToPath, + }; + + Type type; + QVariant data; + }; + static EnvironmentChange fromFixedEnvironment(const Environment &fixedEnv); void applyToEnvironment(Environment &) const; @@ -144,8 +158,6 @@ public: void addUnsetValue(const QString &key); void addPrependToPath(const FilePaths &values); void addAppendToPath(const FilePaths &values); - void addModify(const NameValueItems &items); - void addChange(const Item &item) { m_changeItems.append(item); } private: QList m_changeItems; diff --git a/src/libs/utils/filepath.cpp b/src/libs/utils/filepath.cpp index 07c76f7f753..3823f7974f7 100644 --- a/src/libs/utils/filepath.cpp +++ b/src/libs/utils/filepath.cpp @@ -921,6 +921,29 @@ FilePath FilePath::normalizedPathName() const return result; } +QString FilePath::displayName(const QString &args) const +{ + QString deviceName; + if (needsDevice()) { + QTC_ASSERT(s_deviceHooks.deviceDisplayName, return m_data); + deviceName = s_deviceHooks.deviceDisplayName(*this); + } + + if (args.isEmpty()) { + if (deviceName.isEmpty()) + return m_data; + + return QCoreApplication::translate("Utils::FileUtils", "%1 on %2", "File on device") + .arg(m_data, deviceName); + } + + if (deviceName.isEmpty()) + return m_data + ' ' + args; + + return QCoreApplication::translate("Utils::FileUtils", "%1 %2 on %3", "File and args on device") + .arg(m_data, args, deviceName); +} + /// Constructs a FilePath from \a filename /// \a filename is not checked for validity. FilePath FilePath::fromString(const QString &filepath) diff --git a/src/libs/utils/filepath.h b/src/libs/utils/filepath.h index ecd61beec1f..faf61ee66fb 100644 --- a/src/libs/utils/filepath.h +++ b/src/libs/utils/filepath.h @@ -175,6 +175,7 @@ public: // on Windows and macOS. This is rarely needed. [[nodiscard]] FilePath normalizedPathName() const; + QString displayName(const QString &args = {}) const; QString nativePath() const; QString shortNativePath() const; bool startsWithDriveLetter() const; diff --git a/src/libs/utils/fileutils.h b/src/libs/utils/fileutils.h index dbcc10019ac..995ebfb78b0 100644 --- a/src/libs/utils/fileutils.h +++ b/src/libs/utils/fileutils.h @@ -86,6 +86,7 @@ public: std::function environment; std::function fileSize; std::function bytesAvailable; + std::function deviceDisplayName; template using Continuation = std::function; std::function &, const FilePath &, const FilePath &)> asyncCopyFile; diff --git a/src/libs/utils/link.h b/src/libs/utils/link.h index adcac29a6d7..3b126318e23 100644 --- a/src/libs/utils/link.h +++ b/src/libs/utils/link.h @@ -78,7 +78,7 @@ public: QTCREATOR_UTILS_EXPORT QHashValueType qHash(const Link &l); -using ProcessLinkCallback = std::function; +using LinkHandler = std::function; using Links = QList; } // namespace Utils diff --git a/src/libs/utils/qtcprocess.cpp b/src/libs/utils/qtcprocess.cpp index 40ba6a6afbd..69cf99325bc 100644 --- a/src/libs/utils/qtcprocess.cpp +++ b/src/libs/utils/qtcprocess.cpp @@ -526,13 +526,9 @@ public: , m_stdErr(stdErr) {} QByteArray stdOut() const { return m_stdOut; } QByteArray stdErr() const { return m_stdErr; } - void mergeWith(ReadyReadSignal *newSignal) { - m_stdOut += newSignal->stdOut(); - m_stdErr += newSignal->stdErr(); - } private: - QByteArray m_stdOut; - QByteArray m_stdErr; + const QByteArray m_stdOut; + const QByteArray m_stdErr; }; class DoneSignal : public ProcessInterfaceSignal @@ -554,6 +550,7 @@ public: // Called from caller's thread exclusively. bool waitForSignal(int msecs, SignalType newSignal); void moveToCallerThread(); + void startKillTimer(int killTimeout); private: // Called from caller's thread exclusively. @@ -567,6 +564,7 @@ private: void appendSignal(ProcessInterfaceSignal *newSignal); QtcProcessPrivate *m_caller = nullptr; + QTimer m_killTimer; QMutex m_mutex; QWaitCondition m_waitCondition; }; @@ -615,7 +613,7 @@ public: Environment fullEnvironment() const { Environment env = m_setup.m_environment; - if (env.size() == 0) { + if (!env.isValid()) { // FIXME: Either switch to using EnvironmentChange instead of full Environments, or // feed the full environment into the QtcProcess instead of fixing it up here. // qWarning("QtcProcess::start: Empty environment set when running '%s'.", @@ -659,8 +657,9 @@ public: bool flushFor(SignalType signalType); bool shouldFlush() const { QMutexLocker locker(&m_mutex); return !m_signals.isEmpty(); } Qt::ConnectionType connectionType() const; - void sendControlSignal(ControlSignal controlSignal); + void sendControlSignal(ControlSignal controlSignal, int killTimeout = -1); // Called from ProcessInterfaceHandler thread exclusively. + void kill(); void appendSignal(ProcessInterfaceSignal *launcherSignal); mutable QMutex m_mutex; @@ -690,6 +689,7 @@ public: ProcessInterfaceHandler::ProcessInterfaceHandler(QtcProcessPrivate *caller, ProcessInterface *process) : m_caller(caller) + , m_killTimer(this) { connect(process, &ProcessInterface::started, this, &ProcessInterfaceHandler::handleStarted); @@ -697,6 +697,8 @@ ProcessInterfaceHandler::ProcessInterfaceHandler(QtcProcessPrivate *caller, this, &ProcessInterfaceHandler::handleReadyRead); connect(process, &ProcessInterface::done, this, &ProcessInterfaceHandler::handleDone); + m_killTimer.setSingleShot(true); + connect(&m_killTimer, &QTimer::timeout, caller, &QtcProcessPrivate::kill, Qt::DirectConnection); } // Called from caller's thread exclusively. @@ -720,7 +722,14 @@ bool ProcessInterfaceHandler::waitForSignal(int msecs, SignalType newSignal) void ProcessInterfaceHandler::moveToCallerThread() { QMetaObject::invokeMethod(this, [this] { - moveToThread(m_caller->thread()); }, Qt::BlockingQueuedConnection); + moveToThread(m_caller->thread()); + }, Qt::BlockingQueuedConnection); +} + +void ProcessInterfaceHandler::startKillTimer(int killTimeout) +{ + m_killTimer.setInterval(killTimeout); + m_killTimer.start(); } // Called from caller's thread exclusively. @@ -752,6 +761,7 @@ void ProcessInterfaceHandler::handleReadyRead(const QByteArray &outputData, cons // Called from ProcessInterfaceHandler thread exclusively void ProcessInterfaceHandler::handleDone(const ProcessResultData &data) { + m_killTimer.stop(); appendSignal(new DoneSignal(data)); } @@ -847,48 +857,32 @@ Qt::ConnectionType QtcProcessPrivate::connectionType() const } // Called from caller's thread exclusively -void QtcProcessPrivate::sendControlSignal(ControlSignal controlSignal) +void QtcProcessPrivate::sendControlSignal(ControlSignal controlSignal, int killTimeout) { QTC_ASSERT(QThread::currentThread() == thread(), return); if (!m_process || (m_state == QProcess::NotRunning)) return; - QMetaObject::invokeMethod(m_process.get(), [this, controlSignal] { + QMetaObject::invokeMethod(m_process.get(), [this, controlSignal, killTimeout] { m_process->sendControlSignal(controlSignal); + if (killTimeout >= 0) + m_processHandler->startKillTimer(killTimeout); }, connectionType()); } +// Called from ProcessInterfaceHandler thread exclusively. +void QtcProcessPrivate::kill() +{ + QTC_ASSERT(QThread::currentThread() == m_process->thread(), return); + m_process->sendControlSignal(ControlSignal::Kill); +} + // Called from ProcessInterfaceHandler thread exclusively. void QtcProcessPrivate::appendSignal(ProcessInterfaceSignal *newSignal) { QTC_ASSERT(newSignal->signalType() != SignalType::NoSignal, delete newSignal; return); QMutexLocker locker(&m_mutex); - - // TODO: we might assert if the caller's state is proper, e.g. - // start signal can't appear if we are in Running or NotRunning state, - // or finish signal can't appear if we are in NotRunning or Starting state, - // or readyRead signal can't appear if we are in NotRunning or Starting state, - // or error signal can't appear if we are in NotRunning state - // or FailedToStart error signal can't appear if we are in Running state - // or other than FailedToStart error signal can't appear if we are in Starting state. - if (!m_signals.isEmpty()) { - ProcessInterfaceSignal *lastSignal = m_signals.last(); - - QTC_ASSERT(lastSignal->signalType() != SignalType::Done, - qWarning() << "Buffering new signal for process" << m_setup.m_commandLine - << "while the last done() signal wasn't flushed yet."); - - // Merge ReadyRead signals into one. - if (lastSignal->signalType() == SignalType::ReadyRead - && newSignal->signalType() == SignalType::ReadyRead) { - ReadyReadSignal *lastRead = static_cast(lastSignal); - ReadyReadSignal *newRead = static_cast(newSignal); - lastRead->mergeWith(newRead); - delete newRead; - return; - } - } m_signals.append(newSignal); } @@ -1142,7 +1136,7 @@ QString QtcProcess::toStandaloneCommandLine() const d->m_setup.m_workingDirectory.path(); } parts.append("-i"); - if (d->m_setup.m_environment.size() > 0) { + if (d->m_setup.m_environment.isValid()) { const QStringList envVars = d->m_setup.m_environment.toStringList(); std::transform(envVars.cbegin(), envVars.cend(), std::back_inserter(parts), ProcessArgs::quoteArgUnix); @@ -1507,6 +1501,14 @@ void QtcProcess::close() d->clearForRun(); } +void QtcProcess::stop(int killTimeout) +{ + if (state() == QProcess::NotRunning) + return; + + d->sendControlSignal(ControlSignal::Terminate, killTimeout); +} + QString QtcProcess::locateBinary(const QString &binary) { const QByteArray path = qgetenv("PATH"); diff --git a/src/libs/utils/qtcprocess.h b/src/libs/utils/qtcprocess.h index 539e2a7280a..7d2f39b1deb 100644 --- a/src/libs/utils/qtcprocess.h +++ b/src/libs/utils/qtcprocess.h @@ -65,6 +65,7 @@ public: void interrupt(); void kickoffProcess(); void close(); + void stop(int killTimeout = 500); QByteArray readAllStandardOutput(); QByteArray readAllStandardError(); diff --git a/src/plugins/android/androidrunnerworker.cpp b/src/plugins/android/androidrunnerworker.cpp index a4e5a3c61f9..4734ec50244 100644 --- a/src/plugins/android/androidrunnerworker.cpp +++ b/src/plugins/android/androidrunnerworker.cpp @@ -645,7 +645,7 @@ void AndroidRunnerWorker::asyncStartHelper() << QString::fromLatin1(appArgs.join(' ').toUtf8().toBase64()); } - if (m_extraEnvVars.size() > 0) { + if (m_extraEnvVars.isValid()) { args << "-e" << "extraenvvars" << QString::fromLatin1(m_extraEnvVars.toStringList().join('\t') .toUtf8().toBase64()); diff --git a/src/plugins/android/javalanguageserver.cpp b/src/plugins/android/javalanguageserver.cpp index 6ed0c983622..ca77497c17e 100644 --- a/src/plugins/android/javalanguageserver.cpp +++ b/src/plugins/android/javalanguageserver.cpp @@ -188,7 +188,7 @@ private: TemporaryDirectory m_workspaceDir = TemporaryDirectory("QtCreator-jls-XXXXXX"); }; -LanguageClient::BaseClientInterface *JLSSettings::createInterface() const +LanguageClient::BaseClientInterface *JLSSettings::createInterface(ProjectExplorer::Project *) const { auto interface = new JLSInterface(); CommandLine cmd{m_executable, arguments(), CommandLine::Raw}; diff --git a/src/plugins/android/javalanguageserver.h b/src/plugins/android/javalanguageserver.h index 1727da74a41..c2ef687cd64 100644 --- a/src/plugins/android/javalanguageserver.h +++ b/src/plugins/android/javalanguageserver.h @@ -42,7 +42,8 @@ public: void fromMap(const QVariantMap &map) final; LanguageClient::BaseSettings *copy() const final; LanguageClient::Client *createClient(LanguageClient::BaseClientInterface *interface) const final; - LanguageClient::BaseClientInterface *createInterface() const final; + LanguageClient::BaseClientInterface *createInterface( + ProjectExplorer::Project *project) const final; Utils::FilePath m_languageServer; diff --git a/src/plugins/autotest/testconfiguration.cpp b/src/plugins/autotest/testconfiguration.cpp index 4599fc7606b..e00c2bf2fcf 100644 --- a/src/plugins/autotest/testconfiguration.cpp +++ b/src/plugins/autotest/testconfiguration.cpp @@ -83,8 +83,8 @@ FilePath ITestConfiguration::executableFilePath() const if (!hasExecutable()) return {}; - const Environment env = m_runnable.environment.size() == 0 ? Environment::systemEnvironment() - : m_runnable.environment; + const Environment env = m_runnable.environment.isValid() + ? m_runnable.environment : Environment::systemEnvironment(); return env.searchInPath(m_runnable.command.executable().path()); } diff --git a/src/plugins/autotoolsprojectmanager/autotoolsbuildsystem.cpp b/src/plugins/autotoolsprojectmanager/autotoolsbuildsystem.cpp index 2415f567893..f7e27b8b60d 100644 --- a/src/plugins/autotoolsprojectmanager/autotoolsbuildsystem.cpp +++ b/src/plugins/autotoolsprojectmanager/autotoolsbuildsystem.cpp @@ -116,13 +116,13 @@ void AutotoolsBuildSystem::makefileParsingFinished() const QFileInfo fileInfo = projectFilePath().toFileInfo(); const QDir dir = fileInfo.absoluteDir(); const QStringList files = m_makefileParserThread->sources(); - foreach (const QString& file, files) + for (const QString& file : files) m_files.append(dir.absoluteFilePath(file)); // Watch for changes of Makefile.am files. If a Makefile.am file // has been changed, the project tree must be reparsed. const QStringList makefiles = m_makefileParserThread->makefiles(); - foreach (const QString &makefile, makefiles) { + for (const QString &makefile : makefiles) { const QString absMakefile = dir.absoluteFilePath(makefile); m_files.append(absMakefile); @@ -161,7 +161,7 @@ static QStringList filterIncludes(const QString &absSrc, const QString &absBuild const QStringList &in) { QStringList result; - foreach (const QString i, in) { + for (const QString &i : in) { QString out = i; out.replace(QLatin1String("$(top_srcdir)"), absSrc); out.replace(QLatin1String("$(abs_top_srcdir)"), absSrc); diff --git a/src/plugins/bineditor/bineditorwidget.cpp b/src/plugins/bineditor/bineditorwidget.cpp index d2ea7b28c49..2ae54122494 100644 --- a/src/plugins/bineditor/bineditorwidget.cpp +++ b/src/plugins/bineditor/bineditorwidget.cpp @@ -905,7 +905,7 @@ void BinEditorWidget::paintEvent(QPaintEvent *e) int item_x = -xoffset + m_margin + c * m_columnWidth + m_labelWidth; QColor color; - foreach (const Markup &m, m_markup) { + for (const Markup &m : qAsConst(m_markup)) { if (m.covers(lineAddress + c)) { color = m.color; break; @@ -1236,7 +1236,7 @@ QString BinEditorWidget::toolTip(const QHelpEvent *helpEvent) const str << "

" << tr("Memory at 0x%1").arg(address, 0, 16) << "

"; - foreach (const Markup &m, m_markup) { + for (const Markup &m : qAsConst(m_markup)) { if (m.covers(address) && !m.toolTip.isEmpty()) { str << "

" << m.toolTip << "


"; break; diff --git a/src/plugins/clangcodemodel/CMakeLists.txt b/src/plugins/clangcodemodel/CMakeLists.txt index ef813ff5ef4..acd6b4bbd28 100644 --- a/src/plugins/clangcodemodel/CMakeLists.txt +++ b/src/plugins/clangcodemodel/CMakeLists.txt @@ -27,7 +27,6 @@ add_qtc_plugin(ClangCodeModel clangmodelmanagersupport.cpp clangmodelmanagersupport.h clangpreprocessorassistproposalitem.cpp clangpreprocessorassistproposalitem.h clangtextmark.cpp clangtextmark.h - clanguiheaderondiskmanager.cpp clanguiheaderondiskmanager.h clangutils.cpp clangutils.h tasktimers.cpp tasktimers.h EXPLICIT_MOC clangcodemodelplugin.h diff --git a/src/plugins/clangcodemodel/clangcodemodel.qbs b/src/plugins/clangcodemodel/clangcodemodel.qbs index b16c685c99c..5f4cc2c1b6e 100644 --- a/src/plugins/clangcodemodel/clangcodemodel.qbs +++ b/src/plugins/clangcodemodel/clangcodemodel.qbs @@ -54,8 +54,6 @@ QtcPlugin { "clangpreprocessorassistproposalitem.h", "clangtextmark.cpp", "clangtextmark.h", - "clanguiheaderondiskmanager.cpp", - "clanguiheaderondiskmanager.h", "clangutils.cpp", "clangutils.h", "tasktimers.cpp", diff --git a/src/plugins/clangcodemodel/clangdclient.cpp b/src/plugins/clangcodemodel/clangdclient.cpp index 836ba30cfe3..7d5970c2915 100644 --- a/src/plugins/clangcodemodel/clangdclient.cpp +++ b/src/plugins/clangcodemodel/clangdclient.cpp @@ -359,10 +359,10 @@ class ClangdClient::FollowSymbolData { public: FollowSymbolData(ClangdClient *q, quint64 id, const QTextCursor &cursor, CppEditor::CppEditorWidget *editorWidget, - const DocumentUri &uri, Utils::ProcessLinkCallback &&callback, + const DocumentUri &uri, const Utils::LinkHandler &callback, bool openInSplit) : q(q), id(id), cursor(cursor), editorWidget(editorWidget), uri(uri), - callback(std::move(callback)), virtualFuncAssistProvider(q->d), + callback(callback), virtualFuncAssistProvider(q->d), docRevision(editorWidget ? editorWidget->textDocument()->document()->revision() : -1), openInSplit(openInSplit) {} @@ -395,7 +395,7 @@ public: const QTextCursor cursor; const QPointer editorWidget; const DocumentUri uri; - const Utils::ProcessLinkCallback callback; + const Utils::LinkHandler callback; VirtualFunctionAssistProvider virtualFuncAssistProvider; QList pendingSymbolInfoRequests; QList pendingGotoImplRequests; @@ -418,9 +418,9 @@ class SwitchDeclDefData { public: SwitchDeclDefData(quint64 id, TextDocument *doc, const QTextCursor &cursor, CppEditor::CppEditorWidget *editorWidget, - Utils::ProcessLinkCallback &&callback) + const Utils::LinkHandler &callback) : id(id), document(doc), uri(DocumentUri::fromFilePath(doc->filePath())), - cursor(cursor), editorWidget(editorWidget), callback(std::move(callback)) {} + cursor(cursor), editorWidget(editorWidget), callback(callback) {} Utils::optional getFunctionNode() const { @@ -461,7 +461,7 @@ public: const DocumentUri uri; const QTextCursor cursor; const QPointer editorWidget; - Utils::ProcessLinkCallback callback; + Utils::LinkHandler callback; Utils::optional docSymbols; Utils::optional ast; }; @@ -1726,7 +1726,7 @@ void ClangdClient::Private::finishSearch(const ReferencesData &refData, bool can void ClangdClient::followSymbol(TextDocument *document, const QTextCursor &cursor, CppEditor::CppEditorWidget *editorWidget, - Utils::ProcessLinkCallback &&callback, + const Utils::LinkHandler &callback, bool resolveTarget, bool openInSplit ) @@ -1735,7 +1735,7 @@ void ClangdClient::followSymbol(TextDocument *document, const QTextCursor adjustedCursor = d->adjustedCursor(cursor, document); if (!resolveTarget) { d->followSymbolData.reset(); - symbolSupport().findLinkAt(document, adjustedCursor, std::move(callback), false); + symbolSupport().findLinkAt(document, adjustedCursor, callback, false); return; } @@ -1743,7 +1743,7 @@ void ClangdClient::followSymbol(TextDocument *document, << adjustedCursor.blockNumber() << adjustedCursor.positionInBlock(); d->followSymbolData.emplace(this, ++d->nextJobId, adjustedCursor, editorWidget, DocumentUri::fromFilePath(document->filePath()), - std::move(callback), openInSplit); + callback, openInSplit); // Step 1: Follow the symbol via "Go to Definition". At the same time, request the // AST node corresponding to the cursor position, so we can find out whether @@ -1777,14 +1777,13 @@ void ClangdClient::followSymbol(TextDocument *document, void ClangdClient::switchDeclDef(TextDocument *document, const QTextCursor &cursor, CppEditor::CppEditorWidget *editorWidget, - Utils::ProcessLinkCallback &&callback) + const Utils::LinkHandler &callback) { QTC_ASSERT(documentOpen(document), openDocument(document)); qCDebug(clangdLog) << "switch decl/dev requested" << document->filePath() << cursor.blockNumber() << cursor.positionInBlock(); - d->switchDeclDefData.emplace(++d->nextJobId, document, cursor, editorWidget, - std::move(callback)); + d->switchDeclDefData.emplace(++d->nextJobId, document, cursor, editorWidget, callback); // Retrieve AST and document symbols. const auto astHandler = [this, id = d->switchDeclDefData->id](const ClangdAstNode &ast, diff --git a/src/plugins/clangcodemodel/clangdclient.h b/src/plugins/clangcodemodel/clangdclient.h index 1444c739ff0..b644749f469 100644 --- a/src/plugins/clangcodemodel/clangdclient.h +++ b/src/plugins/clangcodemodel/clangdclient.h @@ -70,14 +70,14 @@ public: void followSymbol(TextEditor::TextDocument *document, const QTextCursor &cursor, CppEditor::CppEditorWidget *editorWidget, - Utils::ProcessLinkCallback &&callback, + const Utils::LinkHandler &callback, bool resolveTarget, bool openInSplit); void switchDeclDef(TextEditor::TextDocument *document, const QTextCursor &cursor, CppEditor::CppEditorWidget *editorWidget, - Utils::ProcessLinkCallback &&callback); + const Utils::LinkHandler &callback); void switchHeaderSource(const Utils::FilePath &filePath, bool inNextSplit); void findLocalUsages(TextEditor::TextDocument *document, const QTextCursor &cursor, diff --git a/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp b/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp index b49c8fc3c7b..66ff519725b 100644 --- a/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp +++ b/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp @@ -100,6 +100,30 @@ static const QList allCppDocuments() return Utils::qobject_container_cast(documents); } +static bool fileIsProjectBuildArtifact(const Client *client, const Utils::FilePath &filePath) +{ + if (const auto p = client->project()) { + if (const auto t = p->activeTarget()) { + if (const auto bc = t->activeBuildConfiguration()) { + if (filePath.isChildOf(bc->buildDirectory())) + return true; + } + } + } + return false; +} + +static Client *clientForGeneratedFile(const Utils::FilePath &filePath) +{ + for (Client * const client : LanguageClientManager::clients()) { + if (qobject_cast(client) && client->reachable() + && fileIsProjectBuildArtifact(client, filePath)) { + return client; + } + } + return nullptr; +} + ClangModelManagerSupport::ClangModelManagerSupport() { QTC_CHECK(!m_instance); @@ -167,32 +191,31 @@ CppEditor::CppCompletionAssistProvider *ClangModelManagerSupport::functionHintAs } void ClangModelManagerSupport::followSymbol(const CppEditor::CursorInEditor &data, - Utils::ProcessLinkCallback &&processLinkCallback, bool resolveTarget, + const Utils::LinkHandler &processLinkCallback, bool resolveTarget, bool inNextSplit) { if (ClangdClient * const client = clientForFile(data.filePath()); client && client->isFullyIndexed()) { client->followSymbol(data.textDocument(), data.cursor(), data.editorWidget(), - std::move(processLinkCallback), resolveTarget, inNextSplit); + processLinkCallback, resolveTarget, inNextSplit); return; } - CppModelManager::followSymbol(data, std::move(processLinkCallback), resolveTarget, inNextSplit, + CppModelManager::followSymbol(data, processLinkCallback, resolveTarget, inNextSplit, CppModelManager::Backend::Builtin); } void ClangModelManagerSupport::switchDeclDef(const CppEditor::CursorInEditor &data, - Utils::ProcessLinkCallback &&processLinkCallback) + const Utils::LinkHandler &processLinkCallback) { if (ClangdClient * const client = clientForFile(data.filePath()); client && client->isFullyIndexed()) { client->switchDeclDef(data.textDocument(), data.cursor(), data.editorWidget(), - std::move(processLinkCallback)); + processLinkCallback); return; } - CppModelManager::switchDeclDef(data, std::move(processLinkCallback), - CppModelManager::Backend::Builtin); + CppModelManager::switchDeclDef(data, processLinkCallback, CppModelManager::Backend::Builtin); } void ClangModelManagerSupport::startLocalRenaming(const CppEditor::CursorInEditor &data, @@ -344,7 +367,7 @@ void ClangModelManagerSupport::updateLanguageClient( if (Client * const oldClient = clientForProject(project)) LanguageClientManager::shutdownClient(oldClient); ClangdClient * const client = createClient(project, jsonDbDir); - connect(client, &Client::initialized, this, [client, project, projectInfo, jsonDbDir] { + connect(client, &Client::initialized, this, [this, client, project, projectInfo, jsonDbDir] { using namespace ProjectExplorer; if (!SessionManager::hasProject(project)) return; @@ -384,6 +407,23 @@ void ClangModelManagerSupport::updateLanguageClient( } } + for (auto it = m_queuedShadowDocuments.begin(); it != m_queuedShadowDocuments.end();) { + if (fileIsProjectBuildArtifact(client, it.key())) { + if (it.value().isEmpty()) + client->removeShadowDocument(it.key()); + else + client->setShadowDocument(it.key(), it.value()); + ClangdClient::handleUiHeaderChange(it.key().fileName()); + it = m_queuedShadowDocuments.erase(it); + } else { + ++it; + } + } + connect(client, &Client::shadowDocumentSwitched, this, + [](const Utils::FilePath &fp) { + ClangdClient::handleUiHeaderChange(fp.fileName()); + }); + if (client->state() == Client::Initialized) updateParserConfig(); else @@ -593,18 +633,28 @@ void ClangModelManagerSupport::onAbstractEditorSupportContentsUpdated(const QStr if (content.size() == 0) return; // Generation not yet finished. - - m_uiHeaderOnDiskManager.write(filePath, content); - ClangdClient::handleUiHeaderChange(Utils::FilePath::fromString(filePath).fileName()); + const auto fp = Utils::FilePath::fromString(filePath); + const QString stringContent = QString::fromUtf8(content); + if (Client * const client = clientForGeneratedFile(fp)) { + client->setShadowDocument(fp, stringContent); + ClangdClient::handleUiHeaderChange(fp.fileName()); + QTC_CHECK(m_queuedShadowDocuments.remove(fp) == 0); + } else { + m_queuedShadowDocuments.insert(fp, stringContent); + } } void ClangModelManagerSupport::onAbstractEditorSupportRemoved(const QString &filePath) { QTC_ASSERT(!filePath.isEmpty(), return); - if (!cppModelManager()->cppEditorDocument(filePath)) { - m_uiHeaderOnDiskManager.remove(filePath); - ClangdClient::handleUiHeaderChange(Utils::FilePath::fromString(filePath).fileName()); + const auto fp = Utils::FilePath::fromString(filePath); + if (Client * const client = clientForGeneratedFile(fp)) { + client->removeShadowDocument(fp); + ClangdClient::handleUiHeaderChange(fp.fileName()); + QTC_CHECK(m_queuedShadowDocuments.remove(fp) == 0); + } else { + m_queuedShadowDocuments.insert(fp, {}); } } @@ -738,16 +788,6 @@ ClangModelManagerSupport *ClangModelManagerSupport::instance() return m_instance; } -QString ClangModelManagerSupport::dummyUiHeaderOnDiskPath(const QString &filePath) const -{ - return m_uiHeaderOnDiskManager.mapPath(filePath); -} - -QString ClangModelManagerSupport::dummyUiHeaderOnDiskDirPath() const -{ - return m_uiHeaderOnDiskManager.directoryPath(); -} - QString ClangModelManagerSupportProvider::id() const { return QLatin1String(Constants::CLANG_MODELMANAGERSUPPORT_ID); diff --git a/src/plugins/clangcodemodel/clangmodelmanagersupport.h b/src/plugins/clangcodemodel/clangmodelmanagersupport.h index 7604dd5cc59..01a4d7429ce 100644 --- a/src/plugins/clangcodemodel/clangmodelmanagersupport.h +++ b/src/plugins/clangcodemodel/clangmodelmanagersupport.h @@ -25,14 +25,14 @@ #pragma once -#include "clanguiheaderondiskmanager.h" - #include #include +#include #include #include +#include #include #include @@ -70,9 +70,6 @@ public: std::unique_ptr createOverviewModel() override; bool usesClangd(const TextEditor::TextDocument *document) const override; - QString dummyUiHeaderOnDiskDirPath() const; - QString dummyUiHeaderOnDiskPath(const QString &filePath) const; - ClangdClient *clientForProject(const ProjectExplorer::Project *project) const; ClangdClient *clientForFile(const Utils::FilePath &file) const; @@ -83,10 +80,10 @@ signals: private: void followSymbol(const CppEditor::CursorInEditor &data, - Utils::ProcessLinkCallback &&processLinkCallback, bool resolveTarget, + const Utils::LinkHandler &processLinkCallback, bool resolveTarget, bool inNextSplit) override; void switchDeclDef(const CppEditor::CursorInEditor &data, - Utils::ProcessLinkCallback &&processLinkCallback) override; + const Utils::LinkHandler &processLinkCallback) override; void startLocalRenaming(const CppEditor::CursorInEditor &data, const CppEditor::ProjectPart *projectPart, CppEditor::RenameCallback &&renameSymbolsCallback) override; @@ -122,10 +119,9 @@ private: void watchForExternalChanges(); void watchForInternalChanges(); - UiHeaderOnDiskManager m_uiHeaderOnDiskManager; - Utils::FutureSynchronizer m_generatorSynchronizer; QList> m_clientsToRestart; + QHash m_queuedShadowDocuments; }; class ClangModelManagerSupportProvider : public CppEditor::ModelManagerSupportProvider diff --git a/src/plugins/clangcodemodel/clanguiheaderondiskmanager.cpp b/src/plugins/clangcodemodel/clanguiheaderondiskmanager.cpp deleted file mode 100644 index 696ced1c6f7..00000000000 --- a/src/plugins/clangcodemodel/clanguiheaderondiskmanager.cpp +++ /dev/null @@ -1,75 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#include "clanguiheaderondiskmanager.h" - -#include -#include - -#include -#include - -namespace ClangCodeModel { -namespace Internal { - -UiHeaderOnDiskManager::UiHeaderOnDiskManager() : m_temporaryDir("clang-uiheader-XXXXXX") -{ - QTC_CHECK(m_temporaryDir.isValid()); -} - -QString UiHeaderOnDiskManager::write(const QString &filePath, const QByteArray &content) -{ - const QString mappedPath = mapPath(filePath); - QFile file(mappedPath); - const bool fileCreated = file.open(QFile::WriteOnly); - const qint64 bytesWritten = file.write(content); - QTC_CHECK(fileCreated && bytesWritten != -1); - - return mappedPath; -} - -QString UiHeaderOnDiskManager::remove(const QString &filePath) -{ - const QString mappedPath = mapPath(filePath); - if (QFileInfo::exists(mappedPath)) { - const bool fileRemoved = QFile::remove(mappedPath); - QTC_CHECK(fileRemoved); - } - - return mappedPath; -} - -QString UiHeaderOnDiskManager::directoryPath() const -{ - return m_temporaryDir.path().path(); -} - -QString UiHeaderOnDiskManager::mapPath(const QString &filePath) const -{ - return directoryPath() + '/' + QFileInfo(filePath).fileName(); -} - -} // namespace Internal -} // namespace ClangCodeModel diff --git a/src/plugins/clangcodemodel/clanguiheaderondiskmanager.h b/src/plugins/clangcodemodel/clanguiheaderondiskmanager.h deleted file mode 100644 index a244fdb1781..00000000000 --- a/src/plugins/clangcodemodel/clanguiheaderondiskmanager.h +++ /dev/null @@ -1,50 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#pragma once - -#include - -namespace ClangCodeModel { -namespace Internal { - -// TODO: Remove once libclang supports unsaved files that do not exist. -class UiHeaderOnDiskManager -{ -public: - UiHeaderOnDiskManager(); - - QString write(const QString &filePath, const QByteArray &content); - QString remove(const QString &filePath); - - QString mapPath(const QString &filePath) const; - QString directoryPath() const; - -private: - ::Utils::TemporaryDirectory m_temporaryDir; -}; - -} // namespace Internal -} // namespace ClangCodeModel diff --git a/src/plugins/clangcodemodel/clangutils.cpp b/src/plugins/clangcodemodel/clangutils.cpp index d2d5e690702..a1c4393eab0 100644 --- a/src/plugins/clangcodemodel/clangutils.cpp +++ b/src/plugins/clangcodemodel/clangutils.cpp @@ -25,8 +25,6 @@ #include "clangutils.h" -#include "clangmodelmanagersupport.h" - #include #include #include @@ -355,12 +353,6 @@ CompilerOptionsBuilder clangOptionsBuilder(const ProjectPart &projectPart, useBuildSystemWarnings, clangIncludeDir); optionsBuilder.provideAdditionalMacros({ProjectExplorer::Macro("Q_CREATOR_RUN", "1")}); optionsBuilder.build(ProjectFile::Unclassified, UsePrecompiledHeaders::No); - const QString uiIncludePath - = ClangModelManagerSupport::instance()->dummyUiHeaderOnDiskDirPath(); - if (!uiIncludePath.isEmpty()) { - optionsBuilder.prepend(QDir::toNativeSeparators(uiIncludePath)); - optionsBuilder.prepend("-I"); - } optionsBuilder.add("-fmessage-length=0", /*gccOnlyOption=*/true); optionsBuilder.add("-fdiagnostics-show-note-include-stack", /*gccOnlyOption=*/true); optionsBuilder.add("-fretain-comments-from-system-headers", /*gccOnlyOption=*/true); diff --git a/src/plugins/clangformat/clangformatsettings.cpp b/src/plugins/clangformat/clangformatsettings.cpp index 66c20760038..2326f7b8ce1 100644 --- a/src/plugins/clangformat/clangformatsettings.cpp +++ b/src/plugins/clangformat/clangformatsettings.cpp @@ -29,6 +29,9 @@ #include namespace ClangFormat { +static const char FORMAT_CODE_INSTEAD_OF_INDENT_ID[] = "ClangFormat.FormatCodeInsteadOfIndent"; +static const char FORMAT_CODE_ON_SAVE_ID[] = "ClangFormat.FormatCodeOnSave"; +static const char FORMAT_WHILE_TYPING_ID[] = "ClangFormat.FormatWhileTyping"; ClangFormatSettings &ClangFormatSettings::instance() { @@ -42,9 +45,25 @@ ClangFormatSettings::ClangFormatSettings() settings->beginGroup(QLatin1String(Constants::SETTINGS_ID)); m_overrideDefaultFile = settings->value(QLatin1String(Constants::OVERRIDE_FILE_ID), false) .toBool(); - m_mode = static_cast( - settings->value(QLatin1String(Constants::MODE_ID), ClangFormatSettings::Mode::Indenting) - .toInt()); + + // Convert old settings to new ones. New settings were added to QtC 8.0 + bool isOldFormattingOn + = settings->value(QLatin1String(FORMAT_CODE_INSTEAD_OF_INDENT_ID), false).toBool() + || settings->value(QLatin1String(FORMAT_CODE_ON_SAVE_ID), false).toBool(); + + Core::ICore::settings()->remove(QLatin1String(FORMAT_CODE_INSTEAD_OF_INDENT_ID)); + Core::ICore::settings()->remove(QLatin1String(FORMAT_CODE_ON_SAVE_ID)); + Core::ICore::settings()->remove(QLatin1String(FORMAT_WHILE_TYPING_ID)); + + if (isOldFormattingOn) { + settings->setValue(QLatin1String(Constants::MODE_ID), + static_cast(ClangFormatSettings::Mode::Formatting)); + m_mode = ClangFormatSettings::Mode::Formatting; + } else + m_mode = static_cast( + settings->value(QLatin1String(Constants::MODE_ID), ClangFormatSettings::Mode::Indenting) + .toInt()); + settings->endGroup(); } diff --git a/src/plugins/clangtools/documentclangtoolrunner.cpp b/src/plugins/clangtools/documentclangtoolrunner.cpp index b0794676d0a..28a4d0df29b 100644 --- a/src/plugins/clangtools/documentclangtoolrunner.cpp +++ b/src/plugins/clangtools/documentclangtoolrunner.cpp @@ -183,7 +183,7 @@ static Environment projectBuildEnvironment(Project *project) if (BuildConfiguration *buildConfig = target->activeBuildConfiguration()) env = buildConfig->environment(); } - if (env.size() == 0) + if (!env.isValid()) env = Environment::systemEnvironment(); return env; } diff --git a/src/plugins/classview/classviewutils.cpp b/src/plugins/classview/classviewutils.cpp index a8d1249d760..3fbf21d5f6e 100644 --- a/src/plugins/classview/classviewutils.cpp +++ b/src/plugins/classview/classviewutils.cpp @@ -43,7 +43,7 @@ namespace Internal { QSet roleToLocations(const QList &locationsVar) { QSet locations; - foreach (const QVariant &loc, locationsVar) { + for (const QVariant &loc : locationsVar) { if (loc.canConvert()) locations.insert(loc.value()); } diff --git a/src/plugins/clearcase/activityselector.cpp b/src/plugins/clearcase/activityselector.cpp index ad7644d3946..952b9278d44 100644 --- a/src/plugins/clearcase/activityselector.cpp +++ b/src/plugins/clearcase/activityselector.cpp @@ -77,9 +77,9 @@ void ActivitySelector::userChanged() bool ActivitySelector::refresh() { int current; - QList activities = ClearCasePlugin::activities(¤t); + const QList activities = ClearCasePlugin::activities(¤t); m_cmbActivity->clear(); - foreach (const QStringPair &activity, activities) + for (const QStringPair &activity : activities) m_cmbActivity->addItem(activity.second, activity.first); m_cmbActivity->setCurrentIndex(current); m_cmbActivity->updateGeometry(); diff --git a/src/plugins/clearcase/clearcasesettings.cpp b/src/plugins/clearcase/clearcasesettings.cpp index be57ebcd4a7..e32593e62b7 100644 --- a/src/plugins/clearcase/clearcasesettings.cpp +++ b/src/plugins/clearcase/clearcasesettings.cpp @@ -88,7 +88,8 @@ void ClearCaseSettings::fromSettings(QSettings *settings) indexOnlyVOBs = settings->value(QLatin1String(indexOnlyVOBsC), QString()).toString(); extDiffAvailable = !Utils::Environment::systemEnvironment().searchInPath(QLatin1String("diff")).isEmpty(); settings->beginGroup(QLatin1String(totalFilesKeyC)); - foreach (const QString &view, settings->childKeys()) + const QStringList views = settings->childKeys(); + for (const QString &view : views) totalFiles[view] = settings->value(view).toInt(); settings->endGroup(); settings->endGroup(); diff --git a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp index 96fc649e28c..e8ebeda2f66 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp +++ b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp @@ -1369,8 +1369,8 @@ FilePath CMakeBuildConfiguration::shadowBuildDirectory(const FilePath &projectFi const QString projectName = projectFilePath.parentDir().fileName(); const FilePath projectDir = Project::projectDirectory(projectFilePath); - FilePath buildPath = BuildConfiguration::buildDirectoryFromTemplate(projectDir, - projectFilePath, projectName, k, bcName, buildType, BuildConfiguration::ReplaceSpaces); + FilePath buildPath = buildDirectoryFromTemplate(projectDir, projectFilePath, projectName, k, + bcName, buildType, "cmake"); if (CMakeGeneratorKitAspect::isMultiConfigGenerator(k)) { QString path = buildPath.path(); diff --git a/src/plugins/cmakeprojectmanager/cmakeeditor.cpp b/src/plugins/cmakeprojectmanager/cmakeeditor.cpp index 1ec3414b2d6..7ca0c54f327 100644 --- a/src/plugins/cmakeprojectmanager/cmakeeditor.cpp +++ b/src/plugins/cmakeprojectmanager/cmakeeditor.cpp @@ -108,7 +108,7 @@ public: private: bool save(const QString &fileName = QString()); void findLinkAt(const QTextCursor &cursor, - Utils::ProcessLinkCallback &&processLinkCallback, + const Utils::LinkHandler &processLinkCallback, bool resolveTarget = true, bool inNextSplit = false) override; void contextMenuEvent(QContextMenuEvent *e) override; @@ -152,7 +152,7 @@ static QString unescape(const QString &s) } void CMakeEditorWidget::findLinkAt(const QTextCursor &cursor, - Utils::ProcessLinkCallback &&processLinkCallback, + const Utils::LinkHandler &processLinkCallback, bool/* resolveTarget*/, bool /*inNextSplit*/) { diff --git a/src/plugins/cppeditor/cppbuiltinmodelmanagersupport.cpp b/src/plugins/cppeditor/cppbuiltinmodelmanagersupport.cpp index 534861e0ea8..50e8387a232 100644 --- a/src/plugins/cppeditor/cppbuiltinmodelmanagersupport.cpp +++ b/src/plugins/cppeditor/cppbuiltinmodelmanagersupport.cpp @@ -140,20 +140,20 @@ std::unique_ptr BuiltinModelManagerSupport::createOvervie } void BuiltinModelManagerSupport::followSymbol(const CursorInEditor &data, - Utils::ProcessLinkCallback &&processLinkCallback, + const Utils::LinkHandler &processLinkCallback, bool resolveTarget, bool inNextSplit) { SymbolFinder finder; - m_followSymbol->findLink(data, std::move(processLinkCallback), + m_followSymbol->findLink(data, processLinkCallback, resolveTarget, CppModelManager::instance()->snapshot(), data.editorWidget()->semanticInfo().doc, &finder, inNextSplit); } void BuiltinModelManagerSupport::switchDeclDef(const CursorInEditor &data, - Utils::ProcessLinkCallback &&processLinkCallback) + const Utils::LinkHandler &processLinkCallback) { SymbolFinder finder; - m_followSymbol->switchDeclDef(data, std::move(processLinkCallback), + m_followSymbol->switchDeclDef(data, processLinkCallback, CppModelManager::instance()->snapshot(), data.editorWidget()->semanticInfo().doc, &finder); } diff --git a/src/plugins/cppeditor/cppbuiltinmodelmanagersupport.h b/src/plugins/cppeditor/cppbuiltinmodelmanagersupport.h index 91fd7abe70e..e6583cb09a0 100644 --- a/src/plugins/cppeditor/cppbuiltinmodelmanagersupport.h +++ b/src/plugins/cppeditor/cppbuiltinmodelmanagersupport.h @@ -51,10 +51,10 @@ public: FollowSymbolUnderCursor &followSymbolInterface() { return *m_followSymbol; } private: - void followSymbol(const CursorInEditor &data, Utils::ProcessLinkCallback &&processLinkCallback, + void followSymbol(const CursorInEditor &data, const Utils::LinkHandler &processLinkCallback, bool resolveTarget, bool inNextSplit) override; void switchDeclDef(const CursorInEditor &data, - Utils::ProcessLinkCallback &&processLinkCallback) override; + const Utils::LinkHandler &processLinkCallback) override; void startLocalRenaming(const CursorInEditor &data, const ProjectPart *projectPart, RenameCallback &&renameSymbolsCallback) override; diff --git a/src/plugins/cppeditor/cppcodemodelsettings.cpp b/src/plugins/cppeditor/cppcodemodelsettings.cpp index f33cc7bbd52..aacf8ff5625 100644 --- a/src/plugins/cppeditor/cppcodemodelsettings.cpp +++ b/src/plugins/cppeditor/cppcodemodelsettings.cpp @@ -303,10 +303,15 @@ FilePath ClangdSettings::clangdIncludePath() const QTC_ASSERT(!clangdPath.isEmpty() && clangdPath.exists(), return {}); const QVersionNumber version = clangdVersion(); QTC_ASSERT(!version.isNull(), return {}); - const FilePath includePath = clangdPath.absolutePath().parentDir().pathAppended("lib/clang") - .pathAppended(version.toString()).pathAppended("include"); - QTC_ASSERT(includePath.exists(), return {}); - return includePath; + static const QStringList libDirs{"lib", "lib64"}; + for (const QString &libDir : libDirs) { + const FilePath includePath = clangdPath.absolutePath().parentDir().pathAppended(libDir) + .pathAppended("clang").pathAppended(version.toString()).pathAppended("include"); + if (includePath.exists()) + return includePath; + } + QTC_CHECK(false); + return {}; } FilePath ClangdSettings::clangdUserConfigFilePath() diff --git a/src/plugins/cppeditor/cppeditorwidget.cpp b/src/plugins/cppeditor/cppeditorwidget.cpp index f6409bdbd1c..1129e107444 100644 --- a/src/plugins/cppeditor/cppeditorwidget.cpp +++ b/src/plugins/cppeditor/cppeditorwidget.cpp @@ -663,57 +663,6 @@ void CppEditorWidget::onIfdefedOutBlocksUpdated(unsigned revision, textDocument()->setIfdefedOutBlocks(ifdefedOutBlocks); } -static QString getDocumentLine(QTextDocument *document, int line) -{ - if (document) - return document->findBlockByNumber(line - 1).text(); - - return {}; -} - -static std::unique_ptr getCurrentDocument(const QString &path) -{ - const QTextCodec *defaultCodec = Core::EditorManager::defaultTextCodec(); - QString contents; - Utils::TextFileFormat format; - QString error; - if (Utils::TextFileFormat::readFile(Utils::FilePath::fromString(path), - defaultCodec, - &contents, - &format, - &error) - != Utils::TextFileFormat::ReadSuccess) { - qWarning() << "Error reading file " << path << " : " << error; - return {}; - } - - return std::make_unique(contents); -} - -static void onReplaceUsagesClicked(const QString &text, - const QList &items, - bool preserveCase) -{ - CppModelManager *modelManager = CppModelManager::instance(); - if (!modelManager) - return; - - const FilePaths filePaths = TextEditor::BaseFileFind::replaceAll(text, items, preserveCase); - if (!filePaths.isEmpty()) { - modelManager->updateSourceFiles(Utils::transform(filePaths, &FilePath::toString)); - SearchResultWindow::instance()->hide(); - } -} - -static QTextDocument *getOpenDocument(const QString &path) -{ - const IDocument *document = DocumentModel::documentForFilePath(FilePath::fromString(path)); - if (document) - return qobject_cast(document)->document(); - - return {}; -} - void CppEditorWidget::findUsages() { findUsages(textCursor()); @@ -940,7 +889,7 @@ void CppEditorWidget::switchDeclarationDefinition(bool inNextSplit) } void CppEditorWidget::findLinkAt(const QTextCursor &cursor, - ProcessLinkCallback &&processLinkCallback, + const LinkHandler &processLinkCallback, bool resolveTarget, bool inNextSplit) { @@ -953,8 +902,8 @@ void CppEditorWidget::findLinkAt(const QTextCursor &cursor, // UI header. QTextCursor c(cursor); c.select(QTextCursor::WordUnderCursor); - ProcessLinkCallback callbackWrapper = [start = c.selectionStart(), end = c.selectionEnd(), - doc = QPointer(cursor.document()), callback = std::move(processLinkCallback), + LinkHandler callbackWrapper = [start = c.selectionStart(), end = c.selectionEnd(), + doc = QPointer(cursor.document()), callback = processLinkCallback, filePath](const Link &link) { const int linkPos = doc ? Text::positionInText(doc, link.targetLine, link.targetColumn + 1) : -1; @@ -978,7 +927,7 @@ void CppEditorWidget::findLinkAt(const QTextCursor &cursor, }; CppModelManager::followSymbol( CursorInEditor{cursor, filePath, this, textDocument()}, - std::move(callbackWrapper), + callbackWrapper, resolveTarget, inNextSplit); } diff --git a/src/plugins/cppeditor/cppeditorwidget.h b/src/plugins/cppeditor/cppeditorwidget.h index 21f4a8bee10..5b27c4e4451 100644 --- a/src/plugins/cppeditor/cppeditorwidget.h +++ b/src/plugins/cppeditor/cppeditorwidget.h @@ -116,7 +116,7 @@ protected: bool handleStringSplitting(QKeyEvent *e) const; void findLinkAt(const QTextCursor &cursor, - Utils::ProcessLinkCallback &&processLinkCallback, + const Utils::LinkHandler &processLinkCallback, bool resolveTarget = true, bool inNextSplit = false) override; diff --git a/src/plugins/cppeditor/cppfollowsymbolundercursor.cpp b/src/plugins/cppeditor/cppfollowsymbolundercursor.cpp index 166ed31cd2d..23a377bc1d0 100644 --- a/src/plugins/cppeditor/cppfollowsymbolundercursor.cpp +++ b/src/plugins/cppeditor/cppfollowsymbolundercursor.cpp @@ -492,7 +492,7 @@ static int skipMatchingParentheses(const Tokens &tokens, int idx, int initialDep void FollowSymbolUnderCursor::findLink( const CursorInEditor &data, - Utils::ProcessLinkCallback &&processLinkCallback, + const Utils::LinkHandler &processLinkCallback, bool resolveTarget, const Snapshot &theSnapshot, const Document::Ptr &documentFromSemanticInfo, @@ -803,7 +803,7 @@ void FollowSymbolUnderCursor::findLink( void FollowSymbolUnderCursor::switchDeclDef( const CursorInEditor &data, - Utils::ProcessLinkCallback &&processLinkCallback, + const Utils::LinkHandler &processLinkCallback, const CPlusPlus::Snapshot &snapshot, const CPlusPlus::Document::Ptr &documentFromSemanticInfo, SymbolFinder *symbolFinder) diff --git a/src/plugins/cppeditor/cppfollowsymbolundercursor.h b/src/plugins/cppeditor/cppfollowsymbolundercursor.h index 8c2a89e2f85..d5780d948ec 100644 --- a/src/plugins/cppeditor/cppfollowsymbolundercursor.h +++ b/src/plugins/cppeditor/cppfollowsymbolundercursor.h @@ -42,7 +42,7 @@ public: FollowSymbolUnderCursor(); void findLink(const CursorInEditor &data, - Utils::ProcessLinkCallback &&processLinkCallback, + const Utils::LinkHandler &processLinkCallback, bool resolveTarget, const CPlusPlus::Snapshot &snapshot, const CPlusPlus::Document::Ptr &documentFromSemanticInfo, @@ -50,7 +50,7 @@ public: bool inNextSplit); void switchDeclDef(const CursorInEditor &data, - Utils::ProcessLinkCallback &&processLinkCallback, + const Utils::LinkHandler &processLinkCallback, const CPlusPlus::Snapshot &snapshot, const CPlusPlus::Document::Ptr &documentFromSemanticInfo, SymbolFinder *symbolFinder); diff --git a/src/plugins/cppeditor/cppmodelmanager.cpp b/src/plugins/cppeditor/cppmodelmanager.cpp index f6ab5f7453d..6afbe669e60 100644 --- a/src/plugins/cppeditor/cppmodelmanager.cpp +++ b/src/plugins/cppeditor/cppmodelmanager.cpp @@ -1631,18 +1631,18 @@ TextEditor::BaseHoverHandler *CppModelManager::createHoverHandler() const } void CppModelManager::followSymbol(const CursorInEditor &data, - Utils::ProcessLinkCallback &&processLinkCallback, + const Utils::LinkHandler &processLinkCallback, bool resolveTarget, bool inNextSplit, Backend backend) { - instance()->modelManagerSupport(backend)->followSymbol(data, std::move(processLinkCallback), + instance()->modelManagerSupport(backend)->followSymbol(data, processLinkCallback, resolveTarget, inNextSplit); } void CppModelManager::switchDeclDef(const CursorInEditor &data, - Utils::ProcessLinkCallback &&processLinkCallback, + const Utils::LinkHandler &processLinkCallback, Backend backend) { - instance()->modelManagerSupport(backend)->switchDeclDef(data, std::move(processLinkCallback)); + instance()->modelManagerSupport(backend)->switchDeclDef(data, processLinkCallback); } Core::ILocatorFilter *CppModelManager::createAuxiliaryCurrentDocumentFilter() diff --git a/src/plugins/cppeditor/cppmodelmanager.h b/src/plugins/cppeditor/cppmodelmanager.h index a475c8e4f37..0177d43d5b7 100644 --- a/src/plugins/cppeditor/cppmodelmanager.h +++ b/src/plugins/cppeditor/cppmodelmanager.h @@ -170,10 +170,10 @@ public: enum class Backend { Builtin, Best }; static void followSymbol(const CursorInEditor &data, - Utils::ProcessLinkCallback &&processLinkCallback, + const Utils::LinkHandler &processLinkCallback, bool resolveTarget, bool inNextSplit, Backend backend = Backend::Best); static void switchDeclDef(const CursorInEditor &data, - Utils::ProcessLinkCallback &&processLinkCallback, + const Utils::LinkHandler &processLinkCallback, Backend backend = Backend::Best); static void startLocalRenaming(const CursorInEditor &data, const ProjectPart *projectPart, RenameCallback &&renameSymbolsCallback, diff --git a/src/plugins/cppeditor/cppmodelmanagersupport.h b/src/plugins/cppeditor/cppmodelmanagersupport.h index 44fff4b4f65..5be236c5d52 100644 --- a/src/plugins/cppeditor/cppmodelmanagersupport.h +++ b/src/plugins/cppeditor/cppmodelmanagersupport.h @@ -65,10 +65,10 @@ public: virtual bool usesClangd(const TextEditor::TextDocument *) const { return false; } virtual void followSymbol(const CursorInEditor &data, - Utils::ProcessLinkCallback &&processLinkCallback, + const Utils::LinkHandler &processLinkCallback, bool resolveTarget, bool inNextSplit) = 0; virtual void switchDeclDef(const CursorInEditor &data, - Utils::ProcessLinkCallback &&processLinkCallback) = 0; + const Utils::LinkHandler &processLinkCallback) = 0; virtual void startLocalRenaming(const CursorInEditor &data, const ProjectPart *projectPart, RenameCallback &&renameSymbolsCallback) = 0; diff --git a/src/plugins/debugger/cdb/cdbengine.cpp b/src/plugins/debugger/cdb/cdbengine.cpp index e2da844b0e8..99f9b14e7fd 100644 --- a/src/plugins/debugger/cdb/cdbengine.cpp +++ b/src/plugins/debugger/cdb/cdbengine.cpp @@ -438,8 +438,8 @@ void CdbEngine::setupEngine() m_outputBuffer.clear(); m_autoBreakPointCorrection = false; - Utils::Environment inferiorEnvironment = sp.inferior.environment.size() == 0 - ? Utils::Environment::systemEnvironment() : sp.inferior.environment; + Environment inferiorEnvironment = sp.inferior.environment.isValid() + ? sp.inferior.environment : Environment::systemEnvironment(); // Make sure that QTestLib uses OutputDebugString for logging. const QString qtLoggingToConsoleKey = QStringLiteral("QT_LOGGING_TO_CONSOLE"); diff --git a/src/plugins/debugger/debuggerengine.cpp b/src/plugins/debugger/debuggerengine.cpp index 25ffb16122e..e95ee836eae 100644 --- a/src/plugins/debugger/debuggerengine.cpp +++ b/src/plugins/debugger/debuggerengine.cpp @@ -123,8 +123,8 @@ QDebug operator<<(QDebug str, const DebuggerRunParameters &sp) nospace << "executable=" << sp.inferior.command.executable() << " coreFile=" << sp.coreFile << " processArgs=" << sp.inferior.command.arguments() - << " inferior environment=<" << sp.inferior.environment.size() << " variables>" - << " debugger environment=<" << sp.debugger.environment.size() << " variables>" + << " inferior environment=<" << sp.inferior.environment.toStringList().size() << " variables>" + << " debugger environment=<" << sp.debugger.environment.toStringList().size() << " variables>" << " workingDir=" << sp.inferior.workingDirectory << " attachPID=" << sp.attachPID.pid() << " remoteChannel=" << sp.remoteChannel diff --git a/src/plugins/debugger/debuggeritem.cpp b/src/plugins/debugger/debuggeritem.cpp index 973a436cc6c..c8f6bdcb7ae 100644 --- a/src/plugins/debugger/debuggeritem.cpp +++ b/src/plugins/debugger/debuggeritem.cpp @@ -165,7 +165,7 @@ void DebuggerItem::reinitializeFromFile(const Environment &sysEnv, QString *erro return; } - Environment env = sysEnv.size() == 0 ? Environment::systemEnvironment() : sysEnv; + Environment env = sysEnv.isValid() ? sysEnv : Environment::systemEnvironment(); // Prevent calling lldb on Windows because the lldb from the llvm package is linked against // python but does not contain a python dll. const bool isAndroidNdkLldb = DebuggerItem::addAndroidLldbPythonEnv(m_command, env); diff --git a/src/plugins/docker/dockerdevice.cpp b/src/plugins/docker/dockerdevice.cpp index 952005a99d9..285b2f4cf6f 100644 --- a/src/plugins/docker/dockerdevice.cpp +++ b/src/plugins/docker/dockerdevice.cpp @@ -135,7 +135,7 @@ public: ~DockerDevicePrivate() { stopCurrentContainer(); } bool runInContainer(const CommandLine &cmd) const; - bool runInShell(const CommandLine &cmd) const; + bool runInShell(const CommandLine &cmd, const QByteArray &stdInData = {}) const; QByteArray outputForRunInShell(const CommandLine &cmd) const; void updateContainerAccess(); @@ -485,7 +485,8 @@ void DockerDevicePrivate::startContainer() "or restart Qt Creator.")); }); - if (!m_shell->waitForStarted()) { + if (m_shell->state() != DeviceShell::State::Succeeded) { + m_shell.reset(); DockerApi::recheckDockerDaemon(); qCWarning(dockerDeviceLog) << "Container shell failed to start"; } @@ -979,36 +980,16 @@ bool DockerDevice::writeFileContents(const FilePath &filePath, const QByteArray QTC_ASSERT(handlesFile(filePath), return {}); updateContainerAccess(); -// This following would be the generic Unix solution. -// But it doesn't pass input. FIXME: Why? -// QtcProcess proc; -// proc.setCommand({"dd", {"of=" + filePath.path()}}); -// proc.setWriteData(data); -// runProcess(proc); -// proc.waitForFinished(); - - TemporaryFile tempFile("dockertransport-XXXXXX"); - tempFile.open(); - tempFile.write(data); - - const QString tempName = tempFile.fileName(); - tempFile.close(); - - CommandLine cmd{"docker", {"cp", tempName, d->m_container + ':' + filePath.path()}}; - - QtcProcess proc; - proc.setCommand(cmd); - proc.runBlocking(); - - return proc.exitCode() == 0; + QTC_ASSERT(handlesFile(filePath), return {}); + return d->runInShell({"dd", {"of=" + filePath.path()}}, data); } Environment DockerDevice::systemEnvironment() const { - if (d->m_cachedEnviroment.size() == 0) + if (!d->m_cachedEnviroment.isValid()) d->fetchSystemEnviroment(); - QTC_CHECK(d->m_cachedEnviroment.size() != 0); + QTC_CHECK(d->m_cachedEnviroment.isValid()); return d->m_cachedEnviroment; } @@ -1058,16 +1039,16 @@ bool DockerDevicePrivate::runInContainer(const CommandLine &cmd) const return exitCode == 0; } -bool DockerDevicePrivate::runInShell(const CommandLine &cmd) const +bool DockerDevicePrivate::runInShell(const CommandLine &cmd, const QByteArray& stdInData) const { QTC_ASSERT(m_shell, return false); - return m_shell->runInShell(cmd); + return m_shell->runInShell(cmd, stdInData); } QByteArray DockerDevicePrivate::outputForRunInShell(const CommandLine &cmd) const { QTC_ASSERT(m_shell.get(), return {}); - return m_shell->outputForRunInShell(cmd).stdOutput; + return m_shell->outputForRunInShell(cmd).stdOut; } // Factory diff --git a/src/plugins/gitlab/gitlabclonedialog.cpp b/src/plugins/gitlab/gitlabclonedialog.cpp index e1dabccc28a..dcb9ec89268 100644 --- a/src/plugins/gitlab/gitlabclonedialog.cpp +++ b/src/plugins/gitlab/gitlabclonedialog.cpp @@ -84,6 +84,7 @@ GitLabCloneDialog::GitLabCloneDialog(const Project &project, QWidget *parent) form->addRow(tr("Directory"), m_directoryLE); m_submodulesCB = new QCheckBox(this); form->addRow(tr("Recursive"), m_submodulesCB); + form->addItem(new QSpacerItem(10, 10)); centerLayout->addLayout(form); m_cloneOutput = new QPlainTextEdit(this); m_cloneOutput->setReadOnly(true); @@ -91,7 +92,6 @@ GitLabCloneDialog::GitLabCloneDialog(const Project &project, QWidget *parent) layout->addLayout(centerLayout); m_infoLabel = new Utils::InfoLabel(this); layout->addWidget(m_infoLabel); - layout->addStretch(1); auto buttons = new QDialogButtonBox(QDialogButtonBox::Cancel, this); m_cloneButton = new QPushButton(tr("Clone"), this); buttons->addButton(m_cloneButton, QDialogButtonBox::ActionRole); diff --git a/src/plugins/gitlab/gitlabdialog.cpp b/src/plugins/gitlab/gitlabdialog.cpp index 6af65fef2dc..df76d25162c 100644 --- a/src/plugins/gitlab/gitlabdialog.cpp +++ b/src/plugins/gitlab/gitlabdialog.cpp @@ -131,6 +131,9 @@ void GitLabDialog::requestMainViewUpdate() } m_ui.remoteCB->setEnabled(!linked); + if (!m_currentServerId.isValid()) + return; + const Query query(Query::User); QueryRunner *runner = new QueryRunner(query, m_currentServerId, this); connect(runner, &QueryRunner::resultRetrieved, this, [this](const QByteArray &result) { diff --git a/src/plugins/gitlab/gitlabprojectsettings.cpp b/src/plugins/gitlab/gitlabprojectsettings.cpp index e24b4699829..cbd1688f909 100644 --- a/src/plugins/gitlab/gitlabprojectsettings.cpp +++ b/src/plugins/gitlab/gitlabprojectsettings.cpp @@ -159,6 +159,8 @@ GitLabProjectSettingsWidget::GitLabProjectSettingsWidget(ProjectExplorer::Projec horizontalLayout->addWidget(m_checkConnection); horizontalLayout->addStretch(1); verticalLayout->addLayout(horizontalLayout); + verticalLayout->addWidget(new QLabel(tr("Projects linked with GitLab receive event " + "notifications in the Version Control output pane."))); connect(m_linkWithGitLab, &QPushButton::clicked, this, [this]() { checkConnection(Link); diff --git a/src/plugins/languageclient/client.cpp b/src/plugins/languageclient/client.cpp index e997a564c5d..e9ab9c5db4e 100644 --- a/src/plugins/languageclient/client.cpp +++ b/src/plugins/languageclient/client.cpp @@ -260,6 +260,9 @@ public: const LanguageServerProtocol::Range &range, const QList &diagnostics); void documentClosed(Core::IDocument *document); + void sendOpenNotification(const FilePath &filePath, const QString &mimeType, + const QString &content, int version); + void sendCloseNotification(const FilePath &filePath); bool reset(); @@ -270,6 +273,11 @@ public: LanguageFilter m_languagFilter; QJsonObject m_initializationOptions; QMap m_openedDocument; + + // Used for build system artifacts (e.g. UI headers) that Qt Creator "live-generates" ahead of + // the build. + QMap m_shadowDocuments; + QSet m_postponedDocuments; QMap m_documentVersions; std::unordered_mapfilePath(); + if (d->m_shadowDocuments.contains(filePath)) { + d->sendCloseNotification(filePath); + emit shadowDocumentSwitched(filePath); + } + const QString method(DidOpenTextDocumentNotification::methodName); if (Utils::optional registered = d->m_dynamicCapabilities.isRegistered(method)) { if (!*registered) @@ -591,14 +604,10 @@ void Client::openDocument(TextEditor::TextDocument *document) [this, document](int position, int charsRemoved, int charsAdded) { documentContentsChanged(document, position, charsRemoved, charsAdded); }); - TextDocumentItem item; - item.setLanguageId(TextDocumentItem::mimeTypeToLanguageId(document->mimeType())); - item.setUri(DocumentUri::fromFilePath(filePath)); - item.setText(document->plainText()); if (!d->m_documentVersions.contains(filePath)) d->m_documentVersions[filePath] = 0; - item.setVersion(d->m_documentVersions[filePath]); - sendMessage(DidOpenTextDocumentNotification(DidOpenTextDocumentParams(item))); + d->sendOpenNotification(filePath, document->mimeType(), document->plainText(), + d->m_documentVersions[filePath]); handleDocumentOpened(document); const Client *currentClient = LanguageClientManager::clientForDocument(document); @@ -635,15 +644,21 @@ void Client::cancelRequest(const MessageId &id) void Client::closeDocument(TextEditor::TextDocument *document) { deactivateDocument(document); - const DocumentUri &uri = DocumentUri::fromFilePath(document->filePath()); d->m_postponedDocuments.remove(document); if (d->m_openedDocument.remove(document) != 0) { handleDocumentClosed(document); - if (d->m_state == Initialized) { - DidCloseTextDocumentParams params(TextDocumentIdentifier{uri}); - sendMessage(DidCloseTextDocumentNotification(params)); - } + if (d->m_state == Initialized) + d->sendCloseNotification(document->filePath()); } + + if (d->m_state != Initialized) + return; + const auto shadowIt = d->m_shadowDocuments.constFind(document->filePath()); + if (shadowIt == d->m_shadowDocuments.constEnd()) + return; + d->sendOpenNotification(document->filePath(), document->mimeType(), shadowIt.value(), + ++d->m_documentVersions[document->filePath()]); + emit shadowDocumentSwitched(document->filePath()); } void ClientPrivate::updateCompletionProvider(TextEditor::TextDocument *document) @@ -834,6 +849,25 @@ void ClientPrivate::documentClosed(Core::IDocument *document) q->closeDocument(textDocument); } +void ClientPrivate::sendOpenNotification(const FilePath &filePath, const QString &mimeType, + const QString &content, int version) +{ + TextDocumentItem item; + item.setLanguageId(TextDocumentItem::mimeTypeToLanguageId(mimeType)); + item.setUri(DocumentUri::fromFilePath(filePath)); + item.setText(content); + item.setVersion(version); + q->sendMessage(DidOpenTextDocumentNotification(DidOpenTextDocumentParams(item)), + Client::SendDocUpdates::Ignore); +} + +void ClientPrivate::sendCloseNotification(const FilePath &filePath) +{ + q->sendMessage(DidCloseTextDocumentNotification(DidCloseTextDocumentParams( + TextDocumentIdentifier{DocumentUri::fromFilePath(filePath)})), + Client::SendDocUpdates::Ignore); +} + bool Client::documentOpen(const TextEditor::TextDocument *document) const { return d->m_openedDocument.contains(const_cast(document)); @@ -848,6 +882,41 @@ TextEditor::TextDocument *Client::documentForFilePath(const Utils::FilePath &fil return nullptr; } +void Client::setShadowDocument(const Utils::FilePath &filePath, const QString &content) +{ + QTC_ASSERT(reachable(), return); + const auto it = d->m_shadowDocuments.find(filePath); + const bool isNew = it == d->m_shadowDocuments.end(); + if (isNew) + d->m_shadowDocuments.insert(filePath, content); + else + it.value() = content; + if (documentForFilePath(filePath)) + return; + const auto uri = DocumentUri::fromFilePath(filePath); + if (isNew) { + const QString mimeType = mimeTypeForFile( + filePath.toString(), MimeMatchMode::MatchExtension).name(); + d->sendOpenNotification(filePath, mimeType, content, 0); + } + + VersionedTextDocumentIdentifier docId(uri); + docId.setVersion(++d->m_documentVersions[filePath]); + const DidChangeTextDocumentParams params(docId, content); + sendMessage(DidChangeTextDocumentNotification(params), SendDocUpdates::Ignore); +} + +void Client::removeShadowDocument(const Utils::FilePath &filePath) +{ + const auto it = d->m_shadowDocuments.find(filePath); + if (it == d->m_shadowDocuments.end()) + return; + d->m_shadowDocuments.erase(it); + if (documentForFilePath(filePath)) + return; + d->sendCloseNotification(filePath); +} + void Client::documentContentsSaved(TextEditor::TextDocument *document) { if (!d->m_openedDocument.contains(document)) @@ -918,7 +987,8 @@ void Client::documentContentsChanged(TextEditor::TextDocument *document, { if (!d->m_openedDocument.contains(document) || !reachable()) return; - d->m_diagnosticManager->disableDiagnostics(document); + if (d->m_diagnosticManager) + d->m_diagnosticManager->disableDiagnostics(document); const QString method(DidChangeTextDocumentNotification::methodName); TextDocumentSyncKind syncKind = d->m_serverCapabilities.textDocumentSyncKindHelper(); if (Utils::optional registered = d->m_dynamicCapabilities.isRegistered(method)) { @@ -1406,6 +1476,7 @@ bool ClientPrivate::reset() qDeleteAll(m_documentHighlightsTimer); m_documentHighlightsTimer.clear(); m_progressManager.reset(); + m_shadowDocuments.clear(); m_documentVersions.clear(); return true; } diff --git a/src/plugins/languageclient/client.h b/src/plugins/languageclient/client.h index 679cf992193..6fcc4d5a984 100644 --- a/src/plugins/languageclient/client.h +++ b/src/plugins/languageclient/client.h @@ -136,6 +136,8 @@ public: void deactivateDocument(TextEditor::TextDocument *document); bool documentOpen(const TextEditor::TextDocument *document) const; TextEditor::TextDocument *documentForFilePath(const Utils::FilePath &file) const; + void setShadowDocument(const Utils::FilePath &filePath, const QString &contents); + void removeShadowDocument(const Utils::FilePath &filePath); void documentContentsSaved(TextEditor::TextDocument *document); void documentWillSave(Core::IDocument *document); void documentContentsChanged(TextEditor::TextDocument *document, @@ -205,6 +207,7 @@ signals: void capabilitiesChanged(const DynamicCapabilities &capabilities); void documentUpdated(TextEditor::TextDocument *document); void workDone(const LanguageServerProtocol::ProgressToken &token); + void shadowDocumentSwitched(const Utils::FilePath &filePath); void finished(); protected: diff --git a/src/plugins/languageclient/languageclientmanager.cpp b/src/plugins/languageclient/languageclientmanager.cpp index 747a6d04cae..0c5872486c5 100644 --- a/src/plugins/languageclient/languageclientmanager.cpp +++ b/src/plugins/languageclient/languageclientmanager.cpp @@ -181,7 +181,7 @@ Client *LanguageClientManager::startClient(const BaseSettings *setting, return client; } -QList LanguageClientManager::clients() +const QList LanguageClientManager::clients() { QTC_ASSERT(managerInstance, return {}); return managerInstance->m_clients; @@ -456,7 +456,7 @@ void LanguageClientManager::editorOpened(Core::IEditor *editor) if (TextEditorWidget *widget = textEditor->editorWidget()) { connect(widget, &TextEditorWidget::requestLinkAt, this, [document = textEditor->textDocument()] - (const QTextCursor &cursor, Utils::ProcessLinkCallback &callback, bool resolveTarget) { + (const QTextCursor &cursor, const Utils::LinkHandler &callback, bool resolveTarget) { if (auto client = clientForDocument(document)) client->symbolSupport().findLinkAt(document, cursor, callback, resolveTarget); }); diff --git a/src/plugins/languageclient/languageclientmanager.h b/src/plugins/languageclient/languageclientmanager.h index c5c8e9e86ad..d1b1467f6ab 100644 --- a/src/plugins/languageclient/languageclientmanager.h +++ b/src/plugins/languageclient/languageclientmanager.h @@ -62,7 +62,7 @@ public: static void clientStarted(Client *client); static void clientFinished(Client *client); static Client *startClient(const BaseSettings *setting, ProjectExplorer::Project *project = nullptr); - static QList clients(); + static const QList clients(); static void addClient(Client *client); static void addExclusiveRequest(const LanguageServerProtocol::MessageId &id, Client *client); diff --git a/src/plugins/languageclient/languageclientoutline.cpp b/src/plugins/languageclient/languageclientoutline.cpp index 68729ba5886..1ba12cee50a 100644 --- a/src/plugins/languageclient/languageclientoutline.cpp +++ b/src/plugins/languageclient/languageclientoutline.cpp @@ -266,7 +266,7 @@ void LanguageClientOutlineWidget::updateSelectionInTree(const QTextCursor &curre { if (LanguageClientOutlineItem *item = itemForCursor(m_model, currentCursor)) { const QModelIndex index = m_proxyModel.mapFromSource(m_model.indexForItem(item)); - m_view.selectionModel()->select(index, QItemSelectionModel::ClearAndSelect); + m_view.setCurrentIndex(index); m_view.scrollTo(index); } else { m_view.clearSelection(); diff --git a/src/plugins/languageclient/languageclientsettings.cpp b/src/plugins/languageclient/languageclientsettings.cpp index bf4c3b9917e..020d1c938da 100644 --- a/src/plugins/languageclient/languageclientsettings.cpp +++ b/src/plugins/languageclient/languageclientsettings.cpp @@ -581,7 +581,7 @@ Client *BaseSettings::createClient(ProjectExplorer::Project *project) const { if (!isValid() || !m_enabled) return nullptr; - BaseClientInterface *interface = createInterfaceWithProject(project); + BaseClientInterface *interface = createInterface(project); QTC_ASSERT(interface, return nullptr); auto *client = createClient(interface); client->setName(Utils::globalMacroExpander()->expand(m_name)); @@ -592,6 +592,11 @@ Client *BaseSettings::createClient(ProjectExplorer::Project *project) const return client; } +BaseClientInterface *BaseSettings::createInterface(ProjectExplorer::Project *) const +{ + return nullptr; +} + Client *BaseSettings::createClient(BaseClientInterface *interface) const { return new Client(interface); @@ -772,7 +777,7 @@ Utils::CommandLine StdIOSettings::command() const return Utils::CommandLine(m_executable, arguments(), Utils::CommandLine::Raw); } -BaseClientInterface *StdIOSettings::createInterfaceWithProject(ProjectExplorer::Project *project) const +BaseClientInterface *StdIOSettings::createInterface(ProjectExplorer::Project *project) const { auto interface = new StdIOClientInterface; interface->setCommandLine(command()); diff --git a/src/plugins/languageclient/languageclientsettings.h b/src/plugins/languageclient/languageclientsettings.h index b7ff578237f..c9a93414914 100644 --- a/src/plugins/languageclient/languageclientsettings.h +++ b/src/plugins/languageclient/languageclientsettings.h @@ -105,13 +105,8 @@ public: virtual void fromMap(const QVariantMap &map); protected: - // TODO: remove in Qt Creator 6 and rename createInterfaceWithProject back to it - virtual BaseClientInterface *createInterface() const { return nullptr; } + virtual BaseClientInterface *createInterface(ProjectExplorer::Project *) const; virtual Client *createClient(BaseClientInterface *interface) const; - virtual BaseClientInterface *createInterfaceWithProject(ProjectExplorer::Project *) const - { - return createInterface(); - } BaseSettings(const BaseSettings &other) = default; BaseSettings(BaseSettings &&other) = default; @@ -141,7 +136,7 @@ public: Utils::CommandLine command() const; protected: - BaseClientInterface *createInterfaceWithProject(ProjectExplorer::Project *project) const override; + BaseClientInterface *createInterface(ProjectExplorer::Project *project) const override; StdIOSettings(const StdIOSettings &other) = default; StdIOSettings(StdIOSettings &&other) = default; diff --git a/src/plugins/languageclient/languageclientsymbolsupport.cpp b/src/plugins/languageclient/languageclientsymbolsupport.cpp index b17a70a7d24..7e6dcdb6b2c 100644 --- a/src/plugins/languageclient/languageclientsymbolsupport.cpp +++ b/src/plugins/languageclient/languageclientsymbolsupport.cpp @@ -75,7 +75,7 @@ static void sendTextDocumentPositionParamsRequest(Client *client, } static void handleGotoDefinitionResponse(const GotoDefinitionRequest::Response &response, - Utils::ProcessLinkCallback callback, + Utils::LinkHandler callback, Utils::optional linkUnderCursor) { if (Utils::optional result = response.result()) { @@ -105,7 +105,7 @@ static TextDocumentPositionParams generateDocPosParams(TextEditor::TextDocument void SymbolSupport::findLinkAt(TextEditor::TextDocument *document, const QTextCursor &cursor, - Utils::ProcessLinkCallback callback, + Utils::LinkHandler callback, const bool resolveTarget) { if (!m_client->reachable()) diff --git a/src/plugins/languageclient/languageclientsymbolsupport.h b/src/plugins/languageclient/languageclientsymbolsupport.h index 6ba376a1d51..2959dbf50bf 100644 --- a/src/plugins/languageclient/languageclientsymbolsupport.h +++ b/src/plugins/languageclient/languageclientsymbolsupport.h @@ -51,7 +51,7 @@ public: void findLinkAt(TextEditor::TextDocument *document, const QTextCursor &cursor, - Utils::ProcessLinkCallback callback, + Utils::LinkHandler callback, const bool resolveTarget); using ResultHandler = std::function &)>; diff --git a/src/plugins/macros/actionmacrohandler.cpp b/src/plugins/macros/actionmacrohandler.cpp index 6238875b0d8..074f5458398 100644 --- a/src/plugins/macros/actionmacrohandler.cpp +++ b/src/plugins/macros/actionmacrohandler.cpp @@ -53,8 +53,8 @@ ActionMacroHandler::ActionMacroHandler() this, &ActionMacroHandler::addCommand); // Register all existing scriptable actions - QList commands = ActionManager::commands(); - foreach (Command *command, commands) { + const QList commands = ActionManager::commands(); + for (Command *command : commands) { if (command->isScriptable()) registerCommand(command->id()); } diff --git a/src/plugins/macros/macro.cpp b/src/plugins/macros/macro.cpp index a102d10040d..eda04527ed9 100644 --- a/src/plugins/macros/macro.cpp +++ b/src/plugins/macros/macro.cpp @@ -143,7 +143,7 @@ bool Macro::save(const QString &fileName, QWidget *parent) QDataStream stream(saver.file()); stream << d->version; stream << d->description; - foreach (const MacroEvent &event, d->events) { + for (const MacroEvent &event : qAsConst(d->events)) { event.save(stream); } saver.setResult(&stream); diff --git a/src/plugins/macros/macromanager.cpp b/src/plugins/macros/macromanager.cpp index 0b757c41546..e7669f8cc3c 100644 --- a/src/plugins/macros/macromanager.cpp +++ b/src/plugins/macros/macromanager.cpp @@ -131,9 +131,9 @@ void MacroManagerPrivate::initialize() const QDir dir(MacroManager::macrosDirectory()); QStringList filter; filter << QLatin1String("*.") + QLatin1String(Constants::M_EXTENSION); - QStringList files = dir.entryList(filter, QDir::Files); + const QStringList files = dir.entryList(filter, QDir::Files); - foreach (const QString &name, files) { + for (const QString &name : files) { QString fileName = dir.absolutePath() + QLatin1Char('/') + name; auto macro = new Macro; if (macro->loadHeader(fileName)) @@ -196,10 +196,11 @@ void MacroManagerPrivate::changeMacroDescription(Macro *macro, const QString &de bool MacroManagerPrivate::executeMacro(Macro *macro) { bool error = !macro->load(); - foreach (const MacroEvent ¯oEvent, macro->events()) { + const QList macroEvents = macro->events(); + for (const MacroEvent ¯oEvent : macroEvents) { if (error) break; - foreach (IMacroHandler *handler, handlers) { + for (IMacroHandler *handler : qAsConst(handlers)) { if (handler->canExecuteEvent(macroEvent)) { if (!handler->executeEvent(macroEvent)) error = true; @@ -255,8 +256,8 @@ MacroManager::MacroManager() : MacroManager::~MacroManager() { // Cleanup macro - QStringList macroList = d->macros.keys(); - foreach (const QString &name, macroList) + const QStringList macroList = d->macros.keys(); + for (const QString &name : macroList) d->removeMacro(name); // Cleanup handlers @@ -277,7 +278,7 @@ void MacroManager::startMacro() Core::ActionManager::command(Constants::END_MACRO)->action()->setEnabled(true); Core::ActionManager::command(Constants::EXECUTE_LAST_MACRO)->action()->setEnabled(false); Core::ActionManager::command(Constants::SAVE_LAST_MACRO)->action()->setEnabled(false); - foreach (IMacroHandler *handler, d->handlers) + for (IMacroHandler *handler : qAsConst(d->handlers)) handler->startRecording(d->currentMacro); const QString endShortcut = Core::ActionManager::command(Constants::END_MACRO) @@ -302,7 +303,7 @@ void MacroManager::endMacro() Core::ActionManager::command(Constants::END_MACRO)->action()->setEnabled(false); Core::ActionManager::command(Constants::EXECUTE_LAST_MACRO)->action()->setEnabled(true); Core::ActionManager::command(Constants::SAVE_LAST_MACRO)->action()->setEnabled(true); - foreach (IMacroHandler *handler, d->handlers) + for (IMacroHandler *handler : qAsConst(d->handlers)) handler->endRecordingMacro(d->currentMacro); d->isRecording = false; diff --git a/src/plugins/macros/macrooptionswidget.cpp b/src/plugins/macros/macrooptionswidget.cpp index 0408dd9377b..d3815cff078 100644 --- a/src/plugins/macros/macrooptionswidget.cpp +++ b/src/plugins/macros/macrooptionswidget.cpp @@ -130,7 +130,7 @@ void MacroOptionsWidget::remove() void MacroOptionsWidget::apply() { // Remove macro - foreach (const QString &name, m_macroToRemove) { + for (const QString &name : qAsConst(m_macroToRemove)) { MacroManager::instance()->deleteMacro(name); m_macroToChange.remove(name); } diff --git a/src/plugins/mcusupport/mcusupport_global.h b/src/plugins/mcusupport/mcusupport_global.h index a4a4daba46f..bb05bd0441d 100644 --- a/src/plugins/mcusupport/mcusupport_global.h +++ b/src/plugins/mcusupport/mcusupport_global.h @@ -48,7 +48,7 @@ using McuToolChainPackagePtr = QSharedPointer; using McuTargetPtr = QSharedPointer; static const QVersionNumber minimalVersion{2, 0, 0}; -static const QVersionNumber newVersion{2, 2}; +static const QVersionNumber newVersion{2, 3}; using Targets = QList; using Packages = QSet; diff --git a/src/plugins/mcusupport/test/unittest.cpp b/src/plugins/mcusupport/test/unittest.cpp index 0c5cb38dacb..e723d3cf0a0 100644 --- a/src/plugins/mcusupport/test/unittest.cpp +++ b/src/plugins/mcusupport/test/unittest.cpp @@ -598,8 +598,9 @@ void McuSupportTest::test_twoDotOneUsesLegacyImplementation() QCOMPARE(McuSupportOptions::isLegacyVersion({2, 0}), true); QCOMPARE(McuSupportOptions::isLegacyVersion({2, 0, 0}), true); QCOMPARE(McuSupportOptions::isLegacyVersion({2, 0, 1}), true); - QCOMPARE(McuSupportOptions::isLegacyVersion({2, 2, 0}), false); - QCOMPARE(McuSupportOptions::isLegacyVersion({2, 2, 1}), false); + QCOMPARE(McuSupportOptions::isLegacyVersion({2, 2, 0}), true); + QCOMPARE(McuSupportOptions::isLegacyVersion({2, 2, 1}), true); + QCOMPARE(McuSupportOptions::isLegacyVersion({2, 3, 0}), false); } void McuSupportTest::test_useFallbackPathForToolchainWhenPathFromSettingsIsNotAvailable() { diff --git a/src/plugins/mercurial/commiteditor.cpp b/src/plugins/mercurial/commiteditor.cpp index 84abca39824..39a88cd294a 100644 --- a/src/plugins/mercurial/commiteditor.cpp +++ b/src/plugins/mercurial/commiteditor.cpp @@ -62,7 +62,7 @@ void CommitEditor::setFields(const QFileInfo &repositoryRoot, const QString &bra QStringList shouldTrack; - foreach (const VcsBaseClient::StatusItem &item, repoStatus) { + for (const VcsBaseClient::StatusItem &item : repoStatus) { if (item.flags == QLatin1String("Untracked")) shouldTrack.append(item.file); else @@ -71,8 +71,8 @@ void CommitEditor::setFields(const QFileInfo &repositoryRoot, const QString &bra VcsBaseSubmitEditor::filterUntrackedFilesOfProject(fileModel->repositoryRoot(), &shouldTrack); - foreach (const QString &track, shouldTrack) { - foreach (const VcsBaseClient::StatusItem &item, repoStatus) { + for (const QString &track : qAsConst(shouldTrack)) { + for (const VcsBaseClient::StatusItem &item : repoStatus) { if (item.file == track) fileModel->addFile(item.file, item.flags, Unchecked); } diff --git a/src/plugins/mercurial/mercurialclient.cpp b/src/plugins/mercurial/mercurialclient.cpp index 21a50b6f1be..c4d7f89abb3 100644 --- a/src/plugins/mercurial/mercurialclient.cpp +++ b/src/plugins/mercurial/mercurialclient.cpp @@ -104,7 +104,7 @@ bool MercurialClient::manifestSync(const FilePath &repository, const QString &re const QFileInfo needle = QFileInfo(repositoryDir, relativeFilename); const QStringList files = proc.stdOut().split(QLatin1Char('\n')); - foreach (const QString &fileName, files) { + for (const QString &fileName : files) { const QFileInfo managedFile(repositoryDir, fileName); if (needle == managedFile) return true; diff --git a/src/plugins/mercurial/mercurialplugin.cpp b/src/plugins/mercurial/mercurialplugin.cpp index a177d410284..6cd0c778ec4 100644 --- a/src/plugins/mercurial/mercurialplugin.cpp +++ b/src/plugins/mercurial/mercurialplugin.cpp @@ -732,7 +732,7 @@ void MercurialPluginPrivate::updateActions(VcsBasePluginPrivate::ActionState as) revertFile->setParameter(filename); statusFile->setParameter(filename); - foreach (QAction *repoAction, m_repositoryActionList) + for (QAction *repoAction : qAsConst(m_repositoryActionList)) repoAction->setEnabled(repoEnabled); } diff --git a/src/plugins/mesonprojectmanager/project/mesonbuildconfiguration.cpp b/src/plugins/mesonprojectmanager/project/mesonbuildconfiguration.cpp index b603f856b35..291d5f05dc9 100644 --- a/src/plugins/mesonprojectmanager/project/mesonbuildconfiguration.cpp +++ b/src/plugins/mesonprojectmanager/project/mesonbuildconfiguration.cpp @@ -83,9 +83,8 @@ FilePath MesonBuildConfiguration::shadowBuildDirectory(const FilePath &projectFi return {}; const QString projectName = projectFilePath.parentDir().fileName(); - return BuildConfiguration::buildDirectoryFromTemplate( - Project::projectDirectory(projectFilePath), - projectFilePath, projectName, k, bcName, buildType, BuildConfiguration::ReplaceSpaces); + return buildDirectoryFromTemplate(Project::projectDirectory(projectFilePath), projectFilePath, + projectName, k, bcName, buildType, "meson"); } ProjectExplorer::BuildSystem *MesonBuildConfiguration::buildSystem() const diff --git a/src/plugins/modeleditor/componentviewcontroller.cpp b/src/plugins/modeleditor/componentviewcontroller.cpp index 1a3c4f0f0ac..11428e64de0 100644 --- a/src/plugins/modeleditor/componentviewcontroller.cpp +++ b/src/plugins/modeleditor/componentviewcontroller.cpp @@ -168,11 +168,12 @@ void UpdateIncludeDependenciesVisitor::visitMComponent(qmt::MComponent *componen CppEditor::CppModelManager *cppModelManager = CppEditor::CppModelManager::instance(); CPlusPlus::Snapshot snapshot = cppModelManager->snapshot(); - QStringList filePaths = findFilePathOfComponent(component); - foreach (const QString &filePath, filePaths) { + const QStringList filePaths = findFilePathOfComponent(component); + for (const QString &filePath : filePaths) { CPlusPlus::Document::Ptr document = snapshot.document(filePath); if (document) { - foreach (const CPlusPlus::Document::Include &include, document->resolvedIncludes()) { + const QList includes = document->resolvedIncludes(); + for (const CPlusPlus::Document::Include &include : includes) { QString includeFilePath = include.resolvedFileName(); // replace proxy header with real one CPlusPlus::Document::Ptr includeDocument = snapshot.document(includeFilePath); @@ -214,7 +215,8 @@ QStringList UpdateIncludeDependenciesVisitor::findFilePathOfComponent(const qmt: } QStringList bestFilePaths; int maxPathLength = 1; - foreach (const Node &node, m_filePaths.values(component->name())) { + const QList nodes = m_filePaths.values(component->name()); + for (const Node &node : nodes) { int i = elementPath.size() - 1; int j = node.m_elementPath.size() - 1; while (i >= 0 && j >= 0 && elementPath.at(i) == node.m_elementPath.at(j)) { @@ -235,14 +237,16 @@ QStringList UpdateIncludeDependenciesVisitor::findFilePathOfComponent(const qmt: void UpdateIncludeDependenciesVisitor::collectElementPaths(const ProjectExplorer::FolderNode *folderNode, QMultiHash *filePathsMap) { - foreach (const ProjectExplorer::FileNode *fileNode, folderNode->fileNodes()) { + const QList fileNodes = folderNode->fileNodes(); + for (const ProjectExplorer::FileNode *fileNode : fileNodes) { QString elementName = qmt::NameController::convertFileNameToElementName(fileNode->filePath().toString()); QFileInfo fileInfo = fileNode->filePath().toFileInfo(); QString nodePath = fileInfo.path(); QStringList elementsPath = qmt::NameController::buildElementsPath(nodePath, false); filePathsMap->insert(elementName, Node(fileNode->filePath().toString(), elementsPath)); } - foreach (const ProjectExplorer::FolderNode *subNode, folderNode->folderNodes()) + const QList subNodes = folderNode->folderNodes(); + for (const ProjectExplorer::FolderNode *subNode : subNodes) collectElementPaths(subNode, filePathsMap); } diff --git a/src/plugins/modeleditor/elementtasks.cpp b/src/plugins/modeleditor/elementtasks.cpp index 82cad867fd5..0672d7d43d2 100644 --- a/src/plugins/modeleditor/elementtasks.cpp +++ b/src/plugins/modeleditor/elementtasks.cpp @@ -114,9 +114,9 @@ bool ElementTasks::hasClassDefinition(const qmt::MElement *element) const return false; QFutureInterface dummyInterface; - QList matches = classesFilter->matchesFor(dummyInterface, - qualifiedClassName); - foreach (const Core::LocatorFilterEntry &entry, matches) { + const QList matches + = classesFilter->matchesFor(dummyInterface, qualifiedClassName); + for (const Core::LocatorFilterEntry &entry : matches) { CppEditor::IndexItem::Ptr info = qvariant_cast(entry.internalData); if (info->scopedSymbolName() != qualifiedClassName) continue; @@ -151,8 +151,9 @@ void ElementTasks::openClassDefinition(const qmt::MElement *element) return; QFutureInterface dummyInterface; - QList matches = classesFilter->matchesFor(dummyInterface, qualifiedClassName); - foreach (const Core::LocatorFilterEntry &entry, matches) { + const QList matches + = classesFilter->matchesFor(dummyInterface, qualifiedClassName); + for (const Core::LocatorFilterEntry &entry : matches) { CppEditor::IndexItem::Ptr info = qvariant_cast(entry.internalData); if (info->scopedSymbolName() != qualifiedClassName) continue; diff --git a/src/plugins/modeleditor/modeleditor.cpp b/src/plugins/modeleditor/modeleditor.cpp index 286e5b560f0..9c641ddb171 100644 --- a/src/plugins/modeleditor/modeleditor.cpp +++ b/src/plugins/modeleditor/modeleditor.cpp @@ -745,7 +745,8 @@ void ModelEditor::updateSelectedArea(SelectedArea selectedArea) if (hasSelection) { qmt::DSelection selection = documentController->diagramsManager()->diagramSceneModel(activeDiagram)->selectedElements(); if (!selection.isEmpty()) { - foreach (qmt::DSelection::Index index, selection.indices()) { + const QList indexes = selection.indices(); + for (qmt::DSelection::Index index : indexes) { qmt::DElement *diagramElement = documentController->diagramController()->findElement(index.elementKey(), activeDiagram); if (diagramElement) propertiesDiagramElements.append(diagramElement); @@ -765,9 +766,9 @@ void ModelEditor::updateSelectedArea(SelectedArea selectedArea) canPaste = hasSingleSelection && !modelsManager->isModelClipboardEmpty(); canSelectAll = activeDiagram && !activeDiagram->diagramElements().isEmpty(); canExportDiagram = activeDiagram != nullptr; - QModelIndexList indexes = d->modelTreeView->selectedSourceModelIndexes(); + const QModelIndexList indexes = d->modelTreeView->selectedSourceModelIndexes(); if (!indexes.isEmpty()) { - foreach (const QModelIndex &propertiesIndex, indexes) { + for (const QModelIndex &propertiesIndex : indexes) { if (propertiesIndex.isValid()) { qmt::MElement *modelElement = documentController->treeModel()->element(propertiesIndex); if (modelElement) @@ -1065,7 +1066,7 @@ void ModelEditor::initToolbars() QList toolbars = stereotypeController->toolbars(); std::stable_sort(toolbars.begin(), toolbars.end(), [=](const qmt::Toolbar &lhs, const qmt::Toolbar &rhs) { return lhs.priority() > rhs.priority(); }); - foreach (const qmt::Toolbar &toolbar, toolbars) { + for (const qmt::Toolbar &toolbar : qAsConst(toolbars)) { QWidget *toolBar = toolBars.value(toolbar.id()); QLayout *toolBarLayout = nullptr; if (!toolBar) { @@ -1081,7 +1082,8 @@ void ModelEditor::initToolbars() toolBarLayout = toolBar->layout(); QMT_ASSERT(toolBarLayout, continue); } - foreach (const qmt::Toolbar::Tool &tool, toolbar.tools()) { + const QList tools = toolbar.tools(); + for (const qmt::Toolbar::Tool &tool : tools) { switch (tool.m_toolType) { case qmt::Toolbar::TooltypeTool: { @@ -1193,7 +1195,7 @@ void ModelEditor::initToolbars() // add stretch to all layouts and calculate width of tool bar int maxWidth = 48; - foreach (QWidget *toolBar, toolBars) { + for (QWidget *toolBar : qAsConst(toolBars)) { QMT_ASSERT(toolBar, continue); auto layout = qobject_cast(toolBar->layout()); QMT_ASSERT(layout, continue); @@ -1442,13 +1444,14 @@ void ModelEditor::synchronizeDiagramWithBrowser() if (currentDiagram()) { bool done = false; qmt::DocumentController *documentController = d->document->documentController(); - QModelIndexList indexes = d->modelTreeView->selectedSourceModelIndexes(); + const QModelIndexList indexes = d->modelTreeView->selectedSourceModelIndexes(); if (!indexes.isEmpty()) { - foreach (const QModelIndex &index, indexes) { + for (const QModelIndex &index : indexes) { if (index.isValid()) { qmt::MElement *modelElement = documentController->treeModel()->element(index); if (modelElement) { - foreach (qmt::DElement *diagramElement, currentDiagram()->diagramElements()) { + const QList diagramElements = currentDiagram()->diagramElements(); + for (qmt::DElement *diagramElement : diagramElements) { if (diagramElement->modelUid() == modelElement->uid()) { // disconnect temporarily avoiding double update of properties Ui disconnect(documentController->diagramsManager(), &qmt::DiagramsManager::diagramSelectionChanged, @@ -1479,7 +1482,8 @@ void ModelEditor::synchronizeBrowserWithDiagram(const qmt::MDiagram *diagram) qmt::DocumentController *documentController = d->document->documentController(); qmt::DSelection selection = documentController->diagramsManager()->diagramSceneModel(diagram)->selectedElements(); if (!selection.isEmpty()) { - foreach (qmt::DSelection::Index index, selection.indices()) { + const QList indexes = selection.indices(); + for (qmt::DSelection::Index index : indexes) { qmt::DElement *diagramElement = documentController->diagramController()->findElement(index.elementKey(), diagram); if (diagramElement) { qmt::MElement *modelElement = documentController->modelController()->findElement(diagramElement->modelUid()); diff --git a/src/plugins/modeleditor/modelindexer.cpp b/src/plugins/modeleditor/modelindexer.cpp index 622e7204cb3..322a873f86b 100644 --- a/src/plugins/modeleditor/modelindexer.cpp +++ b/src/plugins/modeleditor/modelindexer.cpp @@ -431,13 +431,15 @@ void ModelIndexer::scanProject(ProjectExplorer::Project *project) } // remove deleted files from indexed models - foreach (const QString &file, d->indexedModels.keys()) { + const QStringList files = d->indexedModels.keys(); + for (const QString &file : files) { if (!filesSet.contains(QueuedFile(file, project))) removeModelFile(file, project); } // remove deleted files from indexed diagrams - foreach (const QString &file, d->indexedDiagramReferences.keys()) { + const QStringList deletedFiles = d->indexedDiagramReferences.keys(); + for (const QString &file : deletedFiles) { if (!filesSet.contains(QueuedFile(file, project))) removeDiagramReferenceFile(file, project); } @@ -468,11 +470,13 @@ QString ModelIndexer::findFirstModel(ProjectExplorer::FolderNode *folderNode, { if (!mimeType.isValid()) return QString(); - foreach (ProjectExplorer::FileNode *fileNode, folderNode->fileNodes()) { + const QList fileNodes = folderNode->fileNodes(); + for (const ProjectExplorer::FileNode *fileNode : fileNodes) { if (mimeType.suffixes().contains(fileNode->filePath().completeSuffix())) return fileNode->filePath().toString(); } - foreach (ProjectExplorer::FolderNode *subFolderNode, folderNode->folderNodes()) { + const QList subFolderNodes = folderNode->folderNodes(); + for (ProjectExplorer::FolderNode *subFolderNode : subFolderNodes) { QString modelFileName = findFirstModel(subFolderNode, mimeType); if (!modelFileName.isEmpty()) return modelFileName; diff --git a/src/plugins/modeleditor/modelsmanager.cpp b/src/plugins/modeleditor/modelsmanager.cpp index a4a2a1e3da5..09375871765 100644 --- a/src/plugins/modeleditor/modelsmanager.cpp +++ b/src/plugins/modeleditor/modelsmanager.cpp @@ -167,7 +167,7 @@ void ModelsManager::releaseModel(ExtDocumentController *documentController) void ModelsManager::openDiagram(const qmt::Uid &modelUid, const qmt::Uid &diagramUid) { - foreach (const ManagedModel &managedModel, d->managedModels) { + for (const ManagedModel &managedModel : qAsConst(d->managedModels)) { if (managedModel.m_documentController->projectController()->project()->uid() == modelUid) { qmt::MDiagram *diagram = managedModel.m_documentController->modelController()->findObject(diagramUid); QMT_ASSERT(diagram, continue); @@ -230,7 +230,7 @@ void ModelsManager::onAboutToShowContextMenu(ProjectExplorer::Node *node) { bool canOpenDiagram = false; - foreach (const ManagedModel &managedModel, d->managedModels) { + for (const ManagedModel &managedModel : qAsConst(d->managedModels)) { if (managedModel.m_documentController->pxNodeController()->hasDiagramForExplorerNode(node)) { canOpenDiagram = true; break; @@ -248,7 +248,7 @@ void ModelsManager::onOpenDiagramFromProjectExplorer() { if (ProjectExplorer::ProjectTree::currentNode() == d->contextMenuOwnerNode) { qmt::MDiagram *diagram = nullptr; - foreach (const ManagedModel &managedModel, d->managedModels) { + for (const ManagedModel &managedModel : qAsConst(d->managedModels)) { if ((diagram = managedModel.m_documentController->pxNodeController()->findDiagramForExplorerNode(d->contextMenuOwnerNode))) { openDiagram(managedModel.m_documentController, diagram); break; @@ -267,7 +267,7 @@ void ModelsManager::onOpenDefaultModel(const qmt::Uid &modelUid) void ModelsManager::openDiagram(ExtDocumentController *documentController, qmt::MDiagram *diagram) { - foreach (const ManagedModel &managedModel, d->managedModels) { + for (const ManagedModel &managedModel : qAsConst(d->managedModels)) { if (managedModel.m_documentController == documentController) { Core::IEditor *editor = Core::EditorManager::activateEditorForDocument(managedModel.m_modelDocument); if (auto modelEditor = qobject_cast(editor)) { diff --git a/src/plugins/modeleditor/modelutilities.cpp b/src/plugins/modeleditor/modelutilities.cpp index 58c8d7d851d..2232d837df1 100644 --- a/src/plugins/modeleditor/modelutilities.cpp +++ b/src/plugins/modeleditor/modelutilities.cpp @@ -74,7 +74,7 @@ bool ModelUtilities::haveDependency(const qmt::MObject *source, bool ModelUtilities::haveDependency(const qmt::MObject *source, const QList &targets) { - foreach (const qmt::MPackage *target, targets) { + for (const qmt::MPackage *target : targets) { if (haveDependency(source, target)) return true; } diff --git a/src/plugins/modeleditor/pxnodecontroller.cpp b/src/plugins/modeleditor/pxnodecontroller.cpp index fba2f1c8cab..5761b727152 100644 --- a/src/plugins/modeleditor/pxnodecontroller.cpp +++ b/src/plugins/modeleditor/pxnodecontroller.cpp @@ -156,11 +156,12 @@ void PxNodeController::addFileSystemEntry(const QString &filePath, int line, int auto menu = new QMenu; menu->addAction(new MenuAction(tr("Add Component %1").arg(elementName), elementName, MenuAction::TYPE_ADD_COMPONENT, menu)); - QStringList classNames = Utils::toList(d->classViewController->findClassDeclarations(filePath, line, column)); + const QStringList classNames = Utils::toList( + d->classViewController->findClassDeclarations(filePath, line, column)); if (!classNames.empty()) { menu->addSeparator(); int index = 0; - foreach (const QString &className, classNames) { + for (const QString &className : classNames) { auto action = new MenuAction(tr("Add Class %1").arg(className), elementName, MenuAction::TYPE_ADD_CLASS, index, menu); action->className = className; diff --git a/src/plugins/nim/editor/nimtexteditorwidget.cpp b/src/plugins/nim/editor/nimtexteditorwidget.cpp index 7ec1791c962..8f0c517ac71 100644 --- a/src/plugins/nim/editor/nimtexteditorwidget.cpp +++ b/src/plugins/nim/editor/nimtexteditorwidget.cpp @@ -60,7 +60,7 @@ NimTextEditorWidget::NimTextEditorWidget(QWidget *parent) setLanguageSettingsId(Nim::Constants::C_NIMLANGUAGE_ID); } -void NimTextEditorWidget::findLinkAt(const QTextCursor &c, Utils::ProcessLinkCallback &&processLinkCallback, bool /*resolveTarget*/, bool /*inNextSplit*/) +void NimTextEditorWidget::findLinkAt(const QTextCursor &c, const Utils::LinkHandler &processLinkCallback, bool /*resolveTarget*/, bool /*inNextSplit*/) { const Utils::FilePath &path = textDocument()->filePath(); @@ -90,7 +90,7 @@ void NimTextEditorWidget::findLinkAt(const QTextCursor &c, Utils::ProcessLinkCal m_callback(Utils::Link()); m_dirtyFile = std::move(dirtyFile); - m_callback = std::move(processLinkCallback); + m_callback = processLinkCallback; m_request = std::move(request); QObject::connect(m_request.get(), &NimSuggestClientRequest::finished, this, &NimTextEditorWidget::onFindLinkFinished); diff --git a/src/plugins/nim/editor/nimtexteditorwidget.h b/src/plugins/nim/editor/nimtexteditorwidget.h index 8cbc207ebc1..097e81507ca 100644 --- a/src/plugins/nim/editor/nimtexteditorwidget.h +++ b/src/plugins/nim/editor/nimtexteditorwidget.h @@ -36,13 +36,13 @@ public: NimTextEditorWidget(QWidget* parent = nullptr); protected: - void findLinkAt(const QTextCursor &, Utils::ProcessLinkCallback &&processLinkCallback, bool resolveTarget, bool inNextSplit); + void findLinkAt(const QTextCursor &, const Utils::LinkHandler &processLinkCallback, bool resolveTarget, bool inNextSplit); private: void onFindLinkFinished(); std::shared_ptr m_request; - Utils::ProcessLinkCallback m_callback; + Utils::LinkHandler m_callback; std::unique_ptr m_dirtyFile; }; diff --git a/src/plugins/nim/project/nimbuildconfiguration.cpp b/src/plugins/nim/project/nimbuildconfiguration.cpp index 48d91b40a4a..37b2e0d9ef1 100644 --- a/src/plugins/nim/project/nimbuildconfiguration.cpp +++ b/src/plugins/nim/project/nimbuildconfiguration.cpp @@ -53,7 +53,7 @@ static FilePath defaultBuildDirectory(const Kit *k, { return BuildConfiguration::buildDirectoryFromTemplate( projectFilePath.parentDir(), projectFilePath, projectFilePath.baseName(), - k, bc, buildType); + k, bc, buildType, "nim"); } NimBuildConfiguration::NimBuildConfiguration(Target *target, Utils::Id id) diff --git a/src/plugins/perforce/perforceplugin.cpp b/src/plugins/perforce/perforceplugin.cpp index 1b5d2a03f29..b415b219674 100644 --- a/src/plugins/perforce/perforceplugin.cpp +++ b/src/plugins/perforce/perforceplugin.cpp @@ -724,7 +724,8 @@ void PerforcePluginPrivate::printOpenedFileList() QString errorMessage; QString mapped; const QChar delimiter = QLatin1Char('#'); - foreach (const QString &line, perforceResponse.stdOut.split(QLatin1Char('\n'))) { + const QStringList lines = perforceResponse.stdOut.split(QLatin1Char('\n')); + for (const QString &line : lines) { mapped.clear(); const int delimiterPos = line.indexOf(delimiter); if (delimiterPos > 0) @@ -787,9 +788,9 @@ void PerforcePluginPrivate::startSubmitProject() return; } - QStringList filesLines = filesResult.stdOut.split(QLatin1Char('\n')); + const QStringList filesLines = filesResult.stdOut.split(QLatin1Char('\n')); QStringList depotFileNames; - foreach (const QString &line, filesLines) { + for (const QString &line : filesLines) { depotFileNames.append(line.left(line.lastIndexOf(QRegularExpression("#[0-9]+\\s-\\s")))); } if (depotFileNames.isEmpty()) { diff --git a/src/plugins/perforce/perforcesubmiteditor.cpp b/src/plugins/perforce/perforcesubmiteditor.cpp index 115be50ad6f..a26d71ee8b9 100644 --- a/src/plugins/perforce/perforcesubmiteditor.cpp +++ b/src/plugins/perforce/perforcesubmiteditor.cpp @@ -125,7 +125,7 @@ void PerforceSubmitEditor::updateFields() lines = m_entries.value(QLatin1String("Files")).split(newLine); // split up "file#add" and store complete spec line as user data - foreach (const QString &specLine, lines) { + for (const QString &specLine : qAsConst(lines)) { const QStringList list = specLine.split(QLatin1Char('#')); if (list.size() == 2) { const QString file = list.at(0).trimmed(); diff --git a/src/plugins/projectexplorer/buildconfiguration.cpp b/src/plugins/projectexplorer/buildconfiguration.cpp index 6d584e803ee..10758d451e0 100644 --- a/src/plugins/projectexplorer/buildconfiguration.cpp +++ b/src/plugins/projectexplorer/buildconfiguration.cpp @@ -605,7 +605,7 @@ FilePath BuildConfiguration::buildDirectoryFromTemplate(const FilePath &projectD const Kit *kit, const QString &bcName, BuildType buildType, - SpaceHandling spaceHandling) + const QString &buildSystem) { MacroExpander exp; @@ -631,6 +631,10 @@ FilePath BuildConfiguration::buildDirectoryFromTemplate(const FilePath &projectD QCoreApplication::translate( "ProjectExplorer", "Name of the project's active build configuration"), [bcName] { return bcName; }); + exp.registerVariable("BuildSystem:Name", + QCoreApplication::translate( + "ProjectExplorer", "Name of the project's active build system"), + [buildSystem] { return buildSystem; }); exp.registerVariable("CurrentBuild:Type", QCoreApplication::translate("ProjectExplorer", "Type of current build"), [buildType] { return buildTypeName(buildType); }, false); @@ -644,8 +648,7 @@ FilePath BuildConfiguration::buildDirectoryFromTemplate(const FilePath &projectD qCDebug(bcLog) << "build dir template:" << buildDir; buildDir = exp.expand(buildDir); qCDebug(bcLog) << "expanded build:" << buildDir; - if (spaceHandling == ReplaceSpaces) - buildDir.replace(" ", "-"); + buildDir.replace(" ", "-"); return projectDir.resolvePath(buildDir); } diff --git a/src/plugins/projectexplorer/buildconfiguration.h b/src/plugins/projectexplorer/buildconfiguration.h index 5a8c15e7da6..32b505f3ba5 100644 --- a/src/plugins/projectexplorer/buildconfiguration.h +++ b/src/plugins/projectexplorer/buildconfiguration.h @@ -110,14 +110,13 @@ public: static QString buildTypeName(BuildType type); - enum SpaceHandling { KeepSpace, ReplaceSpaces }; static Utils::FilePath buildDirectoryFromTemplate(const Utils::FilePath &projectDir, const Utils::FilePath &mainFilePath, const QString &projectName, const Kit *kit, const QString &bcName, BuildType buildType, - SpaceHandling spaceHandling = ReplaceSpaces); + const QString &buildSystem); bool isActive() const; diff --git a/src/plugins/projectexplorer/deploymentdataview.cpp b/src/plugins/projectexplorer/deploymentdataview.cpp index 689c351125f..3e586938e7f 100644 --- a/src/plugins/projectexplorer/deploymentdataview.cpp +++ b/src/plugins/projectexplorer/deploymentdataview.cpp @@ -84,11 +84,10 @@ public: bool isEditable = false; }; - DeploymentDataView::DeploymentDataView(DeployConfiguration *dc) { auto model = new TreeModel(this); - model->setHeader({tr("Local File Path"), tr("Remote Directory")}); + model->setHeader({tr("Source File Path"), tr("Target Directory")}); auto view = new QTreeView(this); view->setMinimumSize(QSize(100, 100)); diff --git a/src/plugins/projectexplorer/devicesupport/devicemanager.cpp b/src/plugins/projectexplorer/devicesupport/devicemanager.cpp index e97bd096969..deb96f14291 100644 --- a/src/plugins/projectexplorer/devicesupport/devicemanager.cpp +++ b/src/plugins/projectexplorer/devicesupport/devicemanager.cpp @@ -595,6 +595,12 @@ DeviceManager::DeviceManager(bool isInstance) : d(std::make_uniquebytesAvailable(filePath); }; + deviceHooks.deviceDisplayName = [](const FilePath &filePath) { + auto device = DeviceManager::deviceForPath(filePath); + QTC_ASSERT(device, return QString()); + return device->displayName(); + }; + FileUtils::setDeviceFileHooks(deviceHooks); DeviceProcessHooks processHooks; diff --git a/src/plugins/projectexplorer/devicesupport/sshparameters.cpp b/src/plugins/projectexplorer/devicesupport/sshparameters.cpp index 224a4c6595d..90d650c1016 100644 --- a/src/plugins/projectexplorer/devicesupport/sshparameters.cpp +++ b/src/plugins/projectexplorer/devicesupport/sshparameters.cpp @@ -86,7 +86,7 @@ QStringList SshParameters::connectionOptions(const FilePath &binary) const bool SshParameters::setupSshEnvironment(QtcProcess *process) { Environment env = process->controlEnvironment(); - if (env.size() == 0) + if (!env.isValid()) env = Environment::systemEnvironment(); const bool hasDisplay = env.hasKey("DISPLAY") && (env.value("DISPLAY") != QString(":0")); if (SshSettings::askpassFilePath().exists()) { diff --git a/src/plugins/projectexplorer/gccparser.cpp b/src/plugins/projectexplorer/gccparser.cpp index 4ec61aeef6b..36076e8e764 100644 --- a/src/plugins/projectexplorer/gccparser.cpp +++ b/src/plugins/projectexplorer/gccparser.cpp @@ -126,8 +126,8 @@ void GccParser::createOrAmendTask( // If a "required from here" line is present, it is almost always the cause of the problem, // so that's where we should go when the issue is double-clicked. - if ((originalLine.endsWith("required from here") || originalLine.endsWith("requested here")) - && !file.isEmpty() && line > 0) { + if ((originalLine.endsWith("required from here") || originalLine.endsWith("requested here") + || originalLine.endsWith("note: here")) && !file.isEmpty() && line > 0) { m_requiredFromHereFound = true; m_currentTask.setFile(file); m_currentTask.line = line; @@ -1398,6 +1398,39 @@ void ProjectExplorerPlugin::testGccOutputParsers_data() FilePath::fromUserInput("tst_addresscache.cpp"), 79, 13, {})} << QString(); + QTest::newRow(R"("note: here")") + << QString( + "In file included from qmlprofilerstatisticsmodel.h:31,\n" + " from qmlprofilerstatisticsmodel.cpp:26:\n" + "qmlprofilerstatisticsmodel.cpp: In member function ‘virtual QVariant QmlProfiler::QmlProfilerStatisticsModel::data(const QModelIndex&, int) const’:\n" + "qtcassert.h:43:34: warning: this statement may fall through [-Wimplicit-fallthrough=]\n" + " 43 | #define QTC_ASSERT(cond, action) if (Q_LIKELY(cond)) {} else { QTC_ASSERT_STRING(#cond); action; } do {} while (0)\n" + " | ^~\n" + "qtcassert.h:43:34: note: in definition of macro ‘QTC_ASSERT’\n" + " 43 | #define QTC_ASSERT(cond, action) if (Q_LIKELY(cond)) {} else { QTC_ASSERT_STRING(#cond); action; } do {} while (0)\n" + " | ^~\n" + "qmlprofilerstatisticsmodel.cpp:365:5: note: here\n" + " 365 | default:\n" + " | ^~~~~~~") + << OutputParserTester::STDERR + << QString() << QString() + << Tasks{compileTask(Task::Warning, + "this statement may fall through [-Wimplicit-fallthrough=]\n" + "In file included from qmlprofilerstatisticsmodel.h:31,\n" + " from qmlprofilerstatisticsmodel.cpp:26:\n" + "qmlprofilerstatisticsmodel.cpp: In member function ‘virtual QVariant QmlProfiler::QmlProfilerStatisticsModel::data(const QModelIndex&, int) const’:\n" + "qtcassert.h:43:34: warning: this statement may fall through [-Wimplicit-fallthrough=]\n" + " 43 | #define QTC_ASSERT(cond, action) if (Q_LIKELY(cond)) {} else { QTC_ASSERT_STRING(#cond); action; } do {} while (0)\n" + " | ^~\n" + "qtcassert.h:43:34: note: in definition of macro ‘QTC_ASSERT’\n" + " 43 | #define QTC_ASSERT(cond, action) if (Q_LIKELY(cond)) {} else { QTC_ASSERT_STRING(#cond); action; } do {} while (0)\n" + " | ^~\n" + "qmlprofilerstatisticsmodel.cpp:365:5: note: here\n" + " 365 | default:\n" + " | ^~~~~~~", + FilePath::fromUserInput("qmlprofilerstatisticsmodel.cpp"), 365, 5, {})} + << QString(); + QTest::newRow("cc1plus") << QString( "cc1plus: error: one or more PCH files were found, but they were invalid\n" diff --git a/src/plugins/projectexplorer/msvctoolchain.cpp b/src/plugins/projectexplorer/msvctoolchain.cpp index bfafaa83f65..d6eaa3bbaf7 100644 --- a/src/plugins/projectexplorer/msvctoolchain.cpp +++ b/src/plugins/projectexplorer/msvctoolchain.cpp @@ -1145,7 +1145,7 @@ ToolChain::BuiltInHeaderPathsRunner MsvcToolChain::createBuiltInHeaderPathsRunne void MsvcToolChain::addToEnvironment(Utils::Environment &env) const { // We cache the full environment (incoming + modifications by setup script). - if (!m_resultEnvironment.size() || env != m_lastEnvironment) { + if (m_resultEnvironment.isValid() || env != m_lastEnvironment) { qCDebug(Log) << "addToEnvironment: " << displayName(); m_lastEnvironment = env; m_resultEnvironment = readEnvironmentSetting(env); @@ -2121,7 +2121,7 @@ Utils::optional MsvcToolChain::generateEnvironmentSettings(const Utils: // Windows SDK setup scripts require command line switches for environment expansion. CommandLine cmd(cmdPath, {"/E:ON", "/V:ON", "/c", saver.filePath().toUserOutput()}); qCDebug(Log) << "readEnvironmentSetting: " << call << cmd.toUserOutput() - << " Env: " << runEnv.size(); + << " Env: " << runEnv.toStringList().size(); run.setCodec(QTextCodec::codecForName("UTF-8")); run.setCommand(cmd); run.runBlocking(); diff --git a/src/plugins/projectexplorer/project.cpp b/src/plugins/projectexplorer/project.cpp index 9ffd402d3ed..020f04508e3 100644 --- a/src/plugins/projectexplorer/project.cpp +++ b/src/plugins/projectexplorer/project.cpp @@ -454,7 +454,8 @@ bool Project::copySteps(Target *sourceTarget, Target *newTarget) newBc->setBuildDirectory(BuildConfiguration::buildDirectoryFromTemplate( project->projectDirectory(), project->projectFilePath(), project->displayName(), newTarget->kit(), - sourceBc->displayName(), sourceBc->buildType())); + sourceBc->displayName(), sourceBc->buildType(), + sourceBc->buildSystem()->name())); newTarget->addBuildConfiguration(newBc); if (sourceTarget->activeBuildConfiguration() == sourceBc) SessionManager::setActiveBuildConfiguration(newTarget, newBc, SetActive::NoCascade); diff --git a/src/plugins/projectexplorer/runcontrol.cpp b/src/plugins/projectexplorer/runcontrol.cpp index 910e8271615..bd7714546a8 100644 --- a/src/plugins/projectexplorer/runcontrol.cpp +++ b/src/plugins/projectexplorer/runcontrol.cpp @@ -1520,7 +1520,7 @@ void SimpleTargetRunnerPrivate::forwardDone() { if (m_stopReported) return; - const QString executable = m_command.executable().toUserOutput(); + const QString executable = m_command.executable().displayName(); QString msg = tr("%1 exited with code %2").arg(executable).arg(m_resultData.m_exitCode); if (m_resultData.m_exitStatus == QProcess::CrashExit) msg = tr("%1 crashed.").arg(executable); @@ -1570,7 +1570,7 @@ void SimpleTargetRunner::start() d->m_process.setTerminalMode(useTerminal ? Utils::TerminalMode::On : Utils::TerminalMode::Off); d->m_runAsRoot = runAsRoot; - const QString msg = RunControl::tr("Starting %1...").arg(d->m_command.toUserOutput()); + const QString msg = RunControl::tr("Starting %1...").arg(d->m_command.displayName()); appendMessage(msg, NormalMessageFormat); const bool isDesktop = !d->m_command.executable().needsDevice(); diff --git a/src/plugins/projectexplorer/sanitizerparser.cpp b/src/plugins/projectexplorer/sanitizerparser.cpp index da8264e6759..09a0d864549 100644 --- a/src/plugins/projectexplorer/sanitizerparser.cpp +++ b/src/plugins/projectexplorer/sanitizerparser.cpp @@ -29,6 +29,7 @@ #include +#include #include #ifdef WITH_TESTS @@ -134,6 +135,12 @@ void SanitizerParser::flush() return; setDetailsFormat(m_task, m_linkSpecs); + static const int maxLen = 50; + if (m_task.details.length() > maxLen) { + const auto cutOffIt = std::next(m_task.details.begin(), maxLen); + m_task.details.insert(cutOffIt, "..."); + m_task.details.erase(std::next(cutOffIt), std::prev(m_task.details.end())); + } scheduleTask(m_task, m_task.details.count()); m_task.clear(); m_linkSpecs.clear(); diff --git a/src/plugins/python/pythonconstants.h b/src/plugins/python/pythonconstants.h index 330f073ebcb..76f4ca2bb52 100644 --- a/src/plugins/python/pythonconstants.h +++ b/src/plugins/python/pythonconstants.h @@ -49,6 +49,7 @@ const char PYLS_SETTINGS_ID[] = "Python.PyLSSettingsID"; * MIME type ******************************************************************************/ const char C_PY_MIMETYPE[] = "text/x-python"; +const char C_PY3_MIMETYPE[] = "text/x-python3"; const char C_PY_MIME_ICON[] = "text-x-python"; } // namespace Constants diff --git a/src/plugins/python/pythonlanguageclient.cpp b/src/plugins/python/pythonlanguageclient.cpp index 9479fc0c1a1..f40cc8689c6 100644 --- a/src/plugins/python/pythonlanguageclient.cpp +++ b/src/plugins/python/pythonlanguageclient.cpp @@ -389,7 +389,8 @@ PyLSSettings::PyLSSettings() m_settingsTypeId = Constants::PYLS_SETTINGS_ID; m_name = "Python Language Server"; m_startBehavior = RequiresFile; - m_languageFilter.mimeTypes = QStringList(Constants::C_PY_MIMETYPE); + m_languageFilter.mimeTypes = QStringList() + << Constants::C_PY_MIMETYPE << Constants::C_PY3_MIMETYPE; m_arguments = "-m pylsp"; const QJsonDocument config(defaultConfiguration()); m_configuration = QString::fromUtf8(config.toJson()); @@ -416,6 +417,8 @@ void PyLSSettings::fromMap(const QVariantMap &map) const QJsonDocument config(defaultConfiguration()); m_configuration = QString::fromUtf8(config.toJson()); } + m_languageFilter.mimeTypes = QStringList() + << Constants::C_PY_MIMETYPE << Constants::C_PY3_MIMETYPE; setInterpreter(map[interpreterKey].toString()); } @@ -477,8 +480,7 @@ public: TemporaryDirectory m_extraPythonPath; }; -BaseClientInterface *PyLSSettings::createInterfaceWithProject( - ProjectExplorer::Project *project) const +BaseClientInterface *PyLSSettings::createInterface(ProjectExplorer::Project *project) const { auto interface = new PyLSInterface; interface->setCommandLine(command()); @@ -640,7 +642,8 @@ static Client *registerLanguageServer(const FilePath &python) auto *settings = new StdIOSettings(); settings->m_executable = python; settings->m_arguments = "-m pylsp"; - settings->m_languageFilter.mimeTypes = QStringList(Constants::C_PY_MIMETYPE); + settings->m_languageFilter.mimeTypes = QStringList() << Constants::C_PY_MIMETYPE + << Constants::C_PY3_MIMETYPE; } settings->m_name = PyLSConfigureAssistant::tr("Python Language Server (%1)") .arg(pythonName(python)); diff --git a/src/plugins/python/pythonlanguageclient.h b/src/plugins/python/pythonlanguageclient.h index 42319377412..a66e96fcdfc 100644 --- a/src/plugins/python/pythonlanguageclient.h +++ b/src/plugins/python/pythonlanguageclient.h @@ -84,7 +84,7 @@ public: LanguageClient::Client *createClient(LanguageClient::BaseClientInterface *interface) const final; private: - LanguageClient::BaseClientInterface *createInterfaceWithProject( + LanguageClient::BaseClientInterface *createInterface( ProjectExplorer::Project *project) const override; static QJsonObject defaultConfiguration(); diff --git a/src/plugins/python/pythonrunconfiguration.cpp b/src/plugins/python/pythonrunconfiguration.cpp index fe4623dd8de..fc8a367da45 100644 --- a/src/plugins/python/pythonrunconfiguration.cpp +++ b/src/plugins/python/pythonrunconfiguration.cpp @@ -229,6 +229,19 @@ void PythonRunConfiguration::currentInterpreterChanged() } } + // Workaround that pip might return an incomplete file list on windows + if (HostOsInfo::isWindowsHost() && !python.needsDevice() + && !info.location.isEmpty() && m_pySideUicPath.isEmpty()) { + const FilePath scripts = info.location.parentDir().pathAppended("Scripts"); + auto userInstalledPySideTool = [&](const QString &toolName) { + const FilePath tool = scripts.pathAppended(HostOsInfo::withExecutableSuffix(toolName)); + return tool.isExecutableFile() ? tool : FilePath(); + }; + m_pySideUicPath = userInstalledPySideTool("pyside6-uic"); + if (pySideProjectPath.isEmpty()) + pySideProjectPath = userInstalledPySideTool("pyside6-project"); + } + updateExtraCompilers(); if (auto pySideBuildStep = buildSteps->firstOfType()) @@ -236,7 +249,8 @@ void PythonRunConfiguration::currentInterpreterChanged() for (FilePath &file : project()->files(Project::AllFiles)) { if (auto document = TextEditor::TextDocument::textDocumentForFilePath(file)) { - if (document->mimeType() == Constants::C_PY_MIMETYPE) { + if (document->mimeType() == Constants::C_PY_MIMETYPE + || document->mimeType() == Constants::C_PY3_MIMETYPE) { PyLSConfigureAssistant::openDocumentWithPython(python, document); PySideInstaller::checkPySideInstallation(python, document); } diff --git a/src/plugins/qbsprojectmanager/qbsbuildconfiguration.cpp b/src/plugins/qbsprojectmanager/qbsbuildconfiguration.cpp index 2ddd805ab79..22403395dd1 100644 --- a/src/plugins/qbsprojectmanager/qbsbuildconfiguration.cpp +++ b/src/plugins/qbsprojectmanager/qbsbuildconfiguration.cpp @@ -65,7 +65,7 @@ static FilePath defaultBuildDirectory(const FilePath &projectFilePath, const Kit const QString projectName = projectFilePath.completeBaseName(); return BuildConfiguration::buildDirectoryFromTemplate( Project::projectDirectory(projectFilePath), - projectFilePath, projectName, k, bcName, buildType); + projectFilePath, projectName, k, bcName, buildType, "qbs"); } // --------------------------------------------------------------------------- diff --git a/src/plugins/qbsprojectmanager/qbsprojectimporter.cpp b/src/plugins/qbsprojectmanager/qbsprojectimporter.cpp index 24ffe06447c..ee7fb37ed15 100644 --- a/src/plugins/qbsprojectmanager/qbsprojectimporter.cpp +++ b/src/plugins/qbsprojectmanager/qbsprojectimporter.cpp @@ -89,7 +89,7 @@ static FilePath buildDir(const FilePath &projectFilePath, const Kit *k) const QString projectName = projectFilePath.completeBaseName(); return BuildConfiguration::buildDirectoryFromTemplate( Project::projectDirectory(projectFilePath), - projectFilePath, projectName, k, QString(), BuildConfiguration::Unknown); + projectFilePath, projectName, k, QString(), BuildConfiguration::Unknown, "qbs"); } static bool hasBuildGraph(const QString &dir) diff --git a/src/plugins/qmakeprojectmanager/profileeditor.cpp b/src/plugins/qmakeprojectmanager/profileeditor.cpp index be1c5f9dd26..ecb3bfbda78 100644 --- a/src/plugins/qmakeprojectmanager/profileeditor.cpp +++ b/src/plugins/qmakeprojectmanager/profileeditor.cpp @@ -62,7 +62,7 @@ class ProFileEditorWidget : public TextEditorWidget { private: void findLinkAt(const QTextCursor &, - Utils::ProcessLinkCallback &&processLinkCallback, + const Utils::LinkHandler &processLinkCallback, bool resolveTarget = true, bool inNextSplit = false) override; void contextMenuEvent(QContextMenuEvent *) override; @@ -125,7 +125,7 @@ QString ProFileEditorWidget::checkForPrfFile(const QString &baseName) const } void ProFileEditorWidget::findLinkAt(const QTextCursor &cursor, - Utils::ProcessLinkCallback &&processLinkCallback, + const LinkHandler &processLinkCallback, bool /*resolveTarget*/, bool /*inNextSplit*/) { diff --git a/src/plugins/qmakeprojectmanager/qmakebuildconfiguration.cpp b/src/plugins/qmakeprojectmanager/qmakebuildconfiguration.cpp index bcce50dbd88..675b272085c 100644 --- a/src/plugins/qmakeprojectmanager/qmakebuildconfiguration.cpp +++ b/src/plugins/qmakeprojectmanager/qmakebuildconfiguration.cpp @@ -103,8 +103,8 @@ FilePath QmakeBuildConfiguration::shadowBuildDirectory(const FilePath &proFilePa return {}; const QString projectName = proFilePath.completeBaseName(); - return BuildConfiguration::buildDirectoryFromTemplate( - Project::projectDirectory(proFilePath), proFilePath, projectName, k, suffix, buildType); + return buildDirectoryFromTemplate(Project::projectDirectory(proFilePath), proFilePath, + projectName, k, suffix, buildType, "qmake"); } const char BUILD_CONFIGURATION_KEY[] = "Qt4ProjectManager.Qt4BuildConfiguration.BuildConfiguration"; diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 654da4d43a4..dc19983f66c 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -323,6 +323,7 @@ extend_qtc_plugin(QmlDesigner materialeditorqmlbackend.cpp materialeditorqmlbackend.h materialeditortransaction.cpp materialeditortransaction.h materialeditorview.cpp materialeditorview.h + materialeditor.qrc ) extend_qtc_plugin(QmlDesigner diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp index eca26840aa4..30f909f1598 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp @@ -344,13 +344,13 @@ void Edit3DView::createEdit3DActions() m_backgroundColorSelectionAction = new Edit3DAction( QmlDesigner::Constants::EDIT3D_EDIT_SELECT_BACKGROUND_COLOR, View3DActionCommand::SelectBackgroundColor, - QCoreApplication::translate("SelectBackgroundColorAction", "Select Background color"), + QCoreApplication::translate("SelectBackgroundColorAction", "Select Background Color"), {}, false, false, {}, {}, showBackgroundColorSelection, - QCoreApplication::translate("SelectBackgroundColorAction", "Choose a color for the background.")); + QCoreApplication::translate("SelectBackgroundColorAction", "Select a color for the background of the 3D Editor.")); m_resetBackgroundColorAction = new Edit3DAction( QmlDesigner::Constants::EDIT3D_EDIT_RESET_BACKGROUND_COLOR, View3DActionCommand::ResetBackgroundColor, - QCoreApplication::translate("ResetBackgroundColorAction", "Reset Background color"), + QCoreApplication::translate("ResetBackgroundColorAction", "Reset Background Color"), {}, false, false, {}, {}, [](const SelectionContext &) { QList colors = {QRgb(0x222222), QRgb(0x999999)}; auto view = QmlDesignerPlugin::instance()->viewManager().nodeInstanceView(); @@ -362,7 +362,7 @@ void Edit3DView::createEdit3DActions() QmlDesigner::DesignerSettingsKey::EDIT3DVIEW_BACKGROUND_COLOR, QVariant::fromValue(colorsToSave)); }, - QCoreApplication::translate("ResetBackgroundColorAction", "Reset Background color to the default value.")); + QCoreApplication::translate("ResetBackgroundColorAction", "Reset background color of the 3D Editor to the default value.")); m_showSelectionBoxAction = new Edit3DAction( QmlDesigner::Constants::EDIT3D_EDIT_SHOW_SELECTION_BOX, View3DActionCommand::ShowSelectionBox, diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp index 6f685123cfd..afa05dd67e2 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp @@ -141,7 +141,9 @@ Edit3DWidget::Edit3DWidget(Edit3DView *view) : m_visibilityTogglesMenu = new Edit3DVisibilityTogglesMenu(this); handleActions(view->visibilityToggleActions(), m_visibilityTogglesMenu, false); - m_backgroundColorMenu = new Edit3DVisibilityTogglesMenu(this); + m_backgroundColorMenu = new QMenu(this); + m_backgroundColorMenu->setToolTipsVisible(true); + handleActions(view->backgroundColorActions(), m_backgroundColorMenu, false); view->setSeeker(seeker); diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarycategoriesmodel.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarycategoriesmodel.cpp index 4962f5d6b4c..6ca0ddbc749 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarycategoriesmodel.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarycategoriesmodel.cpp @@ -207,10 +207,11 @@ void ItemLibraryCategoriesModel::clearSelectedCategory(int categoryIndex) QPointer ItemLibraryCategoriesModel::selectCategory(int categoryIndex) { - if (categoryIndex == -1 || m_categoryList.isEmpty()) + if (m_categoryList.isEmpty() || categoryIndex < 0 || categoryIndex >= m_categoryList.size()) return nullptr; const QPointer category = m_categoryList.at(categoryIndex); + if (!category->categorySelected()) { category->setCategorySelected(true); emit dataChanged(index(categoryIndex),index(categoryIndex), {m_roleNames.key("categorySelected")}); diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp index e94c5108b13..2149049b004 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp @@ -72,6 +72,7 @@ void ItemLibraryView::modelAttached(Model *model) AbstractView::modelAttached(model); m_widget->clearSearchFilter(); + m_widget->switchToComponentsView(); m_widget->setModel(model); updateImports(); if (model) diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp index 75a564d7384..e6f2b2825e5 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp @@ -278,7 +278,7 @@ void ItemLibraryWidget::handleAddImport(int index) imports.append(import); model->changeImports(imports, {}); - QMetaObject::invokeMethod(m_itemsWidget->rootObject(), "switchToComponentsView"); + switchToComponentsView(); updateSearch(); } @@ -310,7 +310,7 @@ void ItemLibraryWidget::setModel(Model *model) m_subCompEditMode = subCompEditMode; // Switch out of add module view if it's active if (m_subCompEditMode) - QMetaObject::invokeMethod(m_itemsWidget->rootObject(), "switchToComponentsView"); + switchToComponentsView(); emit subCompEditModeChanged(); } } @@ -330,6 +330,11 @@ void ItemLibraryWidget::clearSearchFilter() QMetaObject::invokeMethod(m_itemsWidget->rootObject(), "clearSearchFilter"); } +void ItemLibraryWidget::switchToComponentsView() +{ + QMetaObject::invokeMethod(m_itemsWidget->rootObject(), "switchToComponentsView"); +} + void ItemLibraryWidget::reloadQmlSource() { const QString itemLibraryQmlPath = qmlSourcesPath() + "/ItemsView.qml"; diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h index be905f181ae..1b63c2fc660 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h @@ -73,8 +73,9 @@ public: QList createToolBarWidgets(); static QString qmlSourcesPath(); - void clearSearchFilter(); + void clearSearchFilter(); + void switchToComponentsView(); void delayedUpdateModel(); void updateModel(); void updatePossibleImports(const QList &possibleImports); diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp index 896ec7818c5..d8c7286411f 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp +++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp @@ -252,7 +252,6 @@ void MaterialBrowserView::importsChanged(const QList &addedImports, cons m_hasQuick3DImport = hasQuick3DImport; refreshModel(); - } void MaterialBrowserView::customNotification(const AbstractView *view, const QString &identifier, diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.cpp b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.cpp index 4ca84cdfb9d..156add5d2da 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.cpp +++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.cpp @@ -81,11 +81,15 @@ public: { Q_UNUSED(requestedSize) + static QPixmap defaultPreview = QPixmap::fromImage(QImage(":/materialeditor/images/defaultmaterialpreview.png")); + QPixmap pixmap{150, 150}; qint32 internalId = id.toInt(); if (m_pixmaps.contains(internalId)) pixmap = m_pixmaps.value(internalId); + else + pixmap = defaultPreview; if (size) *size = pixmap.size(); diff --git a/src/plugins/qmldesigner/components/materialeditor/images/defaultmaterialpreview.png b/src/plugins/qmldesigner/components/materialeditor/images/defaultmaterialpreview.png new file mode 100644 index 00000000000..93073719f55 Binary files /dev/null and b/src/plugins/qmldesigner/components/materialeditor/images/defaultmaterialpreview.png differ diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditor.qrc b/src/plugins/qmldesigner/components/materialeditor/materialeditor.qrc new file mode 100644 index 00000000000..4ed3c769175 --- /dev/null +++ b/src/plugins/qmldesigner/components/materialeditor/materialeditor.qrc @@ -0,0 +1,5 @@ + + + images/defaultmaterialpreview.png + + diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.cpp b/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.cpp index 828e5fab006..7c5d8436226 100644 --- a/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.cpp +++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.cpp @@ -77,16 +77,21 @@ public: { Q_UNUSED(requestedSize) + static QPixmap defaultPreview = QPixmap::fromImage(QImage(":/materialeditor/images/defaultmaterialpreview.png")); + QPixmap pixmap{150, 150}; if (id == "preview") { if (!m_previewPixmap.isNull()) pixmap = m_previewPixmap; + else + pixmap = defaultPreview; } else { - QString path = Core::ICore::resourcePath("qmldesigner/materialEditorQmlSources/images/" + id).toString(); - pixmap = QPixmap{path}; + qWarning() << __FUNCTION__ << "Unsupported image id:" << id; + pixmap.fill(Qt::red); } + if (size) *size = pixmap.size(); diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp index e2736068053..aad0ea660ad 100644 --- a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp +++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp @@ -87,28 +87,35 @@ void MaterialEditorView::ensureMaterialLibraryNode() if (m_materialLibrary.isValid()) return; - const QList materials = rootModelNode().subModelNodesOfType("QtQuick3D.Material"); - if (materials.isEmpty()) - return; - // create material library node - TypeName nodeType = rootModelNode().isSubclassOf("QtQuick3D.Node") ? "Quick3D.Node" : "QtQuick.Item"; + TypeName nodeType = rootModelNode().isSubclassOf("QtQuick3D.Node") ? "QtQuick3D.Node" : "QtQuick.Item"; NodeMetaInfo metaInfo = model()->metaInfo(nodeType); m_materialLibrary = createModelNode(nodeType, metaInfo.majorVersion(), metaInfo.minorVersion()); m_materialLibrary.setIdWithoutRefactoring(Constants::MATERIAL_LIB_ID); rootModelNode().defaultNodeListProperty().reparentHere(m_materialLibrary); - // move all materials to under material library node - for (const ModelNode &node : materials) { - // if material has no name, set name to id - QString matName = node.variantProperty("objectName").value().toString(); - if (matName.isEmpty()) { - VariantProperty objNameProp = node.variantProperty("objectName"); - objNameProp.setValue(node.id()); - } + const QList materials = rootModelNode().subModelNodesOfType("QtQuick3D.Material"); + if (materials.isEmpty()) + return; - m_materialLibrary.defaultNodeListProperty().reparentHere(node); + RewriterTransaction transaction = beginRewriterTransaction( + "MaterialEditorView::ensureMaterialLibraryNode"); + + try { + // move all materials to under material library node + for (const ModelNode &node : materials) { + // if material has no name, set name to id + QString matName = node.variantProperty("objectName").value().toString(); + if (matName.isEmpty()) { + VariantProperty objNameProp = node.variantProperty("objectName"); + objNameProp.setValue(node.id()); + } + + m_materialLibrary.defaultNodeListProperty().reparentHere(node); + } + } catch (Exception &e) { + e.showException(); } } @@ -560,8 +567,6 @@ void MaterialEditorView::modelAttached(Model *model) m_hasQuick3DImport = model->hasImport("QtQuick3D"); - ensureMaterialLibraryNode(); - if (!m_setupCompleted) { reloadQml(); m_setupCompleted = true; @@ -739,7 +744,6 @@ void MaterialEditorView::importsChanged(const QList &addedImports, const m_hasQuick3DImport = model()->hasImport("QtQuick3D"); m_qmlBackEnd->contextObject()->setHasQuick3DImport(m_hasQuick3DImport); - ensureMaterialLibraryNode(); // create the material lib if Quick3D import is added resetView(); } diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.cpp b/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.cpp index 7890086bd5f..429ca328afd 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.cpp +++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.cpp @@ -97,8 +97,8 @@ void ImageCacheCollector::start(Utils::SmallStringView name, model->setRewriterView(&rewriterView); bool is3DRoot = !rewriterView.inErrorState() - && (rewriterView.rootModelNode().isSubclassOf("Quick3D.Node") - || rewriterView.rootModelNode().isSubclassOf("Quick3D.Material")); + && (rewriterView.rootModelNode().isSubclassOf("QtQuick3D.Node") + || rewriterView.rootModelNode().isSubclassOf("QtQuick3D.Material")); if (rewriterView.inErrorState() || (!rewriterView.rootModelNode().metaInfo().isGraphicalItem() && !is3DRoot)) { diff --git a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp index 5eef5550da7..1db2bc3cb6a 100644 --- a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp +++ b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp @@ -1150,12 +1150,12 @@ CreateSceneCommand NodeInstanceView::createCreateSceneCommand() if (stateNode.isValid() && stateNode.metaInfo().isSubclassOf("QtQuick.State", 1, 0)) stateInstanceId = stateNode.internalId(); - auto value + QVariant value #ifndef QMLDESIGNER_TEST = QmlDesigner::DesignerSettings::getValue( QmlDesigner::DesignerSettingsKey::EDIT3DVIEW_BACKGROUND_COLOR); #else - = QColor(); + = {}; #endif QList edit3dBackgroundColor; if (value.isValid()) diff --git a/src/plugins/qmldesigner/designersettings.cpp b/src/plugins/qmldesigner/designersettings.cpp index 9c1aec1b364..20083068c16 100644 --- a/src/plugins/qmldesigner/designersettings.cpp +++ b/src/plugins/qmldesigner/designersettings.cpp @@ -80,7 +80,8 @@ void DesignerSettings::fromSettings(QSettings *settings) restoreValue(settings, DesignerSettingsKey::ALWAYS_DESIGN_MODE, true); restoreValue(settings, DesignerSettingsKey::DISABLE_ITEM_LIBRARY_UPDATE_TIMER, false); restoreValue(settings, DesignerSettingsKey::ASK_BEFORE_DELETING_ASSET, true); - restoreValue(settings, DesignerSettingsKey::EDIT3DVIEW_BACKGROUND_COLOR, QList{"#222222", "#999999"}); + const QStringList defaultValue = QStringList() << "#222222" << "#999999"; + restoreValue(settings, DesignerSettingsKey::EDIT3DVIEW_BACKGROUND_COLOR, defaultValue); settings->endGroup(); settings->endGroup(); diff --git a/src/plugins/qmldesigner/qmldesignerplugin.qbs b/src/plugins/qmldesigner/qmldesignerplugin.qbs index 375f9e82233..0ea3b5008a1 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.qbs +++ b/src/plugins/qmldesigner/qmldesignerplugin.qbs @@ -693,6 +693,7 @@ Project { "materialeditor/materialeditortransaction.h", "materialeditor/materialeditorview.cpp", "materialeditor/materialeditorview.h", + "materialeditor/materialeditor.qrc", "navigator/iconcheckboxitemdelegate.cpp", "navigator/iconcheckboxitemdelegate.h", "navigator/nameitemdelegate.cpp", diff --git a/src/plugins/qmljseditor/qmljseditor.cpp b/src/plugins/qmljseditor/qmljseditor.cpp index 1e7534330d4..894e0be8d99 100644 --- a/src/plugins/qmljseditor/qmljseditor.cpp +++ b/src/plugins/qmljseditor/qmljseditor.cpp @@ -753,7 +753,7 @@ void QmlJSEditorWidget::inspectElementUnderCursor() const } void QmlJSEditorWidget::findLinkAt(const QTextCursor &cursor, - Utils::ProcessLinkCallback &&processLinkCallback, + const Utils::LinkHandler &processLinkCallback, bool /*resolveTarget*/, bool /*inNextSplit*/) { diff --git a/src/plugins/qmljseditor/qmljseditor.h b/src/plugins/qmljseditor/qmljseditor.h index bcb75a32238..03e0796fe0e 100644 --- a/src/plugins/qmljseditor/qmljseditor.h +++ b/src/plugins/qmljseditor/qmljseditor.h @@ -102,7 +102,7 @@ protected: void applyFontSettings() override; void createToolBar(); void findLinkAt(const QTextCursor &cursor, - Utils::ProcessLinkCallback &&processLinkCallback, + const Utils::LinkHandler &processLinkCallback, bool resolveTarget = true, bool inNextSplit = false) override; QString foldReplacementText(const QTextBlock &block) const override; diff --git a/src/plugins/remotelinux/customcommanddeploystep.cpp b/src/plugins/remotelinux/customcommanddeploystep.cpp index 318d5cd7a47..fef2d103f6c 100644 --- a/src/plugins/remotelinux/customcommanddeploystep.cpp +++ b/src/plugins/remotelinux/customcommanddeploystep.cpp @@ -26,6 +26,7 @@ #include "customcommanddeploystep.h" #include "abstractremotelinuxdeployservice.h" +#include "abstractremotelinuxdeploystep.h" #include "remotelinux_constants.h" #include @@ -108,35 +109,41 @@ void CustomCommandDeployService::stopDeployment() handleDeploymentDone(); } +class CustomCommandDeployStep : public AbstractRemoteLinuxDeployStep +{ + Q_DECLARE_TR_FUNCTIONS(RemoteLinux::Internal::CustomCommandDeployStep) + +public: + CustomCommandDeployStep(BuildStepList *bsl, Id id) + : AbstractRemoteLinuxDeployStep(bsl, id) + { + auto service = createDeployService(); + + auto commandLine = addAspect(); + commandLine->setSettingsKey("RemoteLinuxCustomCommandDeploymentStep.CommandLine"); + commandLine->setLabelText(tr("Command line:")); + commandLine->setDisplayStyle(StringAspect::LineEditDisplay); + commandLine->setHistoryCompleter("RemoteLinuxCustomCommandDeploymentStep.History"); + + setInternalInitializer([service, commandLine] { + service->setCommandLine(commandLine->value().trimmed()); + return service->isDeploymentPossible(); + }); + + addMacroExpander(); + } +}; + + +// CustomCommandDeployStepFactory + +CustomCommandDeployStepFactory::CustomCommandDeployStepFactory() +{ + registerStep(Constants::CustomCommandDeployStepId); + setDisplayName(CustomCommandDeployStep::tr("Run custom remote command")); + setSupportedConfiguration(RemoteLinux::Constants::DeployToGenericLinux); + setSupportedStepList(ProjectExplorer::Constants::BUILDSTEPS_DEPLOY); +} + } // Internal - -CustomCommandDeployStep::CustomCommandDeployStep(BuildStepList *bsl, Utils::Id id) - : AbstractRemoteLinuxDeployStep(bsl, id) -{ - auto service = createDeployService(); - - auto commandLine = addAspect(); - commandLine->setSettingsKey("RemoteLinuxCustomCommandDeploymentStep.CommandLine"); - commandLine->setLabelText(tr("Command line:")); - commandLine->setDisplayStyle(StringAspect::LineEditDisplay); - commandLine->setHistoryCompleter("RemoteLinuxCustomCommandDeploymentStep.History"); - - setInternalInitializer([service, commandLine] { - service->setCommandLine(commandLine->value().trimmed()); - return service->isDeploymentPossible(); - }); - - addMacroExpander(); -} - -Utils::Id CustomCommandDeployStep::stepId() -{ - return Constants::CustomCommandDeployStepId; -} - -QString CustomCommandDeployStep::displayName() -{ - return tr("Run custom remote command"); -} - -} // namespace RemoteLinux +} // RemoteLinux diff --git a/src/plugins/remotelinux/customcommanddeploystep.h b/src/plugins/remotelinux/customcommanddeploystep.h index 0a9503d666b..09b6aaceafc 100644 --- a/src/plugins/remotelinux/customcommanddeploystep.h +++ b/src/plugins/remotelinux/customcommanddeploystep.h @@ -25,21 +25,16 @@ #pragma once -#include "remotelinux_export.h" - -#include "abstractremotelinuxdeploystep.h" +#include namespace RemoteLinux { +namespace Internal { -class REMOTELINUX_EXPORT CustomCommandDeployStep : public AbstractRemoteLinuxDeployStep +class CustomCommandDeployStepFactory : public ProjectExplorer::BuildStepFactory { - Q_OBJECT - public: - CustomCommandDeployStep(ProjectExplorer::BuildStepList *bsl, Utils::Id id); - - static Utils::Id stepId(); - static QString displayName(); + CustomCommandDeployStepFactory(); }; -} // namespace RemoteLinux +} // Internal +} // RemoteLinux diff --git a/src/plugins/remotelinux/makeinstallstep.cpp b/src/plugins/remotelinux/makeinstallstep.cpp index 08a14ed7dab..a6046382492 100644 --- a/src/plugins/remotelinux/makeinstallstep.cpp +++ b/src/plugins/remotelinux/makeinstallstep.cpp @@ -168,7 +168,7 @@ bool MakeInstallStep::init() "Consider moving it up."))); } const MakeInstallCommand cmd = target()->makeInstallCommand(installRoot().toString()); - if (cmd.environment.size() > 0) { + if (cmd.environment.isValid()) { Environment env = processParameters()->environment(); for (auto it = cmd.environment.constBegin(); it != cmd.environment.constEnd(); ++it) { if (cmd.environment.isEnabled(it)) { diff --git a/src/plugins/remotelinux/remotelinux_constants.h b/src/plugins/remotelinux/remotelinux_constants.h index 843a3d96f36..da6eac43d40 100644 --- a/src/plugins/remotelinux/remotelinux_constants.h +++ b/src/plugins/remotelinux/remotelinux_constants.h @@ -30,6 +30,8 @@ namespace Constants { const char GenericLinuxOsType[] = "GenericLinuxOsType"; +const char DeployToGenericLinux[] = "DeployToGenericLinux"; + const char CheckForFreeDiskSpaceId[] = "RemoteLinux.CheckForFreeDiskSpaceStep"; const char DirectUploadStepId[] = "RemoteLinux.DirectUploadStep"; const char MakeInstallStepId[] = "RemoteLinux.MakeInstall"; diff --git a/src/plugins/remotelinux/remotelinuxdeployconfiguration.cpp b/src/plugins/remotelinux/remotelinuxdeployconfiguration.cpp index ed8f0d0d9bb..db5ecfc5280 100644 --- a/src/plugins/remotelinux/remotelinuxdeployconfiguration.cpp +++ b/src/plugins/remotelinux/remotelinuxdeployconfiguration.cpp @@ -25,14 +25,9 @@ #include "remotelinuxdeployconfiguration.h" -#include "checkforfreediskspacestep.h" -#include "genericdirectuploadstep.h" #include "makeinstallstep.h" -#include "killappstep.h" #include "remotelinux_constants.h" -#include "rsyncdeploystep.h" -#include #include #include #include @@ -43,19 +38,11 @@ using namespace ProjectExplorer; namespace RemoteLinux { - -using namespace Internal; - -Utils::Id genericDeployConfigurationId() -{ - return "DeployToGenericLinux"; -} - namespace Internal { RemoteLinuxDeployConfigurationFactory::RemoteLinuxDeployConfigurationFactory() { - setConfigBaseId(genericDeployConfigurationId()); + setConfigBaseId(RemoteLinux::Constants::DeployToGenericLinux); addSupportedTargetDeviceType(RemoteLinux::Constants::GenericLinuxOsType); setDefaultDisplayName(QCoreApplication::translate("RemoteLinux", "Deploy to Remote Linux Host")); @@ -75,14 +62,14 @@ RemoteLinuxDeployConfigurationFactory::RemoteLinuxDeployConfigurationFactory() } }); - addInitialStep(MakeInstallStep::stepId(), needsMakeInstall); - addInitialStep(CheckForFreeDiskSpaceStep::stepId()); - addInitialStep(KillAppStep::stepId()); - addInitialStep(RsyncDeployStep::stepId(), [](Target *target) { + addInitialStep(Constants::MakeInstallStepId, needsMakeInstall); + addInitialStep(Constants::CheckForFreeDiskSpaceId); + addInitialStep(Constants::KillAppStepId); + addInitialStep(Constants::RsyncDeployStepId, [](Target *target) { auto device = DeviceKitAspect::device(target->kit()); return device && device->extraData(Constants::SupportsRSync).toBool(); }); - addInitialStep(GenericDirectUploadStep::stepId(), [](Target *target) { + addInitialStep(Constants::DirectUploadStepId, [](Target *target) { auto device = DeviceKitAspect::device(target->kit()); return device && !device->extraData(Constants::SupportsRSync).toBool(); }); diff --git a/src/plugins/remotelinux/remotelinuxdeployconfiguration.h b/src/plugins/remotelinux/remotelinuxdeployconfiguration.h index 264d28e1e40..3a4c803d219 100644 --- a/src/plugins/remotelinux/remotelinuxdeployconfiguration.h +++ b/src/plugins/remotelinux/remotelinuxdeployconfiguration.h @@ -28,9 +28,6 @@ #include namespace RemoteLinux { - -Utils::Id genericDeployConfigurationId(); - namespace Internal { class RemoteLinuxDeployConfigurationFactory : public ProjectExplorer::DeployConfigurationFactory diff --git a/src/plugins/remotelinux/remotelinuxplugin.cpp b/src/plugins/remotelinux/remotelinuxplugin.cpp index 4affcd2aef4..1f072a38a50 100644 --- a/src/plugins/remotelinux/remotelinuxplugin.cpp +++ b/src/plugins/remotelinux/remotelinuxplugin.cpp @@ -62,7 +62,7 @@ public: { registerStep(Step::stepId()); setDisplayName(Step::displayName()); - setSupportedConfiguration(genericDeployConfigurationId()); + setSupportedConfiguration(RemoteLinux::Constants::DeployToGenericLinux); setSupportedStepList(ProjectExplorer::Constants::BUILDSTEPS_DEPLOY); } }; @@ -75,10 +75,10 @@ public: RemoteLinuxCustomRunConfigurationFactory customRunConfigurationFactory; RemoteLinuxDeployConfigurationFactory deployConfigurationFactory; GenericDeployStepFactory tarPackageCreationStepFactory; - GenericDeployStepFactory tarPackageDeployStepFactory; + TarPackageDeployStepFactory tarPackageDeployStepFactory; GenericDeployStepFactory genericDirectUploadStepFactory; GenericDeployStepFactory rsyncDeployStepFactory; - GenericDeployStepFactory customCommandDeployStepFactory; + CustomCommandDeployStepFactory customCommandDeployStepFactory; GenericDeployStepFactory checkForFreeDiskSpaceStepFactory; GenericDeployStepFactory killAppStepFactory; GenericDeployStepFactory makeInstallStepFactory; diff --git a/src/plugins/remotelinux/tarpackagedeploystep.cpp b/src/plugins/remotelinux/tarpackagedeploystep.cpp index db8fdec076d..12add3a4faa 100644 --- a/src/plugins/remotelinux/tarpackagedeploystep.cpp +++ b/src/plugins/remotelinux/tarpackagedeploystep.cpp @@ -26,12 +26,14 @@ #include "tarpackagedeploystep.h" #include "abstractremotelinuxdeployservice.h" +#include "abstractremotelinuxdeploystep.h" #include "remotelinux_constants.h" #include "tarpackagecreationstep.h" #include #include #include +#include #include #include @@ -49,10 +51,11 @@ class TarPackageInstaller : public QObject Q_OBJECT public: - TarPackageInstaller(QObject *parent = nullptr); + TarPackageInstaller(); - void installPackage(const ProjectExplorer::IDeviceConstPtr &deviceConfig, - const QString &packageFilePath, bool removePackageFile); + void installPackage(const IDeviceConstPtr &deviceConfig, + const QString &packageFilePath, + bool removePackageFile); void cancelInstallation(); signals: @@ -66,8 +69,7 @@ private: QtcProcess m_killer; }; -TarPackageInstaller::TarPackageInstaller(QObject *parent) - : QObject(parent) +TarPackageInstaller::TarPackageInstaller() { connect(&m_installer, &QtcProcess::readyReadStandardOutput, this, [this] { emit stdoutData(QString::fromUtf8(m_installer.readAllStandardOutput())); @@ -230,44 +232,50 @@ void TarPackageDeployService::setFinished() handleDeploymentDone(); } -} // namespace Internal +// TarPackageDeployStep -using namespace Internal; - -TarPackageDeployStep::TarPackageDeployStep(BuildStepList *bsl, Id id) - : AbstractRemoteLinuxDeployStep(bsl, id) +class TarPackageDeployStep : public AbstractRemoteLinuxDeployStep { - auto service = createDeployService(); + Q_DECLARE_TR_FUNCTIONS(RemoteLinux::Internal::TarPackageDeployStep) - setWidgetExpandedByDefault(false); +public: + TarPackageDeployStep(BuildStepList *bsl, Id id) + : AbstractRemoteLinuxDeployStep(bsl, id) + { + auto service = createDeployService(); - setInternalInitializer([this, service] { - const TarPackageCreationStep *pStep = nullptr; + setWidgetExpandedByDefault(false); - for (BuildStep *step : deployConfiguration()->stepList()->steps()) { - if (step == this) - break; - if ((pStep = qobject_cast(step))) - break; - } - if (!pStep) - return CheckResult::failure(tr("No tarball creation step found.")); + setInternalInitializer([this, service] { + const TarPackageCreationStep *pStep = nullptr; - service->setPackageFilePath(pStep->packageFilePath()); - return service->isDeploymentPossible(); - }); + for (BuildStep *step : deployConfiguration()->stepList()->steps()) { + if (step == this) + break; + if ((pStep = qobject_cast(step))) + break; + } + if (!pStep) + return CheckResult::failure(tr("No tarball creation step found.")); + + service->setPackageFilePath(pStep->packageFilePath()); + return service->isDeploymentPossible(); + }); + } +}; + + +// TarPackageDeployStepFactory + +TarPackageDeployStepFactory::TarPackageDeployStepFactory() +{ + registerStep(Constants::TarPackageDeployStepId); + setDisplayName(TarPackageDeployStep::tr("Deploy tarball via SFTP upload")); + setSupportedConfiguration(RemoteLinux::Constants::DeployToGenericLinux); + setSupportedStepList(ProjectExplorer::Constants::BUILDSTEPS_DEPLOY); } -Id TarPackageDeployStep::stepId() -{ - return Constants::TarPackageDeployStepId; -} - -QString TarPackageDeployStep::displayName() -{ - return tr("Deploy tarball via SFTP upload"); -} - -} //namespace RemoteLinux +} // Internal +} // RemoteLinux #include "tarpackagedeploystep.moc" diff --git a/src/plugins/remotelinux/tarpackagedeploystep.h b/src/plugins/remotelinux/tarpackagedeploystep.h index e1113bebcbc..716fbb7876b 100644 --- a/src/plugins/remotelinux/tarpackagedeploystep.h +++ b/src/plugins/remotelinux/tarpackagedeploystep.h @@ -25,19 +25,16 @@ #pragma once -#include "abstractremotelinuxdeploystep.h" +#include namespace RemoteLinux { +namespace Internal { -class TarPackageDeployStep : public AbstractRemoteLinuxDeployStep +class TarPackageDeployStepFactory : public ProjectExplorer::BuildStepFactory { - Q_OBJECT - public: - TarPackageDeployStep(ProjectExplorer::BuildStepList *bsl, Utils::Id id); - - static Utils::Id stepId(); - static QString displayName(); + TarPackageDeployStepFactory(); }; -} //namespace RemoteLinux +} // Internal +} // RemoteLinux diff --git a/src/plugins/texteditor/texteditor.cpp b/src/plugins/texteditor/texteditor.cpp index 0fdb90137c2..8d0441f47dd 100644 --- a/src/plugins/texteditor/texteditor.cpp +++ b/src/plugins/texteditor/texteditor.cpp @@ -388,7 +388,6 @@ public: restart(); } -private: void abortHandlers() { for (BaseHoverHandler *handler : m_handlers) @@ -396,6 +395,7 @@ private: m_currentHandlerIndex = -1; } +private: void restart() { abortHandlers(); @@ -6081,7 +6081,7 @@ void TextEditorWidget::zoomReset() } void TextEditorWidget::findLinkAt(const QTextCursor &cursor, - Utils::ProcessLinkCallback &&callback, + const Utils::LinkHandler &callback, bool resolveTarget, bool inNextSplit) { @@ -6717,6 +6717,7 @@ void TextEditorWidget::focusInEvent(QFocusEvent *e) void TextEditorWidget::focusOutEvent(QFocusEvent *e) { QPlainTextEdit::focusOutEvent(e); + d->m_hoverHandlerRunner.abortHandlers(); if (viewport()->cursor().shape() == Qt::BlankCursor) viewport()->setCursor(Qt::IBeamCursor); d->m_cursorFlashTimer.stop(); diff --git a/src/plugins/texteditor/texteditor.h b/src/plugins/texteditor/texteditor.h index 8489ccd7683..b29b69b9ccf 100644 --- a/src/plugins/texteditor/texteditor.h +++ b/src/plugins/texteditor/texteditor.h @@ -496,7 +496,7 @@ signals: void requestBlockUpdate(const QTextBlock &); - void requestLinkAt(const QTextCursor &cursor, Utils::ProcessLinkCallback &callback, + void requestLinkAt(const QTextCursor &cursor, const Utils::LinkHandler &callback, bool resolveTarget, bool inNextSplit); void requestUsages(const QTextCursor &cursor); void requestRename(const QTextCursor &cursor); @@ -582,7 +582,7 @@ protected: (it isn't until the link is used). */ virtual void findLinkAt(const QTextCursor &, - Utils::ProcessLinkCallback &&processLinkCallback, + const Utils::LinkHandler &processLinkCallback, bool resolveTarget = true, bool inNextSplit = false); diff --git a/src/plugins/vcsbase/vcsbaseclient.cpp b/src/plugins/vcsbase/vcsbaseclient.cpp index 3c79eee4a81..35e61845028 100644 --- a/src/plugins/vcsbase/vcsbaseclient.cpp +++ b/src/plugins/vcsbase/vcsbaseclient.cpp @@ -215,7 +215,7 @@ void VcsBaseClientImpl::vcsSynchronousExec(QtcProcess &proc, QTextCodec *outputCodec) const { Environment env = processEnvironment(); - VcsCommand command(workingDir, env.size() == 0 ? Environment::systemEnvironment() : env); + VcsCommand command(workingDir, env.isValid() ? env : Environment::systemEnvironment()); proc.setTimeoutS(vcsTimeoutS()); command.addFlags(flags); command.setCodec(outputCodec); diff --git a/tests/auto/utils/CMakeLists.txt b/tests/auto/utils/CMakeLists.txt index 5680cde7aaf..60dd1cfbccc 100644 --- a/tests/auto/utils/CMakeLists.txt +++ b/tests/auto/utils/CMakeLists.txt @@ -9,3 +9,4 @@ add_subdirectory(stringutils) add_subdirectory(templateengine) add_subdirectory(treemodel) add_subdirectory(multicursor) +add_subdirectory(deviceshell) diff --git a/tests/auto/utils/deviceshell/CMakeLists.txt b/tests/auto/utils/deviceshell/CMakeLists.txt new file mode 100644 index 00000000000..61492bef322 --- /dev/null +++ b/tests/auto/utils/deviceshell/CMakeLists.txt @@ -0,0 +1,8 @@ +file(RELATIVE_PATH RELATIVE_TEST_PATH "${PROJECT_BINARY_DIR}" "${CMAKE_CURRENT_BINARY_DIR}") +file(RELATIVE_PATH TEST_RELATIVE_LIBEXEC_PATH "/${RELATIVE_TEST_PATH}" "/${IDE_LIBEXEC_PATH}") + +add_qtc_test(tst_utils_deviceshell + DEFINES "TEST_RELATIVE_LIBEXEC_PATH=\"${TEST_RELATIVE_LIBEXEC_PATH}\"" + DEPENDS Utils + SOURCES tst_deviceshell.cpp +) diff --git a/tests/auto/utils/deviceshell/deviceshell.qbs b/tests/auto/utils/deviceshell/deviceshell.qbs new file mode 100644 index 00000000000..207848f00d8 --- /dev/null +++ b/tests/auto/utils/deviceshell/deviceshell.qbs @@ -0,0 +1,12 @@ +Project { + QtcAutotest { + name: "DeviceShell autotest" + + Depends { name: "Utils" } + Depends { name: "app_version_header" } + + files: [ + "tst_deviceshell.cpp", + ] + } +} diff --git a/tests/auto/utils/deviceshell/tst_deviceshell.cpp b/tests/auto/utils/deviceshell/tst_deviceshell.cpp new file mode 100644 index 00000000000..40181b7d25e --- /dev/null +++ b/tests/auto/utils/deviceshell/tst_deviceshell.cpp @@ -0,0 +1,284 @@ +/**************************************************************************** +** +** Copyright (C) 2022 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace Utils; + +class TestShell : public DeviceShell +{ +public: + TestShell(CommandLine cmdLine) + : m_cmdLine(std::move(cmdLine)) + { + start(); + } + +private: + void setupShellProcess(QtcProcess *shellProcess) override + { + shellProcess->setCommand(m_cmdLine); + } + + CommandLine m_cmdLine; +}; + +bool testDocker(const FilePath &executable) +{ + QtcProcess p; + p.setCommand({executable, {"info"}}); + p.runBlocking(); + return p.result() == ProcessResult::FinishedWithSuccess; +} + +class tst_DeviceShell : public QObject +{ + Q_OBJECT +private: + QByteArray m_asciiTestData{256, Qt::Uninitialized}; + + QList m_availableShells; + bool m_dockerSetupCheckOk{false}; + +private: + QString testString(int length) + { + QRandomGenerator generator; + QString result; + for (int i = 0; i < length; ++i) + result.append(QChar{generator.bounded('a', 'z')}); + + return result; + } + +private slots: + void initTestCase() + { + TemporaryDirectory::setMasterTemporaryDirectory( + QDir::tempPath() + "/" + Core::Constants::IDE_CASED_ID + "-XXXXXX"); + + const QString libExecPath(qApp->applicationDirPath() + '/' + + QLatin1String(TEST_RELATIVE_LIBEXEC_PATH)); + LauncherInterface::setPathToLauncher(libExecPath); + + std::iota(m_asciiTestData.begin(), m_asciiTestData.end(), 0); + + const FilePath dockerExecutable = Environment::systemEnvironment() + .searchInPath("docker", {"/usr/local/bin"}); + + if (dockerExecutable.exists()) { + m_availableShells.append({dockerExecutable, {"run", "-i", "--rm", "alpine"}}); + if (testDocker(dockerExecutable)) { + m_dockerSetupCheckOk = true; + } else { + // On linux, docker needs some post-install steps: https://docs.docker.com/engine/install/linux-postinstall/ + // Also check if you can start a simple container from the command line: "docker run -it alpine". + qWarning() << "Checking docker failed, tests will be skipped."; + } + } + + if (!Utils::HostOsInfo::isWindowsHost()) { + // Windows by default has bash.exe, which does not work unless a working wsl is installed. + // Therefore we only test shells on linux / mac hosts. + const auto shells = {"dash", "bash", "sh", "zsh"}; + + for (const auto &shell : shells) { + const FilePath executable = Environment::systemEnvironment() + .searchInPath(shell, {"/usr/local/bin"}); + if (executable.exists()) + m_availableShells.append({executable, {}}); + } + } + + if (m_availableShells.isEmpty()) { + QSKIP("Skipping deviceshell tests, as no compatible shell could be found"); + } + } + + void cleanupTestCase() { Singleton::deleteAll(); } + + void testArguments_data() + { + QTest::addColumn("cmdLine"); + QTest::addColumn("testData"); + + for (const auto &cmdLine : qAsConst(m_availableShells)) { + QTest::newRow((cmdLine.executable().baseName() + " : simple").toUtf8()) + << cmdLine << "Hallo Welt!"; + QTest::newRow((cmdLine.executable().baseName() + " : japanese").toUtf8()) + << cmdLine + << QString::fromUtf8(u8"\xe8\xac\x9d\xe3\x81\x8d\xe3\x82\x81\xe9\x80\x80\x31\x30" + u8"\xe8\x89\xaf\xe3\x81\x9a\xe3" + u8"\x82\xa4\xe3\x81\xb5\xe3\x81\x8b\xe7\x89\x88\xe8\x84\xb3" + u8"\xe3\x83\xa9\xe3\x83\xaf\xe6" + u8"\xad\xa2\xe9\x80\x9a\xe3\x83\xa8\xe3\x83\xb2\xe3\x82\xad"); + QTest::newRow((cmdLine.executable().baseName() + " : german").toUtf8()) + << cmdLine + << QString::fromUtf8(u8"\x48\x61\x6c\x6c\xc3\xb6\x2c\x20\x77\x69\x65\x20\x67\xc3" + u8"\xa4\x68\x74\x20\x65\x73\x20" + u8"\x64\xc3\xbc\x72"); + + QTest::newRow((cmdLine.executable().baseName() + " : long").toUtf8()) + << cmdLine << testString(4096 * 16); + } + } + + void testArguments() + { + QFETCH(CommandLine, cmdLine); + QFETCH(QString, testData); + + if (cmdLine.executable().toString().contains("docker") && !m_dockerSetupCheckOk) { + QSKIP("Docker was found, but does not seem to be set up correctly, skipping."); + } + + TestShell shell(cmdLine); + QCOMPARE(shell.state(), DeviceShell::State::Succeeded); + + QRandomGenerator generator; + + const DeviceShell::RunResult result = shell.outputForRunInShell({"echo", {testData}}); + QCOMPARE(result.exitCode, 0); + const QString expected = testData + "\n"; + const QString resultAsUtf8 = QString::fromUtf8(result.stdOut); + QCOMPARE(resultAsUtf8.size(), expected.size()); + QCOMPARE(resultAsUtf8, expected); + } + + void testStdin_data() + { + QTest::addColumn("cmdLine"); + QTest::addColumn("testData"); + + for (const auto &cmdLine : qAsConst(m_availableShells)) { + QTest::newRow((cmdLine.executable().baseName() + " : simple").toUtf8()) + << cmdLine << "Hallo Welt!"; + QTest::newRow((cmdLine.executable().baseName() + " : japanese").toUtf8()) + << cmdLine + << QString::fromUtf8(u8"\xe8\xac\x9d\xe3\x81\x8d\xe3\x82\x81\xe9\x80\x80\x31\x30" + u8"\xe8\x89\xaf\xe3\x81\x9a\xe3" + u8"\x82\xa4\xe3\x81\xb5\xe3\x81\x8b\xe7\x89\x88\xe8\x84\xb3" + u8"\xe3\x83\xa9\xe3\x83\xaf\xe6" + u8"\xad\xa2\xe9\x80\x9a\xe3\x83\xa8\xe3\x83\xb2\xe3\x82\xad"); + QTest::newRow((cmdLine.executable().baseName() + " : german").toUtf8()) + << cmdLine + << QString::fromUtf8(u8"\x48\x61\x6c\x6c\xc3\xb6\x2c\x20\x77\x69\x65\x20\x67\xc3" + u8"\xa4\x68\x74\x20\x65\x73\x20" + u8"\x64\xc3\xbc\x72"); + + QTest::newRow((cmdLine.executable().baseName() + " : long").toUtf8()) + << cmdLine << testString(4096 * 16); + } + } + + void testStdin() + { + QFETCH(CommandLine, cmdLine); + QFETCH(QString, testData); + + if (cmdLine.executable().toString().contains("docker") && !m_dockerSetupCheckOk) { + QSKIP("Docker was found, but does not seem to be set up correctly, skipping."); + } + + TestShell shell(cmdLine); + QCOMPARE(shell.state(), DeviceShell::State::Succeeded); + + QRandomGenerator generator; + + const DeviceShell::RunResult result = shell.outputForRunInShell({"cat", {}}, testData.toUtf8()); + QCOMPARE(result.exitCode, 0); + const QString resultAsUtf8 = QString::fromUtf8(result.stdOut); + QCOMPARE(resultAsUtf8.size(), testData.size()); + QCOMPARE(resultAsUtf8, testData); + } + + void testAscii_data() + { + QTest::addColumn("cmdLine"); + for (const auto &cmdLine : qAsConst(m_availableShells)) { + QTest::newRow(cmdLine.executable().baseName().toUtf8()) << cmdLine; + } + } + + void testAscii() + { + QFETCH(CommandLine, cmdLine); + + if (cmdLine.executable().toString().contains("docker") && !m_dockerSetupCheckOk) { + QSKIP("Docker was found, but does not seem to be set up correctly, skipping."); + } + + TestShell shell(cmdLine); + QCOMPARE(shell.state(), DeviceShell::State::Succeeded); + + const DeviceShell::RunResult result = shell.outputForRunInShell({"cat", {}}, + m_asciiTestData); + QCOMPARE(result.stdOut, m_asciiTestData); + } + + void testStdErr_data() + { + QTest::addColumn("cmdLine"); + for (const auto &cmdLine : m_availableShells) { + QTest::newRow(cmdLine.executable().baseName().toUtf8()) << cmdLine; + } + } + + void testStdErr() + { + QFETCH(CommandLine, cmdLine); + + if (cmdLine.executable().toString().contains("docker") && !m_dockerSetupCheckOk) { + QSKIP("Docker was found, but does not seem to be set up correctly, skipping."); + } + + TestShell shell(cmdLine); + QCOMPARE(shell.state(), DeviceShell::State::Succeeded); + + const DeviceShell::RunResult result = shell.outputForRunInShell({"cat", {}}, + m_asciiTestData); + QCOMPARE(result.stdOut, m_asciiTestData); + QVERIFY(result.stdErr.isEmpty()); + + const DeviceShell::RunResult result2 = shell.outputForRunInShell( + {"cat", {"/tmp/i-do-not-exist.none"}}); + QVERIFY(!result2.stdErr.isEmpty()); + } +}; + +QTEST_MAIN(tst_DeviceShell) + +#include "tst_deviceshell.moc" diff --git a/tests/auto/utils/utils.qbs b/tests/auto/utils/utils.qbs index 8960168fc74..5237f24a19e 100644 --- a/tests/auto/utils/utils.qbs +++ b/tests/auto/utils/utils.qbs @@ -14,5 +14,6 @@ Project { "templateengine/templateengine.qbs", "treemodel/treemodel.qbs", "multicursor/multicursor.qbs", + "deviceshell/deviceshell.qbs", ] } diff --git a/tests/manual/CMakeLists.txt b/tests/manual/CMakeLists.txt index 48bd9648fac..eecadda01f7 100644 --- a/tests/manual/CMakeLists.txt +++ b/tests/manual/CMakeLists.txt @@ -17,3 +17,4 @@ add_subdirectory(proparser) # add_subdirectory(search) add_subdirectory(shootout) add_subdirectory(widgets) +add_subdirectory(deviceshell) diff --git a/tests/manual/deviceshell/CMakeLists.txt b/tests/manual/deviceshell/CMakeLists.txt new file mode 100644 index 00000000000..39616223488 --- /dev/null +++ b/tests/manual/deviceshell/CMakeLists.txt @@ -0,0 +1,10 @@ +file(RELATIVE_PATH RELATIVE_TEST_PATH "${PROJECT_BINARY_DIR}" "${CMAKE_CURRENT_BINARY_DIR}") +file(RELATIVE_PATH TEST_RELATIVE_LIBEXEC_PATH "/${RELATIVE_TEST_PATH}" "/${IDE_LIBEXEC_PATH}") + +add_qtc_test(tst_manual_deviceshell + MANUALTEST + DEFINES "TEST_RELATIVE_LIBEXEC_PATH=\"${TEST_RELATIVE_LIBEXEC_PATH}\"" + DEPENDS Utils + SOURCES + tst_deviceshell.cpp +) diff --git a/tests/manual/deviceshell/deviceshell.qbs b/tests/manual/deviceshell/deviceshell.qbs new file mode 100644 index 00000000000..9a4409793fe --- /dev/null +++ b/tests/manual/deviceshell/deviceshell.qbs @@ -0,0 +1,22 @@ +import qbs.FileInfo + +Project { + QtcManualtest { + name: "DeviceShell manualtest" + + Depends { name: "Utils" } + Depends { name: "app_version_header" } + + files: [ + "tst_deviceshell.cpp", + ] + cpp.defines: { + var defines = base; + var absLibExecPath = FileInfo.joinPaths(qbs.installRoot, qbs.installPrefix, + qtc.ide_libexec_path); + var relLibExecPath = FileInfo.relativePath(destinationDirectory, absLibExecPath); + defines.push('TEST_RELATIVE_LIBEXEC_PATH="' + relLibExecPath + '"'); + return defines; + } + } +} diff --git a/tests/manual/deviceshell/tst_deviceshell.cpp b/tests/manual/deviceshell/tst_deviceshell.cpp new file mode 100644 index 00000000000..f9ab9b4e8a3 --- /dev/null +++ b/tests/manual/deviceshell/tst_deviceshell.cpp @@ -0,0 +1,228 @@ +/**************************************************************************** +** +** Copyright (C) 2022 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace Utils; + +class TestShell : public DeviceShell +{ +public: + TestShell() { start(); } + + static CommandLine cmdLine() { + static CommandLine cmd; + + if (cmd.isEmpty()) { + const FilePath dockerExecutable = Environment::systemEnvironment() + .searchInPath("docker", {"/usr/local/bin"}); + const FilePath dashExecutable = Environment::systemEnvironment() + .searchInPath("dash", {"/usr/local/bin"}); + const FilePath bashExecutable = Environment::systemEnvironment() + .searchInPath("bash", {"/usr/local/bin"}); + const FilePath shExecutable = Environment::systemEnvironment() + .searchInPath("sh", {"/usr/local/bin"}); + + if (dockerExecutable.exists()) { + cmd = {dockerExecutable, {"run", "-i", "--rm","alpine"}}; + } else if (dashExecutable.exists()) { + cmd = {dashExecutable, {}}; + } else if (bashExecutable.exists()) { + cmd = {bashExecutable, {}}; + } else if (shExecutable.exists()) { + cmd = {shExecutable, {}}; + } + + if (cmd.isEmpty()) { + return cmd; + } + + qDebug() << "Using shell cmd:" << cmd; + } + + return cmd; + } + +private: + void setupShellProcess(QtcProcess *shellProcess) override + { + shellProcess->setCommand(cmdLine()); + } +}; + +class tst_DeviceShell : public QObject +{ + Q_OBJECT + + QList testArrays(const int numArrays) + { + QRandomGenerator generator; + QList result; + + for (int i = 0; i < numArrays; i++) { + QByteArray data; + auto numLines = generator.bounded(1, 100); + for (int l = 0; l < numLines; l++) { + auto numChars = generator.bounded(10, 40); + for (int c = 0; c < numChars; c++) { + data += static_cast(generator.bounded('a', 'z')); + } + data += '\n'; + } + result.append(data); + } + return result; + } + + void test(int maxNumThreads, int numCalls) + { + TestShell shell; + QCOMPARE(shell.state(), DeviceShell::State::Succeeded); + + QThreadPool::globalInstance()->setMaxThreadCount(maxNumThreads); + + QList testArray = testArrays(numCalls); + + QElapsedTimer t; + t.start(); + + const QList result + = mapped(testArray, [&shell](QByteArray data) -> QByteArray { + return shell.outputForRunInShell({"cat", {}}, data).stdOut; + }, MapReduceOption::Ordered, QThreadPool::globalInstance()); + + QCOMPARE(result, testArray); + + qDebug() << "maxThreads:" << maxNumThreads << ", took:" << t.elapsed() / 1000.0 + << "seconds"; + } + + void testSleep(QList testData, int nThreads) + { + TestShell shell; + QCOMPARE(shell.state(), DeviceShell::State::Succeeded); + + QThreadPool::globalInstance()->setMaxThreadCount(nThreads); + + QElapsedTimer t; + t.start(); + + const auto result = mapped(testData, [&shell](const int &time) { + shell.runInShell({"sleep", {QString("%1").arg(time)}}); + return 0; + }, MapReduceOption::Unordered, QThreadPool::globalInstance()); + + qDebug() << "maxThreads:" << nThreads << ", took:" << t.elapsed() / 1000.0 << "seconds"; + } + +private slots: + void initTestCase() + { + TemporaryDirectory::setMasterTemporaryDirectory( + QDir::tempPath() + "/" + Core::Constants::IDE_CASED_ID + "-XXXXXX"); + + const QString libExecPath(qApp->applicationDirPath() + '/' + + QLatin1String(TEST_RELATIVE_LIBEXEC_PATH)); + LauncherInterface::setPathToLauncher(libExecPath); + + if (TestShell::cmdLine().isEmpty()) { + QSKIP("Skipping deviceshell tests, as no compatible shell could be found"); + } + } + + void cleanupTestCase() { Singleton::deleteAll(); } + + void testEncoding_data() + { + QTest::addColumn("utf8string"); + + QTest::newRow("japanese") << QString::fromUtf8( + u8"\xe8\xac\x9d\xe3\x81\x8d\xe3\x82\x81\xe9\x80\x80\x31\x30\xe8\x89\xaf\xe3\x81\x9a\xe3" + u8"\x82\xa4\xe3\x81\xb5\xe3\x81\x8b\xe7\x89\x88\xe8\x84\xb3\xe3\x83\xa9\xe3\x83\xaf\xe6" + u8"\xad\xa2\xe9\x80\x9a\xe3\x83\xa8\xe3\x83\xb2\xe3\x82\xad\n"); + QTest::newRow("german") << QString::fromUtf8( + u8"\x48\x61\x6c\x6c\xc3\xb6\x2c\x20\x77\x69\x65\x20\x67\xc3\xa4\x68\x74\x20\x65\x73\x20" + u8"\x64\xc3\xbc\x72\n"); + } + + void testEncoding() + { + QFETCH(QString, utf8string); + + TestShell shell; + QCOMPARE(shell.state(), DeviceShell::State::Succeeded); + + const DeviceShell::RunResult r = shell.outputForRunInShell({"cat", {}}, utf8string.toUtf8()); + const QString output = QString::fromUtf8(r.stdOut); + QCOMPARE(output, utf8string); + } + + void testThreading_data() + { + QTest::addColumn("numThreads"); + QTest::addColumn("numIterations"); + + QTest::newRow("multi-threaded") << 10 << 1000; + QTest::newRow("single-threaded") << 1 << 1000; + } + + void testThreading() + { + QFETCH(int, numThreads); + QFETCH(int, numIterations); + + test(numThreads, numIterations); + } + + void testSleepMulti() + { + QList testData{4, 7, 10, 3, 1, 10, 3, 3, 5, 4}; + int full = std::accumulate(testData.begin(), testData.end(), 0); + qDebug() << "Testing sleep, full time is:" << full << "seconds"; + QElapsedTimer t; + t.start(); + testSleep(testData, 10); + const int multiThreadRunTime = t.restart(); + testSleep(testData, 1); + const int singleThreadRunTime = t.elapsed(); + QVERIFY(multiThreadRunTime < singleThreadRunTime); + } +}; + +QTEST_MAIN(tst_DeviceShell) + +#include "tst_deviceshell.moc" diff --git a/tests/manual/manual.qbs b/tests/manual/manual.qbs index 717d3745d5c..f36312fcd69 100644 --- a/tests/manual/manual.qbs +++ b/tests/manual/manual.qbs @@ -8,6 +8,7 @@ Project { references: [ "debugger/gui/gui.qbs", "debugger/simple/simple.qbs", + "deviceshell/deviceshell.qbs", "fakevim/fakevim.qbs", "pluginview/pluginview.qbs", "process/process.qbs", diff --git a/tests/system/shared/suites_qtta.py b/tests/system/shared/suites_qtta.py old mode 100755 new mode 100644 diff --git a/tests/system/suite_CCOM/tst_CCOM01/test.py b/tests/system/suite_CCOM/tst_CCOM01/test.py old mode 100755 new mode 100644 diff --git a/tests/system/suite_CCOM/tst_CCOM02/test.py b/tests/system/suite_CCOM/tst_CCOM02/test.py old mode 100755 new mode 100644 diff --git a/tests/system/suite_HELP/tst_HELP02/test.py b/tests/system/suite_HELP/tst_HELP02/test.py old mode 100755 new mode 100644 diff --git a/tests/system/suite_HELP/tst_HELP05/test.py b/tests/system/suite_HELP/tst_HELP05/test.py old mode 100755 new mode 100644 diff --git a/tests/system/suite_HELP/tst_HELP06/test.py b/tests/system/suite_HELP/tst_HELP06/test.py old mode 100755 new mode 100644 diff --git a/tests/system/suite_WELP/tst_WELP01/test.py b/tests/system/suite_WELP/tst_WELP01/test.py old mode 100755 new mode 100644 diff --git a/tests/system/tools/objectsToTable.py b/tests/system/tools/objectsToTable.py old mode 100755 new mode 100644 diff --git a/tests/unit/unittest/CMakeLists.txt b/tests/unit/unittest/CMakeLists.txt index 502390cedfc..a6b0d3100de 100644 --- a/tests/unit/unittest/CMakeLists.txt +++ b/tests/unit/unittest/CMakeLists.txt @@ -332,7 +332,6 @@ extend_qtc_test(unittest SOURCES_PREFIX ../../../src/plugins/clangcodemodel SOURCES clangactivationsequenceprocessor.cpp clangactivationsequenceprocessor.h - clanguiheaderondiskmanager.cpp clanguiheaderondiskmanager.h ) find_package(yaml-cpp QUIET MODULE) diff --git a/tests/unit/unittest/unittest.qbs b/tests/unit/unittest/unittest.qbs index 598980bec18..00f3e2a8c64 100644 --- a/tests/unit/unittest/unittest.qbs +++ b/tests/unit/unittest/unittest.qbs @@ -203,8 +203,6 @@ Project { files: [ "clangactivationsequenceprocessor.cpp", "clangactivationsequenceprocessor.h", - "clanguiheaderondiskmanager.cpp", - "clanguiheaderondiskmanager.h", ] }