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
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

View File

@@ -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.

View File

@@ -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

View File

@@ -153,6 +153,7 @@ Section {
visible: section.showEasingCurve
}
SecondColumnLayout {
BoolButtonRowButton {
visible: section.showEasingCurve
buttonIcon: StudioTheme.Constants.curveDesigner
@@ -164,5 +165,8 @@ Section {
onClicked: easingCurveEditor.runDialog()
}
ExpandingSpacer {}
}
}
}

View File

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

View File

@@ -28,42 +28,39 @@ 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
Row {
property alias comboBox: itemFilterComboBox
ListViewComboBox {
id: itemFilterComboBox
@@ -76,13 +73,12 @@ Rectangle {
typeFilter: editableListView.typeFilter
editText: modelData
initialModelData: modelData
width: editableListView.width
implicitWidth: StudioTheme.Values.singleControlColumnWidth
width: implicitWidth
onFocusChanged: {
if (itemFilterComboBox.focus) {
if (itemFilterComboBox.focus)
myColumn.currentIndex = index
}
if (itemFilterComboBox.empty && itemFilterComboBox.editText !== "") {
myRepeater.dirty = false
@@ -100,20 +96,39 @@ Rectangle {
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
}
}
}
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
Row {
ActionIndicator {
id: actionIndicator
icon.visible: editableListView.delegateHover
icon.color: extFuncLogic.color
icon.text: extFuncLogic.glyph
onClicked: extFuncLogic.show()
}
Column {
@@ -122,9 +137,13 @@ Rectangle {
property int currentIndex: -1
property Item currentItem
spacing: -1
spacing: StudioTheme.Values.sectionRowSpacing
onCurrentIndexChanged: myColumn.currentItem = myRepeater.itemAt(myColumn.currentIndex)
onCurrentIndexChanged: {
var tmp = myRepeater.itemAt(myColumn.currentIndex)
if (tmp !== null)
myColumn.currentItem = tmp.comboBox
}
Repeater {
id: myRepeater
@@ -161,37 +180,60 @@ Rectangle {
else
myColumn.currentIndex = myRepeater.localModel.length - 1
if (editableListView.activatedReason === ComboBox.ActivatedReason.Other)
if (editableListView.activatedReason === ComboBox.ActivatedReason.Other
&& myColumn.currentItem !== null)
myColumn.currentItem.forceActiveFocus()
}
}
Item {
id: dummyItem
ListViewComboBox {
id: dummyComboBox
//property int myIndex: index
//property bool empty: dummyComboBox.initialModelData === ""
visible: myRepeater.count === 0
width: StudioTheme.Values.height
height: StudioTheme.Values.height
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)
}
}
Row {
id: row
spacing: -StudioTheme.Values.border
onCompressedActivated: {
editableListView.activatedReason = reason
StudioControls.ActionIndicator {
id: actionIndicator
width: actionIndicator.visible ? __actionIndicatorWidth : 0
height: actionIndicator.visible ? __actionIndicatorHeight : 0
border.width: StudioTheme.Values.border
border.color: StudioTheme.Values.themeControlOutline
icon.color: extFuncLogic.color
icon.text: extFuncLogic.glyph
onClicked: extFuncLogic.show()
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
}
}
}

View File

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

View File

@@ -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"

View File

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

View File

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

View File

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

View File

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

View File

@@ -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

View File

@@ -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:

View File

@@ -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())

View File

@@ -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);

View File

@@ -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()

View File

@@ -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();

View File

@@ -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,

View File

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

View File

@@ -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;
}

View File

@@ -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,

View File

@@ -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,

View File

@@ -400,10 +400,11 @@ 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);
if (sendUpdates == SendDocUpdates::Send)
sendPostponedDocumentUpdates();
if (Utils::optional<ResponseHandler> responseHandler = content.responseHandler())
m_responseHandlers[responseHandler->id] = responseHandler->callback;
@@ -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)

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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);
}
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);
}

View File

@@ -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;

View File

@@ -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);

View File

@@ -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

View File

@@ -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();

View File

@@ -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) {

View File

@@ -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

View File

@@ -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,

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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,

View File

@@ -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

View File

@@ -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

View File

@@ -48,6 +48,7 @@ public:
#ifdef WITH_TESTS
private slots:
void testEmSdkEnvParsing();
void testEmSdkEnvParsing_data();
#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.
// 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();

View File

@@ -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,8 +88,8 @@ 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"})
const QStringList makes = environment.osType() == OsTypeWindows
? QStringList({"mingw32-make.exe", "make.exe"})
: QStringList({"make"});
FilePath tmp;
@@ -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++"));

View File

@@ -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

View File

@@ -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

View File

@@ -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):

View File

@@ -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)))

View File

@@ -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():