diff --git a/dist/changes-4.15.0.md b/dist/changes-4.15.0.md index 0063b200108..1ae6ebcdb4a 100644 --- a/dist/changes-4.15.0.md +++ b/dist/changes-4.15.0.md @@ -33,9 +33,11 @@ Editing ### C++ -* Added options for generation of getters and setters (QTCREATORBUG-1532) -* Added `Create Constructor` refactoring operation -* Added filtering of `Find Usages` based on access type (QTCREATORBUG-19373) +* Added `Create Getter and Setter Member Functions` refactoring action + (QTCREATORBUG-1532) +* Added `Generate Constructor` refactoring action +* Added filtering of `Find References to Symbol Under Cursor` based on access + type (QTCREATORBUG-19373) * Added `Open in Editor` and `Open Type Hierarchy` to context menu on items in type hierarchy * Added highlighting of previous class when navigating in type hierarchy diff --git a/doc/qtcreator/images/qtcreator-find-references-to-symbol-under-cursor.png b/doc/qtcreator/images/qtcreator-find-references-to-symbol-under-cursor.png new file mode 100644 index 00000000000..c2ad158b183 Binary files /dev/null and b/doc/qtcreator/images/qtcreator-find-references-to-symbol-under-cursor.png differ diff --git a/doc/qtcreator/images/qtcreator-refactoring-constructor.png b/doc/qtcreator/images/qtcreator-refactoring-constructor.png new file mode 100644 index 00000000000..57d0bd7fd61 Binary files /dev/null and b/doc/qtcreator/images/qtcreator-refactoring-constructor.png differ diff --git a/doc/qtcreator/images/qtcreator-refactoring-find.png b/doc/qtcreator/images/qtcreator-refactoring-find.png index d2524de7ec1..5cf396361e2 100644 Binary files a/doc/qtcreator/images/qtcreator-refactoring-find.png and b/doc/qtcreator/images/qtcreator-refactoring-find.png differ diff --git a/doc/qtcreator/images/qtcreator-refactoring-replace.png b/doc/qtcreator/images/qtcreator-refactoring-replace.png index d0fa9dec475..3ef3359acc3 100644 Binary files a/doc/qtcreator/images/qtcreator-refactoring-replace.png and b/doc/qtcreator/images/qtcreator-refactoring-replace.png differ diff --git a/doc/qtcreator/images/qtcreator-search-cpp-symbols.png b/doc/qtcreator/images/qtcreator-search-cpp-symbols.png index 9f34e8f621c..f5aade540c9 100644 Binary files a/doc/qtcreator/images/qtcreator-search-cpp-symbols.png and b/doc/qtcreator/images/qtcreator-search-cpp-symbols.png differ diff --git a/doc/qtcreator/src/editors/creator-code-refactoring.qdoc b/doc/qtcreator/src/editors/creator-code-refactoring.qdoc index 19f6f53a6e9..23f7d690639 100644 --- a/doc/qtcreator/src/editors/creator-code-refactoring.qdoc +++ b/doc/qtcreator/src/editors/creator-code-refactoring.qdoc @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2020 The Qt Company Ltd. +** Copyright (C) 2021 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Creator documentation. @@ -51,6 +51,8 @@ \uicontrol {C++} > \uicontrol {Find References to Symbol Under Cursor} or press \key {Ctrl+Shift+U}. + \image qtcreator-find-references-to-symbol-under-cursor.png "Search results for finding references to symbols" + \note You can also select \uicontrol Edit > \uicontrol {Find/Replace} > \uicontrol {Advanced Find} > \uicontrol {C++ Symbols} to search for classes, functions, enums, and declarations either from files listed as @@ -100,6 +102,10 @@ \li To expand and collapse the list of all instances, click the \inlineimage qtcreator-expand.png (\uicontrol {Expand All}) button. + \li To filter the search results for the usage of symbols according to + access type, such as read, write, or declaration, click the + \inlineimage filtericon.png + (\uicontrol {Filter Tree}) button and select the access type. \li To clear the search results, click the \inlineimage clean_pane_small.png (\uicontrol Clear) button. @@ -184,7 +190,8 @@ \section1 Creating Functions You can apply refactoring actions to implement member functions, insert - virtual functions of base classes, and greate getter and setter functions. + virtual functions of base classes, create getter and setter functions, + and generate constructors. \section2 Implementing Member Functions @@ -217,6 +224,16 @@ for member variables or only a getter or setter. \image qtcreator-refactoring-getters-and-setters.png "Getters and Setters dialog" + + \section2 Generating Constructors + + You can apply the \uicontrol {Generate Constructor} refactoring action to + create a public, protected, or private constructor for a class. Select the + class members to initialize in the constructor. Drag and drop the parameters + to specify their order in the constructor. + + \image qtcreator-refactoring-constructor.png "Constructor dialog" + \endif \section1 Summary of Refactoring Actions @@ -668,6 +685,10 @@ \li Creates either both getter and setter member functions for member variables or only a getter or setter. \li Member variable in class definition + \row + \li Generate Constructor + \li Creates a constructor for a class. + \li Class definition \row \li Move Function Definition \li Moves a function definition to the implementation file, outside diff --git a/share/qtcreator/qmldesigner/itemLibraryQmlSources/ItemsView.qml b/share/qtcreator/qmldesigner/itemLibraryQmlSources/ItemsView.qml index 1f4ad7ddfd0..2f792372331 100644 --- a/share/qtcreator/qmldesigner/itemLibraryQmlSources/ItemsView.qml +++ b/share/qtcreator/qmldesigner/itemLibraryQmlSources/ItemsView.qml @@ -139,7 +139,10 @@ ScrollView { bottomPadding: 0 expanded: importExpanded expandOnClick: false - onToggleExpand: importExpanded = !importExpanded + onToggleExpand: { + if (categoryModel.rowCount() > 0) + importExpanded = !importExpanded + } onShowContextMenu: { importToRemove = importUsed ? "" : importUrl contextMenu.popup() diff --git a/src/libs/qmljs/qmljsreformatter.cpp b/src/libs/qmljs/qmljsreformatter.cpp index de69df50438..60e32288c97 100644 --- a/src/libs/qmljs/qmljsreformatter.cpp +++ b/src/libs/qmljs/qmljsreformatter.cpp @@ -989,6 +989,21 @@ protected: out("const "); } } + switch (ast->type) { + case PatternElement::Literal: + case PatternElement::Method: + case PatternElement::Binding: + break; + case PatternElement::Getter: + out("get "); + break; + case PatternElement::Setter: + out("set "); + break; + case PatternElement::SpreadElement: + out("..."); + break; + } out(ast->identifierToken); if (ast->initializer) { if (ast->isVariableDeclaration()) @@ -1312,6 +1327,8 @@ protected: bool visit(ArgumentList *ast) override { for (ArgumentList *it = ast; it; it = it->next) { + if (it->isSpreadElement) + out("..."); accept(it->expression); if (it->next) { out(", ", it->commaToken); diff --git a/src/plugins/android/androidavdmanager.cpp b/src/plugins/android/androidavdmanager.cpp index c000a623544..7fb12a49d64 100644 --- a/src/plugins/android/androidavdmanager.cpp +++ b/src/plugins/android/androidavdmanager.cpp @@ -403,7 +403,7 @@ AndroidDeviceInfoList AvdManagerOutputParser::listVirtualDevices(const AndroidCo avdList = parseAvdList(output); } while (output.contains(avdManufacturerError)); - for (const QString &avdPathStr : avdErrorPaths) + for (const QString &avdPathStr : qAsConst(avdErrorPaths)) AvdConfigEditManufacturerTag(avdPathStr, true); return avdList; diff --git a/src/plugins/android/androidconfigurations.cpp b/src/plugins/android/androidconfigurations.cpp index a57e9e851a0..34593bb340f 100644 --- a/src/plugins/android/androidconfigurations.cpp +++ b/src/plugins/android/androidconfigurations.cpp @@ -1494,7 +1494,7 @@ FilePath AndroidConfig::getJdkPath() #endif // Q_OS_WIN } - for (const QString &version : allVersions) { + for (const QString &version : qAsConst(allVersions)) { settings->beginGroup(version); jdkHome = FilePath::fromUserInput(settings->value("JavaHome").toString()); settings->endGroup(); diff --git a/src/plugins/android/androidrunnerworker.cpp b/src/plugins/android/androidrunnerworker.cpp index ac27b3b14c6..b07e6210599 100644 --- a/src/plugins/android/androidrunnerworker.cpp +++ b/src/plugins/android/androidrunnerworker.cpp @@ -538,7 +538,7 @@ void AndroidRunnerWorker::asyncStartHelper() forceStop(); asyncStartLogcat(); - for (const QString &entry : m_beforeStartAdbCommands) + for (const QString &entry : qAsConst(m_beforeStartAdbCommands)) runAdb(entry.split(' ', Qt::SkipEmptyParts)); QStringList args({"shell", "am", "start"}); @@ -825,7 +825,7 @@ void AndroidRunnerWorker::onProcessIdChanged(qint64 pid) m_debugServerProcess.reset(); // Run adb commands after application quit. - for (const QString &entry: m_afterFinishAdbCommands) + for (const QString &entry: qAsConst(m_afterFinishAdbCommands)) runAdb(entry.split(' ', Qt::SkipEmptyParts)); } else { // In debugging cases this will be funneled to the engine to actually start diff --git a/src/plugins/android/androidsdkmanager.cpp b/src/plugins/android/androidsdkmanager.cpp index b84b6fea84f..ca66e26576f 100644 --- a/src/plugins/android/androidsdkmanager.cpp +++ b/src/plugins/android/androidsdkmanager.cpp @@ -566,7 +566,7 @@ void SdkManagerOutputParser::compilePackageAssociations() deleteAlreadyInstalled(images); // Associate the system images with sdk platforms. - for (AndroidSdkPackage *image : images) { + for (AndroidSdkPackage *image : qAsConst(images)) { int imageApi = m_systemImages[image]; auto itr = std::find_if(m_packages.begin(), m_packages.end(), [imageApi](const AndroidSdkPackage *p) { @@ -1151,7 +1151,7 @@ void AndroidSdkManagerPrivate::parseCommonArguments(QFutureInterface &f void AndroidSdkManagerPrivate::clearPackages() { - for (AndroidSdkPackage *p : m_allPackages) + for (AndroidSdkPackage *p : qAsConst(m_allPackages)) delete p; m_allPackages.clear(); } diff --git a/src/plugins/android/androidsdkmodel.cpp b/src/plugins/android/androidsdkmodel.cpp index 50205e735f2..2f83e86156c 100644 --- a/src/plugins/android/androidsdkmodel.cpp +++ b/src/plugins/android/androidsdkmodel.cpp @@ -298,7 +298,7 @@ void AndroidSdkModel::selectMissingEssentials() } // Select SDK platform - for (const SdkPlatform *platform : m_sdkPlatforms) { + for (const SdkPlatform *platform : qAsConst(m_sdkPlatforms)) { if (!platform->installedLocation().isEmpty()) { pendingPkgs.removeOne(platform->sdkStylePath()); } else if (pendingPkgs.contains(platform->sdkStylePath()) && diff --git a/src/plugins/android/androidsdkpackage.cpp b/src/plugins/android/androidsdkpackage.cpp index e9b019782bb..5b218c448c4 100644 --- a/src/plugins/android/androidsdkpackage.cpp +++ b/src/plugins/android/androidsdkpackage.cpp @@ -154,7 +154,7 @@ SdkPlatform::SdkPlatform(QVersionNumber version, QString sdkStylePathStr, int ap SdkPlatform::~SdkPlatform() { - for (SystemImage *image : m_systemImages) + for (SystemImage *image : qAsConst(m_systemImages)) delete image; m_systemImages.clear(); } diff --git a/src/plugins/android/avddialog.cpp b/src/plugins/android/avddialog.cpp index a9ea27f6bc1..9353f148bb4 100644 --- a/src/plugins/android/avddialog.cpp +++ b/src/plugins/android/avddialog.cpp @@ -177,7 +177,7 @@ void AvdDialog::updateDeviceDefinitionComboBox() m_avdDialog.deviceDefinitionTypeComboBox->currentText()); m_avdDialog.deviceDefinitionComboBox->clear(); - for (const DeviceDefinitionStruct &item : m_deviceDefinitionsList) { + for (const DeviceDefinitionStruct &item : qAsConst(m_deviceDefinitionsList)) { if (item.deviceType == curDeviceType) m_avdDialog.deviceDefinitionComboBox->addItem(item.name_id); } @@ -231,7 +231,7 @@ void AvdDialog::updateApiLevelComboBox() }); m_avdDialog.targetApiComboBox->clear(); - for (SystemImage *image : filteredList) { + for (SystemImage *image : qAsConst(filteredList)) { QString imageString = "android-" % QString::number(image->apiLevel()); const QStringList imageSplits = image->sdkStylePath().split(';'); if (imageSplits.size() == 4) diff --git a/src/plugins/android/javalanguageserver.cpp b/src/plugins/android/javalanguageserver.cpp index d0b900a6066..42ee4529067 100644 --- a/src/plugins/android/javalanguageserver.cpp +++ b/src/plugins/android/javalanguageserver.cpp @@ -25,12 +25,21 @@ #include "javalanguageserver.h" +#include "androidconfigurations.h" #include "androidconstants.h" +#include "androidmanager.h" #include +#include #include +#include +#include +#include +#include +#include #include #include +#include #include #include @@ -39,7 +48,6 @@ using namespace Utils; constexpr char languageServerKey[] = "languageServer"; -constexpr char workspaceKey[] = "workspace"; namespace Android { namespace Internal { @@ -67,7 +75,6 @@ JLSSettingsWidget::JLSSettingsWidget(const JLSSettings *settings, QWidget *paren , m_name(new QLineEdit(settings->m_name, this)) , m_java(new PathChooser(this)) , m_ls(new PathChooser(this)) - , m_workspace(new PathChooser(this)) { int row = 0; auto *mainLayout = new QGridLayout; @@ -88,11 +95,6 @@ JLSSettingsWidget::JLSSettingsWidget(const JLSSettings *settings, QWidget *paren m_ls->setPath(QDir::toNativeSeparators(settings->m_languageServer)); mainLayout->addWidget(m_ls, row, 1); - mainLayout->addWidget(new QLabel(tr("Workspace:")), ++row, 0); - m_workspace->setExpectedKind(Utils::PathChooser::Directory); - m_workspace->setPath(QDir::toNativeSeparators(settings->m_workspace)); - mainLayout->addWidget(m_workspace, row, 1); - setLayout(mainLayout); } @@ -117,9 +119,6 @@ bool JLSSettings::applyFromSettingsWidget(QWidget *widget) changed |= m_languageServer != jlswidget->languageServer(); m_languageServer = jlswidget->languageServer(); - changed |= m_workspace != jlswidget->workspace(); - m_workspace = jlswidget->workspace(); - changed |= m_executable != jlswidget->java(); m_executable = jlswidget->java(); @@ -130,8 +129,7 @@ bool JLSSettings::applyFromSettingsWidget(QWidget *widget) "-noverify " "-Xmx1G " "-jar \"%1\" " - "-configuration \"%2\" " - "-data \"%3\""; + "-configuration \"%2\""; QFileInfo languageServerFileInfo(m_languageServer); QDir configDir = languageServerFileInfo.absoluteDir(); @@ -145,7 +143,7 @@ bool JLSSettings::applyFromSettingsWidget(QWidget *widget) configDir.cd("config_mac"); } if (configDir.exists()) { - arguments = arguments.arg(m_languageServer, configDir.absolutePath(), m_workspace); + arguments = arguments.arg(m_languageServer, configDir.absolutePath()); changed |= m_arguments != arguments; m_arguments = arguments; } @@ -159,14 +157,13 @@ QWidget *JLSSettings::createSettingsWidget(QWidget *parent) const bool JLSSettings::isValid() const { - return StdIOSettings::isValid() && !m_languageServer.isEmpty() && !m_workspace.isEmpty(); + return StdIOSettings::isValid() && !m_languageServer.isEmpty(); } QVariantMap JLSSettings::toMap() const { QVariantMap map = StdIOSettings::toMap(); map.insert(languageServerKey, m_languageServer); - map.insert(workspaceKey, m_workspace); return map; } @@ -174,7 +171,6 @@ void JLSSettings::fromMap(const QVariantMap &map) { StdIOSettings::fromMap(map); m_languageServer = map[languageServerKey].toString(); - m_workspace = map[workspaceKey].toString(); } LanguageClient::BaseSettings *JLSSettings::copy() const @@ -182,12 +178,39 @@ LanguageClient::BaseSettings *JLSSettings::copy() const return new JLSSettings(*this); } +class JLSInterface : public LanguageClient::StdIOClientInterface +{ +public: + JLSInterface() = default; + + QString workspaceDir() const { return m_workspaceDir.path(); } + +private: + TemporaryDirectory m_workspaceDir = TemporaryDirectory("QtCreator-jls-XXXXXX"); +}; + +LanguageClient::BaseClientInterface *JLSSettings::createInterface() const +{ + auto interface = new JLSInterface(); + interface->setExecutable(m_executable); + QString arguments = this->arguments(); + arguments += QString(" -data \"%1\"").arg(interface->workspaceDir()); + interface->setArguments(arguments); + return interface; +} + class JLSClient : public LanguageClient::Client { public: using Client::Client; void executeCommand(const LanguageServerProtocol::Command &command) override; + void setCurrentProject(ProjectExplorer::Project *project) override; + void updateProjectFiles(); + void updateTarget(ProjectExplorer::Target *target); + +private: + ProjectExplorer::Target *m_currentTarget = nullptr; }; void JLSClient::executeCommand(const LanguageServerProtocol::Command &command) @@ -206,6 +229,127 @@ void JLSClient::executeCommand(const LanguageServerProtocol::Command &command) } } +void JLSClient::setCurrentProject(ProjectExplorer::Project *project) +{ + Client::setCurrentProject(project); + QTC_ASSERT(project, return); + updateTarget(project->activeTarget()); + updateProjectFiles(); + connect(project, &ProjectExplorer::Project::activeTargetChanged, + this, &JLSClient::updateTarget); +} + +static void generateProjectFile(const FilePath &projectDir, + const QString &qtSrc, + const QString &projectName) +{ + const FilePath projectFilePath = projectDir.pathAppended(".project"); + QFile projectFile(projectFilePath.toString()); + if (projectFile.open(QFile::Truncate | QFile::WriteOnly)) { + QXmlStreamWriter writer(&projectFile); + writer.setAutoFormatting(true); + writer.writeStartDocument(); + writer.writeComment("Autogenerated by Qt Creator. " + "Changes to this file will not be taken into account."); + writer.writeStartElement("projectDescription"); + writer.writeTextElement("name", projectName); + writer.writeStartElement("natures"); + writer.writeTextElement("nature", "org.eclipse.jdt.core.javanature"); + writer.writeEndElement(); // natures + writer.writeStartElement("linkedResources"); + writer.writeStartElement("link"); + writer.writeTextElement("name", "qtSrc"); + writer.writeTextElement("type", "2"); + writer.writeTextElement("location", qtSrc); + writer.writeEndElement(); // link + writer.writeEndElement(); // linkedResources + writer.writeEndElement(); // projectDescription + writer.writeEndDocument(); + projectFile.close(); + } +} + +static void generateClassPathFile(const FilePath &projectDir, + const QString &sourceDir, + const QStringList &libs) +{ + const FilePath classPathFilePath = projectDir.pathAppended(".classpath"); + QFile classPathFile(classPathFilePath.toString()); + if (classPathFile.open(QFile::Truncate | QFile::WriteOnly)) { + QXmlStreamWriter writer(&classPathFile); + writer.setAutoFormatting(true); + writer.writeStartDocument(); + writer.writeComment("Autogenerated by Qt Creator. " + "Changes to this file will not be taken into account."); + writer.writeStartElement("classpath"); + writer.writeEmptyElement("classpathentry"); + writer.writeAttribute("kind", "src"); + writer.writeAttribute("path", sourceDir); + writer.writeEmptyElement("classpathentry"); + writer.writeAttribute("kind", "src"); + writer.writeAttribute("path", "qtSrc"); + for (const QString &lib : libs) { + writer.writeEmptyElement("classpathentry"); + writer.writeAttribute("kind", "lib"); + writer.writeAttribute("path", lib); + } + writer.writeEndElement(); // classpath + writer.writeEndDocument(); + classPathFile.close(); + } +} + +void JLSClient::updateProjectFiles() +{ + using namespace ProjectExplorer; + if (!m_currentTarget) + return; + if (Target *target = m_currentTarget) { + Kit *kit = m_currentTarget->kit(); + if (DeviceTypeKitAspect::deviceTypeId(kit) != Android::Constants::ANDROID_DEVICE_TYPE) + return; + if (ProjectNode *node = project()->findNodeForBuildKey(target->activeBuildKey())) { + QtSupport::BaseQtVersion *version = QtSupport::QtKitAspect::qtVersion(kit); + if (!version) + return; + const QString qtSrc = version->prefix().toString() + "/src/android/java/src"; + const FilePath &projectDir = project()->rootProjectDirectory(); + if (!projectDir.exists()) + return; + const FilePath packageSourceDir = FilePath::fromVariant( + node->data(Constants::AndroidPackageSourceDir)); + FilePath sourceDir = packageSourceDir.pathAppended("src"); + if (!sourceDir.exists()) + return; + sourceDir = sourceDir.relativeChildPath(projectDir); + const FilePath &sdkLocation = AndroidConfigurations::currentConfig().sdkLocation(); + const QString &targetSDK = AndroidManager::buildTargetSDK(m_currentTarget); + const QString androidJar = QString("%1/platforms/%2/android.jar") + .arg(sdkLocation.toString(), targetSDK); + QStringList libs(androidJar); + QDir libDir(packageSourceDir.pathAppended("libs").toString()); + libs << Utils::transform(libDir.entryInfoList({"*.jar"}, QDir::Files), + &QFileInfo::absoluteFilePath); + generateProjectFile(projectDir, qtSrc, project()->displayName()); + generateClassPathFile(projectDir, sourceDir.toString(), libs); + } + } +} + +void JLSClient::updateTarget(ProjectExplorer::Target *target) +{ + if (m_currentTarget) { + disconnect(m_currentTarget, &ProjectExplorer::Target::parsingFinished, + this, &JLSClient::updateProjectFiles); + } + m_currentTarget = target; + if (m_currentTarget) { + connect(m_currentTarget, &ProjectExplorer::Target::parsingFinished, + this, &JLSClient::updateProjectFiles); + } + updateProjectFiles(); +} + LanguageClient::Client *JLSSettings::createClient(LanguageClient::BaseClientInterface *interface) const { return new JLSClient(interface); diff --git a/src/plugins/android/javalanguageserver.h b/src/plugins/android/javalanguageserver.h index 58310070771..ab528cad15e 100644 --- a/src/plugins/android/javalanguageserver.h +++ b/src/plugins/android/javalanguageserver.h @@ -42,9 +42,9 @@ public: void fromMap(const QVariantMap &map) final; LanguageClient::BaseSettings *copy() const final; LanguageClient::Client *createClient(LanguageClient::BaseClientInterface *interface) const final; + LanguageClient::BaseClientInterface *createInterface() const override; QString m_languageServer; - QString m_workspace; private: JLSSettings(const JLSSettings &other) = default; diff --git a/src/plugins/clangtools/clangtoolsunittests.cpp b/src/plugins/clangtools/clangtoolsunittests.cpp index 512e6e71355..9d416f07cc1 100644 --- a/src/plugins/clangtools/clangtoolsunittests.cpp +++ b/src/plugins/clangtools/clangtoolsunittests.cpp @@ -128,7 +128,7 @@ void ClangToolsUnitTests::testProject() QVERIFY(waitForFinishedTool.wait(m_timeout)); // Check for errors - const QString errorText = waitForFinishedTool.takeFirst().first().toString(); + const QString errorText = waitForFinishedTool.takeFirst().constFirst().toString(); const bool finishedSuccessfully = errorText.isEmpty(); if (!finishedSuccessfully) qWarning("Error: %s", qPrintable(errorText)); diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectimporter.cpp b/src/plugins/cmakeprojectmanager/cmakeprojectimporter.cpp index 03f5ecb51b1..4e31bad6b36 100644 --- a/src/plugins/cmakeprojectmanager/cmakeprojectimporter.cpp +++ b/src/plugins/cmakeprojectmanager/cmakeprojectimporter.cpp @@ -245,7 +245,7 @@ static FilePath qmakeFromCMakeCache(const CMakeConfig &config) if (!qmakeLocationTxt.open(QIODevice::ReadOnly)) { return FilePath(); } - FilePath qmakeLocation = FilePath::fromUtf8(qmakeLocationTxt.readLine().data()); + FilePath qmakeLocation = FilePath::fromUtf8(qmakeLocationTxt.readLine().constData()); qCDebug(cmInputLog) << "qmake location: " << qmakeLocation.toUserOutput(); return qmakeLocation; diff --git a/src/plugins/languageclient/client.cpp b/src/plugins/languageclient/client.cpp index 795f67fe050..ce2184d303a 100644 --- a/src/plugins/languageclient/client.cpp +++ b/src/plugins/languageclient/client.cpp @@ -100,6 +100,13 @@ Client::Client(BaseClientInterface *clientInterface) &Client::rehighlight); } +QString Client::name() const +{ + if (m_project && !m_project->displayName().isEmpty()) + return tr("%1 for %2").arg(m_displayName, m_project->displayName()); + return m_displayName; +} + static void updateEditorToolBar(QList documents) { for (TextEditor::TextDocument *document : documents) { @@ -790,6 +797,15 @@ void Client::projectOpened(ProjectExplorer::Project *project) void Client::projectClosed(ProjectExplorer::Project *project) { + if (sendWorkspceFolderChanges()) { + WorkspaceFoldersChangeEvent event; + event.setRemoved({WorkSpaceFolder(DocumentUri::fromFilePath(project->projectDirectory()), + project->displayName())}); + DidChangeWorkspaceFoldersParams params; + params.setEvent(event); + DidChangeWorkspaceFoldersNotification change(params); + sendContent(change); + } if (project == m_project) { if (m_state == Initialized) { shutdown(); @@ -797,16 +813,8 @@ void Client::projectClosed(ProjectExplorer::Project *project) m_state = Shutdown; // otherwise the manager would try to restart this server emit finished(); } + m_project = nullptr; } - if (!sendWorkspceFolderChanges()) - return; - WorkspaceFoldersChangeEvent event; - event.setRemoved({WorkSpaceFolder(DocumentUri::fromFilePath(project->projectDirectory()), - project->displayName())}); - DidChangeWorkspaceFoldersParams params; - params.setEvent(event); - DidChangeWorkspaceFoldersNotification change(params); - sendContent(change); } void Client::setSupportedLanguage(const LanguageFilter &filter) @@ -1260,6 +1268,8 @@ void Client::shutDownCallback(const ShutdownRequest::Response &shutdownResponse) bool Client::sendWorkspceFolderChanges() const { + if (!reachable()) + return false; if (m_dynamicCapabilities.isRegistered( DidChangeWorkspaceFoldersNotification::methodName).value_or(false)) { return true; diff --git a/src/plugins/languageclient/client.h b/src/plugins/languageclient/client.h index 0065779e3f3..e3edbf88b52 100644 --- a/src/plugins/languageclient/client.h +++ b/src/plugins/languageclient/client.h @@ -87,7 +87,7 @@ public: // basic properties Utils::Id id() const { return m_id; } void setName(const QString &name) { m_displayName = name; } - QString name() const { return m_displayName; } + QString name() const; void sendContent(const LanguageServerProtocol::IContent &content); void cancelRequest(const LanguageServerProtocol::MessageId &id); @@ -134,10 +134,10 @@ public: bool documentUpdatePostponed(const Utils::FilePath &fileName) const; // workspace control - void setCurrentProject(ProjectExplorer::Project *project); + virtual void setCurrentProject(ProjectExplorer::Project *project); const ProjectExplorer::Project *project() const; - void projectOpened(ProjectExplorer::Project *project); - void projectClosed(ProjectExplorer::Project *project); + virtual void projectOpened(ProjectExplorer::Project *project); + virtual void projectClosed(ProjectExplorer::Project *project); // commands void requestCodeActions(const LanguageServerProtocol::DocumentUri &uri, diff --git a/src/plugins/languageclient/languageclientinterface.cpp b/src/plugins/languageclient/languageclientinterface.cpp index 91d2562e672..be1f2bdb20c 100644 --- a/src/plugins/languageclient/languageclientinterface.cpp +++ b/src/plugins/languageclient/languageclientinterface.cpp @@ -89,9 +89,7 @@ void BaseClientInterface::parseData(const QByteArray &data) } } -StdIOClientInterface::StdIOClientInterface(const QString &executable, const QString &arguments) - : m_executable(executable) - , m_arguments(arguments) +StdIOClientInterface::StdIOClientInterface() { connect(&m_process, &QProcess::readyReadStandardError, this, &StdIOClientInterface::readError); @@ -99,9 +97,6 @@ StdIOClientInterface::StdIOClientInterface(const QString &executable, const QStr this, &StdIOClientInterface::readOutput); connect(&m_process, QOverload::of(&QProcess::finished), this, &StdIOClientInterface::onProcessFinished); - - m_process.setArguments(Utils::QtcProcess::splitArgs(m_arguments)); - m_process.setProgram(m_executable); } StdIOClientInterface::~StdIOClientInterface() @@ -109,11 +104,6 @@ StdIOClientInterface::~StdIOClientInterface() Utils::SynchronousProcess::stopProcess(m_process); } -bool StdIOClientInterface::needsRestart(const StdIOSettings *settings) const -{ - return m_executable != settings->m_executable || m_arguments != settings->arguments(); -} - bool StdIOClientInterface::start() { m_process.start(); @@ -124,6 +114,16 @@ bool StdIOClientInterface::start() return true; } +void StdIOClientInterface::setExecutable(const QString &executable) +{ + m_process.setProgram(executable); +} + +void StdIOClientInterface::setArguments(const QString &arguments) +{ + m_process.setArguments(Utils::QtcProcess::splitArgs(arguments)); +} + void StdIOClientInterface::setWorkingDirectory(const QString &workingDirectory) { m_process.setWorkingDirectory(workingDirectory); diff --git a/src/plugins/languageclient/languageclientinterface.h b/src/plugins/languageclient/languageclientinterface.h index 182965ab882..69055bc49e4 100644 --- a/src/plugins/languageclient/languageclientinterface.h +++ b/src/plugins/languageclient/languageclientinterface.h @@ -25,6 +25,8 @@ #pragma once +#include "languageclient_global.h" + #include #include @@ -34,13 +36,13 @@ namespace LanguageClient { class StdIOSettings; -class BaseClientInterface : public QObject +class LANGUAGECLIENT_EXPORT BaseClientInterface : public QObject { Q_OBJECT public: BaseClientInterface(); - virtual ~BaseClientInterface(); + ~BaseClientInterface() override; void sendMessage(const LanguageServerProtocol::BaseMessage &message); virtual bool start() { return true; } @@ -61,23 +63,23 @@ private: LanguageServerProtocol::BaseMessage m_currentMessage; }; -class StdIOClientInterface : public BaseClientInterface +class LANGUAGECLIENT_EXPORT StdIOClientInterface : public BaseClientInterface { Q_OBJECT public: - StdIOClientInterface(const QString &executable, const QString &arguments); + StdIOClientInterface(); ~StdIOClientInterface() override; - StdIOClientInterface() = delete; StdIOClientInterface(const StdIOClientInterface &) = delete; StdIOClientInterface(StdIOClientInterface &&) = delete; StdIOClientInterface &operator=(const StdIOClientInterface &) = delete; StdIOClientInterface &operator=(StdIOClientInterface &&) = delete; - bool needsRestart(const StdIOSettings *settings) const; - bool start() override; + // These functions only have an effect if they are called before start + void setExecutable(const QString &executable); + void setArguments(const QString &arguments); void setWorkingDirectory(const QString &workingDirectory); protected: @@ -88,9 +90,6 @@ private: void readError(); void readOutput(); void onProcessFinished(int exitCode, QProcess::ExitStatus exitStatus); - - const QString m_executable; - const QString m_arguments; }; } // namespace LanguageClient diff --git a/src/plugins/languageclient/languageclientmanager.cpp b/src/plugins/languageclient/languageclientmanager.cpp index d09edb78771..2f69bada526 100644 --- a/src/plugins/languageclient/languageclientmanager.cpp +++ b/src/plugins/languageclient/languageclientmanager.cpp @@ -472,13 +472,17 @@ void LanguageClientManager::documentOpened(Core::IDocument *document) continue; // check whether we already have a client running for this project - if (Utils::findOrDefault(clients, - [project](const QPointer &client) { - return client->project() == project; - })) { - continue; + Client *clientForProject = Utils::findOrDefault(clients, + [project](Client *client) { + return client->project() + == project; + }); + if (!clientForProject) { + clientForProject = startClient(setting, project); + clients << clientForProject; } - clients << startClient(setting, project); + QTC_ASSERT(clientForProject, continue); + openDocumentWithClient(textDocument, clientForProject); } } else if (setting->m_startBehavior == BaseSettings::RequiresFile && clients.isEmpty()) { clients << startClient(setting); diff --git a/src/plugins/languageclient/languageclientsettings.cpp b/src/plugins/languageclient/languageclientsettings.cpp index 030fefef3d8..fff4b3d9c0e 100644 --- a/src/plugins/languageclient/languageclientsettings.cpp +++ b/src/plugins/languageclient/languageclientsettings.cpp @@ -722,7 +722,10 @@ Utils::CommandLine StdIOSettings::command() const BaseClientInterface *StdIOSettings::createInterface() const { - return new StdIOClientInterface(m_executable, arguments()); + auto interface = new StdIOClientInterface; + interface->setExecutable(m_executable); + interface->setArguments(arguments()); + return interface; } class JsonTreeItemDelegate : public QStyledItemDelegate diff --git a/src/plugins/projectexplorer/outputparser_test.cpp b/src/plugins/projectexplorer/outputparser_test.cpp index d1d25a3c67b..a70a33a8d88 100644 --- a/src/plugins/projectexplorer/outputparser_test.cpp +++ b/src/plugins/projectexplorer/outputparser_test.cpp @@ -62,7 +62,7 @@ void OutputParserTester::testParsing(const QString &lines, { const auto terminator = new TestTerminator(this); if (!lineParsers().isEmpty()) - terminator->setRedirectionDetector(lineParsers().last()); + terminator->setRedirectionDetector(lineParsers().constLast()); addLineParser(terminator); reset(); @@ -113,7 +113,7 @@ TestTerminator::TestTerminator(OutputParserTester *t) : m_tester(t) { if (!t->lineParsers().isEmpty()) { - for (const Utils::FilePath &searchDir : t->lineParsers().first()->searchDirectories()) + for (const Utils::FilePath &searchDir : t->lineParsers().constFirst()->searchDirectories()) addSearchDir(searchDir); } } diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarycategoriesmodel.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarycategoriesmodel.cpp index fbb2eee9f35..1845c025400 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarycategoriesmodel.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarycategoriesmodel.cpp @@ -96,6 +96,18 @@ QHash ItemLibraryCategoriesModel::roleNames() const return m_roleNames; } +void ItemLibraryCategoriesModel::expandCategories(bool expand) +{ + int i = 0; + for (const auto &category : std::as_const(m_categoryList)) { + if (category->categoryExpanded() != expand) { + category->setExpanded(expand); + emit dataChanged(index(i), index(i), {m_roleNames.key("categoryExpanded")}); + } + ++i; + } +} + void ItemLibraryCategoriesModel::addCategory(ItemLibraryCategory *category) { m_categoryList.append(category); diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarycategoriesmodel.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarycategoriesmodel.h index fb1a7e0e050..982083569d0 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarycategoriesmodel.h +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarycategoriesmodel.h @@ -48,6 +48,7 @@ public: QHash roleNames() const override; void addCategory(ItemLibraryCategory *category); + void expandCategories(bool expand = true); const QList> &categorySections() const; diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarycategory.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarycategory.cpp index cb244839bd0..1662b0ce966 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarycategory.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarycategory.cpp @@ -78,6 +78,10 @@ bool ItemLibraryCategory::updateItemVisibility(const QString &searchText, bool * hasVisibleItems = true; } + // expand category if it has an item matching search criteria + if (hasVisibleItems && !categoryExpanded()) + setExpanded(true); + return hasVisibleItems; } diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryimport.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryimport.cpp index 41483535c5a..f2f22b1b74f 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryimport.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryimport.cpp @@ -80,25 +80,28 @@ QObject *ItemLibraryImport::categoryModel() return &m_categoryModel; } +void ItemLibraryImport::expandCategories(bool expand) +{ + m_categoryModel.expandCategories(expand); +} + bool ItemLibraryImport::updateCategoryVisibility(const QString &searchText, bool *changed) { - bool hasVisibleItems = false; - + bool hasVisibleCategories = false; *changed = false; for (const auto &category : m_categoryModel.categorySections()) { bool categoryChanged = false; - hasVisibleItems = category->updateItemVisibility(searchText, &categoryChanged); + bool hasVisibleItems = category->updateItemVisibility(searchText, &categoryChanged); categoryChanged |= category->setVisible(hasVisibleItems); *changed |= categoryChanged; - *changed |= hasVisibleItems; + + if (hasVisibleItems) + hasVisibleCategories = true; } - if (*changed) - m_categoryModel.resetModel(); - - return hasVisibleItems; + return hasVisibleCategories; } Import ItemLibraryImport::importEntry() const diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryimport.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryimport.h index 8db549c3ae5..c922e984084 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryimport.h +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryimport.h @@ -63,6 +63,7 @@ public: void setImportUsed(bool importUsed); void sortCategorySections(); void setImportExpanded(bool expanded = true); + void expandCategories(bool expand = true); static QString userComponentsTitle(); diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp index c88ebb67e96..25bef1edd4b 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp @@ -61,34 +61,28 @@ bool ItemLibraryModel::loadExpandedState(const QString §ionName) void ItemLibraryModel::expandAll() { - bool changed = false; + int i = 0; for (const QPointer &import : std::as_const(m_importList)) { - if (import->hasCategories() && !import->importExpanded()) { - changed = true; + if (!import->importExpanded()) { import->setImportExpanded(); + emit dataChanged(index(i), index(i), {m_roleNames.key("importExpanded")}); saveExpandedState(true, import->importUrl()); } - } - - if (changed) { - beginResetModel(); - endResetModel(); + import->expandCategories(true); + ++i; } } void ItemLibraryModel::collapseAll() { - bool changed = false; + int i = 0; for (const QPointer &import : std::as_const(m_importList)) { if (import->hasCategories() && import->importExpanded()) { - changed = true; import->setImportExpanded(false); + emit dataChanged(index(i), index(i), {m_roleNames.key("importExpanded")}); saveExpandedState(false, import->importUrl()); } - } - if (changed) { - beginResetModel(); - endResetModel(); + ++i; } } @@ -329,9 +323,18 @@ void ItemLibraryModel::updateVisibility(bool *changed) { for (ItemLibraryImport *import : std::as_const(m_importList)) { bool categoryChanged = false; - import->updateCategoryVisibility(m_searchText, &categoryChanged); + bool hasVisibleItems = import->updateCategoryVisibility(m_searchText, &categoryChanged); *changed |= categoryChanged; + + // expand import if it has an item matching search criteria + if (hasVisibleItems && !import->importExpanded()) + import->setImportExpanded(); + } + + if (changed) { + beginResetModel(); + endResetModel(); } } diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp index ea80e211132..9fdef025483 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp @@ -275,6 +275,7 @@ void ItemLibraryWidget::handleAddImport(int index) m_model->changeImports({import}, {}); QmlDesignerPlugin::instance()->currentDesignDocument()->updateSubcomponentManager(); m_stackedWidget->setCurrentIndex(0); // switch to the Components view after import is added + updateSearch(); } void ItemLibraryWidget::delayedUpdateModel() diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachestorage.h b/src/plugins/qmldesigner/designercore/imagecache/imagecachestorage.h index 2d5d87ab48b..f34dd2fca4b 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/imagecachestorage.h +++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachestorage.h @@ -134,7 +134,7 @@ public: } } - void storeIcon(Utils::SmallStringView name, Sqlite::TimeStamp newTimeStamp, const QIcon &icon) + void storeIcon(Utils::SmallStringView name, Sqlite::TimeStamp newTimeStamp, const QIcon &icon) override { try { Sqlite::ImmediateTransaction transaction{database}; diff --git a/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp b/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp index 0c759c59ced..876bf27074b 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp @@ -337,7 +337,9 @@ void SubComponentManager::registerQmlFile(const QFileInfo &fileInfo, const QStri ItemLibraryEntry itemLibraryEntry; itemLibraryEntry.setType(componentName.toUtf8()); itemLibraryEntry.setName(baseComponentName); +#ifndef QMLDESIGNER_TEST itemLibraryEntry.setCategory(ItemLibraryImport::userComponentsTitle()); +#endif itemLibraryEntry.setCustomComponentSource(fileInfo.absoluteFilePath()); if (!qualifier.isEmpty()) { itemLibraryEntry.setRequiredImport(fixedQualifier); diff --git a/tests/auto/qml/reformatter/jssyntax.js b/tests/auto/qml/reformatter/jssyntax.js index edba1a406cf..b6c2a9e9054 100644 --- a/tests/auto/qml/reformatter/jssyntax.js +++ b/tests/auto/qml/reformatter/jssyntax.js @@ -12,6 +12,17 @@ function foo(a, b) { var foo = function (a, b) {} +function spread() { + iterableObj = [1, 2] + obj = { + "a": 42 + } + foo(...iterableObj) + let arr = [...iterableObj, '4', 'five', 6] + foo(-1, ...args, 2, ...[3]) + console.log(Math.max(...[1, 2, 3, 4])) +} + const func1 = x => x * 2 const func2 = x => { return x * 7