Merge remote-tracking branch 'origin/5.0'

Change-Id: I9dca34a9895d19ace05aef5b73e9a806b23607fb
This commit is contained in:
Eike Ziller
2021-07-09 14:11:59 +02:00
54 changed files with 702 additions and 374 deletions

55
dist/changes-4.15.2.md vendored Normal file
View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 277 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 243 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.5 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.1 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 32 KiB

View File

@@ -131,8 +131,9 @@
\section1 Image Alignment \section1 Image Alignment
You can align images horizontally and vertically. By default, images are You can align images horizontally and vertically in the
centered. \uicontrol {Alignment H} and \uicontrol {Alignment V}
fields. By default, images are centered.
Select the \uicontrol Mirrored check box to horizontally invert the image, Select the \uicontrol Mirrored check box to horizontally invert the image,
effectively displaying a mirrored 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 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 tiled individually. The corner regions are not scaled at all, while the
horizontal and vertical regions are scaled according to the values of the horizontal and vertical regions are scaled according to the values of the
\uicontrol {Horizontal tile mode} and \uicontrol {Vertical tile mode} field, \uicontrol {Tile mode H} and \uicontrol {Tile mode V} field, or both.
or both.
The \uicontrol Stretch option scales the image to fit the available area. 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. 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" \image qtquick-designer-animated-image-properties.png "Animated Image properties"
To pause or play the animation, select the \uicontrol Paused or To play the animation, select the \uicontrol Playing check box.
\uicontrol Playing check boxes.
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 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 animation is cached. Deselect the check box if you are playing a long

View File

@@ -230,6 +230,17 @@
\li Resets anchors to their saved state for the selected component. \li Resets anchors to their saved state for the selected component.
\li \key Ctrl+Shift+R (\key Shift+Cmd+R on \macos) \li \key Ctrl+Shift+R (\key Shift+Cmd+R on \macos)
\li \l{Setting Anchors and Margins} \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 \row
\li \inlineimage row.png \li \inlineimage row.png
\li Uses a \uicontrol Row component to lay out the selected components. \li Uses a \uicontrol Row component to lay out the selected components.

View File

@@ -409,6 +409,22 @@
and vertically. Note that some OpenGL ES 2 implementations do not support and vertically. Note that some OpenGL ES 2 implementations do not support
the \uicontrol Repeat wrap mode with non-power-of-two textures. 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 \section1 Editing Properties Inline
You can double-click components in \l {Form Editor} to edit their You can double-click components in \l {Form Editor} to edit their

View File

@@ -153,16 +153,20 @@ Section {
visible: section.showEasingCurve visible: section.showEasingCurve
} }
BoolButtonRowButton { SecondColumnLayout {
visible: section.showEasingCurve BoolButtonRowButton {
buttonIcon: StudioTheme.Constants.curveDesigner visible: section.showEasingCurve
buttonIcon: StudioTheme.Constants.curveDesigner
EasingCurveEditor { EasingCurveEditor {
id: easingCurveEditor id: easingCurveEditor
modelNodeBackendProperty: modelNodeBackend modelNodeBackendProperty: modelNodeBackend
}
onClicked: easingCurveEditor.runDialog()
} }
onClicked: easingCurveEditor.runDialog() ExpandingSpacer {}
} }
} }
} }

View File

