diff --git a/dist/changes-4.15.2.md b/dist/changes-4.15.2.md new file mode 100644 index 00000000000..cf053f0046a --- /dev/null +++ b/dist/changes-4.15.2.md @@ -0,0 +1,55 @@ +Qt Creator 4.15.2 +================= + +Qt Creator version 4.15.2 contains bug fixes. + +The most important changes are listed in this document. For a complete +list of changes, see the Git log for the Qt Creator sources that +you can check out from the public Git repository. For example: + + git clone git://code.qt.io/qt-creator/qt-creator.git + git log --cherry-pick --pretty=oneline origin/v4.15.1..v4.15.2 + +Projects +-------- + +### CMake + +* Improved performance after project load and reparse +* Fixed crash on session switch (QTCREATORBUG-25837) + +### qmake + +* Fixed issues with executing system calls (QTCREATORBUG-25970) + +Test Integration +---------------- + +### CTest + +* Fixed test detection if `ctest` takes long to run (QTCREATORBUG-25851) + +Platforms +--------- + +### WASM + +* Fixed Python version that is on Windows (QTCREATORBUG-25897) + +Credits for these changes go to: +-------------------------------- +Ahmad Samir +Alessandro Portale +Christian Stenger +Cristian Adam +Eike Ziller +Ivan Komissarov +Kai Köhne +Knud Dollereder +Michael Winkelmann +Mitch Curtis +Robert Löhning +Thomas Hartmann +Tim Blechmann +Tim Jenssen +Tuomo Pelkonen diff --git a/doc/qtcreator/images/icons/copy-formatting.png b/doc/qtcreator/images/icons/copy-formatting.png new file mode 100644 index 00000000000..941e051512d Binary files /dev/null and b/doc/qtcreator/images/icons/copy-formatting.png differ diff --git a/doc/qtcreator/images/icons/paste-formatting.png b/doc/qtcreator/images/icons/paste-formatting.png new file mode 100644 index 00000000000..2bd072071d4 Binary files /dev/null and b/doc/qtcreator/images/icons/paste-formatting.png differ diff --git a/doc/qtcreator/images/qtquick-designer-animated-image-properties.png b/doc/qtcreator/images/qtquick-designer-animated-image-properties.png index 01ed4fc86ac..a122bd8c25e 100644 Binary files a/doc/qtcreator/images/qtquick-designer-animated-image-properties.png and b/doc/qtcreator/images/qtquick-designer-animated-image-properties.png differ diff --git a/doc/qtcreator/images/qtquick-designer-border-image-properties.png b/doc/qtcreator/images/qtquick-designer-border-image-properties.png index 6f082870134..3f54dd428cc 100644 Binary files a/doc/qtcreator/images/qtquick-designer-border-image-properties.png and b/doc/qtcreator/images/qtquick-designer-border-image-properties.png differ diff --git a/doc/qtcreator/images/qtquick-designer-image-properties.png b/doc/qtcreator/images/qtquick-designer-image-properties.png index e229869a890..ad01bbf400a 100644 Binary files a/doc/qtcreator/images/qtquick-designer-image-properties.png and b/doc/qtcreator/images/qtquick-designer-image-properties.png differ diff --git a/doc/qtcreator/src/qtquick/library/qtquick-images.qdoc b/doc/qtcreator/src/qtquick/library/qtquick-images.qdoc index c8cec48b890..2f7f0cffa33 100644 --- a/doc/qtcreator/src/qtquick/library/qtquick-images.qdoc +++ b/doc/qtcreator/src/qtquick/library/qtquick-images.qdoc @@ -131,8 +131,9 @@ \section1 Image Alignment - You can align images horizontally and vertically. By default, images are - centered. + You can align images horizontally and vertically in the + \uicontrol {Alignment H} and \uicontrol {Alignment V} + fields. By default, images are centered. Select the \uicontrol Mirrored check box to horizontally invert the image, effectively displaying a mirrored image. @@ -161,8 +162,7 @@ of each image. A source image is broken into 9 regions that are scaled or tiled individually. The corner regions are not scaled at all, while the horizontal and vertical regions are scaled according to the values of the - \uicontrol {Horizontal tile mode} and \uicontrol {Vertical tile mode} field, - or both. + \uicontrol {Tile mode H} and \uicontrol {Tile mode V} field, or both. The \uicontrol Stretch option scales the image to fit the available area. The \uicontrol Repeat option tiles the image until there is no more space. @@ -193,8 +193,10 @@ \image qtquick-designer-animated-image-properties.png "Animated Image properties" - To pause or play the animation, select the \uicontrol Paused or - \uicontrol Playing check boxes. + To play the animation, select the \uicontrol Playing check box. + + To pause the animation, select the \inlineimage icons/pause-icon.png + (\uicontrol Paused) check box. When the \uicontrol Cache check box is selected, every frame of the animation is cached. Deselect the check box if you are playing a long diff --git a/doc/qtcreator/src/qtquick/qtquick-designer.qdoc b/doc/qtcreator/src/qtquick/qtquick-designer.qdoc index fcdcf9c31a3..7536e8c7574 100644 --- a/doc/qtcreator/src/qtquick/qtquick-designer.qdoc +++ b/doc/qtcreator/src/qtquick/qtquick-designer.qdoc @@ -230,6 +230,17 @@ \li Resets anchors to their saved state for the selected component. \li \key Ctrl+Shift+R (\key Shift+Cmd+R on \macos) \li \l{Setting Anchors and Margins} + \row + \li \inlineimage icons/copy-formatting.png + \li Copies property values from the selected component. + \li + \li \l{Copying and Pasting Formatting} + \row + \li \inlineimage icons/paste-formatting.png + \li Applies copied property values to one or several selected + components. + \li + \li \l{Copying and Pasting Formatting} \row \li \inlineimage row.png \li Uses a \uicontrol Row component to lay out the selected components. diff --git a/doc/qtcreator/src/qtquick/qtquick-properties.qdoc b/doc/qtcreator/src/qtquick/qtquick-properties.qdoc index 75b12c420a5..3a55202b9e9 100644 --- a/doc/qtcreator/src/qtquick/qtquick-properties.qdoc +++ b/doc/qtcreator/src/qtquick/qtquick-properties.qdoc @@ -409,6 +409,22 @@ and vertically. Note that some OpenGL ES 2 implementations do not support the \uicontrol Repeat wrap mode with non-power-of-two textures. + \section1 Copying and Pasting Formatting + + You can copy property values from a component and paste them to one or + several other components. The values are applied if the target components + have those particular properties. + + To copy property values from the selected component, select + \inlineimage icons/copy-formatting.png + on the \uicontrol Design mode \l{Summary of Main Toolbar Actions} + {main toolbar}. + + To apply the values to one or several other components, select + them in \l Navigator or \l{Form Editor}, and then select + \inlineimage icons/paste-formatting.png + . + \section1 Editing Properties Inline You can double-click components in \l {Form Editor} to edit their diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/AnimationSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/AnimationSection.qml index e671c73165b..b4dfe0f7a5a 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/AnimationSection.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/AnimationSection.qml @@ -153,16 +153,20 @@ Section { visible: section.showEasingCurve } - BoolButtonRowButton { - visible: section.showEasingCurve - buttonIcon: StudioTheme.Constants.curveDesigner + SecondColumnLayout { + BoolButtonRowButton { + visible: section.showEasingCurve + buttonIcon: StudioTheme.Constants.curveDesigner - EasingCurveEditor { - id: easingCurveEditor - modelNodeBackendProperty: modelNodeBackend + EasingCurveEditor { + id: easingCurveEditor + modelNodeBackendProperty: modelNodeBackend + } + + onClicked: easingCurveEditor.runDialog() } - onClicked: easingCurveEditor.runDialog() + ExpandingSpacer {} } } } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/AnchorButtons.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/AnchorButtons.qml index 3629ca5ab64..e4eb7108d95 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/AnchorButtons.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/AnchorButtons.qml @@ -32,7 +32,6 @@ StudioControls.ButtonRow { id: buttonRow enabled: anchorBackend.hasParent && isBaseState - opacity: enabled ? 1 : 0.5 actionIndicatorVisible: false StudioControls.ButtonGroup { id: group } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/EditableListView.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/EditableListView.qml index f7273cb5d43..7f34034246a 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/EditableListView.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/EditableListView.qml @@ -28,170 +28,212 @@ import QtQuick.Layouts 1.15 import StudioControls 1.0 as StudioControls import StudioTheme 1.0 as StudioTheme -Rectangle { +Item { id: editableListView - property variant backendValue - ExtendedFunctionLogic { id: extFuncLogic backendValue: editableListView.backendValue } + property var backendValue property var model onModelChanged: myRepeater.updateModel() + property alias actionIndicator: actionIndicator + property alias actionIndicatorVisible: actionIndicator.visible + property real __actionIndicatorWidth: StudioTheme.Values.squareComponentWidth + property real __actionIndicatorHeight: StudioTheme.Values.height + property string typeFilter: "QtQuick3D.Material" + property int activatedReason: ComboBox.ActivatedReason.Other + + property bool delegateHover: false + signal add(string value) signal remove(int idx) signal replace(int idx, string value) - property alias actionIndicator: actionIndicator - - property alias actionIndicatorVisible: actionIndicator.visible - property real __actionIndicatorWidth: StudioTheme.Values.squareComponentWidth - property real __actionIndicatorHeight: StudioTheme.Values.height - - property string typeFilter: "QtQuick3D.Material" - - property int activatedReason: ComboBox.ActivatedReason.Other - - color: "transparent" - border.color: StudioTheme.Values.themeControlOutline - border.width: StudioTheme.Values.border - Layout.preferredWidth: StudioTheme.Values.height * 10 Layout.preferredHeight: myColumn.height Component { id: myDelegate - ListViewComboBox { - id: itemFilterComboBox - - property int myIndex: index - property bool empty: itemFilterComboBox.initialModelData === "" - - validator: RegExpValidator { regExp: /(^[a-z_]\w*|^[A-Z]\w*\.{1}([a-z_]\w*\.?)+)/ } - - actionIndicatorVisible: false - typeFilter: editableListView.typeFilter - editText: modelData - initialModelData: modelData - - width: editableListView.width - - onFocusChanged: { - if (itemFilterComboBox.focus) { - myColumn.currentIndex = index - } - - if (itemFilterComboBox.empty && itemFilterComboBox.editText !== "") { - myRepeater.dirty = false - editableListView.add(itemFilterComboBox.editText) - } - } - - onCompressedActivated: { - editableListView.activatedReason = reason - - if (itemFilterComboBox.empty && itemFilterComboBox.editText !== "") { - myRepeater.dirty = false - editableListView.add(itemFilterComboBox.editText) - } else { - editableListView.replace(itemFilterComboBox.myIndex, itemFilterComboBox.editText) - } - } - } - } - - Rectangle { - id: highlightRect - color: "transparent" - border.width: StudioTheme.Values.border - border.color: StudioTheme.Values.themeInteraction - visible: myColumn.currentItem ? myColumn.currentItem.focus : false - x: myColumn.currentItem ? myColumn.currentItem.x : 0 - y: myColumn.currentItem ? myColumn.currentItem.y : 0 - z: 10 - width: myColumn.currentItem ? myColumn.currentItem.width : 0 - height: myColumn.currentItem ? myColumn.currentItem.height : 0 - } - - Column { - id: myColumn - - property int currentIndex: -1 - property Item currentItem - - spacing: -1 - - onCurrentIndexChanged: myColumn.currentItem = myRepeater.itemAt(myColumn.currentIndex) - - Repeater { - id: myRepeater - - property bool dirty: false - property var localModel: [] - - delegate: myDelegate - - onItemAdded: function(index, item) { - if (index === myColumn.currentIndex) - myColumn.currentItem = item - } - - function updateModel() { - var lastIndex = myColumn.currentIndex - myColumn.currentIndex = -1 - myRepeater.localModel = [] - - editableListView.model.forEach(function(item) { - myRepeater.localModel.push(item) - }); - - // if list view is still dirty, then last state had an unfinished/empty ComboBox - if (myRepeater.dirty) - myRepeater.localModel.push("") - - myRepeater.model = myRepeater.localModel // trigger on change handler - - if (lastIndex < 0 && myRepeater.localModel.length > 0) - myColumn.currentIndex = 0 - else if (myRepeater.localModel.length > lastIndex) - myColumn.currentIndex = lastIndex - else - myColumn.currentIndex = myRepeater.localModel.length - 1 - - if (editableListView.activatedReason === ComboBox.ActivatedReason.Other) - myColumn.currentItem.forceActiveFocus() - } - } - - Item { - id: dummyItem - visible: myRepeater.count === 0 - width: StudioTheme.Values.height - height: StudioTheme.Values.height - } Row { - id: row - spacing: -StudioTheme.Values.border + property alias comboBox: itemFilterComboBox + ListViewComboBox { + id: itemFilterComboBox - StudioControls.ActionIndicator { - id: actionIndicator - width: actionIndicator.visible ? __actionIndicatorWidth : 0 - height: actionIndicator.visible ? __actionIndicatorHeight : 0 + property int myIndex: index + property bool empty: itemFilterComboBox.initialModelData === "" - border.width: StudioTheme.Values.border - border.color: StudioTheme.Values.themeControlOutline + validator: RegExpValidator { regExp: /(^[a-z_]\w*|^[A-Z]\w*\.{1}([a-z_]\w*\.?)+)/ } - icon.color: extFuncLogic.color - icon.text: extFuncLogic.glyph - onClicked: extFuncLogic.show() + actionIndicatorVisible: false + typeFilter: editableListView.typeFilter + editText: modelData + initialModelData: modelData + implicitWidth: StudioTheme.Values.singleControlColumnWidth + width: implicitWidth + + onFocusChanged: { + if (itemFilterComboBox.focus) + myColumn.currentIndex = index + + if (itemFilterComboBox.empty && itemFilterComboBox.editText !== "") { + myRepeater.dirty = false + editableListView.add(itemFilterComboBox.editText) + } + } + + onCompressedActivated: { + editableListView.activatedReason = reason + + if (itemFilterComboBox.empty && itemFilterComboBox.editText !== "") { + myRepeater.dirty = false + editableListView.add(itemFilterComboBox.editText) + } else { + editableListView.replace(itemFilterComboBox.myIndex, itemFilterComboBox.editText) + } + } + + onHoverChanged: editableListView.delegateHover = itemFilterComboBox.hover } + + Spacer { implicitWidth: StudioTheme.Values.twoControlColumnGap } + + IconIndicator { + id: closeIndicator + icon: StudioTheme.Constants.closeCross + onClicked: { + var lastItem = index === myRepeater.localModel.length - 1 + if (myColumn.currentItem.initialModelData === "") { + myRepeater.localModel.pop() + myRepeater.dirty = false + myRepeater.model = myRepeater.localModel // trigger on change handler + } else { + editableListView.remove(index) + } + if (!lastItem) + myColumn.currentIndex = index - 1 + } + onHoveredChanged: editableListView.delegateHover = closeIndicator.hovered + } + } + } + + Row { + ActionIndicator { + id: actionIndicator + icon.visible: editableListView.delegateHover + icon.color: extFuncLogic.color + icon.text: extFuncLogic.glyph + onClicked: extFuncLogic.show() + } + + Column { + id: myColumn + + property int currentIndex: -1 + property Item currentItem + + spacing: StudioTheme.Values.sectionRowSpacing + + onCurrentIndexChanged: { + var tmp = myRepeater.itemAt(myColumn.currentIndex) + if (tmp !== null) + myColumn.currentItem = tmp.comboBox + } + + Repeater { + id: myRepeater + + property bool dirty: false + property var localModel: [] + + delegate: myDelegate + + onItemAdded: function(index, item) { + if (index === myColumn.currentIndex) + myColumn.currentItem = item + } + + function updateModel() { + var lastIndex = myColumn.currentIndex + myColumn.currentIndex = -1 + myRepeater.localModel = [] + + editableListView.model.forEach(function(item) { + myRepeater.localModel.push(item) + }); + + // if list view is still dirty, then last state had an unfinished/empty ComboBox + if (myRepeater.dirty) + myRepeater.localModel.push("") + + myRepeater.model = myRepeater.localModel // trigger on change handler + + if (lastIndex < 0 && myRepeater.localModel.length > 0) + myColumn.currentIndex = 0 + else if (myRepeater.localModel.length > lastIndex) + myColumn.currentIndex = lastIndex + else + myColumn.currentIndex = myRepeater.localModel.length - 1 + + if (editableListView.activatedReason === ComboBox.ActivatedReason.Other + && myColumn.currentItem !== null) + myColumn.currentItem.forceActiveFocus() + } + } + + ListViewComboBox { + id: dummyComboBox + + //property int myIndex: index + //property bool empty: dummyComboBox.initialModelData === "" + + visible: myRepeater.count === 0 + + validator: RegExpValidator { regExp: /(^[a-z_]\w*|^[A-Z]\w*\.{1}([a-z_]\w*\.?)+)/ } + + actionIndicatorVisible: false + typeFilter: editableListView.typeFilter + //editText: modelData + //initialModelData: modelData + implicitWidth: StudioTheme.Values.singleControlColumnWidth + width: implicitWidth + + onFocusChanged: { + //if (itemFilterComboBox.focus) + // myColumn.currentIndex = index + + if (/*dummyComboBox.empty && */dummyComboBox.editText !== "") { + //myRepeater.dirty = false + editableListView.add(dummyComboBox.editText) + } + } + + onCompressedActivated: { + editableListView.activatedReason = reason + + if (/*dummyComboBox.empty && */dummyComboBox.editText !== "") { + //myRepeater.dirty = false + editableListView.add(dummyComboBox.editText) + } else { + editableListView.replace(dummyComboBox.myIndex, dummyComboBox.editText) + } + } + + onHoverChanged: editableListView.delegateHover = dummyComboBox.hover + } + + + StudioControls.AbstractButton { + id: plusButton buttonIcon: StudioTheme.Constants.plus - enabled: !myRepeater.dirty && !(editableListView.backendValue.isInModel && !editableListView.backendValue.isIdList) + enabled: !myRepeater.dirty && !(editableListView.backendValue.isInModel + && !editableListView.backendValue.isIdList) onClicked: { var idx = myRepeater.localModel.push("") - 1 myRepeater.model = myRepeater.localModel // trigger on change handler @@ -199,29 +241,7 @@ Rectangle { myColumn.currentIndex = idx myColumn.currentItem.forceActiveFocus() } - } - StudioControls.AbstractButton { - buttonIcon: StudioTheme.Constants.minus - enabled: myRepeater.model.length && !(editableListView.backendValue.isInModel && !editableListView.backendValue.isIdList) - onClicked: { - var lastItem = myColumn.currentIndex === myRepeater.localModel.length - 1 - if (myColumn.currentItem.initialModelData === "") { - myRepeater.localModel.pop() - myRepeater.dirty = false - myRepeater.model = myRepeater.localModel // trigger on change handler - } else { - editableListView.remove(myColumn.currentIndex) - } - if (!lastItem) - myColumn.currentIndex = myColumn.currentIndex - 1 - } - } - Rectangle { - color: StudioTheme.Values.themeControlBackground - border.width: StudioTheme.Values.border - border.color: StudioTheme.Values.themeControlOutline - height: StudioTheme.Values.height - width: editableListView.width - (StudioTheme.Values.height - StudioTheme.Values.border) * (actionIndicatorVisible ? 3 : 2) + onHoveredChanged: editableListView.delegateHover = plusButton.hovered } } } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ExtendedFunctionLogic.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ExtendedFunctionLogic.qml index 94308e63b3b..d3b6a9deeca 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ExtendedFunctionLogic.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ExtendedFunctionLogic.qml @@ -97,7 +97,7 @@ Item { Connections { target: modelNodeBackend - onSelectionChanged: menu.close() + function onSelectionChanged() { menu.close() } } StudioControls.MenuItem { diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/IconIndicator.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/IconIndicator.qml index 4a1487f4eaa..235f3796136 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/IconIndicator.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/IconIndicator.qml @@ -37,6 +37,8 @@ Rectangle { property alias pixelSize: indicatorIcon.font.pixelSize property alias tooltip: toolTipArea.tooltip + property bool hovered: toolTipArea.containsMouse && root.enabled + signal clicked() color: "transparent" diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/RealSpinBoxIndicator.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/RealSpinBoxIndicator.qml index 9ca34c16802..26373f4db19 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/RealSpinBoxIndicator.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/RealSpinBoxIndicator.qml @@ -258,7 +258,7 @@ Rectangle { } PropertyChanges { target: spinBoxIndicator - color: StudioTheme.Values.themeControlBackground + color: StudioTheme.Values.themeControlBackgroundDisabled } }, State { diff --git a/share/qtcreator/themes/default.creatortheme b/share/qtcreator/themes/default.creatortheme index 6b856d7f34e..0eb98021ba8 100644 --- a/share/qtcreator/themes/default.creatortheme +++ b/share/qtcreator/themes/default.creatortheme @@ -22,7 +22,7 @@ DSdisabledColor=ff8e8e8e DScontrolBackground=ffeaeaea DScontrolBackgroundInteraction=ffc9c9c9 -DScontrolBackgroundDisabled=ff8e8e8e +DScontrolBackgroundDisabled=ffeaeaea DScontrolBackgroundGlobalHover=ffe5e5e5 DScontrolBackgroundHover=ffd1d1d1 diff --git a/share/qtcreator/themes/design-light.creatortheme b/share/qtcreator/themes/design-light.creatortheme index 17783db3c0c..ef1b7bcbb1b 100644 --- a/share/qtcreator/themes/design-light.creatortheme +++ b/share/qtcreator/themes/design-light.creatortheme @@ -36,7 +36,7 @@ DSdisabledColor=ff8e8e8e DScontrolBackground=ffeaeaea DScontrolBackgroundInteraction=ffc9c9c9 -DScontrolBackgroundDisabled=ff8e8e8e +DScontrolBackgroundDisabled=ffeaeaea DScontrolBackgroundGlobalHover=ffe5e5e5 DScontrolBackgroundHover=ffd1d1d1 diff --git a/share/qtcreator/themes/flat-light.creatortheme b/share/qtcreator/themes/flat-light.creatortheme index 123c680fc6a..1fa9747d3a3 100644 --- a/share/qtcreator/themes/flat-light.creatortheme +++ b/share/qtcreator/themes/flat-light.creatortheme @@ -31,7 +31,7 @@ DSdisabledColor=ff8e8e8e DScontrolBackground=ffeaeaea DScontrolBackgroundInteraction=ffc9c9c9 -DScontrolBackgroundDisabled=ff8e8e8e +DScontrolBackgroundDisabled=ffeaeaea DScontrolBackgroundGlobalHover=ffe5e5e5 DScontrolBackgroundHover=ffd1d1d1 diff --git a/src/libs/utils/fileutils.cpp b/src/libs/utils/fileutils.cpp index c739b74ec31..87ffc501492 100644 --- a/src/libs/utils/fileutils.cpp +++ b/src/libs/utils/fileutils.cpp @@ -1374,17 +1374,17 @@ FilePath FilePath::onDevice(const FilePath &deviceTemplate) const Example usage: \code binary = FilePath::fromUrl("docker://123/./make); - fullPath = binary.onDeviceSearchInPath(); + fullPath = binary.searchOnDevice(); assert(fullPath == FilePath::fromUrl("docker://123/usr/bin/make")) \endcode */ -FilePath FilePath::onDeviceSearchInPath(const FilePaths &additionalDirs) const +FilePath FilePath::searchOnDevice(const FilePaths &dirs) const { if (needsDevice()) { QTC_ASSERT(s_deviceHooks.searchInPath, return {}); - return s_deviceHooks.searchInPath(*this, additionalDirs); + return s_deviceHooks.searchInPath(*this, dirs); } - return Environment::systemEnvironment().searchInPath(path(), additionalDirs); + return Environment::systemEnvironment().searchInPath(path(), dirs); } Environment FilePath::deviceEnvironment() const diff --git a/src/libs/utils/fileutils.h b/src/libs/utils/fileutils.h index 24da9d380d1..70988a3002d 100644 --- a/src/libs/utils/fileutils.h +++ b/src/libs/utils/fileutils.h @@ -216,7 +216,7 @@ public: static void setDeviceFileHooks(const DeviceFileHooks &hooks); - FilePath onDeviceSearchInPath(const QList &additionalDirs = {}) const; + FilePath searchOnDevice(const QList &dirs) const; Environment deviceEnvironment() const; private: diff --git a/src/plugins/autotest/qtest/qttestoutputreader.cpp b/src/plugins/autotest/qtest/qttestoutputreader.cpp index a15a0fc1d4d..a6be21019d9 100644 --- a/src/plugins/autotest/qtest/qttestoutputreader.cpp +++ b/src/plugins/autotest/qtest/qttestoutputreader.cpp @@ -196,6 +196,8 @@ void QtTestOutputReader::processXMLOutput(const QByteArray &outputLine) break; } } + if (m_cdataMode == Description) + m_xmlReader.addData("\n"); m_xmlReader.addData(QString::fromUtf8(outputLine)); while (!m_xmlReader.atEnd()) { if (m_futureInterface.isCanceled()) diff --git a/src/plugins/autotest/qtest/qttestparser.cpp b/src/plugins/autotest/qtest/qttestparser.cpp index 18e0f48ca5e..e6dfb7802a0 100644 --- a/src/plugins/autotest/qtest/qttestparser.cpp +++ b/src/plugins/autotest/qtest/qttestparser.cpp @@ -410,8 +410,7 @@ QtTestParseResult *QtTestParser::createParseResult( dataTag->itemType = tag.m_type; dataTag->name = tag.m_name; dataTag->displayName = tag.m_name; - dataTag->fileName = Utils::FilePath::fromString( - data.testFunctions.value(it.key() + "_data").m_name); + dataTag->fileName = data.testFunctions.value(it.key() + "_data").m_filePath; dataTag->line = tag.m_line; dataTag->column = tag.m_column; dataTag->setInherited(tag.m_inherited); diff --git a/src/plugins/clangcodemodel/clangdclient.cpp b/src/plugins/clangcodemodel/clangdclient.cpp index a427006d05a..ae1e29a8eb8 100644 --- a/src/plugins/clangcodemodel/clangdclient.cpp +++ b/src/plugins/clangcodemodel/clangdclient.cpp @@ -839,13 +839,15 @@ void ClangdClient::openExtraFile(const Utils::FilePath &filePath, const QString item.setUri(DocumentUri::fromFilePath(filePath)); item.setText(!content.isEmpty() ? content : QString::fromUtf8(cxxFile.readAll())); item.setVersion(0); - sendContent(DidOpenTextDocumentNotification(DidOpenTextDocumentParams(item))); + sendContent(DidOpenTextDocumentNotification(DidOpenTextDocumentParams(item)), + SendDocUpdates::Ignore); } void ClangdClient::closeExtraFile(const Utils::FilePath &filePath) { sendContent(DidCloseTextDocumentNotification(DidCloseTextDocumentParams( - TextDocumentIdentifier{DocumentUri::fromFilePath(filePath)}))); + TextDocumentIdentifier{DocumentUri::fromFilePath(filePath)})), + SendDocUpdates::Ignore); } void ClangdClient::findUsages(TextEditor::TextDocument *document, const QTextCursor &cursor, @@ -1036,7 +1038,7 @@ void ClangdClient::Private::handleFindUsagesResult(quint64 key, const QListpendingAstRequests << request.id(); - q->sendContent(request); + q->sendContent(request, SendDocUpdates::Ignore); if (extraOpen) q->closeExtraFile(it.key().toFilePath()); @@ -1194,7 +1196,7 @@ void ClangdClient::followSymbol( if (d->followSymbolData->defLink.hasValidTarget()) d->handleGotoDefinitionResult(); }); - sendContent(astRequest); + sendContent(astRequest, SendDocUpdates::Ignore); } void ClangdClient::switchDeclDef(TextEditor::TextDocument *document, const QTextCursor &cursor, @@ -1228,7 +1230,7 @@ void ClangdClient::switchDeclDef(TextEditor::TextDocument *document, const QText d->handleDeclDefSwitchReplies(); }); - sendContent(astRequest); + sendContent(astRequest, SendDocUpdates::Ignore); documentSymbolCache()->requestSymbols(d->switchDeclDefData->uri); } @@ -1315,8 +1317,7 @@ void ClangdClient::findLocalUsages(TextEditor::TextDocument *document, const QTe d->localRefsData.reset(); }); qCDebug(clangdLog) << "sending ast request for link"; - sendContent(astRequest); - + sendContent(astRequest, SendDocUpdates::Ignore); }; symbolSupport().findLinkAt(document, cursor, std::move(gotoDefCallback), true); } @@ -1401,7 +1402,7 @@ void ClangdClient::gatherHelpItemForTooltip(const HoverRequest::Response &hoverR // with mainOverload = true, such information would get ignored anyway. d->setHelpItemForTooltip(id, fqn, HelpItem::Function, isFunction ? type : "()"); }); - sendContent(symReq); + sendContent(symReq, SendDocUpdates::Ignore); return; } if ((node.role() == "expression" && node.kind() == "DeclRef") @@ -1466,7 +1467,7 @@ void ClangdClient::gatherHelpItemForTooltip(const HoverRequest::Response &hoverR } d->setHelpItemForTooltip(id); }); - sendContent(req); + sendContent(req, SendDocUpdates::Ignore); } void ClangdClient::Private::handleGotoDefinitionResult() @@ -1507,7 +1508,7 @@ void ClangdClient::Private::sendGotoImplementationRequest(const Utils::Link &lin followSymbolData->pendingGotoImplRequests.removeOne(reqId); handleGotoImplementationResult(response); }); - q->sendContent(req); + q->sendContent(req, SendDocUpdates::Ignore); followSymbolData->pendingGotoImplRequests << req.id(); qCDebug(clangdLog) << "sending go to implementation request" << link.targetLine; } @@ -1594,7 +1595,7 @@ void ClangdClient::Private::handleGotoImplementationResult( }); followSymbolData->pendingSymbolInfoRequests << symReq.id(); qCDebug(clangdLog) << "sending symbol info request"; - q->sendContent(symReq); + q->sendContent(symReq, SendDocUpdates::Ignore); if (link == followSymbolData->defLink) continue; @@ -1628,7 +1629,7 @@ void ClangdClient::Private::handleGotoImplementationResult( followSymbolData->pendingGotoDefRequests << defReq.id(); qCDebug(clangdLog) << "sending additional go to definition request" << link.targetFilePath << link.targetLine; - q->sendContent(defReq); + q->sendContent(defReq, SendDocUpdates::Ignore); } const DocumentUri defLinkUri @@ -1651,7 +1652,7 @@ void ClangdClient::Private::handleGotoImplementationResult( } }); qCDebug(clangdLog) << "sending ast request for def link"; - q->sendContent(astRequest); + q->sendContent(astRequest, SendDocUpdates::Ignore); } void ClangdClient::Private::handleDocumentInfoResults() @@ -2385,7 +2386,7 @@ void ClangdClient::Private::handleSemanticTokens(TextEditor::TextDocument *doc, it->second.setHighlightingRunner(runner); it->second.run(); }); - q->sendContent(astReq); + q->sendContent(astReq, SendDocUpdates::Ignore); } void ClangdClient::VirtualFunctionAssistProcessor::cancel() diff --git a/src/plugins/clangcodemodel/clangdiagnosticmanager.h b/src/plugins/clangcodemodel/clangdiagnosticmanager.h index 5c2ed3c8db4..e3b8537e6a7 100644 --- a/src/plugins/clangcodemodel/clangdiagnosticmanager.h +++ b/src/plugins/clangcodemodel/clangdiagnosticmanager.h @@ -65,11 +65,11 @@ public: static void clearTaskHubIssues(); void generateTaskHubIssues(); + void cleanMarks(); static void addTask(const ClangBackEnd::DiagnosticContainer &diagnostic, bool isChild = false); private: - void cleanMarks(); QString filePath() const; void filterDiagnostics(const QVector &diagnostics); void generateEditorSelections(); diff --git a/src/plugins/clangcodemodel/clangeditordocumentprocessor.cpp b/src/plugins/clangcodemodel/clangeditordocumentprocessor.cpp index 8e4a83c7be7..9306d8c67e9 100644 --- a/src/plugins/clangcodemodel/clangeditordocumentprocessor.cpp +++ b/src/plugins/clangcodemodel/clangeditordocumentprocessor.cpp @@ -251,6 +251,14 @@ void ClangEditorDocumentProcessor::generateTaskHubIssues() m_diagnosticManager.generateTaskHubIssues(); } +void ClangEditorDocumentProcessor::clearTextMarks(const Utils::FilePath &filePath) +{ + if (ClangEditorDocumentProcessor * const proc = get(filePath.toString())) { + proc->m_diagnosticManager.cleanMarks(); + emit proc->codeWarningsUpdated(proc->revision(), {}, {}, {}); + } +} + void ClangEditorDocumentProcessor::updateHighlighting( const QVector &tokenInfos, const QVector &skippedPreprocessorRanges, diff --git a/src/plugins/clangcodemodel/clangeditordocumentprocessor.h b/src/plugins/clangcodemodel/clangeditordocumentprocessor.h index 90890c47102..bedf3fecafe 100644 --- a/src/plugins/clangcodemodel/clangeditordocumentprocessor.h +++ b/src/plugins/clangcodemodel/clangeditordocumentprocessor.h @@ -108,6 +108,8 @@ public: static void clearTaskHubIssues(); void generateTaskHubIssues(); + static void clearTextMarks(const Utils::FilePath &filePath); + public: static ClangEditorDocumentProcessor *get(const QString &filePath); diff --git a/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp b/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp index 90be3893dfc..1787a0034eb 100644 --- a/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp +++ b/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp @@ -316,6 +316,7 @@ void ClangModelManagerSupport::updateLanguageClient(ProjectExplorer::Project *pr if (!project->isKnownFile(entry->fileName())) continue; client->openDocument(textDocument); + ClangEditorDocumentProcessor::clearTextMarks(textDocument->filePath()); hasDocuments = true; } diff --git a/src/plugins/docker/dockerdevice.cpp b/src/plugins/docker/dockerdevice.cpp index d90c83ee485..6136591de93 100644 --- a/src/plugins/docker/dockerdevice.cpp +++ b/src/plugins/docker/dockerdevice.cpp @@ -500,7 +500,7 @@ QList KitDetectorPrivate::autoDetectQtVersions() const emit q->logOutput('\n' + tr("Searching Qt installations...")); for (const QString &candidate : candidates) { emit q->logOutput(tr("Searching for %1 executable...").arg(candidate)); - const FilePath qmake = m_device->searchInPath(FilePath::fromString(candidate)); + const FilePath qmake = m_device->searchExecutableInPath(candidate); if (qmake.isEmpty()) continue; BaseQtVersion *qtVersion = QtVersionFactory::createQtVersionFromQMakePath(qmake, false, m_sharedId, &error); @@ -546,8 +546,8 @@ void KitDetectorPrivate::autoDetectCMake() QString error; const QStringList candidates = {"cmake"}; for (const QString &candidate : candidates) { - const FilePath cmake = m_device->searchInPath(FilePath::fromString(candidate)); - if (cmake.isExecutableFile()) { + const FilePath cmake = m_device->searchExecutableInPath(candidate); + if (!cmake.isEmpty()) { emit q->logOutput(tr("Found CMake binary: %1").arg(cmake.toUserOutput())); const bool res = QMetaObject::invokeMethod(cmakeManager, "registerCMakeByPath", @@ -1046,35 +1046,6 @@ FilePath DockerDevice::symLinkTarget(const FilePath &filePath) const return {}; } -FilePath DockerDevice::searchInPath(const FilePath &filePath, const FilePaths &additionalDirs) const -{ - QTC_ASSERT(handlesFile(filePath), return {}); - tryCreateLocalFileAccess(); - - const QString path = filePath.path(); - - // FIXME: Check whether local search via deviceEnvironment/PATH is faster? - CommandLine dcmd{"docker", {"exec", d->m_container, "which", path}}; - QtcProcess proc; - proc.setCommand(dcmd); - proc.start(); - proc.waitForFinished(); - - LOG("Run sync:" << dcmd.toUserOutput() << " result: " << proc.exitCode()); - if (proc.exitCode() == 0) { - const QString output = proc.stdOut().trimmed(); - return mapToGlobalPath(FilePath::fromString(output)); - } - - for (const FilePath &dir : additionalDirs) { - const FilePath candidate = dir / filePath.path(); - if (candidate.exists()) - return candidate; - } - - return {}; -} - FilePaths DockerDevice::directoryEntries(const FilePath &filePath, const QStringList &nameFilters, QDir::Filters filters, diff --git a/src/plugins/docker/dockerdevice.h b/src/plugins/docker/dockerdevice.h index 0c994e73082..471d5f80273 100644 --- a/src/plugins/docker/dockerdevice.h +++ b/src/plugins/docker/dockerdevice.h @@ -88,8 +88,6 @@ public: bool removeRecursively(const Utils::FilePath &filePath) const override; bool copyFile(const Utils::FilePath &filePath, const Utils::FilePath &target) const override; bool renameFile(const Utils::FilePath &filePath, const Utils::FilePath &target) const override; - Utils::FilePath searchInPath(const Utils::FilePath &filePath, - const Utils::FilePaths &additionalDirs) const override; Utils::FilePath symLinkTarget(const Utils::FilePath &filePath) const override; QList directoryEntries(const Utils::FilePath &filePath, const QStringList &nameFilters, diff --git a/src/plugins/languageclient/client.cpp b/src/plugins/languageclient/client.cpp index a7868be19ab..aaf8d3caba5 100644 --- a/src/plugins/languageclient/client.cpp +++ b/src/plugins/languageclient/client.cpp @@ -400,11 +400,12 @@ void Client::openDocument(TextEditor::TextDocument *document) } } -void Client::sendContent(const IContent &content) +void Client::sendContent(const IContent &content, SendDocUpdates sendUpdates) { QTC_ASSERT(m_clientInterface, return); QTC_ASSERT(m_state == Initialized, return); - sendPostponedDocumentUpdates(); + if (sendUpdates == SendDocUpdates::Send) + sendPostponedDocumentUpdates(); if (Utils::optional responseHandler = content.responseHandler()) m_responseHandlers[responseHandler->id] = responseHandler->callback; QString error; @@ -418,7 +419,7 @@ void Client::sendContent(const IContent &content) void Client::cancelRequest(const MessageId &id) { m_responseHandlers.remove(id); - sendContent(CancelRequest(CancelParameter(id))); + sendContent(CancelRequest(CancelParameter(id)), SendDocUpdates::Ignore); } void Client::closeDocument(TextEditor::TextDocument *document) diff --git a/src/plugins/languageclient/client.h b/src/plugins/languageclient/client.h index 650f0155102..c783e76aafa 100644 --- a/src/plugins/languageclient/client.h +++ b/src/plugins/languageclient/client.h @@ -92,7 +92,11 @@ public: Utils::Id id() const { return m_id; } void setName(const QString &name) { m_displayName = name; } QString name() const; - void sendContent(const LanguageServerProtocol::IContent &content); + + enum class SendDocUpdates { Send, Ignore }; + void sendContent(const LanguageServerProtocol::IContent &content, + SendDocUpdates sendUpdates = SendDocUpdates::Send); + void cancelRequest(const LanguageServerProtocol::MessageId &id); // server state handling diff --git a/src/plugins/languageclient/languageclientoutline.cpp b/src/plugins/languageclient/languageclientoutline.cpp index 4257efb1ff8..a9c3bf5b8b6 100644 --- a/src/plugins/languageclient/languageclientoutline.cpp +++ b/src/plugins/languageclient/languageclientoutline.cpp @@ -283,7 +283,7 @@ private: void updateModel(const DocumentUri &resultUri, const DocumentSymbolsResult &result); void updateEntry(); void activateEntry(); - void requestSymbols(); + void documentUpdated(TextEditor::TextDocument *document); LanguageClientOutlineModel m_model; QPointer m_client; @@ -318,13 +318,12 @@ OutlineComboBox::OutlineComboBox(Client *client, TextEditor::BaseTextEditor *edi connect(client->documentSymbolCache(), &DocumentSymbolCache::gotSymbols, this, &OutlineComboBox::updateModel); - connect(editor->textDocument(), &TextEditor::TextDocument::contentsChanged, - this, &OutlineComboBox::requestSymbols); + connect(client, &Client::documentUpdated, this, &OutlineComboBox::documentUpdated); connect(m_editorWidget, &TextEditor::TextEditorWidget::cursorPositionChanged, this, &OutlineComboBox::updateEntry); connect(this, QOverload::of(&QComboBox::activated), this, &OutlineComboBox::activateEntry); - requestSymbols(); + documentUpdated(editor->textDocument()); } void OutlineComboBox::updateModel(const DocumentUri &resultUri, const DocumentSymbolsResult &result) @@ -365,9 +364,9 @@ void OutlineComboBox::activateEntry() } } -void OutlineComboBox::requestSymbols() +void OutlineComboBox::documentUpdated(TextEditor::TextDocument *document) { - if (m_client) + if (document == m_editorWidget->textDocument()) m_client->documentSymbolCache()->requestSymbols(m_uri); } diff --git a/src/plugins/languageclient/semantichighlightsupport.cpp b/src/plugins/languageclient/semantichighlightsupport.cpp index 3aafaf5b42f..ea5adceccd1 100644 --- a/src/plugins/languageclient/semantichighlightsupport.cpp +++ b/src/plugins/languageclient/semantichighlightsupport.cpp @@ -371,11 +371,11 @@ SemanticRequestTypes SemanticTokenSupport::supportedSemanticRequests(TextDocumen void SemanticTokenSupport::handleSemanticTokens(const Utils::FilePath &filePath, const SemanticTokensResult &result) { - if (auto tokens = Utils::get_if(&result)) + if (auto tokens = Utils::get_if(&result)) { m_tokens[filePath] = *tokens; - else - m_tokens.remove(filePath); - highlight(filePath); + highlight(filePath); + } + m_tokens.remove(filePath); } void SemanticTokenSupport::handleSemanticTokensDelta( @@ -427,6 +427,7 @@ void SemanticTokenSupport::handleSemanticTokensDelta( tokens.setResultId(tokensDelta->resultId()); } else { m_tokens.remove(filePath); + return; } highlight(filePath); } diff --git a/src/plugins/nim/project/nimtoolchainfactory.cpp b/src/plugins/nim/project/nimtoolchainfactory.cpp index 7876cd30a68..b55a9e9119b 100644 --- a/src/plugins/nim/project/nimtoolchainfactory.cpp +++ b/src/plugins/nim/project/nimtoolchainfactory.cpp @@ -58,7 +58,7 @@ QList NimToolChainFactory::autoDetect(const QList &alr QList result; IDevice::ConstPtr dev = device ? device : DeviceManager::defaultDesktopDevice(); - const FilePath compilerPath = dev->searchInPath(FilePath::fromString("nim")); + const FilePath compilerPath = dev->searchExecutableInPath("nim"); if (compilerPath.isEmpty()) return result; diff --git a/src/plugins/projectexplorer/abi.cpp b/src/plugins/projectexplorer/abi.cpp index 0fa05caf3b2..0582fb4d3aa 100644 --- a/src/plugins/projectexplorer/abi.cpp +++ b/src/plugins/projectexplorer/abi.cpp @@ -1168,11 +1168,8 @@ Abis Abi::abisOfBinary(const Utils::FilePath &path) && getUint8(data, 6) == '>' && getUint8(data, 7) == 0x0a) { // We got an ar file: possibly a static lib for ELF, PE or Mach-O - // FIXME: Implement remote - QTC_ASSERT(!path.needsDevice(), return tmp); QFile f(path.toString()); - if (!f.open(QFile::ReadOnly)) - return tmp; + const bool canRead = f.open(QFile::ReadOnly); data = data.mid(8); // Cut of ar file magic quint64 offset = 8; @@ -1199,6 +1196,11 @@ Abis Abi::abisOfBinary(const Utils::FilePath &path) if (!tmp.isEmpty() && tmp.at(0).binaryFormat() != MachOFormat) break; + if (!canRead) { + // FIXME: Implement remote + QTC_ASSERT(!path.needsDevice(), return {}); + } + offset += (offset % 2); // ar is 2 byte aligned f.seek(offset); data = f.read(1024); diff --git a/src/plugins/projectexplorer/devicesupport/desktopdevice.cpp b/src/plugins/projectexplorer/devicesupport/desktopdevice.cpp index a11095435c1..5ee6d5b4bf4 100644 --- a/src/plugins/projectexplorer/devicesupport/desktopdevice.cpp +++ b/src/plugins/projectexplorer/devicesupport/desktopdevice.cpp @@ -43,6 +43,7 @@ #include #include +#include using namespace ProjectExplorer::Constants; using namespace Utils; @@ -194,4 +195,101 @@ Environment DesktopDevice::systemEnvironment() const return Environment::systemEnvironment(); } +bool DesktopDevice::isExecutableFile(const FilePath &filePath) const +{ + QTC_ASSERT(handlesFile(filePath), return false); + return filePath.isExecutableFile(); +} + +bool DesktopDevice::isReadableFile(const FilePath &filePath) const +{ + QTC_ASSERT(handlesFile(filePath), return false); + return filePath.isReadableFile(); +} + +bool DesktopDevice::isWritableFile(const Utils::FilePath &filePath) const +{ + QTC_ASSERT(handlesFile(filePath), return false); + return filePath.isWritableFile(); +} + +bool DesktopDevice::isReadableDirectory(const FilePath &filePath) const +{ + QTC_ASSERT(handlesFile(filePath), return false); + return filePath.isReadableDir(); +} + +bool DesktopDevice::isWritableDirectory(const FilePath &filePath) const +{ + QTC_ASSERT(handlesFile(filePath), return false); + return filePath.isWritableDir(); +} + +bool DesktopDevice::createDirectory(const FilePath &filePath) const +{ + QTC_ASSERT(handlesFile(filePath), return false); + return filePath.createDir(); +} + +bool DesktopDevice::exists(const FilePath &filePath) const +{ + QTC_ASSERT(handlesFile(filePath), return false); + return filePath.exists(); +} + +bool DesktopDevice::ensureExistingFile(const FilePath &filePath) const +{ + QTC_ASSERT(handlesFile(filePath), return false); + return filePath.ensureExistingFile(); +} + +bool DesktopDevice::removeFile(const FilePath &filePath) const +{ + QTC_ASSERT(handlesFile(filePath), return false); + return filePath.removeFile(); +} + +bool DesktopDevice::removeRecursively(const FilePath &filePath) const +{ + QTC_ASSERT(handlesFile(filePath), return false); + return filePath.removeRecursively(); +} + +bool DesktopDevice::copyFile(const FilePath &filePath, const FilePath &target) const +{ + QTC_ASSERT(handlesFile(filePath), return false); + return filePath.copyFile(target); +} + +bool DesktopDevice::renameFile(const FilePath &filePath, const FilePath &target) const +{ + QTC_ASSERT(handlesFile(filePath), return false); + QTC_ASSERT(handlesFile(target), return false); + return filePath.renameFile(target); +} + +QDateTime DesktopDevice::lastModified(const FilePath &filePath) const +{ + QTC_ASSERT(handlesFile(filePath), return {}); + return filePath.lastModified(); +} + +FilePath DesktopDevice::symLinkTarget(const FilePath &filePath) const +{ + QTC_ASSERT(handlesFile(filePath), return {}); + return filePath.symLinkTarget(); +} + +QByteArray DesktopDevice::fileContents(const FilePath &filePath, int limit) const +{ + QTC_ASSERT(handlesFile(filePath), return {}); + return filePath.fileContents(limit); +} + +bool DesktopDevice::writeFileContents(const Utils::FilePath &filePath, const QByteArray &data) const +{ + QTC_ASSERT(handlesFile(filePath), return {}); + return filePath.writeFileContents(data); +} + } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/devicesupport/desktopdevice.h b/src/plugins/projectexplorer/devicesupport/desktopdevice.h index 9c1aac3af1f..a1a5379f00f 100644 --- a/src/plugins/projectexplorer/devicesupport/desktopdevice.h +++ b/src/plugins/projectexplorer/devicesupport/desktopdevice.h @@ -54,13 +54,29 @@ public: DeviceProcessSignalOperation::Ptr signalOperation() const override; DeviceEnvironmentFetcher::Ptr environmentFetcher() const override; QUrl toolControlChannel(const ControlChannelHint &) const override; + bool handlesFile(const Utils::FilePath &filePath) const override; QList directoryEntries(const Utils::FilePath &filePath, const QStringList &nameFilters, QDir::Filters filters, QDir::SortFlags sort) const override; Utils::Environment systemEnvironment() const override; - + bool isExecutableFile(const Utils::FilePath &filePath) const override; + bool isReadableFile(const Utils::FilePath &filePath) const override; + bool isWritableFile(const Utils::FilePath &filePath) const override; + bool isReadableDirectory(const Utils::FilePath &filePath) const override; + bool isWritableDirectory(const Utils::FilePath &filePath) const override; + bool ensureExistingFile(const Utils::FilePath &filePath) const override; + bool createDirectory(const Utils::FilePath &filePath) const override; + bool exists(const Utils::FilePath &filePath) const override; + bool removeFile(const Utils::FilePath &filePath) const override; + bool removeRecursively(const Utils::FilePath &filePath) const override; + bool copyFile(const Utils::FilePath &filePath, const Utils::FilePath &target) const override; + bool renameFile(const Utils::FilePath &filePath, const Utils::FilePath &target) const override; + QDateTime lastModified(const Utils::FilePath &filePath) const override; + Utils::FilePath symLinkTarget(const Utils::FilePath &filePath) const override; + QByteArray fileContents(const Utils::FilePath &filePath, int limit) const override; + bool writeFileContents(const Utils::FilePath &filePath, const QByteArray &data) const override; protected: DesktopDevice(); diff --git a/src/plugins/projectexplorer/devicesupport/devicemanager.cpp b/src/plugins/projectexplorer/devicesupport/devicemanager.cpp index 3d7439522dd..6bf8249695c 100644 --- a/src/plugins/projectexplorer/devicesupport/devicemanager.cpp +++ b/src/plugins/projectexplorer/devicesupport/devicemanager.cpp @@ -458,10 +458,10 @@ DeviceManager::DeviceManager(bool isInstance) : d(std::make_uniquerenameFile(filePath, target); }; - deviceHooks.searchInPath = [](const FilePath &filePath, const FilePaths &additionalDirs) { + deviceHooks.searchInPath = [](const FilePath &filePath, const FilePaths &dirs) { auto device = DeviceManager::deviceForPath(filePath); QTC_ASSERT(device, return FilePath{}); - return device->searchInPath(filePath, additionalDirs); + return device->searchExecutable(filePath.path(), dirs); }; deviceHooks.symLinkTarget = [](const FilePath &filePath) { diff --git a/src/plugins/projectexplorer/devicesupport/idevice.cpp b/src/plugins/projectexplorer/devicesupport/idevice.cpp index d51040d5862..bb6219dc666 100644 --- a/src/plugins/projectexplorer/devicesupport/idevice.cpp +++ b/src/plugins/projectexplorer/devicesupport/idevice.cpp @@ -311,9 +311,26 @@ bool IDevice::renameFile(const FilePath &filePath, const FilePath &target) const return false; } -FilePath IDevice::searchInPath(const FilePath &filePath, const FilePaths &additionalDirs) const +FilePath IDevice::searchExecutableInPath(const QString &fileName) const { - return Environment::systemEnvironment().searchInPath(filePath.path()); + FilePaths paths; + for (const FilePath &path : systemEnvironment().path()) + paths.append(mapToGlobalPath(path)); + return searchExecutable(fileName, paths); +} + +FilePath IDevice::searchExecutable(const QString &fileName, const FilePaths &dirs) const +{ + for (FilePath dir : dirs) { + if (!handlesFile(dir)) // Allow device-local dirs to be used. + dir = mapToGlobalPath(dir); + QTC_CHECK(handlesFile(dir)); + const FilePath candidate = dir / fileName; + if (isExecutableFile(candidate)) + return candidate; + } + + return {}; } FilePath IDevice::symLinkTarget(const FilePath &filePath) const diff --git a/src/plugins/projectexplorer/devicesupport/idevice.h b/src/plugins/projectexplorer/devicesupport/idevice.h index d5ad9858ad0..9e323e21944 100644 --- a/src/plugins/projectexplorer/devicesupport/idevice.h +++ b/src/plugins/projectexplorer/devicesupport/idevice.h @@ -249,8 +249,9 @@ public: virtual bool removeRecursively(const Utils::FilePath &filePath) const; virtual bool copyFile(const Utils::FilePath &filePath, const Utils::FilePath &target) const; virtual bool renameFile(const Utils::FilePath &filePath, const Utils::FilePath &target) const; - virtual Utils::FilePath searchInPath(const Utils::FilePath &filePath, - const QList &additionalDirs = {}) const; + virtual Utils::FilePath searchExecutableInPath(const QString &fileName) const; + virtual Utils::FilePath searchExecutable(const QString &fileName, + const QList &dirs) const; virtual Utils::FilePath symLinkTarget(const Utils::FilePath &filePath) const; virtual QList directoryEntries(const Utils::FilePath &filePath, const QStringList &nameFilters, diff --git a/src/plugins/qmakeprojectmanager/qmakeproject.cpp b/src/plugins/qmakeprojectmanager/qmakeproject.cpp index acb1c14e1f4..83377bc987e 100644 --- a/src/plugins/qmakeprojectmanager/qmakeproject.cpp +++ b/src/plugins/qmakeprojectmanager/qmakeproject.cpp @@ -284,8 +284,11 @@ QmakeBuildSystem::~QmakeBuildSystem() delete m_qmakeVfs; m_qmakeVfs = nullptr; - m_asyncUpdateFutureInterface.reportCanceled(); - m_asyncUpdateFutureInterface.reportFinished(); + if (m_asyncUpdateFutureInterface) { + m_asyncUpdateFutureInterface->reportCanceled(); + m_asyncUpdateFutureInterface->reportFinished(); + m_asyncUpdateFutureInterface.reset(); + } } void QmakeBuildSystem::updateCodeModels() @@ -591,8 +594,9 @@ void QmakeBuildSystem::incrementPendingEvaluateFutures() } ++m_pendingEvaluateFuturesCount; TRACE("pending inc to: " << m_pendingEvaluateFuturesCount); - m_asyncUpdateFutureInterface.setProgressRange(m_asyncUpdateFutureInterface.progressMinimum(), - m_asyncUpdateFutureInterface.progressMaximum() + 1); + m_asyncUpdateFutureInterface->setProgressRange(m_asyncUpdateFutureInterface->progressMinimum(), + m_asyncUpdateFutureInterface->progressMaximum() + + 1); } void QmakeBuildSystem::decrementPendingEvaluateFutures() @@ -605,15 +609,17 @@ void QmakeBuildSystem::decrementPendingEvaluateFutures() return; // We are closing the project! } - m_asyncUpdateFutureInterface.setProgressValue(m_asyncUpdateFutureInterface.progressValue() + 1); + m_asyncUpdateFutureInterface->setProgressValue(m_asyncUpdateFutureInterface->progressValue() + + 1); if (m_pendingEvaluateFuturesCount == 0) { // We are done! setRootProjectNode(QmakeNodeTreeBuilder::buildTree(this)); if (!m_rootProFile->validParse()) - m_asyncUpdateFutureInterface.reportCanceled(); + m_asyncUpdateFutureInterface->reportCanceled(); - m_asyncUpdateFutureInterface.reportFinished(); + m_asyncUpdateFutureInterface->reportFinished(); + m_asyncUpdateFutureInterface.reset(); m_cancelEvaluate = false; // TODO clear the profile cache ? @@ -659,12 +665,13 @@ void QmakeBuildSystem::asyncUpdate() m_qmakeVfs->invalidateCache(); } - m_asyncUpdateFutureInterface.setProgressRange(0, 0); - Core::ProgressManager::addTask(m_asyncUpdateFutureInterface.future(), + m_asyncUpdateFutureInterface.reset(new QFutureInterface); + m_asyncUpdateFutureInterface->setProgressRange(0, 0); + Core::ProgressManager::addTask(m_asyncUpdateFutureInterface->future(), tr("Reading Project \"%1\"").arg(project()->displayName()), Constants::PROFILE_EVALUATE); - m_asyncUpdateFutureInterface.reportStarted(); + m_asyncUpdateFutureInterface->reportStarted(); const auto watcher = new QFutureWatcher(this); connect(watcher, &QFutureWatcher::canceled, this, [this, watcher] { if (!m_qmakeGlobals) @@ -676,7 +683,7 @@ void QmakeBuildSystem::asyncUpdate() watcher->disconnect(); watcher->deleteLater(); }); - watcher->setFuture(m_asyncUpdateFutureInterface.future()); + watcher->setFuture(m_asyncUpdateFutureInterface->future()); const Kit *const k = kit(); QtSupport::BaseQtVersion *const qtVersion = QtSupport::QtKitAspect::qtVersion(k); @@ -687,8 +694,9 @@ void QmakeBuildSystem::asyncUpdate() .arg(project()->displayName(), k->displayName()) : tr("Cannot parse project \"%1\": No kit selected.").arg(project()->displayName()); proFileParseError(errorMessage, project()->projectFilePath()); - m_asyncUpdateFutureInterface.reportCanceled(); - m_asyncUpdateFutureInterface.reportFinished(); + m_asyncUpdateFutureInterface->reportCanceled(); + m_asyncUpdateFutureInterface->reportFinished(); + m_asyncUpdateFutureInterface.reset(); return; } diff --git a/src/plugins/qmakeprojectmanager/qmakeproject.h b/src/plugins/qmakeprojectmanager/qmakeproject.h index 6f281b6e710..369b8c03415 100644 --- a/src/plugins/qmakeprojectmanager/qmakeproject.h +++ b/src/plugins/qmakeprojectmanager/qmakeproject.h @@ -200,7 +200,7 @@ private: QString m_qmakeSysroot; - QFutureInterface m_asyncUpdateFutureInterface; + std::unique_ptr> m_asyncUpdateFutureInterface; int m_pendingEvaluateFuturesCount = 0; AsyncUpdateState m_asyncUpdateState = Base; bool m_cancelEvaluate = false; diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp index 369df95a322..77cd00d7fa2 100644 --- a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp +++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp @@ -1007,20 +1007,32 @@ void DesignerActionManager::createDefaultDesignerActions() &selectionNotEmptyAndHasXorYProperty)); const QString fontName = "qtds_propertyIconFont.ttf"; - const QColor iconColorNormal(Theme::getColor(Theme::IconsBaseColor)); - const QIcon pasteIcon = Utils::StyleHelper::getIconFromIconFont(fontName, - Theme::getIconUnicode( - Theme::Icon::pasteStyle), - 28, - 28, - iconColorNormal); + const QColor iconColorDefault(Theme::getColor(Theme::IconsBaseColor)); + const QColor iconColorDisabled(Theme::getColor(Theme::IconsDisabledColor)); + const QString copyUnicode = Theme::getIconUnicode(Theme::Icon::copyStyle); + const QString pasteUnicode = Theme::getIconUnicode(Theme::Icon::pasteStyle); + const auto copyDefault = Utils::StyleHelper::IconFontHelper(copyUnicode, + iconColorDefault, + QSize(28, 28), + QIcon::Normal); + const auto copyDisabled = Utils::StyleHelper::IconFontHelper(copyUnicode, + iconColorDisabled, + QSize(28, 28), + QIcon::Disabled); const QIcon copyIcon = Utils::StyleHelper::getIconFromIconFont(fontName, - Theme::getIconUnicode( - Theme::Icon::copyStyle), - 28, - 28, - iconColorNormal); + {copyDefault, copyDisabled}); + + const auto pasteDefault = Utils::StyleHelper::IconFontHelper(pasteUnicode, + iconColorDefault, + QSize(28, 28), + QIcon::Normal); + const auto pasteDisabled = Utils::StyleHelper::IconFontHelper(pasteUnicode, + iconColorDisabled, + QSize(28, 28), + QIcon::Disabled); + const QIcon pasteIcon = Utils::StyleHelper::getIconFromIconFont(fontName, + {pasteDefault, pasteDisabled}); addDesignerAction(new ModelNodeAction(copyFormatCommandId, copyFormatDisplayName, diff --git a/src/plugins/webassembly/webassemblyemsdk.cpp b/src/plugins/webassembly/webassemblyemsdk.cpp index 5997c83f06d..2449dbed7ac 100644 --- a/src/plugins/webassembly/webassemblyemsdk.cpp +++ b/src/plugins/webassembly/webassemblyemsdk.cpp @@ -43,35 +43,36 @@ using namespace Utils; namespace WebAssembly { namespace Internal { -using EmSdkEnvCache = QCache; +using EmSdkEnvCache = QCache; Q_GLOBAL_STATIC_WITH_ARGS(EmSdkEnvCache, emSdkEnvCache, (10)) using EmSdkVersionCache = QCache; Q_GLOBAL_STATIC_WITH_ARGS(EmSdkVersionCache, emSdkVersionCache, (10)) -static QByteArray emSdkEnvOutput(const FilePath &sdkRoot) +static QString emSdkEnvOutput(const FilePath &sdkRoot) { const QString cacheKey = sdkRoot.toString(); + const bool isWindows = sdkRoot.osType() == OsTypeWindows; if (!emSdkEnvCache()->contains(cacheKey)) { const QString scriptFile = sdkRoot.pathAppended(QLatin1String("emsdk_env") + - (HostOsInfo::isWindowsHost() ? ".bat" : ".sh")).toString(); + (isWindows ? ".bat" : ".sh")).path(); QtcProcess emSdkEnv; - if (HostOsInfo::isWindowsHost()) { + if (isWindows) { emSdkEnv.setCommand(CommandLine(scriptFile)); } else { // File needs to be source'd, not executed. - emSdkEnv.setCommand({"bash", {"-c", ". " + scriptFile}}); + emSdkEnv.setCommand({FilePath::fromString("bash").onDevice(sdkRoot), + {"-c", ". " + scriptFile}}); } - emSdkEnv.start(); - if (!emSdkEnv.waitForFinished()) - return {}; - emSdkEnvCache()->insert(cacheKey, new QByteArray(emSdkEnv.readAllStandardError())); + emSdkEnv.runBlocking(); + const QString output = emSdkEnv.allOutput(); + emSdkEnvCache()->insert(cacheKey, new QString(output)); } return *emSdkEnvCache()->object(cacheKey); } -static void parseEmSdkEnvOutputAndAddToEnv(const QByteArray &output, Environment &env) +static void parseEmSdkEnvOutputAndAddToEnv(const QString &output, Environment &env) { - const QStringList lines = QString::fromLocal8Bit(output).split('\n'); + const QStringList lines = output.split('\n'); for (const QString &line : lines) { const QStringList prependParts = line.trimmed().split(" += "); @@ -104,18 +105,17 @@ QVersionNumber WebAssemblyEmSdk::version(const FilePath &sdkRoot) return {}; const QString cacheKey = sdkRoot.toString(); if (!emSdkVersionCache()->contains(cacheKey)) { - Environment env = Environment::systemEnvironment(); + Environment env; WebAssemblyEmSdk::addToEnvironment(sdkRoot, env); - const QString scriptFile = - QLatin1String("emcc") + QLatin1String(HostOsInfo::isWindowsHost() ? ".bat" : ""); - const CommandLine command(env.searchInPath(scriptFile), {"-dumpversion"}); + QLatin1String scriptFile{sdkRoot.osType() == OsType::OsTypeWindows ? "emcc.bat" : "emcc"}; + FilePath script = + FilePath::fromString(scriptFile).onDevice(sdkRoot).searchOnDevice(env.path()); + const CommandLine command(script, {"-dumpversion"}); QtcProcess emcc; emcc.setCommand(command); emcc.setEnvironment(env); - emcc.start(); - if (!emcc.waitForFinished()) - return {}; - const QString version = QLatin1String(emcc.readAllStandardOutput()); + emcc.runBlocking(); + const QString version = emcc.stdOut(); emSdkVersionCache()->insert(cacheKey, new QVersionNumber(QVersionNumber::fromString(version))); } @@ -146,10 +146,31 @@ void WebAssemblyEmSdk::clearCaches() // Unit tests: #ifdef WITH_TESTS void WebAssemblyPlugin::testEmSdkEnvParsing() +{ + QFETCH(QString, emSdkEnvOutput); + QFETCH(int, osType); + QFETCH(int, pathCount); + QFETCH(QString, emsdk); + QFETCH(QString, em_config); + + Environment env{OsType(osType)}; + parseEmSdkEnvOutputAndAddToEnv(emSdkEnvOutput, env); + + QVERIFY(env.path().count() == pathCount); + QCOMPARE(env.value("EMSDK"), emsdk); + QCOMPARE(env.value("EM_CONFIG"), em_config); +} + +void WebAssemblyPlugin::testEmSdkEnvParsing_data() { // Output of "emsdk_env" - const QByteArray emSdkEnvOutput = HostOsInfo::isWindowsHost() ? - R"( + QTest::addColumn("emSdkEnvOutput"); + QTest::addColumn("osType"); + QTest::addColumn("pathCount"); + QTest::addColumn("emsdk"); + QTest::addColumn("em_config"); + + QTest::newRow("windows") << R"( Adding directories to PATH: PATH += C:\Users\user\dev\emsdk PATH += C:\Users\user\dev\emsdk\upstream\emscripten @@ -165,7 +186,9 @@ EM_CACHE = C:/Users/user/dev/emsdk/upstream/emscripten\cache EMSDK_NODE = C:\Users\user\dev\emsdk\node\12.18.1_64bit\bin\node.exe EMSDK_PYTHON = C:\Users\user\dev\emsdk\python\3.7.4-pywin32_64bit\python.exe JAVA_HOME = C:\Users\user\dev\emsdk\java\8.152_64bit - )" : R"( + )" << int(OsTypeWindows) << 5 << "C:/Users/user/dev/emsdk" << "C:\\Users\\user\\dev\\emsdk\\.emscripten"; + + QTest::newRow("linux") << R"( Adding directories to PATH: PATH += /home/user/dev/emsdk PATH += /home/user/dev/emsdk/upstream/emscripten @@ -177,20 +200,9 @@ EMSDK = /home/user/dev/emsdk EM_CONFIG = /home/user/dev/emsdk/.emscripten EM_CACHE = /home/user/dev/emsdk/upstream/emscripten/cache EMSDK_NODE = /home/user/dev/emsdk/node/12.18.1_64bit/bin/node - )"; - Environment env; - parseEmSdkEnvOutputAndAddToEnv(emSdkEnvOutput, env); - - if (HostOsInfo::isWindowsHost()) { - QVERIFY(env.path().count() == 5); - QCOMPARE(env.value("EMSDK"), "C:/Users/user/dev/emsdk"); - QCOMPARE(env.value("EM_CONFIG"), "C:\\Users\\user\\dev\\emsdk\\.emscripten"); - } else { - QVERIFY(env.path().count() == 3); - QCOMPARE(env.value("EMSDK"), "/home/user/dev/emsdk"); - QCOMPARE(env.value("EM_CONFIG"), "/home/user/dev/emsdk/.emscripten"); - } + )" << int(OsTypeLinux) << 3 << "/home/user/dev/emsdk" << "/home/user/dev/emsdk/.emscripten"; } + #endif // WITH_TESTS } // namespace Internal diff --git a/src/plugins/webassembly/webassemblyoptionspage.cpp b/src/plugins/webassembly/webassemblyoptionspage.cpp index 209db2ef430..67c037ee13d 100644 --- a/src/plugins/webassembly/webassemblyoptionspage.cpp +++ b/src/plugins/webassembly/webassemblyoptionspage.cpp @@ -129,7 +129,7 @@ static QString environmentDisplay(const FilePath &sdkRoot) WebAssemblyEmSdk::addToEnvironment(sdkRoot, env); QString result; result.append(WebAssemblyOptionsWidget::tr("

