Merge remote-tracking branch 'origin/5.0'
Change-Id: I9dca34a9895d19ace05aef5b73e9a806b23607fb
55
dist/changes-4.15.2.md
vendored
Normal file
@@ -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
|
BIN
doc/qtcreator/images/icons/copy-formatting.png
Normal file
After Width: | Height: | Size: 277 B |
BIN
doc/qtcreator/images/icons/paste-formatting.png
Normal file
After Width: | Height: | Size: 243 B |
Before Width: | Height: | Size: 9.5 KiB After Width: | Height: | Size: 3.8 KiB |
Before Width: | Height: | Size: 8.1 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 32 KiB |
@@ -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
|
||||
|
@@ -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.
|
||||
|
@@ -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
|
||||
|
@@ -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 {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -32,7 +32,6 @@ StudioControls.ButtonRow {
|
||||
id: buttonRow
|
||||
|
||||
enabled: anchorBackend.hasParent && isBaseState
|
||||
opacity: enabled ? 1 : 0.5
|
||||
actionIndicatorVisible: false
|
||||
|
||||
StudioControls.ButtonGroup { id: group }
|
||||
|
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -97,7 +97,7 @@ Item {
|
||||
|
||||
Connections {
|
||||
target: modelNodeBackend
|
||||
onSelectionChanged: menu.close()
|
||||
function onSelectionChanged() { menu.close() }
|
||||
}
|
||||
|
||||
StudioControls.MenuItem {
|
||||
|
@@ -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"
|
||||
|
@@ -258,7 +258,7 @@ Rectangle {
|
||||
}
|
||||
PropertyChanges {
|
||||
target: spinBoxIndicator
|
||||
color: StudioTheme.Values.themeControlBackground
|
||||
color: StudioTheme.Values.themeControlBackgroundDisabled
|
||||
}
|
||||
},
|
||||
State {
|
||||
|
@@ -22,7 +22,7 @@ DSdisabledColor=ff8e8e8e
|
||||
|
||||
DScontrolBackground=ffeaeaea
|
||||
DScontrolBackgroundInteraction=ffc9c9c9
|
||||
DScontrolBackgroundDisabled=ff8e8e8e
|
||||
DScontrolBackgroundDisabled=ffeaeaea
|
||||
DScontrolBackgroundGlobalHover=ffe5e5e5
|
||||
DScontrolBackgroundHover=ffd1d1d1
|
||||
|
||||
|
@@ -36,7 +36,7 @@ DSdisabledColor=ff8e8e8e
|
||||
|
||||
DScontrolBackground=ffeaeaea
|
||||
DScontrolBackgroundInteraction=ffc9c9c9
|
||||
DScontrolBackgroundDisabled=ff8e8e8e
|
||||
DScontrolBackgroundDisabled=ffeaeaea
|
||||
DScontrolBackgroundGlobalHover=ffe5e5e5
|
||||
DScontrolBackgroundHover=ffd1d1d1
|
||||
|
||||
|
@@ -31,7 +31,7 @@ DSdisabledColor=ff8e8e8e
|
||||
|
||||
DScontrolBackground=ffeaeaea
|
||||
DScontrolBackgroundInteraction=ffc9c9c9
|
||||
DScontrolBackgroundDisabled=ff8e8e8e
|
||||
DScontrolBackgroundDisabled=ffeaeaea
|
||||
DScontrolBackgroundGlobalHover=ffe5e5e5
|
||||
DScontrolBackgroundHover=ffd1d1d1
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -216,7 +216,7 @@ public:
|
||||
|
||||
static void setDeviceFileHooks(const DeviceFileHooks &hooks);
|
||||
|
||||
FilePath onDeviceSearchInPath(const QList<FilePath> &additionalDirs = {}) const;
|
||||
FilePath searchOnDevice(const QList<FilePath> &dirs) const;
|
||||
Environment deviceEnvironment() const;
|
||||
|
||||
private:
|
||||
|
@@ -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())
|
||||
|
@@ -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);
|
||||
|
@@ -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 QList<Loca
|
||||
});
|
||||
qCDebug(clangdLog) << "requesting AST for" << it.key().toFilePath();
|
||||
refData->pendingAstRequests << 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()
|
||||
|
@@ -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<ClangBackEnd::DiagnosticContainer> &diagnostics);
|
||||
void generateEditorSelections();
|
||||
|
@@ -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<ClangBackEnd::TokenInfoContainer> &tokenInfos,
|
||||
const QVector<ClangBackEnd::SourceRangeContainer> &skippedPreprocessorRanges,
|
||||
|
@@ -108,6 +108,8 @@ public:
|
||||
static void clearTaskHubIssues();
|
||||
void generateTaskHubIssues();
|
||||
|
||||
static void clearTextMarks(const Utils::FilePath &filePath);
|
||||
|
||||
public:
|
||||
static ClangEditorDocumentProcessor *get(const QString &filePath);
|
||||
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -500,7 +500,7 @@ QList<BaseQtVersion *> 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,
|
||||
|
@@ -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<Utils::FilePath> directoryEntries(const Utils::FilePath &filePath,
|
||||
const QStringList &nameFilters,
|
||||
|
@@ -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> 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)
|
||||
|
@@ -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
|
||||
|
@@ -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<Client> 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<int>::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);
|
||||
}
|
||||
|
||||
|
@@ -371,11 +371,11 @@ SemanticRequestTypes SemanticTokenSupport::supportedSemanticRequests(TextDocumen
|
||||
void SemanticTokenSupport::handleSemanticTokens(const Utils::FilePath &filePath,
|
||||
const SemanticTokensResult &result)
|
||||
{
|
||||
if (auto tokens = Utils::get_if<SemanticTokens>(&result))
|
||||
if (auto tokens = Utils::get_if<SemanticTokens>(&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);
|
||||
}
|
||||
|
@@ -58,7 +58,7 @@ QList<ToolChain *> NimToolChainFactory::autoDetect(const QList<ToolChain *> &alr
|
||||
QList<ToolChain *> 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;
|
||||
|
||||
|
@@ -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);
|
||||
|
@@ -43,6 +43,7 @@
|
||||
#include <utils/url.h>
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDateTime>
|
||||
|
||||
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
|
||||
|
@@ -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<Utils::FilePath> 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();
|
||||
|
@@ -458,10 +458,10 @@ DeviceManager::DeviceManager(bool isInstance) : d(std::make_unique<DeviceManager
|
||||
return device->renameFile(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) {
|
||||
|
@@ -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
|
||||
|
@@ -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<Utils::FilePath> &additionalDirs = {}) const;
|
||||
virtual Utils::FilePath searchExecutableInPath(const QString &fileName) const;
|
||||
virtual Utils::FilePath searchExecutable(const QString &fileName,
|
||||
const QList<Utils::FilePath> &dirs) const;
|
||||
virtual Utils::FilePath symLinkTarget(const Utils::FilePath &filePath) const;
|
||||
virtual QList<Utils::FilePath> directoryEntries(const Utils::FilePath &filePath,
|
||||
const QStringList &nameFilters,
|
||||
|
@@ -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<void>);
|
||||
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<void>(this);
|
||||
connect(watcher, &QFutureWatcher<void>::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;
|
||||
}
|
||||
|
||||
|
@@ -200,7 +200,7 @@ private:
|
||||
|
||||
QString m_qmakeSysroot;
|
||||
|
||||
QFutureInterface<void> m_asyncUpdateFutureInterface;
|
||||
std::unique_ptr<QFutureInterface<void>> m_asyncUpdateFutureInterface;
|
||||
int m_pendingEvaluateFuturesCount = 0;
|
||||
AsyncUpdateState m_asyncUpdateState = Base;
|
||||
bool m_cancelEvaluate = false;
|
||||
|
@@ -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,
|
||||
|
@@ -43,35 +43,36 @@ using namespace Utils;
|
||||
namespace WebAssembly {
|
||||
namespace Internal {
|
||||
|
||||
using EmSdkEnvCache = QCache<QString, QByteArray>;
|
||||
using EmSdkEnvCache = QCache<QString, QString>;
|
||||
Q_GLOBAL_STATIC_WITH_ARGS(EmSdkEnvCache, emSdkEnvCache, (10))
|
||||
using EmSdkVersionCache = QCache<QString, QVersionNumber>;
|
||||
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<QString>("emSdkEnvOutput");
|
||||
QTest::addColumn<int>("osType");
|
||||
QTest::addColumn<int>("pathCount");
|
||||
QTest::addColumn<QString>("emsdk");
|
||||
QTest::addColumn<QString>("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
|
||||
|
@@ -129,7 +129,7 @@ static QString environmentDisplay(const FilePath &sdkRoot)
|
||||
WebAssemblyEmSdk::addToEnvironment(sdkRoot, env);
|
||||
QString result;
|
||||
result.append(WebAssemblyOptionsWidget::tr("<h4>Adding directories to PATH:</h4>"));
|
||||
result.append(env.value("PATH").replace(HostOsInfo::pathListSeparator(), "<br/>"));
|
||||
result.append(env.value("PATH").replace(OsSpecificAspects::pathListSeparator(sdkRoot.osType()), "<br/>"));
|
||||
result.append(WebAssemblyOptionsWidget::tr("<h4>Setting environment variables:</h4>"));
|
||||
for (const QString &envVar : env.toStringList()) {
|
||||
if (!envVar.startsWith("PATH")) // Path was already printed out above
|
||||
|
@@ -48,6 +48,7 @@ public:
|
||||
#ifdef WITH_TESTS
|
||||
private slots:
|
||||
void testEmSdkEnvParsing();
|
||||
void testEmSdkEnvParsing_data();
|
||||
#endif // WITH_TESTS
|
||||
};
|
||||
|
||||
|
@@ -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();
|
||||
|
@@ -27,20 +27,20 @@
|
||||
#include "webassemblyconstants.h"
|
||||
#include "webassemblyemsdk.h"
|
||||
|
||||
#include <projectexplorer/devicesupport/devicemanager.h>
|
||||
#include <projectexplorer/kitmanager.h>
|
||||
#include <projectexplorer/projectexplorerconstants.h>
|
||||
#include <projectexplorer/projectmacro.h>
|
||||
#include <projectexplorer/toolchainmanager.h>
|
||||
|
||||
#include <qtsupport/qtkitinformation.h>
|
||||
|
||||
#include <utils/algorithm.h>
|
||||
#include <utils/environment.h>
|
||||
#include <utils/fileutils.h>
|
||||
#include <utils/hostosinfo.h>
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
#include <QDir>
|
||||
#include <QSettings>
|
||||
|
||||
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<ToolChain *> 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<ToolChain *> result;
|
||||
@@ -178,9 +177,11 @@ QList<ToolChain *> 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++"));
|
||||
|
@@ -25,6 +25,7 @@
|
||||
|
||||
#include "proitems.h"
|
||||
|
||||
#include <qdebug.h>
|
||||
#include <qfileinfo.h>
|
||||
#include <qset.h>
|
||||
#include <qstringlist.h>
|
||||
@@ -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
|
||||
|
@@ -64,6 +64,8 @@ class ProFile;
|
||||
class ProString {
|
||||
public:
|
||||
ProString();
|
||||
ProString(const ProString &other);
|
||||
ProString &operator=(const ProString &) = default;
|
||||
template<typename A, typename B>
|
||||
ProString &operator=(const QStringBuilder<A, B> &str)
|
||||
{ return *this = QString(str); }
|
||||
@@ -74,7 +76,6 @@ public:
|
||||
ProString(const QStringBuilder<A, B> &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<typename A, typename B>
|
||||
ProString &append(const QStringBuilder<A, B> &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<typename A, typename B>
|
||||
@@ -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<A, B> &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<ProString> : private QAbstractConcatenable
|
||||
{
|
||||
@@ -228,6 +229,8 @@ template <> struct QConcatenable<ProString> : 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<ProKey> : 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<typename A, typename B>
|
||||
QTextStream &operator<<(QTextStream &t, const QStringBuilder<A, B> &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<ProString> {
|
||||
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<ProKey, ProStringList> ProValueMap;
|
||||
|
||||
// For std::list (sic!)
|
||||
#ifdef Q_CC_MSVC
|
||||
inline bool operator<(const ProValueMap &, const ProValueMap &)
|
||||
{
|
||||
Q_ASSERT(false);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
typedef QMap<ProKey, ProStringList> 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<ProKey, ProFunctionDef> testFunctions;
|
||||
QHash<ProKey, ProFunctionDef> replaceFunctions;
|
||||
};
|
||||
|
||||
QDebug operator<<(QDebug debug, const ProString &str);
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
@@ -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):
|
||||
|
@@ -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)))
|
||||
|
@@ -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("<Ctrl+Alt+a>")
|
||||
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():
|
||||
|