@@ -32,7 +32,6 @@ StudioControls.ButtonRow {
id: buttonRow id: buttonRow
enabled: anchorBackend.hasParent && isBaseState enabled: anchorBackend.hasParent && isBaseState
opacity: enabled ? 1 : 0.5
actionIndicatorVisible: false actionIndicatorVisible: false
StudioControls.ButtonGroup { id: group } StudioControls.ButtonGroup { id: group }

View File

@@ -28,170 +28,212 @@ import QtQuick.Layouts 1.15
import StudioControls 1.0 as StudioControls import StudioControls 1.0 as StudioControls
import StudioTheme 1.0 as StudioTheme import StudioTheme 1.0 as StudioTheme
Rectangle { Item {
id: editableListView id: editableListView
property variant backendValue
ExtendedFunctionLogic { ExtendedFunctionLogic {
id: extFuncLogic id: extFuncLogic
backendValue: editableListView.backendValue backendValue: editableListView.backendValue
} }
property var backendValue
property var model property var model
onModelChanged: myRepeater.updateModel() 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 add(string value)
signal remove(int idx) signal remove(int idx)
signal replace(int idx, string value) 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.preferredWidth: StudioTheme.Values.height * 10
Layout.preferredHeight: myColumn.height Layout.preferredHeight: myColumn.height
Component { Component {
id: myDelegate 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 { Row {
id: row property alias comboBox: itemFilterComboBox
spacing: -StudioTheme.Values.border ListViewComboBox {
id: itemFilterComboBox
StudioControls.ActionIndicator { property int myIndex: index
id: actionIndicator property bool empty: itemFilterComboBox.initialModelData === ""
width: actionIndicator.visible ? __actionIndicatorWidth : 0
height: actionIndicator.visible ? __actionIndicatorHeight : 0
border.width: StudioTheme.Values.border validator: RegExpValidator { regExp: /(^[a-z_]\w*|^[A-Z]\w*\.{1}([a-z_]\w*\.?)+)/ }
border.color: StudioTheme.Values.themeControlOutline
icon.color: extFuncLogic.color actionIndicatorVisible: false
icon.text: extFuncLogic.glyph typeFilter: editableListView.typeFilter
onClicked: extFuncLogic.show() 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 { StudioControls.AbstractButton {
id: plusButton
buttonIcon: StudioTheme.Constants.plus buttonIcon: StudioTheme.Constants.plus
enabled: !myRepeater.dirty && !(editableListView.backendValue.isInModel && !editableListView.backendValue.isIdList) enabled: !myRepeater.dirty && !(editableListView.backendValue.isInModel
&& !editableListView.backendValue.isIdList)
onClicked: { onClicked: {
var idx = myRepeater.localModel.push("") - 1 var idx = myRepeater.localModel.push("") - 1
myRepeater.model = myRepeater.localModel // trigger on change handler myRepeater.model = myRepeater.localModel // trigger on change handler
@@ -199,29 +241,7 @@ Rectangle {
myColumn.currentIndex = idx myColumn.currentIndex = idx
myColumn.currentItem.forceActiveFocus() myColumn.currentItem.forceActiveFocus()
} }
} onHoveredChanged: editableListView.delegateHover = plusButton.hovered
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)
} }
} }
} }

View File

@@ -97,7 +97,7 @@ Item {
Connections { Connections {
target: modelNodeBackend target: modelNodeBackend
onSelectionChanged: menu.close() function onSelectionChanged() { menu.close() }
} }
StudioControls.MenuItem { StudioControls.MenuItem {

View File

@@ -37,6 +37,8 @@ Rectangle {
property alias pixelSize: indicatorIcon.font.pixelSize property alias pixelSize: indicatorIcon.font.pixelSize
property alias tooltip: toolTipArea.tooltip property alias tooltip: toolTipArea.tooltip
property bool hovered: toolTipArea.containsMouse && root.enabled
signal clicked() signal clicked()
color: "transparent" color: "transparent"

View File

@@ -258,7 +258,7 @@ Rectangle {
} }
PropertyChanges { PropertyChanges {
target: spinBoxIndicator target: spinBoxIndicator
color: StudioTheme.Values.themeControlBackground color: StudioTheme.Values.themeControlBackgroundDisabled
} }
}, },
State { State {

View File

@@ -22,7 +22,7 @@ DSdisabledColor=ff8e8e8e
DScontrolBackground=ffeaeaea DScontrolBackground=ffeaeaea
DScontrolBackgroundInteraction=ffc9c9c9 DScontrolBackgroundInteraction=ffc9c9c9
DScontrolBackgroundDisabled=ff8e8e8e DScontrolBackgroundDisabled=ffeaeaea
DScontrolBackgroundGlobalHover=ffe5e5e5 DScontrolBackgroundGlobalHover=ffe5e5e5
DScontrolBackgroundHover=ffd1d1d1 DScontrolBackgroundHover=ffd1d1d1

View File

@@ -36,7 +36,7 @@ DSdisabledColor=ff8e8e8e
DScontrolBackground=ffeaeaea DScontrolBackground=ffeaeaea
DScontrolBackgroundInteraction=ffc9c9c9 DScontrolBackgroundInteraction=ffc9c9c9
DScontrolBackgroundDisabled=ff8e8e8e DScontrolBackgroundDisabled=ffeaeaea
DScontrolBackgroundGlobalHover=ffe5e5e5 DScontrolBackgroundGlobalHover=ffe5e5e5
DScontrolBackgroundHover=ffd1d1d1 DScontrolBackgroundHover=ffd1d1d1

View File

@@ -31,7 +31,7 @@ DSdisabledColor=ff8e8e8e
DScontrolBackground=ffeaeaea DScontrolBackground=ffeaeaea
DScontrolBackgroundInteraction=ffc9c9c9 DScontrolBackgroundInteraction=ffc9c9c9
DScontrolBackgroundDisabled=ff8e8e8e DScontrolBackgroundDisabled=ffeaeaea
DScontrolBackgroundGlobalHover=ffe5e5e5 DScontrolBackgroundGlobalHover=ffe5e5e5
DScontrolBackgroundHover=ffd1d1d1 DScontrolBackgroundHover=ffd1d1d1

View File

@@ -1374,17 +1374,17 @@ FilePath FilePath::onDevice(const FilePath &deviceTemplate) const
Example usage: Example usage:
\code \code
binary = FilePath::fromUrl("docker://123/./make); binary = FilePath::fromUrl("docker://123/./make);
fullPath = binary.onDeviceSearchInPath(); fullPath = binary.searchOnDevice();
assert(fullPath == FilePath::fromUrl("docker://123/usr/bin/make")) assert(fullPath == FilePath::fromUrl("docker://123/usr/bin/make"))
\endcode \endcode
*/ */
FilePath FilePath::onDeviceSearchInPath(const FilePaths &additionalDirs) const FilePath FilePath::searchOnDevice(const FilePaths &dirs) const
{ {
if (needsDevice()) { if (needsDevice()) {
QTC_ASSERT(s_deviceHooks.searchInPath, return {}); 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 Environment FilePath::deviceEnvironment() const

View File

@@ -216,7 +216,7 @@ public:
static void setDeviceFileHooks(const DeviceFileHooks &hooks); static void setDeviceFileHooks(const DeviceFileHooks &hooks);
FilePath onDeviceSearchInPath(const QList<FilePath> &additionalDirs = {}) const; FilePath searchOnDevice(const QList<FilePath> &dirs) const;
Environment deviceEnvironment() const; Environment deviceEnvironment() const;
private: private:

View File

@@ -196,6 +196,8 @@ void QtTestOutputReader::processXMLOutput(const QByteArray &outputLine)
break; break;
} }
} }
if (m_cdataMode == Description)
m_xmlReader.addData("\n");
m_xmlReader.addData(QString::fromUtf8(outputLine)); m_xmlReader.addData(QString::fromUtf8(outputLine));
while (!m_xmlReader.atEnd()) { while (!m_xmlReader.atEnd()) {
if (m_futureInterface.isCanceled()) if (m_futureInterface.isCanceled())

View File

@@ -410,8 +410,7 @@ QtTestParseResult *QtTestParser::createParseResult(
dataTag->itemType = tag.m_type; dataTag->itemType = tag.m_type;
dataTag->name = tag.m_name; dataTag->name = tag.m_name;
dataTag->displayName = tag.m_name; dataTag->displayName = tag.m_name;
dataTag->fileName = Utils::FilePath::fromString( dataTag->fileName = data.testFunctions.value(it.key() + "_data").m_filePath;
data.testFunctions.value(it.key() + "_data").m_name);
dataTag->line = tag.m_line; dataTag->line = tag.m_line;
dataTag->column = tag.m_column; dataTag->column = tag.m_column;
dataTag->setInherited(tag.m_inherited); dataTag->setInherited(tag.m_inherited);

View File

@@ -839,13 +839,15 @@ void ClangdClient::openExtraFile(const Utils::FilePath &filePath, const QString
item.setUri(DocumentUri::fromFilePath(filePath)); item.setUri(DocumentUri::fromFilePath(filePath));
item.setText(!content.isEmpty() ? content : QString::fromUtf8(cxxFile.readAll())); item.setText(!content.isEmpty() ? content : QString::fromUtf8(cxxFile.readAll()));
item.setVersion(0); item.setVersion(0);
sendContent(DidOpenTextDocumentNotification(DidOpenTextDocumentParams(item))); sendContent(DidOpenTextDocumentNotification(DidOpenTextDocumentParams(item)),
SendDocUpdates::Ignore);
} }
void ClangdClient::closeExtraFile(const Utils::FilePath &filePath) void ClangdClient::closeExtraFile(const Utils::FilePath &filePath)
{ {
sendContent(DidCloseTextDocumentNotification(DidCloseTextDocumentParams( sendContent(DidCloseTextDocumentNotification(DidCloseTextDocumentParams(
TextDocumentIdentifier{DocumentUri::fromFilePath(filePath)}))); TextDocumentIdentifier{DocumentUri::fromFilePath(filePath)})),
SendDocUpdates::Ignore);
} }
void ClangdClient::findUsages(TextEditor::TextDocument *document, const QTextCursor &cursor, 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(); qCDebug(clangdLog) << "requesting AST for" << it.key().toFilePath();
refData->pendingAstRequests << request.id(); refData->pendingAstRequests << request.id();
q->sendContent(request); q->sendContent(request, SendDocUpdates::Ignore);
if (extraOpen) if (extraOpen)
q->closeExtraFile(it.key().toFilePath()); q->closeExtraFile(it.key().toFilePath());
@@ -1194,7 +1196,7 @@ void ClangdClient::followSymbol(
if (d->followSymbolData->defLink.hasValidTarget()) if (d->followSymbolData->defLink.hasValidTarget())
d->handleGotoDefinitionResult(); d->handleGotoDefinitionResult();
}); });
sendContent(astRequest); sendContent(astRequest, SendDocUpdates::Ignore);
} }
void ClangdClient::switchDeclDef(TextEditor::TextDocument *document, const QTextCursor &cursor, void ClangdClient::switchDeclDef(TextEditor::TextDocument *document, const QTextCursor &cursor,
@@ -1228,7 +1230,7 @@ void ClangdClient::switchDeclDef(TextEditor::TextDocument *document, const QText
d->handleDeclDefSwitchReplies(); d->handleDeclDefSwitchReplies();
}); });
sendContent(astRequest); sendContent(astRequest, SendDocUpdates::Ignore);
documentSymbolCache()->requestSymbols(d->switchDeclDefData->uri); documentSymbolCache()->requestSymbols(d->switchDeclDefData->uri);
} }
@@ -1315,8 +1317,7 @@ void ClangdClient::findLocalUsages(TextEditor::TextDocument *document, const QTe
d->localRefsData.reset(); d->localRefsData.reset();
}); });
qCDebug(clangdLog) << "sending ast request for link"; qCDebug(clangdLog) << "sending ast request for link";
sendContent(astRequest); sendContent(astRequest, SendDocUpdates::Ignore);
}; };
symbolSupport().findLinkAt(document, cursor, std::move(gotoDefCallback), true); 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. // with mainOverload = true, such information would get ignored anyway.
d->setHelpItemForTooltip(id, fqn, HelpItem::Function, isFunction ? type : "()"); d->setHelpItemForTooltip(id, fqn, HelpItem::Function, isFunction ? type : "()");
}); });
sendContent(symReq); sendContent(symReq, SendDocUpdates::Ignore);
return; return;
} }
if ((node.role() == "expression" && node.kind() == "DeclRef") if ((node.role() == "expression" && node.kind() == "DeclRef")
@@ -1466,7 +1467,7 @@ void ClangdClient::gatherHelpItemForTooltip(const HoverRequest::Response &hoverR
} }
d->setHelpItemForTooltip(id); d->setHelpItemForTooltip(id);
}); });
sendContent(req); sendContent(req, SendDocUpdates::Ignore);
} }
void ClangdClient::Private::handleGotoDefinitionResult() void ClangdClient::Private::handleGotoDefinitionResult()
@@ -1507,7 +1508,7 @@ void ClangdClient::Private::sendGotoImplementationRequest(const Utils::Link &lin
followSymbolData->pendingGotoImplRequests.removeOne(reqId); followSymbolData->pendingGotoImplRequests.removeOne(reqId);
handleGotoImplementationResult(response); handleGotoImplementationResult(response);
}); });
q->sendContent(req); q->sendContent(req, SendDocUpdates::Ignore);
followSymbolData->pendingGotoImplRequests << req.id(); followSymbolData->pendingGotoImplRequests << req.id();
qCDebug(clangdLog) << "sending go to implementation request" << link.targetLine; qCDebug(clangdLog) << "sending go to implementation request" << link.targetLine;
} }
@@ -1594,7 +1595,7 @@ void ClangdClient::Private::handleGotoImplementationResult(
}); });
followSymbolData->pendingSymbolInfoRequests << symReq.id(); followSymbolData->pendingSymbolInfoRequests << symReq.id();
qCDebug(clangdLog) << "sending symbol info request"; qCDebug(clangdLog) << "sending symbol info request";
q->sendContent(symReq); q->sendContent(symReq, SendDocUpdates::Ignore);
if (link == followSymbolData->defLink) if (link == followSymbolData->defLink)
continue; continue;
@@ -1628,7 +1629,7 @@ void ClangdClient::Private::handleGotoImplementationResult(
followSymbolData->pendingGotoDefRequests << defReq.id(); followSymbolData->pendingGotoDefRequests << defReq.id();
qCDebug(clangdLog) << "sending additional go to definition request" qCDebug(clangdLog) << "sending additional go to definition request"
<< link.targetFilePath << link.targetLine; << link.targetFilePath << link.targetLine;
q->sendContent(defReq); q->sendContent(defReq, SendDocUpdates::Ignore);
} }
const DocumentUri defLinkUri const DocumentUri defLinkUri
@@ -1651,7 +1652,7 @@ void ClangdClient::Private::handleGotoImplementationResult(
} }
}); });
qCDebug(clangdLog) << "sending ast request for def link"; qCDebug(clangdLog) << "sending ast request for def link";
q->sendContent(astRequest); q->sendContent(astRequest, SendDocUpdates::Ignore);
} }
void ClangdClient::Private::handleDocumentInfoResults() void ClangdClient::Private::handleDocumentInfoResults()
@@ -2385,7 +2386,7 @@ void ClangdClient::Private::handleSemanticTokens(TextEditor::TextDocument *doc,
it->second.setHighlightingRunner(runner); it->second.setHighlightingRunner(runner);
it->second.run(); it->second.run();
}); });
q->sendContent(astReq); q->sendContent(astReq, SendDocUpdates::Ignore);
} }
void ClangdClient::VirtualFunctionAssistProcessor::cancel() void ClangdClient::VirtualFunctionAssistProcessor::cancel()