Adding directories to PATH:

")); - result.append(env.value("PATH").replace(HostOsInfo::pathListSeparator(), "
")); + result.append(env.value("PATH").replace(OsSpecificAspects::pathListSeparator(sdkRoot.osType()), "
")); result.append(WebAssemblyOptionsWidget::tr("

Setting environment variables:

")); for (const QString &envVar : env.toStringList()) { if (!envVar.startsWith("PATH")) // Path was already printed out above diff --git a/src/plugins/webassembly/webassemblyplugin.h b/src/plugins/webassembly/webassemblyplugin.h index eedeb89d0a6..c17909542ba 100644 --- a/src/plugins/webassembly/webassemblyplugin.h +++ b/src/plugins/webassembly/webassemblyplugin.h @@ -48,6 +48,7 @@ public: #ifdef WITH_TESTS private slots: void testEmSdkEnvParsing(); + void testEmSdkEnvParsing_data(); #endif // WITH_TESTS }; diff --git a/src/plugins/webassembly/webassemblyrunconfiguration.cpp b/src/plugins/webassembly/webassemblyrunconfiguration.cpp index 8bef1249dcc..d50c3abc230 100644 --- a/src/plugins/webassembly/webassemblyrunconfiguration.cpp +++ b/src/plugins/webassembly/webassemblyrunconfiguration.cpp @@ -50,8 +50,9 @@ static CommandLine emrunCommand(Target *target, const QString &browser, const QS // that the web server is killed when the application is stopped in Qt Creator. // On Non-windows, we prefer using the shell script, because that knows how to find the // right python (not part of emsdk). The shell script stays attached to the server process. - const FilePath interpreter = bc->environment().searchInPath( - QLatin1String(HostOsInfo::isWindowsHost() ? "python" : "sh")); + const FilePath interpreter = HostOsInfo::isWindowsHost() + ? FilePath::fromUserInput(bc->environment().value("EMSDK_PYTHON")) + : bc->environment().searchInPath("sh"); const QString emrunLaunchScript = HostOsInfo::isWindowsHost() ? emrun.absolutePath() + "/" + emrun.baseName() + ".py" : emrun.absoluteFilePath(); diff --git a/src/plugins/webassembly/webassemblytoolchain.cpp b/src/plugins/webassembly/webassemblytoolchain.cpp index f83189efce6..d0020cb6fde 100644 --- a/src/plugins/webassembly/webassemblytoolchain.cpp +++ b/src/plugins/webassembly/webassemblytoolchain.cpp @@ -27,20 +27,20 @@ #include "webassemblyconstants.h" #include "webassemblyemsdk.h" +#include #include #include #include #include + #include + #include #include #include #include #include -#include -#include - using namespace ProjectExplorer; using namespace QtSupport; using namespace Utils; @@ -73,7 +73,7 @@ static void addRegisteredMinGWToEnvironment(Environment &env) void WebAssemblyToolChain::addToEnvironment(Environment &env) const { WebAssemblyEmSdk::addToEnvironment(WebAssemblyEmSdk::registeredEmSdk(), env); - if (HostOsInfo::isWindowsHost()) + if (env.osType() == OsTypeWindows) addRegisteredMinGWToEnvironment(env); } @@ -88,9 +88,9 @@ WebAssemblyToolChain::WebAssemblyToolChain() : FilePath WebAssemblyToolChain::makeCommand(const Environment &environment) const { // Diverged duplicate of ClangToolChain::makeCommand and MingwToolChain::makeCommand - const QStringList makes - = HostOsInfo::isWindowsHost() ? QStringList({"mingw32-make.exe", "make.exe"}) - : QStringList({"make"}); + const QStringList makes = environment.osType() == OsTypeWindows + ? QStringList({"mingw32-make.exe", "make.exe"}) + : QStringList({"make"}); FilePath tmp; for (const QString &make : makes) { @@ -162,13 +162,12 @@ QList WebAssemblyToolChainFactory::autoDetect( const IDevice::Ptr &device) { Q_UNUSED(alreadyKnown) - Q_UNUSED(device) const FilePath sdk = WebAssemblyEmSdk::registeredEmSdk(); if (!WebAssemblyEmSdk::isValid(sdk)) return {}; - Environment env; + Environment env = sdk.deviceEnvironment(); WebAssemblyEmSdk::addToEnvironment(sdk, env); QList result; @@ -178,9 +177,11 @@ QList WebAssemblyToolChainFactory::autoDetect( toolChain->setLanguage(languageId); toolChain->setDetection(ToolChain::AutoDetection); const bool cLanguage = languageId == ProjectExplorer::Constants::C_LANGUAGE_ID; - const QString scriptFile = QLatin1String(cLanguage ? "emcc" : "em++") - + QLatin1String(HostOsInfo::isWindowsHost() ? ".bat" : ""); - toolChain->setCompilerCommand(env.searchInPath(scriptFile)); + const QString script = QLatin1String(cLanguage ? "emcc" : "em++") + + QLatin1String(sdk.osType() == OsTypeWindows ? ".bat" : ""); + const FilePath scriptFile = + FilePath::fromString(script).onDevice(sdk).searchOnDevice(env.path()); + toolChain->setCompilerCommand(scriptFile); const QString displayName = WebAssemblyToolChain::tr("Emscripten Compiler %1 for %2") .arg(toolChain->version(), QLatin1String(cLanguage ? "C" : "C++")); diff --git a/src/shared/proparser/proitems.cpp b/src/shared/proparser/proitems.cpp index f689c419237..1f4a436115e 100644 --- a/src/shared/proparser/proitems.cpp +++ b/src/shared/proparser/proitems.cpp @@ -25,6 +25,7 @@ #include "proitems.h" +#include #include #include #include @@ -50,6 +51,11 @@ ProString::ProString() : { } +ProString::ProString(const ProString &other) : + m_string(other.m_string), m_offset(other.m_offset), m_length(other.m_length), m_file(other.m_file), m_hash(other.m_hash) +{ +} + ProString::ProString(const ProString &other, OmitPreHashing) : m_string(other.m_string), m_offset(other.m_offset), m_length(other.m_length), m_file(other.m_file), m_hash(0x80000000) { @@ -72,13 +78,13 @@ ProString::ProString(Utils::StringView str) : } ProString::ProString(const char *str, DoPreHashing) : - m_string(QString::fromLatin1(str)), m_offset(0), m_length(qstrlen(str)), m_file(0) + m_string(QString::fromLatin1(str)), m_offset(0), m_length(int(qstrlen(str))), m_file(0) { updatedHash(); } ProString::ProString(const char *str) : - m_string(QString::fromLatin1(str)), m_offset(0), m_length(qstrlen(str)), m_file(0), m_hash(0x80000000) + m_string(QString::fromLatin1(str)), m_offset(0), m_length(int(qstrlen(str))), m_file(0), m_hash(0x80000000) { } @@ -148,7 +154,8 @@ QString ProString::toQString() const QString &ProString::toQString(QString &tmp) const { - return tmp.setRawData(m_string.constData() + m_offset, m_length); + tmp = m_string.mid(m_offset, m_length); + return tmp; } ProString &ProString::prepend(const ProString &other) @@ -493,4 +500,9 @@ ProKey ProFile::getHashStr(const ushort *&tPtr) return ret; } +QDebug operator<<(QDebug debug, const ProString &str) +{ + return debug << str.toQString(); +} + QT_END_NAMESPACE diff --git a/src/shared/proparser/proitems.h b/src/shared/proparser/proitems.h index 3e0686fe136..9d851a9d578 100644 --- a/src/shared/proparser/proitems.h +++ b/src/shared/proparser/proitems.h @@ -64,6 +64,8 @@ class ProFile; class ProString { public: ProString(); + ProString(const ProString &other); + ProString &operator=(const ProString &) = default; template ProString &operator=(const QStringBuilder &str) { return *this = QString(str); } @@ -74,7 +76,6 @@ public: ProString(const QStringBuilder &str) : ProString(QString(str)) {} - ProString(const QString &str, int offset, int length); void setValue(const QString &str); void clear() { m_string.clear(); m_length = 0; } @@ -83,14 +84,14 @@ public: int sourceFile() const { return m_file; } ProString &prepend(const ProString &other); - ProString &append(const ProString &other, bool *pending = 0); + ProString &append(const ProString &other, bool *pending = nullptr); ProString &append(const QString &other) { return append(ProString(other)); } template ProString &append(const QStringBuilder &other) { return append(QString(other)); } ProString &append(const QLatin1String other); ProString &append(const char *other) { return append(QLatin1String(other)); } ProString &append(QChar other); - ProString &append(const ProStringList &other, bool *pending = 0, bool skipEmpty1st = false); + ProString &append(const ProStringList &other, bool *pending = nullptr, bool skipEmpty1st = false); ProString &operator+=(const ProString &other) { return append(other); } ProString &operator+=(const QString &other) { return append(other); } template @@ -146,9 +147,9 @@ public: bool contains(const QString &s, Qt::CaseSensitivity cs = Qt::CaseSensitive) const { return indexOf(s, 0, cs) >= 0; } bool contains(const char *s, Qt::CaseSensitivity cs = Qt::CaseSensitive) const { return indexOf(QLatin1String(s), 0, cs) >= 0; } bool contains(QChar c, Qt::CaseSensitivity cs = Qt::CaseSensitive) const { return indexOf(c, 0, cs) >= 0; } - int toLongLong(bool *ok = 0, int base = 10) const { return toStringView().toLongLong(ok, base); } - int toInt(bool *ok = 0, int base = 10) const { return toStringView().toInt(ok, base); } - short toShort(bool *ok = 0, int base = 10) const { return toStringView().toShort(ok, base); } + qlonglong toLongLong(bool *ok = nullptr, int base = 10) const { return toStringView().toLongLong(ok, base); } + int toInt(bool *ok = nullptr, int base = 10) const { return toStringView().toInt(ok, base); } + short toShort(bool *ok = nullptr, int base = 10) const { return toStringView().toShort(ok, base); } uint hash() const { return m_hash; } static uint hash(const QChar *p, int n); @@ -185,7 +186,8 @@ private: friend QString operator+(const ProString &one, const ProString &two); friend class ProKey; }; -Q_DECLARE_TYPEINFO(ProString, Q_MOVABLE_TYPE); +Q_DECLARE_TYPEINFO(ProString, Q_RELOCATABLE_TYPE); + class ProKey : public ProString { public: @@ -195,7 +197,6 @@ public: ProKey(const QStringBuilder &str) : ProString(str) {} - PROITEM_EXPLICIT ProKey(const char *str); ProKey(const QString &str, int off, int len); ProKey(const QString &str, int off, int len, uint hash); @@ -217,7 +218,7 @@ public: private: ProKey(const ProString &other); }; -Q_DECLARE_TYPEINFO(ProKey, Q_MOVABLE_TYPE); +Q_DECLARE_TYPEINFO(ProKey, Q_RELOCATABLE_TYPE); template <> struct QConcatenable : private QAbstractConcatenable { @@ -228,6 +229,8 @@ template <> struct QConcatenable : private QAbstractConcatenable static inline void appendTo(const ProString &a, QChar *&out) { const auto n = a.size(); + if (!n) + return; memcpy(out, a.toStringView().data(), sizeof(QChar) * n); out += n; } @@ -242,6 +245,8 @@ template <> struct QConcatenable : private QAbstractConcatenable static inline void appendTo(const ProKey &a, QChar *&out) { const auto n = a.size(); + if (!n) + return; memcpy(out, a.toStringView().data(), sizeof(QChar) * n); out += n; } @@ -257,6 +262,54 @@ QTextStream &operator<<(QTextStream &t, const ProString &str); template QTextStream &operator<<(QTextStream &t, const QStringBuilder &str) { return t << QString(str); } +// This class manages read-only access to a ProString via a raw data QString +// temporary, ensuring that the latter is accessed exclusively. +class ProStringRoUser +{ +public: + ProStringRoUser(QString &rs) + { + m_rs = &rs; + } + ProStringRoUser(const ProString &ps, QString &rs) + : ProStringRoUser(rs) + { + ps.toQString(rs); + } + // No destructor, as a RAII pattern cannot be used: references to the + // temporary string can legitimately outlive instances of this class + // (if they are held by Qt, e.g. in QRegExp). + QString &set(const ProString &ps) { return ps.toQString(*m_rs); } + QString &str() { return *m_rs; } + +protected: + QString *m_rs; +}; + +// This class manages read-write access to a ProString via a raw data QString +// temporary, ensuring that the latter is accessed exclusively, and that raw +// data does not leak outside its source's refcounting. +class ProStringRwUser : public ProStringRoUser +{ +public: + ProStringRwUser(QString &rs) + : ProStringRoUser(rs), m_ps(nullptr) {} + ProStringRwUser(const ProString &ps, QString &rs) + : ProStringRoUser(ps, rs), m_ps(&ps) {} + QString &set(const ProString &ps) { m_ps = &ps; return ProStringRoUser::set(ps); } + ProString extract(const QString &s) const + { return s.isSharedWith(*m_rs) ? *m_ps : ProString(s).setSource(*m_ps); } + ProString extract(const QString &s, const ProStringRwUser &other) const + { + if (other.m_ps && s.isSharedWith(*other.m_rs)) + return *other.m_ps; + return extract(s); + } + +private: + const ProString *m_ps; +}; + class ProStringList : public QVector { public: ProStringList() {} @@ -290,21 +343,12 @@ public: { return contains(ProString(str), cs); } bool contains(const char *str, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; }; -Q_DECLARE_TYPEINFO(ProStringList, Q_MOVABLE_TYPE); +Q_DECLARE_TYPEINFO(ProStringList, Q_RELOCATABLE_TYPE); inline ProStringList operator+(const ProStringList &one, const ProStringList &two) { ProStringList ret = one; ret += two; return ret; } -typedef QHash ProValueMap; - -// For std::list (sic!) -#ifdef Q_CC_MSVC -inline bool operator<(const ProValueMap &, const ProValueMap &) -{ - Q_ASSERT(false); - return false; -} -#endif +typedef QMap ProValueMap; // These token definitions affect both ProFileEvaluator and ProWriter enum ProToken { @@ -419,7 +463,7 @@ class ProFunctionDef { public: ProFunctionDef(ProFile *pro, int offset) : m_pro(pro), m_offset(offset) { m_pro->ref(); } ProFunctionDef(const ProFunctionDef &o) : m_pro(o.m_pro), m_offset(o.m_offset) { m_pro->ref(); } - ProFunctionDef(ProFunctionDef &&other) Q_DECL_NOTHROW + ProFunctionDef(ProFunctionDef &&other) noexcept : m_pro(other.m_pro), m_offset(other.m_offset) { other.m_pro = nullptr; } ~ProFunctionDef() { if (m_pro) m_pro->deref(); } ProFunctionDef &operator=(const ProFunctionDef &o) @@ -433,13 +477,13 @@ public: } return *this; } - ProFunctionDef &operator=(ProFunctionDef &&other) Q_DECL_NOTHROW + ProFunctionDef &operator=(ProFunctionDef &&other) noexcept { ProFunctionDef moved(std::move(other)); swap(moved); return *this; } - void swap(ProFunctionDef &other) Q_DECL_NOTHROW + void swap(ProFunctionDef &other) noexcept { qSwap(m_pro, other.m_pro); qSwap(m_offset, other.m_offset); @@ -452,11 +496,13 @@ private: int m_offset; }; -Q_DECLARE_TYPEINFO(ProFunctionDef, Q_MOVABLE_TYPE); +Q_DECLARE_TYPEINFO(ProFunctionDef, Q_RELOCATABLE_TYPE); struct ProFunctionDefs { QHash testFunctions; QHash replaceFunctions; }; +QDebug operator<<(QDebug debug, const ProString &str); + QT_END_NAMESPACE diff --git a/src/shared/qbs b/src/shared/qbs index bd2d37ae336..3a408a87c15 160000 --- a/src/shared/qbs +++ b/src/shared/qbs @@ -1 +1 @@ -Subproject commit bd2d37ae336e3c88e2fd484cc64eb05b46d07888 +Subproject commit 3a408a87c1583d4a5bef0aee2ed56afcf30dd002 diff --git a/tests/system/shared/qtcreator.py b/tests/system/shared/qtcreator.py index d78da01d925..0419a4c7589 100644 --- a/tests/system/shared/qtcreator.py +++ b/tests/system/shared/qtcreator.py @@ -144,9 +144,10 @@ def checkForStillRunningQmlExecutable(possibleNames): continue else: if subprocess.call(["taskkill", "/F", "/FI", "IMAGENAME eq %s" % qmlHelper]) == 0: - print "Killed still running %s" % qmlHelper + print("Killed still running %s" % qmlHelper) else: - print "%s is still running - failed to kill it" % qmlHelper + print("%s is still running - failed to kill it" % qmlHelper) + def __removeTestingDir__(): def __removeIt__(directory): diff --git a/tests/system/shared/utils.py b/tests/system/shared/utils.py index e31ed30c200..c34e39af7b8 100644 --- a/tests/system/shared/utils.py +++ b/tests/system/shared/utils.py @@ -579,7 +579,7 @@ def dumpChildren(item): def writeTestResults(folder): if not os.path.exists(folder): - print "Skipping writing test results (folder '%s' does not exist)." % folder + print("Skipping writing test results (folder '%s' does not exist)." % folder) return resultFile = open("%s.srf" % os.path.join(folder, os.path.basename(squishinfo.testCase)), "w") resultFile.write("suite:%s\n" % os.path.basename(os.path.dirname(squishinfo.testCase))) diff --git a/tests/system/suite_HELP/tst_HELP02/test.py b/tests/system/suite_HELP/tst_HELP02/test.py index 1d776689be7..2492be950fa 100755 --- a/tests/system/suite_HELP/tst_HELP02/test.py +++ b/tests/system/suite_HELP/tst_HELP02/test.py @@ -66,6 +66,11 @@ def checkQtCreatorHelpVersion(expectedVersion): test.log("Exception caught", "%s(%s)" % (str(t), str(v))) test.fail("Missing Qt Creator Manual.") + +def _shortcutMatches_(shortcutEdit, expectedText): + return str(findObject(shortcutEdit).text) == expectedText + + def setKeyboardShortcutForAboutQtC(): invokeMenuItem("Tools", "Options...") mouseClick(waitForObjectItem(":Options_QListView", "Environment")) @@ -85,20 +90,19 @@ def setKeyboardShortcutForAboutQtC(): "visible='1' text~='(Stop Recording|Record)'}" % shortcutGB) shortcut = ("{container=%s type='Utils::FancyLineEdit' unnamed='1' visible='1' " "placeholderText='Enter key sequence as text'}" % shortcutGB) + expected = 'Ctrl+Opt+A' if platform.system() == 'Darwin' else 'Ctrl+Alt+A' clickButton(record) nativeType("") + waitFor("_shortcutMatches_(shortcut, expected)", 5000) clickButton(record) - expected = 'Ctrl+Alt+A' - if platform.system() == 'Darwin': - expected = 'Ctrl+Opt+A' - shortcutMatches = waitFor("str(findObject(shortcut).text) == expected", 5000) - if not shortcutMatches and platform.system() == 'Darwin': + gotExpectedShortcut = _shortcutMatches_(shortcut, expected) + if not gotExpectedShortcut and platform.system() == 'Darwin': test.warning("Squish Issue: shortcut was set to %s - entering it manually now" % waitForObject(shortcut).text) replaceEditorContent(shortcut, expected) else: - test.verify(shortcutMatches, "Expected key sequence is displayed.") + test.verify(gotExpectedShortcut, "Expected key sequence is displayed.") clickButton(waitForObject(":Options.OK_QPushButton")) def main():