View File

@@ -65,11 +65,11 @@ public:
static void clearTaskHubIssues(); static void clearTaskHubIssues();
void generateTaskHubIssues(); void generateTaskHubIssues();
void cleanMarks();
static void addTask(const ClangBackEnd::DiagnosticContainer &diagnostic, bool isChild = false); static void addTask(const ClangBackEnd::DiagnosticContainer &diagnostic, bool isChild = false);
private: private:
void cleanMarks();
QString filePath() const; QString filePath() const;
void filterDiagnostics(const QVector<ClangBackEnd::DiagnosticContainer> &diagnostics); void filterDiagnostics(const QVector<ClangBackEnd::DiagnosticContainer> &diagnostics);
void generateEditorSelections(); void generateEditorSelections();

View File

@@ -251,6 +251,14 @@ void ClangEditorDocumentProcessor::generateTaskHubIssues()
m_diagnosticManager.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( void ClangEditorDocumentProcessor::updateHighlighting(
const QVector<ClangBackEnd::TokenInfoContainer> &tokenInfos, const QVector<ClangBackEnd::TokenInfoContainer> &tokenInfos,
const QVector<ClangBackEnd::SourceRangeContainer> &skippedPreprocessorRanges, const QVector<ClangBackEnd::SourceRangeContainer> &skippedPreprocessorRanges,

View File

@@ -108,6 +108,8 @@ public:
static void clearTaskHubIssues(); static void clearTaskHubIssues();
void generateTaskHubIssues(); void generateTaskHubIssues();
static void clearTextMarks(const Utils::FilePath &filePath);
public: public:
static ClangEditorDocumentProcessor *get(const QString &filePath); static ClangEditorDocumentProcessor *get(const QString &filePath);

View File

@@ -316,6 +316,7 @@ void ClangModelManagerSupport::updateLanguageClient(ProjectExplorer::Project *pr
if (!project->isKnownFile(entry->fileName())) if (!project->isKnownFile(entry->fileName()))
continue; continue;
client->openDocument(textDocument); client->openDocument(textDocument);
ClangEditorDocumentProcessor::clearTextMarks(textDocument->filePath());
hasDocuments = true; hasDocuments = true;
} }

View File

@@ -500,7 +500,7 @@ QList<BaseQtVersion *> KitDetectorPrivate::autoDetectQtVersions() const
emit q->logOutput('\n' + tr("Searching Qt installations...")); emit q->logOutput('\n' + tr("Searching Qt installations..."));
for (const QString &candidate : candidates) { for (const QString &candidate : candidates) {
emit q->logOutput(tr("Searching for %1 executable...").arg(candidate)); 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()) if (qmake.isEmpty())
continue; continue;
BaseQtVersion *qtVersion = QtVersionFactory::createQtVersionFromQMakePath(qmake, false, m_sharedId, &error); BaseQtVersion *qtVersion = QtVersionFactory::createQtVersionFromQMakePath(qmake, false, m_sharedId, &error);
@@ -546,8 +546,8 @@ void KitDetectorPrivate::autoDetectCMake()
QString error; QString error;
const QStringList candidates = {"cmake"}; const QStringList candidates = {"cmake"};
for (const QString &candidate : candidates) { for (const QString &candidate : candidates) {
const FilePath cmake = m_device->searchInPath(FilePath::fromString(candidate)); const FilePath cmake = m_device->searchExecutableInPath(candidate);
if (cmake.isExecutableFile()) { if (!cmake.isEmpty()) {
emit q->logOutput(tr("Found CMake binary: %1").arg(cmake.toUserOutput())); emit q->logOutput(tr("Found CMake binary: %1").arg(cmake.toUserOutput()));
const bool res = QMetaObject::invokeMethod(cmakeManager, const bool res = QMetaObject::invokeMethod(cmakeManager,
"registerCMakeByPath", "registerCMakeByPath",
@@ -1046,35 +1046,6 @@ FilePath DockerDevice::symLinkTarget(const FilePath &filePath) const
return {}; 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, FilePaths DockerDevice::directoryEntries(const FilePath &filePath,
const QStringList &nameFilters, const QStringList &nameFilters,
QDir::Filters filters, QDir::Filters filters,

View File

@@ -88,8 +88,6 @@ public:
bool removeRecursively(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 copyFile(const Utils::FilePath &filePath, const Utils::FilePath &target) const override;
bool renameFile(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; Utils::FilePath symLinkTarget(const Utils::FilePath &filePath) const override;
QList<Utils::FilePath> directoryEntries(const Utils::FilePath &filePath, QList<Utils::FilePath> directoryEntries(const Utils::FilePath &filePath,
const QStringList &nameFilters, const QStringList &nameFilters,

View File

@@ -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_clientInterface, return);
QTC_ASSERT(m_state == Initialized, return); QTC_ASSERT(m_state == Initialized, return);
sendPostponedDocumentUpdates(); if (sendUpdates == SendDocUpdates::Send)
sendPostponedDocumentUpdates();
if (Utils::optional<ResponseHandler> responseHandler = content.responseHandler()) if (Utils::optional<ResponseHandler> responseHandler = content.responseHandler())
m_responseHandlers[responseHandler->id] = responseHandler->callback; m_responseHandlers[responseHandler->id] = responseHandler->callback;
QString error; QString error;
@@ -418,7 +419,7 @@ void Client::sendContent(const IContent &content)
void Client::cancelRequest(const MessageId &id) void Client::cancelRequest(const MessageId &id)
{ {
m_responseHandlers.remove(id); m_responseHandlers.remove(id);
sendContent(CancelRequest(CancelParameter(id))); sendContent(CancelRequest(CancelParameter(id)), SendDocUpdates::Ignore);
} }
void Client::closeDocument(TextEditor::TextDocument *document) void Client::closeDocument(TextEditor::TextDocument *document)

View File

@@ -92,7 +92,11 @@ public:
Utils::Id id() const { return m_id; } Utils::Id id() const { return m_id; }
void setName(const QString &name) { m_displayName = name; } void setName(const QString &name) { m_displayName = name; }
QString name() const; 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); void cancelRequest(const LanguageServerProtocol::MessageId &id);
// server state handling // server state handling

View File

@@ -283,7 +283,7 @@ private:
void updateModel(const DocumentUri &resultUri, const DocumentSymbolsResult &result); void updateModel(const DocumentUri &resultUri, const DocumentSymbolsResult &result);
void updateEntry(); void updateEntry();
void activateEntry(); void activateEntry();
void requestSymbols(); void documentUpdated(TextEditor::TextDocument *document);
LanguageClientOutlineModel m_model; LanguageClientOutlineModel m_model;
QPointer<Client> m_client; QPointer<Client> m_client;
@@ -318,13 +318,12 @@ OutlineComboBox::OutlineComboBox(Client *client, TextEditor::BaseTextEditor *edi
connect(client->documentSymbolCache(), &DocumentSymbolCache::gotSymbols, connect(client->documentSymbolCache(), &DocumentSymbolCache::gotSymbols,
this, &OutlineComboBox::updateModel); this, &OutlineComboBox::updateModel);
connect(editor->textDocument(), &TextEditor::TextDocument::contentsChanged, connect(client, &Client::documentUpdated, this, &OutlineComboBox::documentUpdated);
this, &OutlineComboBox::requestSymbols);
connect(m_editorWidget, &TextEditor::TextEditorWidget::cursorPositionChanged, connect(m_editorWidget, &TextEditor::TextEditorWidget::cursorPositionChanged,
this, &OutlineComboBox::updateEntry); this, &OutlineComboBox::updateEntry);
connect(this, QOverload<int>::of(&QComboBox::activated), this, &OutlineComboBox::activateEntry); connect(this, QOverload<int>::of(&QComboBox::activated), this, &OutlineComboBox::activateEntry);
requestSymbols(); documentUpdated(editor->textDocument());
} }
void OutlineComboBox::updateModel(const DocumentUri &resultUri, const DocumentSymbolsResult &result) 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); m_client->documentSymbolCache()->requestSymbols(m_uri);
} }

View File

@@ -371,11 +371,11 @@ SemanticRequestTypes SemanticTokenSupport::supportedSemanticRequests(TextDocumen
void SemanticTokenSupport::handleSemanticTokens(const Utils::FilePath &filePath, void SemanticTokenSupport::handleSemanticTokens(const Utils::FilePath &filePath,
const SemanticTokensResult &result) const SemanticTokensResult &result)
{ {
if (auto tokens = Utils::get_if<SemanticTokens>(&result)) if (auto tokens = Utils::get_if<SemanticTokens>(&result)) {
m_tokens[filePath] = *tokens; m_tokens[filePath] = *tokens;
else highlight(filePath);
m_tokens.remove(filePath); }
highlight(filePath); m_tokens.remove(filePath);
} }
void SemanticTokenSupport::handleSemanticTokensDelta( void SemanticTokenSupport::handleSemanticTokensDelta(
@@ -427,6 +427,7 @@ void SemanticTokenSupport::handleSemanticTokensDelta(
tokens.setResultId(tokensDelta->resultId()); tokens.setResultId(tokensDelta->resultId());
} else { } else {
m_tokens.remove(filePath); m_tokens.remove(filePath);
return;
} }
highlight(filePath); highlight(filePath);
} }

View File

@@ -58,7 +58,7 @@ QList<ToolChain *> NimToolChainFactory::autoDetect(const QList<ToolChain *> &alr
QList<ToolChain *> result; QList<ToolChain *> result;
IDevice::ConstPtr dev = device ? device : DeviceManager::defaultDesktopDevice(); IDevice::ConstPtr dev = device ? device : DeviceManager::defaultDesktopDevice();
const FilePath compilerPath = dev->searchInPath(FilePath::fromString("nim")); const FilePath compilerPath = dev->searchExecutableInPath("nim");
if (compilerPath.isEmpty()) if (compilerPath.isEmpty())
return result; return result;

View File

@@ -1168,11 +1168,8 @@ Abis Abi::abisOfBinary(const Utils::FilePath &path)
&& getUint8(data, 6) == '>' && getUint8(data, 7) == 0x0a) { && getUint8(data, 6) == '>' && getUint8(data, 7) == 0x0a) {
// We got an ar file: possibly a static lib for ELF, PE or Mach-O // 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()); QFile f(path.toString());
if (!f.open(QFile::ReadOnly)) const bool canRead = f.open(QFile::ReadOnly);
return tmp;
data = data.mid(8); // Cut of ar file magic data = data.mid(8); // Cut of ar file magic
quint64 offset = 8; quint64 offset = 8;
@@ -1199,6 +1196,11 @@ Abis Abi::abisOfBinary(const Utils::FilePath &path)
if (!tmp.isEmpty() && tmp.at(0).binaryFormat() != MachOFormat) if (!tmp.isEmpty() && tmp.at(0).binaryFormat() != MachOFormat)
break; break;
if (!canRead) {
// FIXME: Implement remote
QTC_ASSERT(!path.needsDevice(), return {});
}
offset += (offset % 2); // ar is 2 byte aligned offset += (offset % 2); // ar is 2 byte aligned
f.seek(offset); f.seek(offset);
data = f.read(1024); data = f.read(1024);

View File

@@ -43,6 +43,7 @@
#include <utils/url.h> #include <utils/url.h>
#include <QCoreApplication> #include <QCoreApplication>
#include <QDateTime>
using namespace ProjectExplorer::Constants; using namespace ProjectExplorer::Constants;
using namespace Utils; using namespace Utils;
@@ -194,4 +195,101 @@ Environment DesktopDevice::systemEnvironment() const
return Environment::systemEnvironment(); 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 } // namespace ProjectExplorer

View File

@@ -54,13 +54,29 @@ public:
DeviceProcessSignalOperation::Ptr signalOperation() const override; DeviceProcessSignalOperation::Ptr signalOperation() const override;
DeviceEnvironmentFetcher::Ptr environmentFetcher() const override; DeviceEnvironmentFetcher::Ptr environmentFetcher() const override;
QUrl toolControlChannel(const ControlChannelHint &) const override; QUrl toolControlChannel(const ControlChannelHint &) const override;
bool handlesFile(const Utils::FilePath &filePath) const override; bool handlesFile(const Utils::FilePath &filePath) const override;
QList<Utils::FilePath> directoryEntries(const Utils::FilePath &filePath, QList<Utils::FilePath> directoryEntries(const Utils::FilePath &filePath,
const QStringList &nameFilters, const QStringList &nameFilters,
QDir::Filters filters, QDir::Filters filters,
QDir::SortFlags sort) const override; QDir::SortFlags sort) const override;
Utils::Environment systemEnvironment() 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: protected:
DesktopDevice(); DesktopDevice();

View File

@@ -458,10 +458,10 @@ DeviceManager::DeviceManager(bool isInstance) : d(std::make_unique<DeviceManager
return device->renameFile(filePath, target); 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); auto device = DeviceManager::deviceForPath(filePath);
QTC_ASSERT(device, return FilePath{}); QTC_ASSERT(device, return FilePath{});
return device->searchInPath(filePath, additionalDirs); return device->searchExecutable(filePath.path(), dirs);
}; };
deviceHooks.symLinkTarget = [](const FilePath &filePath) { deviceHooks.symLinkTarget = [](const FilePath &filePath) {

View File

@@ -311,9 +311,26 @@ bool IDevice::renameFile(const FilePath &filePath, const FilePath &target) const
return false; 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 FilePath IDevice::symLinkTarget(const FilePath &filePath) const

View File

@@ -249,8 +249,9 @@ public:
virtual bool removeRecursively(const Utils::FilePath &filePath) const; virtual bool removeRecursively(const Utils::FilePath &filePath) const;
virtual bool copyFile(const Utils::FilePath &filePath, const Utils::FilePath &target) 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 bool renameFile(const Utils::FilePath &filePath, const Utils::FilePath &target) const;
virtual Utils::FilePath searchInPath(const Utils::FilePath &filePath, virtual Utils::FilePath searchExecutableInPath(const QString &fileName) const;
const QList<Utils::FilePath> &additionalDirs = {}) const; virtual Utils::FilePath searchExecutable(const QString &fileName,
const QList<Utils::FilePath> &dirs) const;
virtual Utils::FilePath symLinkTarget(const Utils::FilePath &filePath) const; virtual Utils::FilePath symLinkTarget(const Utils::FilePath &filePath) const;
virtual QList<Utils::FilePath> directoryEntries(const Utils::FilePath &filePath, virtual QList<Utils::FilePath> directoryEntries(const Utils::FilePath &filePath,
const QStringList &nameFilters, const QStringList &nameFilters,

View File

@@ -284,8 +284,11 @@ QmakeBuildSystem::~QmakeBuildSystem()
delete m_qmakeVfs; delete m_qmakeVfs;
m_qmakeVfs = nullptr; m_qmakeVfs = nullptr;
m_asyncUpdateFutureInterface.reportCanceled(); if (m_asyncUpdateFutureInterface) {
m_asyncUpdateFutureInterface.reportFinished(); m_asyncUpdateFutureInterface->reportCanceled();
m_asyncUpdateFutureInterface->reportFinished();
m_asyncUpdateFutureInterface.reset();
}
} }
void QmakeBuildSystem::updateCodeModels() void QmakeBuildSystem::updateCodeModels()
@@ -591,8 +594,9 @@ void QmakeBuildSystem::incrementPendingEvaluateFutures()
} }
++m_pendingEvaluateFuturesCount; ++m_pendingEvaluateFuturesCount;
TRACE("pending inc to: " << m_pendingEvaluateFuturesCount); TRACE("pending inc to: " << m_pendingEvaluateFuturesCount);
m_asyncUpdateFutureInterface.setProgressRange(m_asyncUpdateFutureInterface.progressMinimum(), m_asyncUpdateFutureInterface->setProgressRange(m_asyncUpdateFutureInterface->progressMinimum(),
m_asyncUpdateFutureInterface.progressMaximum() + 1); m_asyncUpdateFutureInterface->progressMaximum()
+ 1);
} }
void QmakeBuildSystem::decrementPendingEvaluateFutures() void QmakeBuildSystem::decrementPendingEvaluateFutures()
@@ -605,15 +609,17 @@ void QmakeBuildSystem::decrementPendingEvaluateFutures()
return; // We are closing the project! return; // We are closing the project!
} }
m_asyncUpdateFutureInterface.setProgressValue(m_asyncUpdateFutureInterface.progressValue() + 1); m_asyncUpdateFutureInterface->setProgressValue(m_asyncUpdateFutureInterface->progressValue()
+ 1);
if (m_pendingEvaluateFuturesCount == 0) { if (m_pendingEvaluateFuturesCount == 0) {
// We are done! // We are done!
setRootProjectNode(QmakeNodeTreeBuilder::buildTree(this)); setRootProjectNode(QmakeNodeTreeBuilder::buildTree(this));
if (!m_rootProFile->validParse()) if (!m_rootProFile->validParse())
m_asyncUpdateFutureInterface.reportCanceled(); m_asyncUpdateFutureInterface->reportCanceled();
m_asyncUpdateFutureInterface.reportFinished(); m_asyncUpdateFutureInterface->reportFinished();
m_asyncUpdateFutureInterface.reset();
m_cancelEvaluate = false; m_cancelEvaluate = false;
// TODO clear the profile cache ? // TODO clear the profile cache ?
@@ -659,12 +665,13 @@ void QmakeBuildSystem::asyncUpdate()
m_qmakeVfs->invalidateCache(); m_qmakeVfs->invalidateCache();
} }
m_asyncUpdateFutureInterface.setProgressRange(0, 0); m_asyncUpdateFutureInterface.reset(new QFutureInterface<void>);
Core::ProgressManager::addTask(m_asyncUpdateFutureInterface.future(), m_asyncUpdateFutureInterface->setProgressRange(0, 0);
Core::ProgressManager::addTask(m_asyncUpdateFutureInterface->future(),
tr("Reading Project \"%1\"").arg(project()->displayName()), tr("Reading Project \"%1\"").arg(project()->displayName()),
Constants::PROFILE_EVALUATE); Constants::PROFILE_EVALUATE);
m_asyncUpdateFutureInterface.reportStarted(); m_asyncUpdateFutureInterface->reportStarted();
const auto watcher = new QFutureWatcher<void>(this); const auto watcher = new QFutureWatcher<void>(this);
connect(watcher, &QFutureWatcher<void>::canceled, this, [this, watcher] { connect(watcher, &QFutureWatcher<void>::canceled, this, [this, watcher] {
if (!m_qmakeGlobals) if (!m_qmakeGlobals)
@@ -676,7 +683,7 @@ void QmakeBuildSystem::asyncUpdate()
watcher->disconnect(); watcher->disconnect();
watcher->deleteLater(); watcher->deleteLater();
}); });
watcher->setFuture(m_asyncUpdateFutureInterface.future()); watcher->setFuture(m_asyncUpdateFutureInterface->future());
const Kit *const k = kit(); const Kit *const k = kit();
QtSupport::BaseQtVersion *const qtVersion = QtSupport::QtKitAspect::qtVersion(k); QtSupport::BaseQtVersion *const qtVersion = QtSupport::QtKitAspect::qtVersion(k);
@@ -687,8 +694,9 @@ void QmakeBuildSystem::asyncUpdate()
.arg(project()->displayName(), k->displayName()) .arg(project()->displayName(), k->displayName())
: tr("Cannot parse project \"%1\": No kit selected.").arg(project()->displayName()); : tr("Cannot parse project \"%1\": No kit selected.").arg(project()->displayName());
proFileParseError(errorMessage, project()->projectFilePath()); proFileParseError(errorMessage, project()->projectFilePath());
m_asyncUpdateFutureInterface.reportCanceled(); m_asyncUpdateFutureInterface->reportCanceled();
m_asyncUpdateFutureInterface.reportFinished(); m_asyncUpdateFutureInterface->reportFinished();
m_asyncUpdateFutureInterface.reset();
return; return;
} }

View File

@@ -200,7 +200,7 @@ private:
QString m_qmakeSysroot; QString m_qmakeSysroot;
QFutureInterface<void> m_asyncUpdateFutureInterface; std::unique_ptr<QFutureInterface<void>> m_asyncUpdateFutureInterface;
int m_pendingEvaluateFuturesCount = 0; int m_pendingEvaluateFuturesCount = 0;
AsyncUpdateState m_asyncUpdateState = Base; AsyncUpdateState m_asyncUpdateState = Base;
bool m_cancelEvaluate = false; bool m_cancelEvaluate = false;

View File

@@ -1007,20 +1007,32 @@ void DesignerActionManager::createDefaultDesignerActions()
&selectionNotEmptyAndHasXorYProperty)); &selectionNotEmptyAndHasXorYProperty));
const QString fontName = "qtds_propertyIconFont.ttf"; const QString fontName = "qtds_propertyIconFont.ttf";
const QColor iconColorNormal(Theme::getColor(Theme::IconsBaseColor)); const QColor iconColorDefault(Theme::getColor(Theme::IconsBaseColor));
const QIcon pasteIcon = Utils::StyleHelper::getIconFromIconFont(fontName, const QColor iconColorDisabled(Theme::getColor(Theme::IconsDisabledColor));
Theme::getIconUnicode( const QString copyUnicode = Theme::getIconUnicode(Theme::Icon::copyStyle);
Theme::Icon::pasteStyle), const QString pasteUnicode = Theme::getIconUnicode(Theme::Icon::pasteStyle);
28,
28,
iconColorNormal);
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, const QIcon copyIcon = Utils::StyleHelper::getIconFromIconFont(fontName,
Theme::getIconUnicode( {copyDefault, copyDisabled});
Theme::Icon::copyStyle),
28, const auto pasteDefault = Utils::StyleHelper::IconFontHelper(pasteUnicode,
28, iconColorDefault,
iconColorNormal); 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, addDesignerAction(new ModelNodeAction(copyFormatCommandId,
copyFormatDisplayName, copyFormatDisplayName,

View File

@@ -43,35 +43,36 @@ using namespace Utils;
namespace WebAssembly { namespace WebAssembly {
namespace Internal { namespace Internal {
using EmSdkEnvCache = QCache<QString, QByteArray>; using EmSdkEnvCache = QCache<QString, QString>;
Q_GLOBAL_STATIC_WITH_ARGS(EmSdkEnvCache, emSdkEnvCache, (10)) Q_GLOBAL_STATIC_WITH_ARGS(EmSdkEnvCache, emSdkEnvCache, (10))
using EmSdkVersionCache = QCache<QString, QVersionNumber>; using EmSdkVersionCache = QCache<QString, QVersionNumber>;
Q_GLOBAL_STATIC_WITH_ARGS(EmSdkVersionCache, emSdkVersionCache, (10)) 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 QString cacheKey = sdkRoot.toString();
const bool isWindows = sdkRoot.osType() == OsTypeWindows;
if (!emSdkEnvCache()->contains(cacheKey)) { if (!emSdkEnvCache()->contains(cacheKey)) {
const QString scriptFile = sdkRoot.pathAppended(QLatin1String("emsdk_env") + const QString scriptFile = sdkRoot.pathAppended(QLatin1String("emsdk_env") +
(HostOsInfo::isWindowsHost() ? ".bat" : ".sh")).toString(); (isWindows ? ".bat" : ".sh")).path();
QtcProcess emSdkEnv; QtcProcess emSdkEnv;
if (HostOsInfo::isWindowsHost()) { if (isWindows) {
emSdkEnv.setCommand(CommandLine(scriptFile)); emSdkEnv.setCommand(CommandLine(scriptFile));
} else { } else {
// File needs to be source'd, not executed. // File needs to be source'd, not executed.
emSdkEnv.setCommand({"bash", {"-c", ". " + scriptFile}}); emSdkEnv.setCommand({FilePath::fromString("bash").onDevice(sdkRoot),
{"-c", ". " + scriptFile}});
} }
emSdkEnv.start(); emSdkEnv.runBlocking();
if (!emSdkEnv.waitForFinished()) const QString output = emSdkEnv.allOutput();
return {}; emSdkEnvCache()->insert(cacheKey, new QString(output));
emSdkEnvCache()->insert(cacheKey, new QByteArray(emSdkEnv.readAllStandardError()));
} }
return *emSdkEnvCache()->object(cacheKey); 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) { for (const QString &line : lines) {
const QStringList prependParts = line.trimmed().split(" += "); const QStringList prependParts = line.trimmed().split(" += ");
@@ -104,18 +105,17 @@ QVersionNumber WebAssemblyEmSdk::version(const FilePath &sdkRoot)
return {}; return {};
const QString cacheKey = sdkRoot.toString(); const QString cacheKey = sdkRoot.toString();
if (!emSdkVersionCache()->contains(cacheKey)) { if (!emSdkVersionCache()->contains(cacheKey)) {
Environment env = Environment::systemEnvironment(); Environment env;
WebAssemblyEmSdk::addToEnvironment(sdkRoot, env); WebAssemblyEmSdk::addToEnvironment(sdkRoot, env);
const QString scriptFile = QLatin1String scriptFile{sdkRoot.osType() == OsType::OsTypeWindows ? "emcc.bat" : "emcc"};
QLatin1String("emcc") + QLatin1String(HostOsInfo::isWindowsHost() ? ".bat" : ""); FilePath script =
const CommandLine command(env.searchInPath(scriptFile), {"-dumpversion"}); FilePath::fromString(scriptFile).onDevice(sdkRoot).searchOnDevice(env.path());
const CommandLine command(script, {"-dumpversion"});
QtcProcess emcc; QtcProcess emcc;
emcc.setCommand(command); emcc.setCommand(command);
emcc.setEnvironment(env); emcc.setEnvironment(env);
emcc.start(); emcc.runBlocking();
if (!emcc.waitForFinished()) const QString version = emcc.stdOut();
return {};
const QString version = QLatin1String(emcc.readAllStandardOutput());
emSdkVersionCache()->insert(cacheKey, emSdkVersionCache()->insert(cacheKey,
new QVersionNumber(QVersionNumber::fromString(version))); new QVersionNumber(QVersionNumber::fromString(version)));
} }
@@ -146,10 +146,31 @@ void WebAssemblyEmSdk::clearCaches()
// Unit tests: // Unit tests:
#ifdef WITH_TESTS #ifdef WITH_TESTS
void WebAssemblyPlugin::testEmSdkEnvParsing() 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" // Output of "emsdk_env"
const QByteArray emSdkEnvOutput = HostOsInfo::isWindowsHost() ? QTest::addColumn<QString>("emSdkEnvOutput");
R"( 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: Adding directories to PATH:
PATH += C:\Users\user\dev\emsdk PATH += C:\Users\user\dev\emsdk
PATH += C:\Users\user\dev\emsdk\upstream\emscripten 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_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 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 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: Adding directories to PATH:
PATH += /home/user/dev/emsdk PATH += /home/user/dev/emsdk
PATH += /home/user/dev/emsdk/upstream/emscripten PATH += /home/user/dev/emsdk/upstream/emscripten
@@ -177,20 +200,9 @@ EMSDK = /home/user/dev/emsdk
EM_CONFIG = /home/user/dev/emsdk/.emscripten EM_CONFIG = /home/user/dev/emsdk/.emscripten
EM_CACHE = /home/user/dev/emsdk/upstream/emscripten/cache EM_CACHE = /home/user/dev/emsdk/upstream/emscripten/cache
EMSDK_NODE = /home/user/dev/emsdk/node/12.18.1_64bit/bin/node EMSDK_NODE = /home/user/dev/emsdk/node/12.18.1_64bit/bin/node
)"; )" << int(OsTypeLinux) << 3 << "/home/user/dev/emsdk" << "/home/user/dev/emsdk/.emscripten";
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");
}
} }
#endif // WITH_TESTS #endif // WITH_TESTS
} // namespace Internal } // namespace Internal

View File

@@ -129,7 +129,7 @@ static QString environmentDisplay(const FilePath &sdkRoot)
WebAssemblyEmSdk::addToEnvironment(sdkRoot, env); WebAssemblyEmSdk::addToEnvironment(sdkRoot, env);
QString result; QString result;
result.append(WebAssemblyOptionsWidget::tr("<h4>Adding directories to PATH:</h4>")); 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>")); result.append(WebAssemblyOptionsWidget::tr("<h4>Setting environment variables:</h4>"));
for (const QString &envVar : env.toStringList()) { for (const QString &envVar : env.toStringList()) {
if (!envVar.startsWith("PATH")) // Path was already printed out above if (!envVar.startsWith("PATH")) // Path was already printed out above

View File

@@ -48,6 +48,7 @@ public:
#ifdef WITH_TESTS #ifdef WITH_TESTS
private slots: private slots:
void testEmSdkEnvParsing(); void testEmSdkEnvParsing();
void testEmSdkEnvParsing_data();
#endif // WITH_TESTS #endif // WITH_TESTS
}; };

View File

@@ -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. // 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 // 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. // right python (not part of emsdk). The shell script stays attached to the server process.
const FilePath interpreter = bc->environment().searchInPath( const FilePath interpreter = HostOsInfo::isWindowsHost()
QLatin1String(HostOsInfo::isWindowsHost() ? "python" : "sh")); ? FilePath::fromUserInput(bc->environment().value("EMSDK_PYTHON"))
: bc->environment().searchInPath("sh");
const QString emrunLaunchScript = HostOsInfo::isWindowsHost() const QString emrunLaunchScript = HostOsInfo::isWindowsHost()
? emrun.absolutePath() + "/" + emrun.baseName() + ".py" ? emrun.absolutePath() + "/" + emrun.baseName() + ".py"
: emrun.absoluteFilePath(); : emrun.absoluteFilePath();

View File

@@ -27,20 +27,20 @@
#include "webassemblyconstants.h" #include "webassemblyconstants.h"
#include "webassemblyemsdk.h" #include "webassemblyemsdk.h"
#include <projectexplorer/devicesupport/devicemanager.h>
#include <projectexplorer/kitmanager.h> #include <projectexplorer/kitmanager.h>
#include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/projectmacro.h> #include <projectexplorer/projectmacro.h>
#include <projectexplorer/toolchainmanager.h> #include <projectexplorer/toolchainmanager.h>
#include <qtsupport/qtkitinformation.h> #include <qtsupport/qtkitinformation.h>
#include <utils/algorithm.h> #include <utils/algorithm.h>
#include <utils/environment.h> #include <utils/environment.h>
#include <utils/fileutils.h> #include <utils/fileutils.h>
#include <utils/hostosinfo.h> #include <utils/hostosinfo.h>
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
#include <QDir>
#include <QSettings>
using namespace ProjectExplorer; using namespace ProjectExplorer;
using namespace QtSupport; using namespace QtSupport;
using namespace Utils; using namespace Utils;
@@ -73,7 +73,7 @@ static void addRegisteredMinGWToEnvironment(Environment &env)
void WebAssemblyToolChain::addToEnvironment(Environment &env) const void WebAssemblyToolChain::addToEnvironment(Environment &env) const
{ {
WebAssemblyEmSdk::addToEnvironment(WebAssemblyEmSdk::registeredEmSdk(), env); WebAssemblyEmSdk::addToEnvironment(WebAssemblyEmSdk::registeredEmSdk(), env);
if (HostOsInfo::isWindowsHost()) if (env.osType() == OsTypeWindows)
addRegisteredMinGWToEnvironment(env); addRegisteredMinGWToEnvironment(env);
} }
@@ -88,9 +88,9 @@ WebAssemblyToolChain::WebAssemblyToolChain() :
FilePath WebAssemblyToolChain::makeCommand(const Environment &environment) const FilePath WebAssemblyToolChain::makeCommand(const Environment &environment) const
{ {
// Diverged duplicate of ClangToolChain::makeCommand and MingwToolChain::makeCommand // Diverged duplicate of ClangToolChain::makeCommand and MingwToolChain::makeCommand
const QStringList makes const QStringList makes = environment.osType() == OsTypeWindows
= HostOsInfo::isWindowsHost() ? QStringList({"mingw32-make.exe", "make.exe"}) ? QStringList({"mingw32-make.exe", "make.exe"})
: QStringList({"make"}); : QStringList({"make"});
FilePath tmp; FilePath tmp;
for (const QString &make : makes) { for (const QString &make : makes) {
@@ -162,13 +162,12 @@ QList<ToolChain *> WebAssemblyToolChainFactory::autoDetect(
const IDevice::Ptr &device) const IDevice::Ptr &device)
{ {
Q_UNUSED(alreadyKnown) Q_UNUSED(alreadyKnown)
Q_UNUSED(device)
const FilePath sdk = WebAssemblyEmSdk::registeredEmSdk(); const FilePath sdk = WebAssemblyEmSdk::registeredEmSdk();
if (!WebAssemblyEmSdk::isValid(sdk)) if (!WebAssemblyEmSdk::isValid(sdk))
return {}; return {};
Environment env; Environment env = sdk.deviceEnvironment();
WebAssemblyEmSdk::addToEnvironment(sdk, env); WebAssemblyEmSdk::addToEnvironment(sdk, env);
QList<ToolChain *> result; QList<ToolChain *> result;
@@ -178,9 +177,11 @@ QList<ToolChain *> WebAssemblyToolChainFactory::autoDetect(
toolChain->setLanguage(languageId); toolChain->setLanguage(languageId);
toolChain->setDetection(ToolChain::AutoDetection); toolChain->setDetection(ToolChain::AutoDetection);
const bool cLanguage = languageId == ProjectExplorer::Constants::C_LANGUAGE_ID; const bool cLanguage = languageId == ProjectExplorer::Constants::C_LANGUAGE_ID;
const QString scriptFile = QLatin1String(cLanguage ? "emcc" : "em++") const QString script = QLatin1String(cLanguage ? "emcc" : "em++")
+ QLatin1String(HostOsInfo::isWindowsHost() ? ".bat" : ""); + QLatin1String(sdk.osType() == OsTypeWindows ? ".bat" : "");
toolChain->setCompilerCommand(env.searchInPath(scriptFile)); const FilePath scriptFile =
FilePath::fromString(script).onDevice(sdk).searchOnDevice(env.path());
toolChain->setCompilerCommand(scriptFile);
const QString displayName = WebAssemblyToolChain::tr("Emscripten Compiler %1 for %2") const QString displayName = WebAssemblyToolChain::tr("Emscripten Compiler %1 for %2")
.arg(toolChain->version(), QLatin1String(cLanguage ? "C" : "C++")); .arg(toolChain->version(), QLatin1String(cLanguage ? "C" : "C++"));

View File

@@ -25,6 +25,7 @@
#include "proitems.h" #include "proitems.h"
#include <qdebug.h>
#include <qfileinfo.h> #include <qfileinfo.h>
#include <qset.h> #include <qset.h>
#include <qstringlist.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) : 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) 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) : 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(); updatedHash();
} }
ProString::ProString(const char *str) : 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 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) ProString &ProString::prepend(const ProString &other)
@@ -493,4 +500,9 @@ ProKey ProFile::getHashStr(const ushort *&tPtr)
return ret; return ret;
} }
QDebug operator<<(QDebug debug, const ProString &str)
{
return debug << str.toQString();
}
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@@ -64,6 +64,8 @@ class ProFile;
class ProString { class ProString {
public: public:
ProString(); ProString();
ProString(const ProString &other);
ProString &operator=(const ProString &) = default;
template<typename A, typename B> template<typename A, typename B>
ProString &operator=(const QStringBuilder<A, B> &str) ProString &operator=(const QStringBuilder<A, B> &str)
{ return *this = QString(str); } { return *this = QString(str); }
@@ -74,7 +76,6 @@ public:
ProString(const QStringBuilder<A, B> &str) ProString(const QStringBuilder<A, B> &str)
: ProString(QString(str)) : ProString(QString(str))
{} {}
ProString(const QString &str, int offset, int length); ProString(const QString &str, int offset, int length);
void setValue(const QString &str); void setValue(const QString &str);
void clear() { m_string.clear(); m_length = 0; } void clear() { m_string.clear(); m_length = 0; }
@@ -83,14 +84,14 @@ public:
int sourceFile() const { return m_file; } int sourceFile() const { return m_file; }
ProString &prepend(const ProString &other); 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)); } ProString &append(const QString &other) { return append(ProString(other)); }
template<typename A, typename B> template<typename A, typename B>
ProString &append(const QStringBuilder<A, B> &other) { return append(QString(other)); } ProString &append(const QStringBuilder<A, B> &other) { return append(QString(other)); }
ProString &append(const QLatin1String other); ProString &append(const QLatin1String other);
ProString &append(const char *other) { return append(QLatin1String(other)); } ProString &append(const char *other) { return append(QLatin1String(other)); }
ProString &append(QChar 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 ProString &other) { return append(other); }
ProString &operator+=(const QString &other) { return append(other); } ProString &operator+=(const QString &other) { return append(other); }
template<typename A, typename B> 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 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(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; } 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); } qlonglong toLongLong(bool *ok = nullptr, int base = 10) const { return toStringView().toLongLong(ok, base); }
int toInt(bool *ok = 0, int base = 10) const { return toStringView().toInt(ok, base); } int toInt(bool *ok = nullptr, int base = 10) const { return toStringView().toInt(ok, base); }
short toShort(bool *ok = 0, int base = 10) const { return toStringView().toShort(ok, base); } short toShort(bool *ok = nullptr, int base = 10) const { return toStringView().toShort(ok, base); }
uint hash() const { return m_hash; } uint hash() const { return m_hash; }
static uint hash(const QChar *p, int n); static uint hash(const QChar *p, int n);
@@ -185,7 +186,8 @@ private:
friend QString operator+(const ProString &one, const ProString &two); friend QString operator+(const ProString &one, const ProString &two);
friend class ProKey; friend class ProKey;
}; };
Q_DECLARE_TYPEINFO(ProString, Q_MOVABLE_TYPE); Q_DECLARE_TYPEINFO(ProString, Q_RELOCATABLE_TYPE);
class ProKey : public ProString { class ProKey : public ProString {
public: public:
@@ -195,7 +197,6 @@ public:
ProKey(const QStringBuilder<A, B> &str) ProKey(const QStringBuilder<A, B> &str)
: ProString(str) : ProString(str)
{} {}
PROITEM_EXPLICIT ProKey(const char *str); PROITEM_EXPLICIT ProKey(const char *str);
ProKey(const QString &str, int off, int len); ProKey(const QString &str, int off, int len);
ProKey(const QString &str, int off, int len, uint hash); ProKey(const QString &str, int off, int len, uint hash);
@@ -217,7 +218,7 @@ public:
private: private:
ProKey(const ProString &other); ProKey(const ProString &other);
}; };
Q_DECLARE_TYPEINFO(ProKey, Q_MOVABLE_TYPE); Q_DECLARE_TYPEINFO(ProKey, Q_RELOCATABLE_TYPE);
template <> struct QConcatenable<ProString> : private QAbstractConcatenable 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) static inline void appendTo(const ProString &a, QChar *&out)
{ {
const auto n = a.size(); const auto n = a.size();
if (!n)
return;
memcpy(out, a.toStringView().data(), sizeof(QChar) * n); memcpy(out, a.toStringView().data(), sizeof(QChar) * n);
out += n; out += n;
} }
@@ -242,6 +245,8 @@ template <> struct QConcatenable<ProKey> : private QAbstractConcatenable
static inline void appendTo(const ProKey &a, QChar *&out) static inline void appendTo(const ProKey &a, QChar *&out)
{ {
const auto n = a.size(); const auto n = a.size();
if (!n)
return;
memcpy(out, a.toStringView().data(), sizeof(QChar) * n); memcpy(out, a.toStringView().data(), sizeof(QChar) * n);
out += n; out += n;
} }
@@ -257,6 +262,54 @@ QTextStream &operator<<(QTextStream &t, const ProString &str);
template<typename A, typename B> template<typename A, typename B>
QTextStream &operator<<(QTextStream &t, const QStringBuilder<A, B> &str) { return t << QString(str); } 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> { class ProStringList : public QVector<ProString> {
public: public:
ProStringList() {} ProStringList() {}
@@ -290,21 +343,12 @@ public:
{ return contains(ProString(str), cs); } { return contains(ProString(str), cs); }
bool contains(const char *str, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; 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) inline ProStringList operator+(const ProStringList &one, const ProStringList &two)
{ ProStringList ret = one; ret += two; return ret; } { ProStringList ret = one; ret += two; return ret; }
typedef QHash<ProKey, ProStringList> ProValueMap; typedef QMap<ProKey, ProStringList> ProValueMap;
// For std::list (sic!)
#ifdef Q_CC_MSVC
inline bool operator<(const ProValueMap &, const ProValueMap &)
{
Q_ASSERT(false);
return false;
}
#endif
// These token definitions affect both ProFileEvaluator and ProWriter // These token definitions affect both ProFileEvaluator and ProWriter
enum ProToken { enum ProToken {
@@ -419,7 +463,7 @@ class ProFunctionDef {
public: public:
ProFunctionDef(ProFile *pro, int offset) : m_pro(pro), m_offset(offset) { m_pro->ref(); } 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(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; } : m_pro(other.m_pro), m_offset(other.m_offset) { other.m_pro = nullptr; }
~ProFunctionDef() { if (m_pro) m_pro->deref(); } ~ProFunctionDef() { if (m_pro) m_pro->deref(); }
ProFunctionDef &operator=(const ProFunctionDef &o) ProFunctionDef &operator=(const ProFunctionDef &o)
@@ -433,13 +477,13 @@ public:
} }
return *this; return *this;
} }
ProFunctionDef &operator=(ProFunctionDef &&other) Q_DECL_NOTHROW ProFunctionDef &operator=(ProFunctionDef &&other) noexcept
{ {
ProFunctionDef moved(std::move(other)); ProFunctionDef moved(std::move(other));
swap(moved); swap(moved);
return *this; return *this;
} }
void swap(ProFunctionDef &other) Q_DECL_NOTHROW void swap(ProFunctionDef &other) noexcept
{ {
qSwap(m_pro, other.m_pro); qSwap(m_pro, other.m_pro);
qSwap(m_offset, other.m_offset); qSwap(m_offset, other.m_offset);
@@ -452,11 +496,13 @@ private:
int m_offset; int m_offset;
}; };
Q_DECLARE_TYPEINFO(ProFunctionDef, Q_MOVABLE_TYPE); Q_DECLARE_TYPEINFO(ProFunctionDef, Q_RELOCATABLE_TYPE);
struct ProFunctionDefs { struct ProFunctionDefs {
QHash<ProKey, ProFunctionDef> testFunctions; QHash<ProKey, ProFunctionDef> testFunctions;
QHash<ProKey, ProFunctionDef> replaceFunctions; QHash<ProKey, ProFunctionDef> replaceFunctions;
}; };
QDebug operator<<(QDebug debug, const ProString &str);
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@@ -144,9 +144,10 @@ def checkForStillRunningQmlExecutable(possibleNames):
continue continue
else: else:
if subprocess.call(["taskkill", "/F", "/FI", "IMAGENAME eq %s" % qmlHelper]) == 0: if subprocess.call(["taskkill", "/F", "/FI", "IMAGENAME eq %s" % qmlHelper]) == 0:
print "Killed still running %s" % qmlHelper print("Killed still running %s" % qmlHelper)
else: else:
print "%s is still running - failed to kill it" % qmlHelper print("%s is still running - failed to kill it" % qmlHelper)
def __removeTestingDir__(): def __removeTestingDir__():
def __removeIt__(directory): def __removeIt__(directory):

View File

@@ -579,7 +579,7 @@ def dumpChildren(item):
def writeTestResults(folder): def writeTestResults(folder):
if not os.path.exists(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 return
resultFile = open("%s.srf" % os.path.join(folder, os.path.basename(squishinfo.testCase)), "w") 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))) resultFile.write("suite:%s\n" % os.path.basename(os.path.dirname(squishinfo.testCase)))

View File

@@ -66,6 +66,11 @@ def checkQtCreatorHelpVersion(expectedVersion):
test.log("Exception caught", "%s(%s)" % (str(t), str(v))) test.log("Exception caught", "%s(%s)" % (str(t), str(v)))
test.fail("Missing Qt Creator Manual.") test.fail("Missing Qt Creator Manual.")
def _shortcutMatches_(shortcutEdit, expectedText):
return str(findObject(shortcutEdit).text) == expectedText
def setKeyboardShortcutForAboutQtC(): def setKeyboardShortcutForAboutQtC():
invokeMenuItem("Tools", "Options...") invokeMenuItem("Tools", "Options...")
mouseClick(waitForObjectItem(":Options_QListView", "Environment")) mouseClick(waitForObjectItem(":Options_QListView", "Environment"))
@@ -85,20 +90,19 @@ def setKeyboardShortcutForAboutQtC():
"visible='1' text~='(Stop Recording|Record)'}" % shortcutGB) "visible='1' text~='(Stop Recording|Record)'}" % shortcutGB)
shortcut = ("{container=%s type='Utils::FancyLineEdit' unnamed='1' visible='1' " shortcut = ("{container=%s type='Utils::FancyLineEdit' unnamed='1' visible='1' "
"placeholderText='Enter key sequence as text'}" % shortcutGB) "placeholderText='Enter key sequence as text'}" % shortcutGB)
expected = 'Ctrl+Opt+A' if platform.system() == 'Darwin' else 'Ctrl+Alt+A'
clickButton(record) clickButton(record)
nativeType("<Ctrl+Alt+a>") nativeType("<Ctrl+Alt+a>")
waitFor("_shortcutMatches_(shortcut, expected)", 5000)
clickButton(record) clickButton(record)
expected = 'Ctrl+Alt+A'
if platform.system() == 'Darwin':
expected = 'Ctrl+Opt+A'
shortcutMatches = waitFor("str(findObject(shortcut).text) == expected", 5000) gotExpectedShortcut = _shortcutMatches_(shortcut, expected)
if not shortcutMatches and platform.system() == 'Darwin': if not gotExpectedShortcut and platform.system() == 'Darwin':
test.warning("Squish Issue: shortcut was set to %s - entering it manually now" test.warning("Squish Issue: shortcut was set to %s - entering it manually now"
% waitForObject(shortcut).text) % waitForObject(shortcut).text)
replaceEditorContent(shortcut, expected) replaceEditorContent(shortcut, expected)
else: else:
test.verify(shortcutMatches, "Expected key sequence is displayed.") test.verify(gotExpectedShortcut, "Expected key sequence is displayed.")
clickButton(waitForObject(":Options.OK_QPushButton")) clickButton(waitForObject(":Options.OK_QPushButton"))
def main(): def main():