diff --git a/src/libs/utils/aspects.cpp b/src/libs/utils/aspects.cpp index 957bf6db9e8..4296a3d1f07 100644 --- a/src/libs/utils/aspects.cpp +++ b/src/libs/utils/aspects.cpp @@ -974,6 +974,11 @@ void StringAspect::setEnvironmentChange(const EnvironmentChange &change) d->m_pathChooserDisplay->setEnvironmentChange(change); } +void StringAspect::setEnvironment(const Environment &env) +{ + setEnvironmentChange(EnvironmentChange::fromDictionary(env.toDictionary())); +} + void StringAspect::setBaseFileName(const FilePath &baseFileName) { d->m_baseFileName = baseFileName; diff --git a/src/libs/utils/aspects.h b/src/libs/utils/aspects.h index 30d51c2c8a5..5a99515e67e 100644 --- a/src/libs/utils/aspects.h +++ b/src/libs/utils/aspects.h @@ -356,6 +356,7 @@ public: void setHistoryCompleter(const QString &historyCompleterKey); void setExpectedKind(const PathChooser::Kind expectedKind); void setEnvironmentChange(const EnvironmentChange &change); + void setEnvironment(const Environment &env); void setBaseFileName(const FilePath &baseFileName); void setUndoRedoEnabled(bool readOnly); void setAcceptRichText(bool acceptRichText); diff --git a/src/libs/utils/environment.cpp b/src/libs/utils/environment.cpp index a020e6518d0..f537f606927 100644 --- a/src/libs/utils/environment.cpp +++ b/src/libs/utils/environment.cpp @@ -23,7 +23,7 @@ NameValueItems Environment::diff(const Environment &other, bool checkAppendPrepe return m_dict.diff(other.m_dict, checkAppendPrepend); } -int Environment::isValid() const +bool Environment::hasChanges() const { return m_dict.size() != 0; } @@ -417,10 +417,10 @@ void EnvironmentChange::addAppendToPath(const FilePaths &values) m_changeItems.append(Item{std::in_place_index_t(), value}); } -EnvironmentChange EnvironmentChange::fromFixedEnvironment(const Environment &fixedEnv) +EnvironmentChange EnvironmentChange::fromDictionary(const NameValueDictionary &dict) { EnvironmentChange change; - change.m_changeItems.append(Item{std::in_place_index_t(), fixedEnv}); + change.m_changeItems.append(Item{std::in_place_index_t(), dict}); return change; } @@ -431,8 +431,8 @@ void EnvironmentChange::applyToEnvironment(Environment &env) const case SetSystemEnvironment: env = Environment::systemEnvironment(); break; - case SetFixedEnvironment: - env = std::get(item); + case SetFixedDictionary: + env = Environment(std::get(item)); break; case SetValue: { const QPair data = std::get(item); diff --git a/src/libs/utils/environment.h b/src/libs/utils/environment.h index 06f3177f1f5..795f65e2e4b 100644 --- a/src/libs/utils/environment.h +++ b/src/libs/utils/environment.h @@ -35,7 +35,7 @@ public: void unset(const QString &key) { m_dict.unset(key); } void modify(const NameValueItems &items) { m_dict.modify(items); } - int isValid() const; + bool hasChanges() const; void clear() { return m_dict.clear(); } QStringList toStringList() const { return m_dict.toStringList(); } @@ -111,7 +111,7 @@ public: enum Type { SetSystemEnvironment, - SetFixedEnvironment, + SetFixedDictionary, SetValue, UnsetValue, PrependToPath, @@ -120,14 +120,14 @@ public: using Item = std::variant< std::monostate, // SetSystemEnvironment dummy - Environment, // SetFixedEnvironment + NameValueDictionary, // SetFixedDictionary QPair, // SetValue QString, // UnsetValue FilePath, // PrependToPath FilePath // AppendToPath >; - static EnvironmentChange fromFixedEnvironment(const Environment &fixedEnv); + static EnvironmentChange fromDictionary(const NameValueDictionary &dict); void applyToEnvironment(Environment &) const; diff --git a/src/libs/utils/pathchooser.cpp b/src/libs/utils/pathchooser.cpp index 05825220af7..6f6a2173219 100644 --- a/src/libs/utils/pathchooser.cpp +++ b/src/libs/utils/pathchooser.cpp @@ -324,6 +324,11 @@ void PathChooser::setBaseDirectory(const FilePath &base) triggerChanged(); } +void PathChooser::setEnvironment(const Environment &env) +{ + setEnvironmentChange(EnvironmentChange::fromDictionary(env.toDictionary())); +} + FilePath PathChooser::baseDirectory() const { return d->m_baseDirectory; diff --git a/src/libs/utils/pathchooser.h b/src/libs/utils/pathchooser.h index 2a3ee05610c..92b5973b2de 100644 --- a/src/libs/utils/pathchooser.h +++ b/src/libs/utils/pathchooser.h @@ -76,6 +76,7 @@ public: FilePath baseDirectory() const; void setBaseDirectory(const FilePath &base); + void setEnvironment(const Environment &env); void setEnvironmentChange(const EnvironmentChange &change); /** Returns the suggested label title when used in a form layout. */ diff --git a/src/libs/utils/qtcprocess.cpp b/src/libs/utils/qtcprocess.cpp index c36f4097782..25e40fad26a 100644 --- a/src/libs/utils/qtcprocess.cpp +++ b/src/libs/utils/qtcprocess.cpp @@ -668,7 +668,7 @@ public: Environment fullEnvironment() const { Environment env = m_setup.m_environment; - if (!env.isValid()) { + if (!env.hasChanges()) { // FIXME: Either switch to using EnvironmentChange instead of full Environments, or // feed the full environment into the QtcProcess instead of fixing it up here. // qWarning("QtcProcess::start: Empty environment set when running '%s'.", @@ -1190,7 +1190,7 @@ QString QtcProcess::toStandaloneCommandLine() const d->m_setup.m_workingDirectory.path(); } parts.append("-i"); - if (d->m_setup.m_environment.isValid()) { + if (d->m_setup.m_environment.hasChanges()) { const QStringList envVars = d->m_setup.m_environment.toStringList(); std::transform(envVars.cbegin(), envVars.cend(), std::back_inserter(parts), ProcessArgs::quoteArgUnix); diff --git a/src/plugins/CMakeLists.txt b/src/plugins/CMakeLists.txt index 8051db6e59f..1f5c2e8342a 100644 --- a/src/plugins/CMakeLists.txt +++ b/src/plugins/CMakeLists.txt @@ -22,6 +22,7 @@ add_subdirectory(silversearcher) # Level 3: (only depends on Level 2 and below) add_subdirectory(bookmarks) add_subdirectory(cppeditor) +add_subdirectory(haskell) add_subdirectory(help) add_subdirectory(resourceeditor) add_subdirectory(nim) diff --git a/src/plugins/android/androidrunnerworker.cpp b/src/plugins/android/androidrunnerworker.cpp index 75d6f175bf7..53721cebdb0 100644 --- a/src/plugins/android/androidrunnerworker.cpp +++ b/src/plugins/android/androidrunnerworker.cpp @@ -605,7 +605,7 @@ void AndroidRunnerWorker::asyncStartHelper() << QString::fromLatin1(appArgs.join(' ').toUtf8().toBase64()); } - if (m_extraEnvVars.isValid()) { + if (m_extraEnvVars.hasChanges()) { args << "-e" << "extraenvvars" << QString::fromLatin1(m_extraEnvVars.toStringList().join('\t') .toUtf8().toBase64()); diff --git a/src/plugins/autotest/autotestplugin.cpp b/src/plugins/autotest/autotestplugin.cpp index 3f8c2ff8192..27e433896a1 100644 --- a/src/plugins/autotest/autotestplugin.cpp +++ b/src/plugins/autotest/autotestplugin.cpp @@ -66,6 +66,7 @@ #endif using namespace Core; +using namespace Utils; namespace Autotest { namespace Internal { @@ -361,7 +362,7 @@ void AutotestPluginPrivate::onRunFileTriggered() if (!document) return; - const Utils::FilePath &fileName = document->filePath(); + const FilePath &fileName = document->filePath(); if (fileName.isEmpty()) return; @@ -398,7 +399,7 @@ void AutotestPluginPrivate::onRunUnderCursorTriggered(TestRunMode mode) // check whether we have been triggered on a test function definition const int line = currentEditor->currentLine(); - const Utils::FilePath &filePath = currentEditor->textDocument()->filePath(); + const FilePath &filePath = currentEditor->textDocument()->filePath(); QList filteredItems = Utils::filtered(testsItems, [&](ITestTreeItem *it){ return it->line() == line && it->filePath() == filePath; }); diff --git a/src/plugins/autotest/autotestunittests.cpp b/src/plugins/autotest/autotestunittests.cpp index d655280d306..e72cc0cbbb7 100644 --- a/src/plugins/autotest/autotestunittests.cpp +++ b/src/plugins/autotest/autotestunittests.cpp @@ -68,7 +68,7 @@ void AutoTestUnitTests::initTestCase() if (!qtcEnvironmentVariableIsEmpty("BOOST_INCLUDE_DIR")) { m_checkBoost = true; } else { - if (Utils::HostOsInfo::isLinuxHost() + if (HostOsInfo::isLinuxHost() && (QFileInfo::exists("/usr/include/boost/version.hpp") || QFileInfo::exists("/usr/local/include/boost/version.hpp"))) { qDebug() << "Found boost at system level - will run boost parser test."; @@ -77,7 +77,7 @@ void AutoTestUnitTests::initTestCase() } // Enable quick check for derived tests - static const Utils::Id id = Utils::Id("AutoTest.Framework.QtTest"); + static const Id id = Id("AutoTest.Framework.QtTest"); static_cast( TestFrameworkManager::frameworkForId(id)->testSettings()) ->quickCheckForDerivedTests.setValue(true); @@ -257,7 +257,7 @@ void AutoTestUnitTests::testCodeParserBoostTest() QCOMPARE(m_model->boostTestNamesCount(), 5); - const Utils::FilePath basePath = projectInfo->projectRoot(); + const FilePath basePath = projectInfo->projectRoot(); QVERIFY(!basePath.isEmpty()); QMap expectedSuitesAndTests; diff --git a/src/plugins/autotest/boost/boosttestconfiguration.cpp b/src/plugins/autotest/boost/boosttestconfiguration.cpp index 5a9bd6beaab..50d8a863f4a 100644 --- a/src/plugins/autotest/boost/boosttestconfiguration.cpp +++ b/src/plugins/autotest/boost/boosttestconfiguration.cpp @@ -13,11 +13,13 @@ #include #include +using namespace Utils; + namespace Autotest { namespace Internal { TestOutputReader *BoostTestConfiguration::createOutputReader( - const QFutureInterface &fi, Utils::QtcProcess *app) const + const QFutureInterface &fi, QtcProcess *app) const { auto settings = static_cast(framework()->testSettings()); return new BoostTestOutputReader(fi, app, buildDirectory(), projectFile(), @@ -113,11 +115,11 @@ QStringList BoostTestConfiguration::argumentsForTestRunner(QStringList *omitted) return arguments; } -Utils::Environment BoostTestConfiguration::filteredEnvironment(const Utils::Environment &original) const +Environment BoostTestConfiguration::filteredEnvironment(const Environment &original) const { const QStringList interferingEnv = interfering(InterferingType::EnvironmentVariables); - Utils::Environment result = original; + Environment result = original; if (!result.hasKey("BOOST_TEST_COLOR_OUTPUT")) result.set("BOOST_TEST_COLOR_OUTPUT", "1"); // use colored output by default for (const QString &key : interferingEnv) diff --git a/src/plugins/autotest/boost/boosttestconfiguration.h b/src/plugins/autotest/boost/boosttestconfiguration.h index e1cf2f1d56f..8afb74049fd 100644 --- a/src/plugins/autotest/boost/boosttestconfiguration.h +++ b/src/plugins/autotest/boost/boosttestconfiguration.h @@ -13,7 +13,7 @@ class BoostTestConfiguration : public DebuggableTestConfiguration public: explicit BoostTestConfiguration(ITestFramework *framework) : DebuggableTestConfiguration(framework) {} - TestOutputReader *createOutputReader(const QFutureInterface &fi, + TestOutputReader *createOutputReader(const QFutureInterface &fi, Utils::QtcProcess *app) const override; QStringList argumentsForTestRunner(QStringList *omitted = nullptr) const override; Utils::Environment filteredEnvironment(const Utils::Environment &original) const override; diff --git a/src/plugins/autotest/boost/boosttestframework.cpp b/src/plugins/autotest/boost/boosttestframework.cpp index 4c692058eac..b431bbed76b 100644 --- a/src/plugins/autotest/boost/boosttestframework.cpp +++ b/src/plugins/autotest/boost/boosttestframework.cpp @@ -18,10 +18,7 @@ ITestParser *BoostTestFramework::createTestParser() ITestTreeItem *BoostTestFramework::createRootNode() { - return new BoostTestTreeItem( - this, - displayName(), - Utils::FilePath(), ITestTreeItem::Root); + return new BoostTestTreeItem(this, displayName(), {}, ITestTreeItem::Root); } const char *BoostTestFramework::name() const diff --git a/src/plugins/autotest/boost/boosttestoutputreader.cpp b/src/plugins/autotest/boost/boosttestoutputreader.cpp index 9cd2d6c1685..5f549cca320 100644 --- a/src/plugins/autotest/boost/boosttestoutputreader.cpp +++ b/src/plugins/autotest/boost/boosttestoutputreader.cpp @@ -14,23 +14,29 @@ #include #include +using namespace Utils; + namespace Autotest { namespace Internal { static Q_LOGGING_CATEGORY(orLog, "qtc.autotest.boost.outputreader", QtWarningMsg) -BoostTestOutputReader::BoostTestOutputReader(const QFutureInterface &futureInterface, - Utils::QtcProcess *testApplication, - const Utils::FilePath &buildDirectory, - const Utils::FilePath &projectFile, +BoostTestOutputReader::BoostTestOutputReader(const QFutureInterface &futureInterface, + QtcProcess *testApplication, + const FilePath &buildDirectory, + const FilePath &projectFile, LogLevel log, ReportLevel report) : TestOutputReader(futureInterface, testApplication, buildDirectory) , m_projectFile(projectFile) , m_logLevel(log) , m_reportLevel(report) { - if (m_testApplication) - connect(m_testApplication, &Utils::QtcProcess::done, this, &BoostTestOutputReader::onDone); + if (!testApplication) + return; + + connect(testApplication, &QtcProcess::done, this, [this, testApplication] { + onDone(testApplication->exitCode()); + }); } // content of "error:..." / "info:..." / ... messages @@ -42,23 +48,23 @@ static QString caseFromContent(const QString &content) if (index != 17 || length <= 18) { qCDebug(orLog) << "double quote position" << index << " or content length" << length << "wrong on content" << content; - return QString(); + return {}; } index = content.indexOf('"', 18); if (index == -1) { qCDebug(orLog) << "no closing double quote" << content; - return QString(); + return {}; } return content.mid(18, index - 1); } int index = content.indexOf(": in "); if (index == -1) // "info: check true has passed" - return QString(); + return {}; if (index <= 4 || length < index + 4) { qCDebug(orLog) << "unexpected position" << index << "for info" << content; - return QString(); + return {}; } QString result = content.mid(index + 5); @@ -66,7 +72,7 @@ static QString caseFromContent(const QString &content) const QRegularExpressionMatch matcher = functionName.match(result); if (!matcher.hasMatch()) { qCDebug(orLog) << "got no match"; - return QString(); + return {}; } return matcher.captured(1); } @@ -74,19 +80,18 @@ static QString caseFromContent(const QString &content) void BoostTestOutputReader::sendCompleteInformation() { QTC_ASSERT(m_result != ResultType::Invalid, return); - BoostTestResult *result = new BoostTestResult(id(), m_currentModule, m_projectFile, - m_currentTest, m_currentSuite); + BoostTestResult result(id(), m_currentModule, m_projectFile, m_currentTest, m_currentSuite); if (m_lineNumber) { - result->setLine(m_lineNumber); - result->setFileName(m_fileName); - } else if (const ITestTreeItem *it = result->findTestTreeItem()) { - result->setLine(it->line()); - result->setFileName(it->filePath()); + result.setLine(m_lineNumber); + result.setFileName(m_fileName); + } else if (const ITestTreeItem *it = result.findTestTreeItem()) { + result.setLine(it->line()); + result.setFileName(it->filePath()); } - result->setDescription(m_description); - result->setResult(m_result); - reportResult(TestResultPtr(result)); + result.setDescription(m_description); + result.setResult(m_result); + reportResult(result); m_result = ResultType::Invalid; } @@ -212,10 +217,10 @@ void BoostTestOutputReader::processOutputLine(const QByteArray &outputLine) if (match.hasMatch()) { if (m_result != ResultType::Invalid) sendCompleteInformation(); - BoostTestResult *result = new BoostTestResult(id(), m_currentModule, m_projectFile); - result->setDescription(match.captured(0)); - result->setResult(ResultType::MessageInfo); - reportResult(TestResultPtr(result)); + BoostTestResult result(id(), m_currentModule, m_projectFile); + result.setDescription(match.captured(0)); + result.setResult(ResultType::MessageInfo); + reportResult(result); return; } @@ -234,17 +239,17 @@ void BoostTestOutputReader::processOutputLine(const QByteArray &outputLine) sendCompleteInformation(); if (match.captured(1).startsWith("Entering")) { m_currentModule = match.captured(2); - BoostTestResult *result = new BoostTestResult(id(), m_currentModule, m_projectFile); - result->setDescription(Tr::tr("Executing test module %1").arg(m_currentModule)); - result->setResult(ResultType::TestStart); - reportResult(TestResultPtr(result)); + BoostTestResult result(id(), m_currentModule, m_projectFile); + result.setDescription(Tr::tr("Executing test module %1").arg(m_currentModule)); + result.setResult(ResultType::TestStart); + reportResult(result); m_description.clear(); } else { QTC_CHECK(m_currentModule == match.captured(3)); - BoostTestResult *result = new BoostTestResult(id(), m_currentModule, m_projectFile); - result->setDescription(Tr::tr("Test module execution took %1").arg(match.captured(4))); - result->setResult(ResultType::TestEnd); - reportResult(TestResultPtr(result)); + BoostTestResult result(id(), m_currentModule, m_projectFile); + result.setDescription(Tr::tr("Test module execution took %1").arg(match.captured(4))); + result.setResult(ResultType::TestEnd); + reportResult(result); m_currentTest.clear(); m_currentSuite.clear(); @@ -325,16 +330,16 @@ void BoostTestOutputReader::processOutputLine(const QByteArray &outputLine) if (match.hasMatch()) { if (m_result != ResultType::Invalid) sendCompleteInformation(); - BoostTestResult *result = new BoostTestResult(id(), {}, m_projectFile); - int failed = match.captured(1).toInt(); - int fatals = m_summary.value(ResultType::MessageFatal); + BoostTestResult result(id(), {}, m_projectFile); + const int failed = match.captured(1).toInt(); + const int fatals = m_summary.value(ResultType::MessageFatal); QString txt = Tr::tr("%1 failures detected in %2.").arg(failed).arg(match.captured(3)); - int passed = qMax(0, m_testCaseCount - failed); + const int passed = qMax(0, m_testCaseCount - failed); if (m_testCaseCount != -1) txt.append(' ').append(Tr::tr("%1 tests passed.").arg(passed)); - result->setDescription(txt); - result->setResult(ResultType::MessageInfo); - reportResult(TestResultPtr(result)); + result.setDescription(txt); + result.setResult(ResultType::MessageInfo); + reportResult(result); if (m_reportLevel == ReportLevel::Confirm) { // for the final summary m_summary[ResultType::Pass] += passed; m_summary[ResultType::Fail] += failed - fatals; @@ -346,13 +351,13 @@ void BoostTestOutputReader::processOutputLine(const QByteArray &outputLine) if (line == noErrors) { if (m_result != ResultType::Invalid) sendCompleteInformation(); - BoostTestResult *result = new BoostTestResult(id(), {}, m_projectFile); + BoostTestResult result(id(), {}, m_projectFile); QString txt = Tr::tr("No errors detected."); if (m_testCaseCount != -1) txt.append(' ').append(Tr::tr("%1 tests passed.").arg(m_testCaseCount)); - result->setDescription(txt); - result->setResult(ResultType::MessageInfo); - reportResult(TestResultPtr(result)); + result.setDescription(txt); + result.setResult(ResultType::MessageInfo); + reportResult(result); if (m_reportLevel == ReportLevel::Confirm) // for the final summary m_summary.insert(ResultType::Pass, m_testCaseCount); return; @@ -372,16 +377,15 @@ void BoostTestOutputReader::processStdError(const QByteArray &outputLine) emit newOutputLineAvailable(outputLine, OutputChannel::StdErr); } -TestResultPtr BoostTestOutputReader::createDefaultResult() const +TestResult BoostTestOutputReader::createDefaultResult() const { - return TestResultPtr(new BoostTestResult(id(), m_currentModule, m_projectFile, - m_currentTest, m_currentSuite)); + return BoostTestResult(id(), m_currentModule, m_projectFile, m_currentTest, m_currentSuite); } -void BoostTestOutputReader::onDone() { - int exitCode = m_testApplication->exitCode(); +void BoostTestOutputReader::onDone(int exitCode) +{ if (m_reportLevel == ReportLevel::No && m_testCaseCount != -1) { - int reportedFailsAndSkips = m_summary[ResultType::Fail] + m_summary[ResultType::Skip]; + const int reportedFailsAndSkips = m_summary[ResultType::Fail] + m_summary[ResultType::Skip]; m_summary.insert(ResultType::Pass, m_testCaseCount - reportedFailsAndSkips); } // boost::exit_success (0), boost::exit_test_failure (201) @@ -416,11 +420,11 @@ void BoostTestOutputReader::onDone() { void BoostTestOutputReader::reportNoOutputFinish(const QString &description, ResultType type) { - BoostTestResult *result = new BoostTestResult(id(), m_currentModule, m_projectFile, - Tr::tr("Running tests without output.")); - result->setDescription(description); - result->setResult(type); - reportResult(TestResultPtr(result)); + BoostTestResult result(id(), m_currentModule, m_projectFile, + Tr::tr("Running tests without output.")); + result.setDescription(description); + result.setResult(type); + reportResult(result); } } // namespace Internal diff --git a/src/plugins/autotest/boost/boosttestoutputreader.h b/src/plugins/autotest/boost/boosttestoutputreader.h index 74cc82271d9..8449a0708ab 100644 --- a/src/plugins/autotest/boost/boosttestoutputreader.h +++ b/src/plugins/autotest/boost/boosttestoutputreader.h @@ -15,16 +15,16 @@ class BoostTestOutputReader : public TestOutputReader { Q_OBJECT public: - BoostTestOutputReader(const QFutureInterface &futureInterface, + BoostTestOutputReader(const QFutureInterface &futureInterface, Utils::QtcProcess *testApplication, const Utils::FilePath &buildDirectory, const Utils::FilePath &projectFile, LogLevel log, ReportLevel report); protected: void processOutputLine(const QByteArray &outputLine) override; void processStdError(const QByteArray &outputLine) override; - TestResultPtr createDefaultResult() const override; + TestResult createDefaultResult() const override; private: - void onDone(); + void onDone(int exitCode); void sendCompleteInformation(); void handleMessageMatch(const QRegularExpressionMatch &match); void reportNoOutputFinish(const QString &description, ResultType type); diff --git a/src/plugins/autotest/boost/boosttestparser.cpp b/src/plugins/autotest/boost/boosttestparser.cpp index 4d261a4ca57..bbf6dfd56ce 100644 --- a/src/plugins/autotest/boost/boosttestparser.cpp +++ b/src/plugins/autotest/boost/boosttestparser.cpp @@ -78,8 +78,8 @@ static bool hasBoostTestMacros(const CPlusPlus::Document::Ptr &doc) return false; } -static BoostTestParseResult *createParseResult(const QString &name, const Utils::FilePath &filePath, - const Utils::FilePath &projectFile, +static BoostTestParseResult *createParseResult(const QString &name, const FilePath &filePath, + const FilePath &projectFile, ITestFramework *framework, TestTreeItem::Type type, const BoostTestInfo &info) { @@ -97,7 +97,7 @@ static BoostTestParseResult *createParseResult(const QString &name, const Utils: } bool BoostTestParser::processDocument(QFutureInterface &futureInterface, - const Utils::FilePath &fileName) + const FilePath &fileName) { CPlusPlus::Document::Ptr doc = document(fileName); if (doc.isNull() || !includesBoostTest(doc, m_cppSnapshot) || !hasBoostTestMacros(doc)) @@ -109,7 +109,7 @@ bool BoostTestParser::processDocument(QFutureInterface &futu if (projectParts.isEmpty()) // happens if shutting down while parsing return false; const CppEditor::ProjectPart::ConstPtr projectPart = projectParts.first(); - const auto projectFile = Utils::FilePath::fromString(projectPart->projectFile); + const auto projectFile = FilePath::fromString(projectPart->projectFile); const QByteArray &fileContent = getFileContent(fileName); BoostCodeParser codeParser(fileContent, projectPart->languageFeatures, doc, m_cppSnapshot); diff --git a/src/plugins/autotest/boost/boosttestsettings.cpp b/src/plugins/autotest/boost/boosttestsettings.cpp index 6280945253f..c995cab9b6c 100644 --- a/src/plugins/autotest/boost/boosttestsettings.cpp +++ b/src/plugins/autotest/boost/boosttestsettings.cpp @@ -81,7 +81,7 @@ BoostTestSettings::BoostTestSettings() memLeaks.setToolTip(Tr::tr("Enable memory leak detection.")); } -BoostTestSettingsPage::BoostTestSettingsPage(BoostTestSettings *settings, Utils::Id settingsId) +BoostTestSettingsPage::BoostTestSettingsPage(BoostTestSettings *settings, Id settingsId) { setId(settingsId); setCategory(Constants::AUTOTEST_SETTINGS_CATEGORY); @@ -120,7 +120,7 @@ QString BoostTestSettings::logLevelToOption(const LogLevel logLevel) case LogLevel::Nothing: return QString("nothing"); case LogLevel::Warning: return QString("warning"); } - return QString(); + return {}; } QString BoostTestSettings::reportLevelToOption(const ReportLevel reportLevel) @@ -131,7 +131,7 @@ QString BoostTestSettings::reportLevelToOption(const ReportLevel reportLevel) case ReportLevel::Detailed: return QString("detailed"); case ReportLevel::No: return QString("no"); } - return QString(); + return {}; } } // namespace Internal diff --git a/src/plugins/autotest/boost/boosttesttreeitem.cpp b/src/plugins/autotest/boost/boosttesttreeitem.cpp index 41e203c5925..4b53d92754e 100644 --- a/src/plugins/autotest/boost/boosttesttreeitem.cpp +++ b/src/plugins/autotest/boost/boosttesttreeitem.cpp @@ -16,6 +16,8 @@ #include +using namespace Utils; + namespace Autotest { namespace Internal { @@ -123,7 +125,7 @@ bool BoostTestTreeItem::modify(const TestParseResult *result) TestTreeItem *BoostTestTreeItem::createParentGroupNode() const { - const Utils::FilePath &absPath = filePath().absolutePath(); + const FilePath &absPath = filePath().absolutePath(); return new BoostTestTreeItem(framework(), absPath.baseName(), absPath, TestTreeItem::GroupNode); } @@ -164,7 +166,7 @@ QList BoostTestTreeItem::getAllTestConfigurations() const }; // we only need the unique project files (and number of test cases for the progress indicator) - QHash testsPerProjectfile; + QHash testsPerProjectfile; forAllChildItems([&testsPerProjectfile](TestTreeItem *item){ if (item->type() != TestSuite) return; @@ -207,7 +209,7 @@ QList BoostTestTreeItem::getTestConfigurations( QSet internalTargets; }; - QHash testCasesForProjectFile; + QHash testCasesForProjectFile; forAllChildren([&testCasesForProjectFile, &predicate](TreeItem *it){ auto item = static_cast(it); if (item->type() != TestCase) @@ -346,10 +348,10 @@ bool BoostTestTreeItem::enabled() const TestTreeItem *BoostTestTreeItem::findChildByNameStateAndFile(const QString &name, BoostTestTreeItem::TestStates state, - const Utils::FilePath &proFile) const + const FilePath &proFile) const { return static_cast( - findAnyChild([name, state, proFile](const Utils::TreeItem *other){ + findAnyChild([name, state, proFile](const TreeItem *other){ const BoostTestTreeItem *boostItem = static_cast(other); return boostItem->proFile() == proFile && boostItem->fullName() == name && boostItem->state() == state; diff --git a/src/plugins/autotest/boost/boosttesttreeitem.h b/src/plugins/autotest/boost/boosttesttreeitem.h index a9d41754012..daa7192ff50 100644 --- a/src/plugins/autotest/boost/boosttesttreeitem.h +++ b/src/plugins/autotest/boost/boosttesttreeitem.h @@ -27,8 +27,8 @@ public: Q_DECLARE_FLAGS(TestStates, TestState) explicit BoostTestTreeItem(ITestFramework *framework, - const QString &name = QString(), - const Utils::FilePath &filePath = Utils::FilePath(), + const QString &name = {}, + const Utils::FilePath &filePath = {}, Type type = Root) : TestTreeItem(framework, name, filePath, type) {} diff --git a/src/plugins/autotest/catch/catchconfiguration.cpp b/src/plugins/autotest/catch/catchconfiguration.cpp index 7f82aa629eb..c08f7ac82b6 100644 --- a/src/plugins/autotest/catch/catchconfiguration.cpp +++ b/src/plugins/autotest/catch/catchconfiguration.cpp @@ -11,11 +11,13 @@ #include +using namespace Utils; + namespace Autotest { namespace Internal { -TestOutputReader *CatchConfiguration::createOutputReader(const QFutureInterface &fi, - Utils::QtcProcess *app) const +TestOutputReader *CatchConfiguration::createOutputReader(const QFutureInterface &fi, + QtcProcess *app) const { return new CatchOutputReader(fi, app, buildDirectory(), projectFile()); } @@ -115,7 +117,7 @@ QStringList CatchConfiguration::argumentsForTestRunner(QStringList *omitted) con return arguments; } -Utils::Environment CatchConfiguration::filteredEnvironment(const Utils::Environment &original) const +Environment CatchConfiguration::filteredEnvironment(const Environment &original) const { return original; } diff --git a/src/plugins/autotest/catch/catchconfiguration.h b/src/plugins/autotest/catch/catchconfiguration.h index 422cf7af4fb..bfa37f01644 100644 --- a/src/plugins/autotest/catch/catchconfiguration.h +++ b/src/plugins/autotest/catch/catchconfiguration.h @@ -12,7 +12,7 @@ class CatchConfiguration : public DebuggableTestConfiguration { public: CatchConfiguration(ITestFramework *framework) : DebuggableTestConfiguration(framework) {} - TestOutputReader *createOutputReader(const QFutureInterface &fi, + TestOutputReader *createOutputReader(const QFutureInterface &fi, Utils::QtcProcess *app) const override; QStringList argumentsForTestRunner(QStringList *omitted = nullptr) const override; Utils::Environment filteredEnvironment(const Utils::Environment &original) const override; diff --git a/src/plugins/autotest/catch/catchframework.cpp b/src/plugins/autotest/catch/catchframework.cpp index 585c1b6dd0b..d2afeddaad2 100644 --- a/src/plugins/autotest/catch/catchframework.cpp +++ b/src/plugins/autotest/catch/catchframework.cpp @@ -32,9 +32,7 @@ ITestParser *CatchFramework::createTestParser() ITestTreeItem *CatchFramework::createRootNode() { - return new CatchTreeItem(this, - displayName(), - Utils::FilePath(), ITestTreeItem::Root); + return new CatchTreeItem(this, displayName(), {}, ITestTreeItem::Root); } } // namespace Internal diff --git a/src/plugins/autotest/catch/catchoutputreader.cpp b/src/plugins/autotest/catch/catchoutputreader.cpp index 893b458281d..95e437fdc4b 100644 --- a/src/plugins/autotest/catch/catchoutputreader.cpp +++ b/src/plugins/autotest/catch/catchoutputreader.cpp @@ -9,6 +9,8 @@ #include #include +using namespace Utils; + namespace Autotest { namespace Internal { @@ -29,10 +31,10 @@ namespace CatchXml { const char TestCaseResultElement[] = "OverallResult"; } -CatchOutputReader::CatchOutputReader(const QFutureInterface &futureInterface, - Utils::QtcProcess *testApplication, - const Utils::FilePath &buildDirectory, - const Utils::FilePath &projectFile) +CatchOutputReader::CatchOutputReader(const QFutureInterface &futureInterface, + QtcProcess *testApplication, + const FilePath &buildDirectory, + const FilePath &projectFile) : TestOutputReader (futureInterface, testApplication, buildDirectory) , m_projectFile(projectFile) { @@ -169,22 +171,17 @@ void CatchOutputReader::processOutputLine(const QByteArray &outputLineWithNewLin } } -TestResultPtr CatchOutputReader::createDefaultResult() const +TestResult CatchOutputReader::createDefaultResult() const { - CatchResult *result = nullptr; - if (m_testCaseInfo.size() > 0) { - result = new CatchResult(id(), m_testCaseInfo.first().name, m_sectionDepth); - result->setDescription(m_testCaseInfo.last().name); - result->setLine(m_testCaseInfo.last().line); - const QString givenPath = m_testCaseInfo.last().filename; - if (!givenPath.isEmpty()) { - result->setFileName(constructSourceFilePath(m_buildDir, givenPath)); - } - } else { - result = new CatchResult(id(), {}, m_sectionDepth); - } - - return TestResultPtr(result); + if (m_testCaseInfo.size() == 0) + return CatchResult(id(), {}, m_sectionDepth); + CatchResult result = CatchResult(id(), m_testCaseInfo.first().name, m_sectionDepth); + result.setDescription(m_testCaseInfo.last().name); + result.setLine(m_testCaseInfo.last().line); + const QString givenPath = m_testCaseInfo.last().filename; + if (!givenPath.isEmpty()) + result.setFileName(constructSourceFilePath(m_buildDir, givenPath)); + return result; } void CatchOutputReader::recordTestInformation(const QXmlStreamAttributes &attributes) @@ -242,37 +239,38 @@ void CatchOutputReader::recordBenchmarkDetails( void CatchOutputReader::sendResult(const ResultType result) { - TestResultPtr catchResult = createDefaultResult(); - catchResult->setResult(result); + TestResult catchResult = createDefaultResult(); + catchResult.setResult(result); if (result == ResultType::TestStart && m_testCaseInfo.size() > 0) { - catchResult->setDescription(Tr::tr("Executing %1 \"%2\"").arg(testOutputNodeToString().toLower()) - .arg(catchResult->description())); + catchResult.setDescription(Tr::tr("Executing %1 \"%2\"") + .arg(testOutputNodeToString().toLower(), catchResult.description())); } else if (result == ResultType::Pass || result == ResultType::UnexpectedPass) { if (result == ResultType::UnexpectedPass) ++m_xpassCount; if (m_currentExpression.isEmpty()) { - catchResult->setDescription(Tr::tr("%1 \"%2\" passed").arg(testOutputNodeToString()) - .arg(catchResult->description())); + catchResult.setDescription(Tr::tr("%1 \"%2\" passed") + .arg(testOutputNodeToString(), catchResult.description())); } else { - catchResult->setDescription(Tr::tr("Expression passed") + catchResult.setDescription(Tr::tr("Expression passed") .append('\n').append(m_currentExpression)); } m_reportedSectionResult = true; m_reportedResult = true; } else if (result == ResultType::Fail || result == ResultType::ExpectedFail) { - catchResult->setDescription(Tr::tr("Expression failed: %1").arg(m_currentExpression.trimmed())); + catchResult.setDescription(Tr::tr("Expression failed: %1") + .arg(m_currentExpression.trimmed())); if (!m_reportedSectionResult) m_reportedSectionResult = true; m_reportedResult = true; } else if (result == ResultType::TestEnd) { - catchResult->setDescription(Tr::tr("Finished executing %1 \"%2\"").arg(testOutputNodeToString().toLower()) - .arg(catchResult->description())); + catchResult.setDescription(Tr::tr("Finished executing %1 \"%2\"") + .arg(testOutputNodeToString().toLower(), catchResult.description())); } else if (result == ResultType::Benchmark || result == ResultType::MessageFatal) { - catchResult->setDescription(m_currentExpression); + catchResult.setDescription(m_currentExpression); } else if (result == ResultType::MessageWarn || result == ResultType::MessageInfo) { - catchResult->setDescription(m_currentExpression.trimmed()); + catchResult.setDescription(m_currentExpression.trimmed()); } reportResult(catchResult); @@ -319,8 +317,7 @@ QString CatchOutputReader::testOutputNodeToString() const case SectionNode: return QStringLiteral("Section"); } - - return QString(); + return {}; } } // namespace Internal diff --git a/src/plugins/autotest/catch/catchoutputreader.h b/src/plugins/autotest/catch/catchoutputreader.h index 59bcc4e03f4..51e8c1e3389 100644 --- a/src/plugins/autotest/catch/catchoutputreader.h +++ b/src/plugins/autotest/catch/catchoutputreader.h @@ -14,13 +14,13 @@ namespace Internal { class CatchOutputReader : public TestOutputReader { public: - CatchOutputReader(const QFutureInterface &futureInterface, + CatchOutputReader(const QFutureInterface &futureInterface, Utils::QtcProcess *testApplication, const Utils::FilePath &buildDirectory, const Utils::FilePath &projectFile); protected: void processOutputLine(const QByteArray &outputLineWithNewLine) override; - TestResultPtr createDefaultResult() const override; + TestResult createDefaultResult() const override; private: enum TestOutputNodeType { diff --git a/src/plugins/autotest/catch/catchtestparser.cpp b/src/plugins/autotest/catch/catchtestparser.cpp index a3e9f5e01b4..dd01c01c90b 100644 --- a/src/plugins/autotest/catch/catchtestparser.cpp +++ b/src/plugins/autotest/catch/catchtestparser.cpp @@ -92,7 +92,7 @@ static bool hasCatchNames(const CPlusPlus::Document::Ptr &document) } bool CatchTestParser::processDocument(QFutureInterface &futureInterface, - const Utils::FilePath &fileName) + const FilePath &fileName) { CPlusPlus::Document::Ptr doc = document(fileName); if (doc.isNull() || !includesCatchHeader(doc, m_cppSnapshot)) @@ -117,9 +117,9 @@ bool CatchTestParser::processDocument(QFutureInterface &futu const QList projectParts = modelManager->projectPart(fileName); if (projectParts.isEmpty()) // happens if shutting down while parsing return false; - Utils::FilePath proFile; + FilePath proFile; const CppEditor::ProjectPart::ConstPtr projectPart = projectParts.first(); - proFile = Utils::FilePath::fromString(projectPart->projectFile); + proFile = FilePath::fromString(projectPart->projectFile); CatchCodeParser codeParser(fileContent, projectPart->languageFeatures); const CatchTestCodeLocationList foundTests = codeParser.findTests(); diff --git a/src/plugins/autotest/catch/catchtestsettings.cpp b/src/plugins/autotest/catch/catchtestsettings.cpp index 5dace57f69e..6fd0cde16d5 100644 --- a/src/plugins/autotest/catch/catchtestsettings.cpp +++ b/src/plugins/autotest/catch/catchtestsettings.cpp @@ -114,7 +114,7 @@ CatchTestSettings::CatchTestSettings() }); } -CatchTestSettingsPage::CatchTestSettingsPage(CatchTestSettings *settings, Utils::Id settingsId) +CatchTestSettingsPage::CatchTestSettingsPage(CatchTestSettings *settings, Id settingsId) { setId(settingsId); setCategory(Constants::AUTOTEST_SETTINGS_CATEGORY); diff --git a/src/plugins/autotest/catch/catchtreeitem.cpp b/src/plugins/autotest/catch/catchtreeitem.cpp index 2df21bdd84d..fe69fece87e 100644 --- a/src/plugins/autotest/catch/catchtreeitem.cpp +++ b/src/plugins/autotest/catch/catchtreeitem.cpp @@ -14,6 +14,8 @@ #include #include +using namespace Utils; + namespace Autotest { namespace Internal { @@ -72,7 +74,7 @@ TestTreeItem *CatchTreeItem::find(const TestParseResult *result) switch (type()) { case Root: if (result->framework->grouping()) { - const Utils::FilePath path = result->fileName.absolutePath(); + const FilePath path = result->fileName.absolutePath(); for (int row = 0; row < childCount(); ++row) { TestTreeItem *group = childItem(row); if (group->filePath() != path) @@ -123,7 +125,7 @@ bool CatchTreeItem::modify(const TestParseResult *result) TestTreeItem *CatchTreeItem::createParentGroupNode() const { - const Utils::FilePath absPath = filePath().absolutePath(); + const FilePath absPath = filePath().absolutePath(); return new CatchTreeItem(framework(), absPath.baseName(), absPath, TestTreeItem::GroupNode); } @@ -172,7 +174,7 @@ struct CatchTestCases }; static void collectTestInfo(const TestTreeItem *item, - QHash &testCasesForProfile, + QHash &testCasesForProfile, bool ignoreCheckState) { QTC_ASSERT(item, return); @@ -189,7 +191,7 @@ static void collectTestInfo(const TestTreeItem *item, QTC_ASSERT(childCount != 0, return); QTC_ASSERT(item->type() == TestTreeItem::TestSuite, return); if (ignoreCheckState || item->checked() == Qt::Checked) { - const Utils::FilePath &projectFile = item->childItem(0)->proFile(); + const FilePath &projectFile = item->childItem(0)->proFile(); item->forAllChildItems([&testCasesForProfile, &projectFile](TestTreeItem *it) { CatchTreeItem *current = static_cast(it); testCasesForProfile[projectFile].names.append(current->testCasesString()); @@ -210,7 +212,7 @@ static void collectTestInfo(const TestTreeItem *item, } static void collectFailedTestInfo(const CatchTreeItem *item, - QHash &testCasesForProfile) + QHash &testCasesForProfile) { QTC_ASSERT(item, return); QTC_ASSERT(item->type() == TestTreeItem::Root, return); @@ -246,7 +248,7 @@ QList CatchTreeItem::getFailedTestConfigurations() const if (!project || type() != Root) return result; - QHash testCasesForProFile; + QHash testCasesForProFile; collectFailedTestInfo(this, testCasesForProFile); for (auto it = testCasesForProFile.begin(), end = testCasesForProFile.end(); it != end; ++it) { @@ -263,7 +265,7 @@ QList CatchTreeItem::getFailedTestConfigurations() const return result; } -QList CatchTreeItem::getTestConfigurationsForFile(const Utils::FilePath &fileName) const +QList CatchTreeItem::getTestConfigurationsForFile(const FilePath &fileName) const { QList result; const auto cppMM = CppEditor::CppModelManager::instance(); @@ -316,7 +318,7 @@ QList CatchTreeItem::getTestConfigurations(bool ignoreChec if (!project || type() != Root) return result; - QHash testCasesForProfile; + QHash testCasesForProfile; for (int row = 0, end = childCount(); row < end; ++row) collectTestInfo(childItem(row), testCasesForProfile, ignoreCheckState); diff --git a/src/plugins/autotest/catch/catchtreeitem.h b/src/plugins/autotest/catch/catchtreeitem.h index 5db5452dd56..700887a93ae 100644 --- a/src/plugins/autotest/catch/catchtreeitem.h +++ b/src/plugins/autotest/catch/catchtreeitem.h @@ -20,8 +20,8 @@ public: Q_FLAGS(TestState) Q_DECLARE_FLAGS(TestStates, TestState) - explicit CatchTreeItem(ITestFramework *testFramework, const QString &name = QString(), - const Utils::FilePath &filePath = Utils::FilePath(), Type type = Root) + explicit CatchTreeItem(ITestFramework *testFramework, const QString &name = {}, + const Utils::FilePath &filePath = {}, Type type = Root) : TestTreeItem(testFramework, name, filePath, type) {} void setStates(CatchTreeItem::TestStates state) { m_state = state; } diff --git a/src/plugins/autotest/ctest/ctestconfiguration.cpp b/src/plugins/autotest/ctest/ctestconfiguration.cpp index ed54b7028f4..9208888033e 100644 --- a/src/plugins/autotest/ctest/ctestconfiguration.cpp +++ b/src/plugins/autotest/ctest/ctestconfiguration.cpp @@ -13,7 +13,7 @@ CTestConfiguration::CTestConfiguration(ITestBase *testBase) setDisplayName("CTest"); } -TestOutputReader *CTestConfiguration::createOutputReader(const QFutureInterface &fi, +TestOutputReader *CTestConfiguration::createOutputReader(const QFutureInterface &fi, Utils::QtcProcess *app) const { return new CTestOutputReader(fi, app, workingDirectory()); diff --git a/src/plugins/autotest/ctest/ctestconfiguration.h b/src/plugins/autotest/ctest/ctestconfiguration.h index b19bf8d6981..db0efb17173 100644 --- a/src/plugins/autotest/ctest/ctestconfiguration.h +++ b/src/plugins/autotest/ctest/ctestconfiguration.h @@ -13,7 +13,7 @@ class CTestConfiguration final : public Autotest::TestToolConfiguration public: explicit CTestConfiguration(ITestBase *testBase); - TestOutputReader *createOutputReader(const QFutureInterface &fi, + TestOutputReader *createOutputReader(const QFutureInterface &fi, Utils::QtcProcess *app) const final; }; diff --git a/src/plugins/autotest/ctest/ctestoutputreader.cpp b/src/plugins/autotest/ctest/ctestoutputreader.cpp index 8373a1ab59c..70ec5f0659b 100644 --- a/src/plugins/autotest/ctest/ctestoutputreader.cpp +++ b/src/plugins/autotest/ctest/ctestoutputreader.cpp @@ -52,9 +52,9 @@ public: {} }; -CTestOutputReader::CTestOutputReader(const QFutureInterface &futureInterface, - Utils::QtcProcess *testApplication, - const Utils::FilePath &buildDirectory) +CTestOutputReader::CTestOutputReader(const QFutureInterface &futureInterface, + QtcProcess *testApplication, + const FilePath &buildDirectory) : TestOutputReader(futureInterface, testApplication, buildDirectory) { } @@ -91,9 +91,9 @@ void CTestOutputReader::processOutputLine(const QByteArray &outputLine) if (!m_testName.isEmpty()) // possible? sendCompleteInformation(); m_project = match.captured(1); - TestResultPtr testResult = createDefaultResult(); - testResult->setResult(ResultType::TestStart); - testResult->setDescription(Tr::tr("Running tests for %1").arg(m_project)); + TestResult testResult = createDefaultResult(); + testResult.setResult(ResultType::TestStart); + testResult.setDescription(Tr::tr("Running tests for %1").arg(m_project)); reportResult(testResult); } else if (ExactMatch match = testCase1.match(line)) { int current = match.captured("current").toInt(); @@ -125,9 +125,9 @@ void CTestOutputReader::processOutputLine(const QByteArray &outputLine) } else if (ExactMatch match = summary.match(line)) { if (!m_testName.isEmpty()) sendCompleteInformation(); - TestResultPtr testResult = createDefaultResult(); - testResult->setResult(ResultType::MessageInfo); - testResult->setDescription(match.captured()); + TestResult testResult = createDefaultResult(); + testResult.setResult(ResultType::MessageInfo); + testResult.setDescription(match.captured()); reportResult(testResult); int failed = match.captured(1).toInt(); int testCount = match.captured(2).toInt(); @@ -136,9 +136,9 @@ void CTestOutputReader::processOutputLine(const QByteArray &outputLine) } else if (ExactMatch match = summaryTime.match(line)) { if (!m_testName.isEmpty()) // possible? sendCompleteInformation(); - TestResultPtr testResult = createDefaultResult(); - testResult->setResult(ResultType::TestEnd); - testResult->setDescription(match.captured()); + TestResult testResult = createDefaultResult(); + testResult.setResult(ResultType::TestEnd); + testResult.setDescription(match.captured()); reportResult(testResult); } else if (ExactMatch match = testCrash.match(line)) { m_description = match.captured(); @@ -156,9 +156,9 @@ void CTestOutputReader::processOutputLine(const QByteArray &outputLine) } } -TestResultPtr CTestOutputReader::createDefaultResult() const +TestResult CTestOutputReader::createDefaultResult() const { - return TestResultPtr(new CTestResult(id(), m_project, m_testName)); + return CTestResult(id(), m_project, m_testName); } void CTestOutputReader::sendCompleteInformation() @@ -168,10 +168,9 @@ void CTestOutputReader::sendCompleteInformation() QTC_CHECK(m_currentTestNo == -1 && m_testName.isEmpty()); return; } - - TestResultPtr testResult = createDefaultResult(); - testResult->setResult(m_result); - testResult->setDescription(m_description); + TestResult testResult = createDefaultResult(); + testResult.setResult(m_result); + testResult.setDescription(m_description); reportResult(testResult); m_testName.clear(); m_description.clear(); diff --git a/src/plugins/autotest/ctest/ctestoutputreader.h b/src/plugins/autotest/ctest/ctestoutputreader.h index 7c330fac133..8a6e1f124e4 100644 --- a/src/plugins/autotest/ctest/ctestoutputreader.h +++ b/src/plugins/autotest/ctest/ctestoutputreader.h @@ -13,12 +13,12 @@ namespace Internal { class CTestOutputReader final : public Autotest::TestOutputReader { public: - CTestOutputReader(const QFutureInterface &futureInterface, + CTestOutputReader(const QFutureInterface &futureInterface, Utils::QtcProcess *testApplication, const Utils::FilePath &buildDirectory); protected: void processOutputLine(const QByteArray &outputLineWithNewLine) final; - TestResultPtr createDefaultResult() const final; + TestResult createDefaultResult() const final; private: void sendCompleteInformation(); int m_currentTestNo = -1; diff --git a/src/plugins/autotest/ctest/ctesttool.cpp b/src/plugins/autotest/ctest/ctesttool.cpp index ac60f8134ac..fd2fe675282 100644 --- a/src/plugins/autotest/ctest/ctesttool.cpp +++ b/src/plugins/autotest/ctest/ctesttool.cpp @@ -36,9 +36,7 @@ QString CTestTool::displayName() const ITestTreeItem *CTestTool::createRootNode() { - return new CTestTreeItem(this, - displayName(), - Utils::FilePath(), ITestTreeItem::Root); + return new CTestTreeItem(this, displayName(), {}, ITestTreeItem::Root); } } // namespace Internal diff --git a/src/plugins/autotest/ctest/ctesttreeitem.cpp b/src/plugins/autotest/ctest/ctesttreeitem.cpp index 186b5e58529..3e59c2db81c 100644 --- a/src/plugins/autotest/ctest/ctesttreeitem.cpp +++ b/src/plugins/autotest/ctest/ctesttreeitem.cpp @@ -20,11 +20,13 @@ #include #include +using namespace Utils; + namespace Autotest { namespace Internal { CTestTreeItem::CTestTreeItem(ITestBase *testBase, const QString &name, - const Utils::FilePath &filepath, Type type) + const FilePath &filepath, Type type) : ITestTreeItem(testBase, name, filepath, type) { } @@ -69,7 +71,7 @@ QVariant CTestTreeItem::data(int column, int role) const return checked(); if (role == LinkRole) { QVariant itemLink; - itemLink.setValue(Utils::Link(filePath(), line())); + itemLink.setValue(Link(filePath(), line())); return itemLink; } return ITestTreeItem::data(column, role); @@ -89,7 +91,7 @@ QList CTestTreeItem::testConfigurationsFor(const QStringLi QStringList options{"--timeout", QString::number(AutotestPlugin::settings()->timeout / 1000)}; auto ctestSettings = static_cast(testBase()->testSettings()); options << ctestSettings->activeSettingsAsOptions(); - Utils::CommandLine command = buildSystem->commandLineForTests(selected, options); + CommandLine command = buildSystem->commandLineForTests(selected, options); if (command.executable().isEmpty()) return {}; @@ -97,12 +99,12 @@ QList CTestTreeItem::testConfigurationsFor(const QStringLi config->setProject(project); config->setCommandLine(command); const ProjectExplorer::RunConfiguration *runConfig = target->activeRunConfiguration(); - Utils::Environment env = Utils::Environment::systemEnvironment(); + Environment env = Environment::systemEnvironment(); if (QTC_GUARD(runConfig)) { if (auto envAspect = runConfig->aspect()) env = envAspect->environment(); } - if (Utils::HostOsInfo::isWindowsHost()) { + if (HostOsInfo::isWindowsHost()) { env.set("QT_FORCE_STDERR_LOGGING", "1"); env.set("QT_LOGGING_TO_CONSOLE", "1"); } diff --git a/src/plugins/autotest/gtest/gtestconfiguration.cpp b/src/plugins/autotest/gtest/gtestconfiguration.cpp index c78c09c5d31..d2f869d7645 100644 --- a/src/plugins/autotest/gtest/gtestconfiguration.cpp +++ b/src/plugins/autotest/gtest/gtestconfiguration.cpp @@ -12,11 +12,13 @@ #include #include +using namespace Utils; + namespace Autotest { namespace Internal { -TestOutputReader *GTestConfiguration::createOutputReader(const QFutureInterface &fi, - Utils::QtcProcess *app) const +TestOutputReader *GTestConfiguration::createOutputReader(const QFutureInterface &fi, + QtcProcess *app) const { return new GTestOutputReader(fi, app, buildDirectory(), projectFile()); } @@ -88,13 +90,13 @@ QStringList GTestConfiguration::argumentsForTestRunner(QStringList *omitted) con return arguments; } -Utils::Environment GTestConfiguration::filteredEnvironment(const Utils::Environment &original) const +Environment GTestConfiguration::filteredEnvironment(const Environment &original) const { const QStringList interfering{"GTEST_FILTER", "GTEST_ALSO_RUN_DISABLED_TESTS", "GTEST_REPEAT", "GTEST_SHUFFLE", "GTEST_RANDOM_SEED", "GTEST_OUTPUT", "GTEST_BREAK_ON_FAILURE", "GTEST_PRINT_TIME", "GTEST_CATCH_EXCEPTIONS"}; - Utils::Environment result = original; + Environment result = original; if (!result.hasKey("GTEST_COLOR")) result.set("GTEST_COLOR", "1"); // use colored output by default for (const QString &key : interfering) diff --git a/src/plugins/autotest/gtest/gtestconfiguration.h b/src/plugins/autotest/gtest/gtestconfiguration.h index 786df2467d4..97bcbd654fc 100644 --- a/src/plugins/autotest/gtest/gtestconfiguration.h +++ b/src/plugins/autotest/gtest/gtestconfiguration.h @@ -14,7 +14,7 @@ public: explicit GTestConfiguration(ITestFramework *framework) : DebuggableTestConfiguration(framework) {} - TestOutputReader *createOutputReader(const QFutureInterface &fi, + TestOutputReader *createOutputReader(const QFutureInterface &fi, Utils::QtcProcess *app) const override; QStringList argumentsForTestRunner(QStringList *omitted = nullptr) const override; Utils::Environment filteredEnvironment(const Utils::Environment &original) const override; diff --git a/src/plugins/autotest/gtest/gtestframework.cpp b/src/plugins/autotest/gtest/gtestframework.cpp index 1db400b2a06..cbfd29b66df 100644 --- a/src/plugins/autotest/gtest/gtestframework.cpp +++ b/src/plugins/autotest/gtest/gtestframework.cpp @@ -25,10 +25,7 @@ ITestParser *GTestFramework::createTestParser() ITestTreeItem *GTestFramework::createRootNode() { - return new GTestTreeItem( - this, - displayName(), - Utils::FilePath(), ITestTreeItem::Root); + return new GTestTreeItem(this, displayName(), {}, ITestTreeItem::Root); } const char *GTestFramework::name() const diff --git a/src/plugins/autotest/gtest/gtestoutputreader.cpp b/src/plugins/autotest/gtest/gtestoutputreader.cpp index 19d977e1af0..4ad273063e9 100644 --- a/src/plugins/autotest/gtest/gtestoutputreader.cpp +++ b/src/plugins/autotest/gtest/gtestoutputreader.cpp @@ -12,25 +12,27 @@ #include +using namespace Utils; + namespace Autotest { namespace Internal { -GTestOutputReader::GTestOutputReader(const QFutureInterface &futureInterface, - Utils::QtcProcess *testApplication, - const Utils::FilePath &buildDirectory, - const Utils::FilePath &projectFile) +GTestOutputReader::GTestOutputReader(const QFutureInterface &futureInterface, + QtcProcess *testApplication, + const FilePath &buildDirectory, + const FilePath &projectFile) : TestOutputReader(futureInterface, testApplication, buildDirectory) , m_projectFile(projectFile) { - if (m_testApplication) { - connect(m_testApplication, &Utils::QtcProcess::done, this, [this] { - const int exitCode = m_testApplication->exitCode(); + if (testApplication) { + connect(testApplication, &QtcProcess::done, this, [this, testApplication] { + const int exitCode = testApplication->exitCode(); if (exitCode == 1 && !m_description.isEmpty()) { createAndReportResult(Tr::tr("Running tests failed.\n %1\nExecutable: %2") .arg(m_description).arg(id()), ResultType::MessageFatal); } // on Windows abort() will result in normal termination, but exit code will be set to 3 - if (Utils::HostOsInfo::isWindowsHost() && exitCode == 3) + if (HostOsInfo::isWindowsHost() && exitCode == 3) reportCrash(); }); } @@ -70,9 +72,9 @@ void GTestOutputReader::processOutputLine(const QByteArray &outputLine) m_description = line; if (m_iteration > 1) m_description.append(' ' + Tr::tr("(iteration %1)").arg(m_iteration)); - TestResultPtr testResult = TestResultPtr(new GTestResult(id(), {}, m_projectFile)); - testResult->setResult(ResultType::MessageInternal); - testResult->setDescription(m_description); + GTestResult testResult(id(), {}, m_projectFile); + testResult.setResult(ResultType::MessageInternal); + testResult.setDescription(m_description); reportResult(testResult); m_description.clear(); } else if (ExactMatch match = disabledTests.match(line)) { @@ -83,66 +85,66 @@ void GTestOutputReader::processOutputLine(const QByteArray &outputLine) } if (ExactMatch match = testEnds.match(line)) { - TestResultPtr testResult = createDefaultResult(); - testResult->setResult(ResultType::TestEnd); - testResult->setDescription(Tr::tr("Test execution took %1").arg(match.captured(2))); + TestResult testResult = createDefaultResult(); + testResult.setResult(ResultType::TestEnd); + testResult.setDescription(Tr::tr("Test execution took %1").arg(match.captured(2))); reportResult(testResult); m_currentTestSuite.clear(); m_currentTestCase.clear(); } else if (ExactMatch match = newTestStarts.match(line)) { setCurrentTestSuite(match.captured(1)); - TestResultPtr testResult = createDefaultResult(); - testResult->setResult(ResultType::TestStart); + TestResult testResult = createDefaultResult(); + testResult.setResult(ResultType::TestStart); if (m_iteration > 1) { - testResult->setDescription(Tr::tr("Repeating test suite %1 (iteration %2)") + testResult.setDescription(Tr::tr("Repeating test suite %1 (iteration %2)") .arg(m_currentTestSuite).arg(m_iteration)); } else { - testResult->setDescription(Tr::tr("Executing test suite %1").arg(m_currentTestSuite)); + testResult.setDescription(Tr::tr("Executing test suite %1").arg(m_currentTestSuite)); } reportResult(testResult); } else if (ExactMatch match = newTestSetStarts.match(line)) { m_testSetStarted = true; setCurrentTestCase(match.captured(1)); - TestResultPtr testResult = TestResultPtr(new GTestResult({}, {}, m_projectFile)); - testResult->setResult(ResultType::MessageCurrentTest); - testResult->setDescription(Tr::tr("Entering test case %1").arg(m_currentTestCase)); + GTestResult testResult("internal", {}, m_projectFile); + testResult.setResult(ResultType::MessageCurrentTest); + testResult.setDescription(Tr::tr("Entering test case %1").arg(m_currentTestCase)); reportResult(testResult); m_description.clear(); } else if (ExactMatch match = testSetSuccess.match(line)) { m_testSetStarted = false; - TestResultPtr testResult = createDefaultResult(); - testResult->setResult(ResultType::Pass); - testResult->setDescription(m_description); + TestResult testResult = createDefaultResult(); + testResult.setResult(ResultType::Pass); + testResult.setDescription(m_description); reportResult(testResult); m_description.clear(); testResult = createDefaultResult(); - testResult->setResult(ResultType::MessageInternal); - testResult->setDescription(Tr::tr("Execution took %1.").arg(match.captured(2))); + testResult.setResult(ResultType::MessageInternal); + testResult.setDescription(Tr::tr("Execution took %1.").arg(match.captured(2))); reportResult(testResult); m_futureInterface.setProgressValue(m_futureInterface.progressValue() + 1); } else if (ExactMatch match = testSetFail.match(line)) { m_testSetStarted = false; - TestResultPtr testResult = createDefaultResult(); - testResult->setResult(ResultType::Fail); + TestResult testResult = createDefaultResult(); + testResult.setResult(ResultType::Fail); m_description.chop(1); handleDescriptionAndReportResult(testResult); testResult = createDefaultResult(); - testResult->setResult(ResultType::MessageInternal); - testResult->setDescription(Tr::tr("Execution took %1.").arg(match.captured(2))); + testResult.setResult(ResultType::MessageInternal); + testResult.setDescription(Tr::tr("Execution took %1.").arg(match.captured(2))); reportResult(testResult); m_futureInterface.setProgressValue(m_futureInterface.progressValue() + 1); } else if (ExactMatch match = testSetSkipped.match(line)) { if (!m_testSetStarted) // ignore SKIPPED at summary return; m_testSetStarted = false; - TestResultPtr testResult = createDefaultResult(); - testResult->setResult(ResultType::Skip); + TestResult testResult = createDefaultResult(); + testResult.setResult(ResultType::Skip); m_description.chop(1); m_description.prepend(match.captured(1) + '\n'); handleDescriptionAndReportResult(testResult); testResult = createDefaultResult(); - testResult->setResult(ResultType::MessageInternal); - testResult->setDescription(Tr::tr("Execution took %1.").arg(match.captured(2))); + testResult.setResult(ResultType::MessageInternal); + testResult.setDescription(Tr::tr("Execution took %1.").arg(match.captured(2))); reportResult(testResult); } else if (ExactMatch match = logging.match(line)) { const QString severity = match.captured(1).trimmed(); @@ -153,13 +155,13 @@ void GTestOutputReader::processOutputLine(const QByteArray &outputLine) case 'E': type = ResultType::MessageError; break; // ERROR case 'F': type = ResultType::MessageFatal; break; // FATAL } - TestResultPtr testResult = createDefaultResult(); - testResult->setResult(type); - testResult->setLine(match.captured(3).toInt()); - const Utils::FilePath file = constructSourceFilePath(m_buildDir, match.captured(2)); + TestResult testResult = createDefaultResult(); + testResult.setResult(type); + testResult.setLine(match.captured(3).toInt()); + const FilePath file = constructSourceFilePath(m_buildDir, match.captured(2)); if (file.exists()) - testResult->setFileName(file); - testResult->setDescription(match.captured(4)); + testResult.setFileName(file); + testResult.setDescription(match.captured(4)); reportResult(testResult); } else if (ExactMatch match = testDeath.match(line)) { m_description.append(line); @@ -175,19 +177,15 @@ void GTestOutputReader::processStdError(const QByteArray &outputLine) emit newOutputLineAvailable(outputLine, OutputChannel::StdErr); } -TestResultPtr GTestOutputReader::createDefaultResult() const +TestResult GTestOutputReader::createDefaultResult() const { - GTestResult *result = new GTestResult(id(), m_currentTestSuite, m_projectFile, - m_currentTestCase, m_iteration); - - const ITestTreeItem *testItem = result->findTestTreeItem(); - + GTestResult result(id(), m_currentTestSuite, m_projectFile, m_currentTestCase, m_iteration); + const ITestTreeItem *testItem = result.findTestTreeItem(); if (testItem && testItem->line()) { - result->setFileName(testItem->filePath()); - result->setLine(testItem->line()); + result.setFileName(testItem->filePath()); + result.setLine(testItem->line()); } - - return TestResultPtr(result); + return result; } void GTestOutputReader::setCurrentTestCase(const QString &testCase) @@ -200,13 +198,13 @@ void GTestOutputReader::setCurrentTestSuite(const QString &testSuite) m_currentTestSuite = testSuite; } -void GTestOutputReader::handleDescriptionAndReportResult(TestResultPtr testResult) +void GTestOutputReader::handleDescriptionAndReportResult(const TestResult &testResult) { static const QRegularExpression failureLocation("^(.*):(\\d+): Failure$"); static const QRegularExpression skipOrErrorLocation("^(.*)\\((\\d+)\\): (Skipped|error:.*)$"); QStringList resultDescription; - + TestResult result = testResult; for (const QString &output : m_description.split('\n')) { QRegularExpressionMatch innerMatch = failureLocation.match(output); if (!innerMatch.hasMatch()) { @@ -216,20 +214,20 @@ void GTestOutputReader::handleDescriptionAndReportResult(TestResultPtr testResul continue; } } - testResult->setDescription(resultDescription.join('\n')); + result.setDescription(resultDescription.join('\n')); reportResult(testResult); resultDescription.clear(); - testResult = createDefaultResult(); - testResult->setResult(ResultType::MessageLocation); - testResult->setLine(innerMatch.captured(2).toInt()); - const Utils::FilePath file = constructSourceFilePath(m_buildDir, innerMatch.captured(1)); + result = createDefaultResult(); + result.setResult(ResultType::MessageLocation); + result.setLine(innerMatch.captured(2).toInt()); + const FilePath file = constructSourceFilePath(m_buildDir, innerMatch.captured(1)); if (file.exists()) - testResult->setFileName(file); + result.setFileName(file); resultDescription << output; } - testResult->setDescription(resultDescription.join('\n')); - reportResult(testResult); + result.setDescription(resultDescription.join('\n')); + reportResult(result); m_description.clear(); } diff --git a/src/plugins/autotest/gtest/gtestoutputreader.h b/src/plugins/autotest/gtest/gtestoutputreader.h index 64de29c73a3..e73ab0e8236 100644 --- a/src/plugins/autotest/gtest/gtestoutputreader.h +++ b/src/plugins/autotest/gtest/gtestoutputreader.h @@ -11,18 +11,18 @@ namespace Internal { class GTestOutputReader : public TestOutputReader { public: - GTestOutputReader(const QFutureInterface &futureInterface, + GTestOutputReader(const QFutureInterface &futureInterface, Utils::QtcProcess *testApplication, const Utils::FilePath &buildDirectory, const Utils::FilePath &projectFile); protected: void processOutputLine(const QByteArray &outputLine) override; void processStdError(const QByteArray &outputLine) override; - TestResultPtr createDefaultResult() const override; + TestResult createDefaultResult() const override; private: void setCurrentTestCase(const QString &testCase); void setCurrentTestSuite(const QString &testSuite); - void handleDescriptionAndReportResult(TestResultPtr testResult); + void handleDescriptionAndReportResult(const TestResult &testResult); Utils::FilePath m_projectFile; QString m_currentTestSuite; diff --git a/src/plugins/autotest/gtest/gtestparser.cpp b/src/plugins/autotest/gtest/gtestparser.cpp index 5c0d308c91e..aa59bb94c2e 100644 --- a/src/plugins/autotest/gtest/gtestparser.cpp +++ b/src/plugins/autotest/gtest/gtestparser.cpp @@ -71,7 +71,7 @@ static bool hasGTestNames(const CPlusPlus::Document::Ptr &document) } bool GTestParser::processDocument(QFutureInterface &futureInterface, - const Utils::FilePath &fileName) + const FilePath &fileName) { CPlusPlus::Document::Ptr doc = document(fileName); if (doc.isNull() || !includesGTest(doc, m_cppSnapshot)) @@ -93,10 +93,10 @@ bool GTestParser::processDocument(QFutureInterface &futureIn visitor.accept(ast); const QMap result = visitor.gtestFunctions(); - Utils::FilePath proFile; + FilePath proFile; const QList &ppList = modelManager->projectPart(filePath); if (!ppList.isEmpty()) - proFile = Utils::FilePath::fromString(ppList.first()->projectFile); + proFile = FilePath::fromString(ppList.first()->projectFile); else return false; // happens if shutting down while parsing diff --git a/src/plugins/autotest/gtest/gtestsettings.cpp b/src/plugins/autotest/gtest/gtestsettings.cpp index d855060cd96..babea83f78a 100644 --- a/src/plugins/autotest/gtest/gtestsettings.cpp +++ b/src/plugins/autotest/gtest/gtestsettings.cpp @@ -30,7 +30,7 @@ GTestSettings::GTestSettings() registerAspect(&seed); seed.setSettingsKey("Seed"); - seed.setSpecialValueText(QString()); + seed.setSpecialValueText({}); seed.setEnabled(false); seed.setLabelText(Tr::tr("Seed:")); seed.setToolTip(Tr::tr("A seed of 0 generates a seed based on the current timestamp.")); @@ -106,7 +106,7 @@ GTestSettings::GTestSettings() }); } -GTestSettingsPage::GTestSettingsPage(GTestSettings *settings, Utils::Id settingsId) +GTestSettingsPage::GTestSettingsPage(GTestSettings *settings, Id settingsId) { setId(settingsId); setCategory(Constants::AUTOTEST_SETTINGS_CATEGORY); diff --git a/src/plugins/autotest/gtest/gtesttreeitem.cpp b/src/plugins/autotest/gtest/gtesttreeitem.cpp index b6cdb714fed..c79ae93d1b9 100644 --- a/src/plugins/autotest/gtest/gtesttreeitem.cpp +++ b/src/plugins/autotest/gtest/gtesttreeitem.cpp @@ -20,6 +20,8 @@ #include +using namespace Utils; + namespace Autotest { namespace Internal { @@ -110,8 +112,8 @@ QVariant GTestTreeItem::data(int column, int role) const case Qt::DecorationRole: if (type() == GroupNode && GTestFramework::groupMode() == GTest::Constants::GTestFilter) { - static const QIcon filterIcon = Utils::Icon({{":/utils/images/filtericon.png", - Utils::Theme::PanelTextColorMid}}).icon(); + static const QIcon filterIcon = Icon({{":/utils/images/filtericon.png", + Theme::PanelTextColorMid}}).icon(); return filterIcon; } break; @@ -195,7 +197,7 @@ struct GTestCases }; static void collectTestInfo(const GTestTreeItem *item, - QHash &testCasesForProFile, + QHash &testCasesForProFile, bool ignoreCheckState) { QTC_ASSERT(item, return); @@ -210,7 +212,7 @@ static void collectTestInfo(const GTestTreeItem *item, QTC_ASSERT(childCount != 0, return); QTC_ASSERT(item->type() == TestTreeItem::TestSuite, return); if (ignoreCheckState || item->checked() == Qt::Checked) { - const Utils::FilePath &projectFile = item->childItem(0)->proFile(); + const FilePath &projectFile = item->childItem(0)->proFile(); testCasesForProFile[projectFile].filters.append( gtestFilter(item->state()).arg(item->name()).arg('*')); testCasesForProFile[projectFile].testSetCount += childCount - 1; @@ -229,7 +231,7 @@ static void collectTestInfo(const GTestTreeItem *item, } static void collectFailedTestInfo(const GTestTreeItem *item, - QHash &testCasesForProfile) + QHash &testCasesForProfile) { QTC_ASSERT(item, return); QTC_ASSERT(item->type() == TestTreeItem::Root, return); @@ -254,7 +256,7 @@ QList GTestTreeItem::getTestConfigurations(bool ignoreChec if (!project || type() != Root) return result; - QHash testCasesForProFile; + QHash testCasesForProFile; for (int row = 0, count = childCount(); row < count; ++row) { auto child = static_cast(childAt(row)); collectTestInfo(child, testCasesForProFile, ignoreCheckState); @@ -293,7 +295,7 @@ QList GTestTreeItem::getFailedTestConfigurations() const if (!project || type() != Root) return result; - QHash testCasesForProFile; + QHash testCasesForProFile; collectFailedTestInfo(this, testCasesForProFile); for (auto it = testCasesForProFile.begin(), end = testCasesForProFile.end(); it != end; ++it) { @@ -311,14 +313,14 @@ QList GTestTreeItem::getFailedTestConfigurations() const return result; } -QList GTestTreeItem::getTestConfigurationsForFile(const Utils::FilePath &fileName) const +QList GTestTreeItem::getTestConfigurationsForFile(const FilePath &fileName) const { QList result; ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject(); if (!project || type() != Root) return result; - QHash testCases; + QHash testCases; forAllChildItems([&testCases, &fileName](TestTreeItem *node) { if (node->type() == Type::TestCase && node->filePath() == fileName) { QTC_ASSERT(node->parentItem(), return); @@ -358,7 +360,7 @@ TestTreeItem *GTestTreeItem::find(const TestParseResult *result) case Root: if (result->framework->grouping()) { if (GTestFramework::groupMode() == GTest::Constants::Directory) { - const Utils::FilePath base = parseResult->fileName.absolutePath(); + const FilePath base = parseResult->fileName.absolutePath(); for (int row = 0; row < childCount(); ++row) { GTestTreeItem *group = static_cast(childAt(row)); if (group->filePath() != base.absoluteFilePath()) @@ -442,7 +444,7 @@ bool GTestTreeItem::modify(const TestParseResult *result) TestTreeItem *GTestTreeItem::createParentGroupNode() const { if (GTestFramework::groupMode() == GTest::Constants::Directory) { - const Utils::FilePath &absPath = filePath().absolutePath(); + const FilePath &absPath = filePath().absolutePath(); return new GTestTreeItem(framework(), absPath.baseName(), absPath, TestTreeItem::GroupNode); } else { // GTestFilter QTC_ASSERT(childCount(), return nullptr); // paranoia @@ -453,7 +455,7 @@ TestTreeItem *GTestTreeItem::createParentGroupNode() const matchesFilter(activeFilter, fullTestName) ? matchingString() : notMatchingString(); // FIXME activeFilter is not a FilePath auto groupNode = new GTestTreeItem(framework(), groupNodeName, - Utils::FilePath::fromString(activeFilter), + FilePath::fromString(activeFilter), TestTreeItem::GroupNode); if (groupNodeName == notMatchingString()) groupNode->setData(0, Qt::Unchecked, Qt::CheckStateRole); @@ -475,7 +477,7 @@ bool GTestTreeItem::modifyTestSetContent(const GTestParseResult *result) TestTreeItem *GTestTreeItem::findChildByNameStateAndFile(const QString &name, GTestTreeItem::TestStates state, - const Utils::FilePath &proFile) const + const FilePath &proFile) const { return findFirstLevelChildItem([name, state, proFile](const TestTreeItem *other) { const GTestTreeItem *gtestItem = static_cast(other); @@ -504,13 +506,13 @@ QSet internalTargets(const TestTreeItem &item) const auto projectInfo = cppMM->projectInfo(ProjectExplorer::SessionManager::startupProject()); if (!projectInfo) return {}; - const Utils::FilePath filePath = item.filePath(); + const FilePath filePath = item.filePath(); const QVector projectParts = projectInfo->projectParts(); if (projectParts.isEmpty()) return cppMM->dependingInternalTargets(item.filePath()); for (const CppEditor::ProjectPart::ConstPtr &projectPart : projectParts) { - if (Utils::FilePath::fromString(projectPart->projectFile) == item.proFile() - && Utils::anyOf(projectPart->files, [&filePath] (const CppEditor::ProjectFile &pf) { + if (FilePath::fromString(projectPart->projectFile) == item.proFile() + && Utils::anyOf(projectPart->files, [&filePath](const CppEditor::ProjectFile &pf) { return pf.path == filePath; })) { result.insert(projectPart->buildSystemTarget); diff --git a/src/plugins/autotest/gtest/gtesttreeitem.h b/src/plugins/autotest/gtest/gtesttreeitem.h index 3ea710bc99a..19e2448cbd9 100644 --- a/src/plugins/autotest/gtest/gtesttreeitem.h +++ b/src/plugins/autotest/gtest/gtesttreeitem.h @@ -24,10 +24,8 @@ public: Q_FLAGS(TestState) Q_DECLARE_FLAGS(TestStates, TestState) - explicit GTestTreeItem(ITestFramework *testFramework, - const QString &name = QString(), - const Utils::FilePath &filePath = Utils::FilePath(), - Type type = Root) + explicit GTestTreeItem(ITestFramework *testFramework, const QString &name = {}, + const Utils::FilePath &filePath = {}, Type type = Root) : TestTreeItem(testFramework, name, filePath, type), m_state(Enabled) {} diff --git a/src/plugins/autotest/itestframework.cpp b/src/plugins/autotest/itestframework.cpp index bc4c98e9989..093bb42aaaf 100644 --- a/src/plugins/autotest/itestframework.cpp +++ b/src/plugins/autotest/itestframework.cpp @@ -8,6 +8,8 @@ #include "testtreeitem.h" #include "testtreemodel.h" +using namespace Utils; + namespace Autotest { ITestBase::ITestBase(bool activeByDefault, const ITestBase::TestBaseType type) @@ -15,15 +17,15 @@ ITestBase::ITestBase(bool activeByDefault, const ITestBase::TestBaseType type) , m_type(type) {} -Utils::Id ITestBase::settingsId() const +Id ITestBase::settingsId() const { - return Utils::Id(Constants::SETTINGSPAGE_PREFIX) + return Id(Constants::SETTINGSPAGE_PREFIX) .withSuffix(QString("%1.%2").arg(priority()).arg(QLatin1String(name()))); } -Utils::Id ITestBase::id() const +Id ITestBase::id() const { - return Utils::Id(Constants::FRAMEWORK_PREFIX).withSuffix(name()); + return Id(Constants::FRAMEWORK_PREFIX).withSuffix(name()); } void ITestBase::resetRootNode() diff --git a/src/plugins/autotest/itestframework.h b/src/plugins/autotest/itestframework.h index 0e308790ec4..a2570eb6170 100644 --- a/src/plugins/autotest/itestframework.h +++ b/src/plugins/autotest/itestframework.h @@ -72,7 +72,7 @@ public: bool grouping() const { return m_grouping; } void setGrouping(bool group) { m_grouping = group; } // framework specific tool tip to be displayed on the general settings page - virtual QString groupingToolTip() const { return QString(); } + virtual QString groupingToolTip() const { return {}; } ITestFramework *asFramework() final { return this; } diff --git a/src/plugins/autotest/itestparser.cpp b/src/plugins/autotest/itestparser.cpp index 9e9f8f17d63..d8f1d6953b3 100644 --- a/src/plugins/autotest/itestparser.cpp +++ b/src/plugins/autotest/itestparser.cpp @@ -24,7 +24,7 @@ CppParser::CppParser(ITestFramework *framework) { } -void CppParser::init(const Utils::FilePaths &filesToParse, bool fullParse) +void CppParser::init(const FilePaths &filesToParse, bool fullParse) { Q_UNUSED(filesToParse) Q_UNUSED(fullParse) @@ -32,7 +32,7 @@ void CppParser::init(const Utils::FilePaths &filesToParse, bool fullParse) m_workingCopy = CppEditor::CppModelManager::instance()->workingCopy(); } -bool CppParser::selectedForBuilding(const Utils::FilePath &fileName) +bool CppParser::selectedForBuilding(const FilePath &fileName) { QList projParts = CppEditor::CppModelManager::instance()->projectPart(fileName); @@ -40,7 +40,7 @@ bool CppParser::selectedForBuilding(const Utils::FilePath &fileName) return !projParts.isEmpty() && projParts.at(0)->selectedForBuilding; } -QByteArray CppParser::getFileContent(const Utils::FilePath &filePath) const +QByteArray CppParser::getFileContent(const FilePath &filePath) const { QByteArray fileContent; if (m_workingCopy.contains(filePath)) { @@ -48,8 +48,8 @@ QByteArray CppParser::getFileContent(const Utils::FilePath &filePath) const } else { QString error; const QTextCodec *codec = Core::EditorManager::defaultTextCodec(); - if (Utils::TextFileFormat::readFileUTF8(filePath, codec, &fileContent, &error) - != Utils::TextFileFormat::ReadSuccess) { + if (TextFileFormat::readFileUTF8(filePath, codec, &fileContent, &error) + != TextFileFormat::ReadSuccess) { qDebug() << "Failed to read file" << filePath << ":" << error; } } @@ -58,7 +58,7 @@ QByteArray CppParser::getFileContent(const Utils::FilePath &filePath) const } bool precompiledHeaderContains(const CPlusPlus::Snapshot &snapshot, - const Utils::FilePath &filePath, + const FilePath &filePath, const QString &cacheString, const std::function &checker) { @@ -82,7 +82,7 @@ bool precompiledHeaderContains(const CPlusPlus::Snapshot &snapshot, } bool CppParser::precompiledHeaderContains(const CPlusPlus::Snapshot &snapshot, - const Utils::FilePath &filePath, + const FilePath &filePath, const QString &headerFilePath) { return Autotest::precompiledHeaderContains(snapshot, @@ -94,7 +94,7 @@ bool CppParser::precompiledHeaderContains(const CPlusPlus::Snapshot &snapshot, } bool CppParser::precompiledHeaderContains(const CPlusPlus::Snapshot &snapshot, - const Utils::FilePath &filePath, + const FilePath &filePath, const QRegularExpression &headerFileRegex) { return Autotest::precompiledHeaderContains(snapshot, @@ -113,7 +113,7 @@ void CppParser::release() s_pchLookupCache.clear(); } -CPlusPlus::Document::Ptr CppParser::document(const Utils::FilePath &fileName) +CPlusPlus::Document::Ptr CppParser::document(const FilePath &fileName) { return selectedForBuilding(fileName) ? m_cppSnapshot.document(fileName) : nullptr; } diff --git a/src/plugins/autotest/qtest/qttest_utils.cpp b/src/plugins/autotest/qtest/qttest_utils.cpp index 5f16776b2e2..1069280bee1 100644 --- a/src/plugins/autotest/qtest/qttest_utils.cpp +++ b/src/plugins/autotest/qtest/qttest_utils.cpp @@ -14,6 +14,8 @@ #include #include +using namespace Utils; + namespace Autotest { namespace Internal { namespace QTestUtils { @@ -25,10 +27,9 @@ bool isQTestMacro(const QByteArray ¯o) return valid.contains(macro); } -QHash testCaseNamesForFiles(ITestFramework *framework, - const Utils::FilePaths &files) +QHash testCaseNamesForFiles(ITestFramework *framework, const FilePaths &files) { - QHash result; + QHash result; TestTreeItem *rootNode = framework->rootNode(); QTC_ASSERT(rootNode, return result); @@ -48,18 +49,17 @@ QHash testCaseNamesForFiles(ITestFramework *framewor return result; } -QMultiHash alternativeFiles(ITestFramework *framework, - const Utils::FilePaths &files) +QMultiHash alternativeFiles(ITestFramework *framework, const FilePaths &files) { - QMultiHash result; + QMultiHash result; TestTreeItem *rootNode = framework->rootNode(); QTC_ASSERT(rootNode, return result); rootNode->forFirstLevelChildren([&result, &files](ITestTreeItem *child) { - const Utils::FilePath &baseFilePath = child->filePath(); + const FilePath &baseFilePath = child->filePath(); for (int childRow = 0, count = child->childCount(); childRow < count; ++childRow) { auto grandChild = static_cast(child->childAt(childRow)); - const Utils::FilePath &filePath = grandChild->filePath(); + const FilePath &filePath = grandChild->filePath(); if (grandChild->inherited() && baseFilePath != filePath && files.contains(filePath)) { if (!result.contains(filePath, baseFilePath)) result.insert(filePath, baseFilePath); @@ -128,10 +128,10 @@ QStringList filterInterfering(const QStringList &provided, QStringList *omitted, return allowed; } -Utils::Environment prepareBasicEnvironment(const Utils::Environment &env) +Environment prepareBasicEnvironment(const Environment &env) { - Utils::Environment result(env); - if (Utils::HostOsInfo::isWindowsHost()) { + Environment result(env); + if (HostOsInfo::isWindowsHost()) { result.set("QT_FORCE_STDERR_LOGGING", "1"); result.set("QT_LOGGING_TO_CONSOLE", "1"); } diff --git a/src/plugins/autotest/qtest/qttestconfiguration.cpp b/src/plugins/autotest/qtest/qttestconfiguration.cpp index 98f010187a2..8a77f305e34 100644 --- a/src/plugins/autotest/qtest/qttestconfiguration.cpp +++ b/src/plugins/autotest/qtest/qttestconfiguration.cpp @@ -13,6 +13,8 @@ #include #include +using namespace Utils; + namespace Autotest { namespace Internal { @@ -26,8 +28,8 @@ static QStringList quoteIfNeeded(const QStringList &testCases, bool debugMode) }); } -TestOutputReader *QtTestConfiguration::createOutputReader(const QFutureInterface &fi, - Utils::QtcProcess *app) const +TestOutputReader *QtTestConfiguration::createOutputReader(const QFutureInterface &fi, + QtcProcess *app) const { auto qtSettings = static_cast(framework()->testSettings()); const QtTestOutputReader::OutputMode mode = qtSettings && qtSettings->useXMLOutput.value() @@ -71,7 +73,7 @@ QStringList QtTestConfiguration::argumentsForTestRunner(QStringList *omitted) co return arguments; } -Utils::Environment QtTestConfiguration::filteredEnvironment(const Utils::Environment &original) const +Environment QtTestConfiguration::filteredEnvironment(const Environment &original) const { return QTestUtils::prepareBasicEnvironment(original); } diff --git a/src/plugins/autotest/qtest/qttestconfiguration.h b/src/plugins/autotest/qtest/qttestconfiguration.h index b9800684c9b..a99c93f0b75 100644 --- a/src/plugins/autotest/qtest/qttestconfiguration.h +++ b/src/plugins/autotest/qtest/qttestconfiguration.h @@ -13,7 +13,7 @@ class QtTestConfiguration : public DebuggableTestConfiguration public: explicit QtTestConfiguration(ITestFramework *framework) : DebuggableTestConfiguration(framework) {} - TestOutputReader *createOutputReader(const QFutureInterface &fi, + TestOutputReader *createOutputReader(const QFutureInterface &fi, Utils::QtcProcess *app) const override; QStringList argumentsForTestRunner(QStringList *omitted = nullptr) const override; Utils::Environment filteredEnvironment(const Utils::Environment &original) const override; diff --git a/src/plugins/autotest/qtest/qttestframework.cpp b/src/plugins/autotest/qtest/qttestframework.cpp index db9323c7952..75a913ebdfe 100644 --- a/src/plugins/autotest/qtest/qttestframework.cpp +++ b/src/plugins/autotest/qtest/qttestframework.cpp @@ -18,10 +18,7 @@ ITestParser *QtTestFramework::createTestParser() ITestTreeItem *QtTestFramework::createRootNode() { - return new QtTestTreeItem( - this, - displayName(), - Utils::FilePath(), ITestTreeItem::Root); + return new QtTestTreeItem(this, displayName(), {}, ITestTreeItem::Root); } const char *QtTestFramework::name() const diff --git a/src/plugins/autotest/qtest/qttestoutputreader.cpp b/src/plugins/autotest/qtest/qttestoutputreader.cpp index c2d06042bdd..a93e74eea42 100644 --- a/src/plugins/autotest/qtest/qttestoutputreader.cpp +++ b/src/plugins/autotest/qtest/qttestoutputreader.cpp @@ -14,6 +14,8 @@ #include +using namespace Utils; + namespace Autotest { namespace Internal { @@ -103,10 +105,10 @@ static QString constructBenchmarkInformation(const QString &metric, double value .arg(iterations); } -QtTestOutputReader::QtTestOutputReader(const QFutureInterface &futureInterface, - Utils::QtcProcess *testApplication, - const Utils::FilePath &buildDirectory, - const Utils::FilePath &projectFile, +QtTestOutputReader::QtTestOutputReader(const QFutureInterface &futureInterface, + QtcProcess *testApplication, + const FilePath &buildDirectory, + const FilePath &projectFile, OutputMode mode, TestType type) : TestOutputReader(futureInterface, testApplication, buildDirectory) , m_projectFile(projectFile) @@ -130,11 +132,9 @@ void QtTestOutputReader::processOutputLine(const QByteArray &outputLine) } } -TestResultPtr QtTestOutputReader::createDefaultResult() const +TestResult QtTestOutputReader::createDefaultResult() const { - QtTestResult *result = new QtTestResult(id(), m_className, m_projectFile, m_testType, - m_testCase, m_dataTag); - return TestResultPtr(result); + return QtTestResult(id(), m_className, m_projectFile, m_testType, m_testCase, m_dataTag); } static QString trQtVersion(const QString &version) @@ -455,73 +455,75 @@ void QtTestOutputReader::processSummaryFinishOutput() void QtTestOutputReader::sendCompleteInformation() { - TestResultPtr testResult = createDefaultResult(); - testResult->setResult(m_result); + TestResult testResult = createDefaultResult(); + testResult.setResult(m_result); if (m_lineNumber) { - testResult->setFileName(m_file); - testResult->setLine(m_lineNumber); + testResult.setFileName(m_file); + testResult.setLine(m_lineNumber); } else { - const ITestTreeItem *testItem = testResult->findTestTreeItem(); + const ITestTreeItem *testItem = testResult.findTestTreeItem(); if (testItem && testItem->line()) { - testResult->setFileName(testItem->filePath()); - testResult->setLine(testItem->line()); + testResult.setFileName(testItem->filePath()); + testResult.setLine(testItem->line()); } } - testResult->setDescription(m_description); + testResult.setDescription(m_description); reportResult(testResult); } void QtTestOutputReader::sendMessageCurrentTest() { - QtTestResult *testResult = new QtTestResult({}, {}, m_projectFile, m_testType); - testResult->setResult(ResultType::MessageCurrentTest); - testResult->setDescription(Tr::tr("Entering test function %1::%2").arg(m_className, m_testCase)); - reportResult(TestResultPtr(testResult)); + QtTestResult result("internal", {}, m_projectFile, m_testType); + result.setResult(ResultType::MessageCurrentTest); + result.setDescription(Tr::tr("Entering test function %1::%2").arg(m_className, m_testCase)); + reportResult(result); } void QtTestOutputReader::sendStartMessage(bool isFunction) { - TestResultPtr testResult = createDefaultResult(); - testResult->setResult(ResultType::TestStart); - testResult->setDescription(isFunction ? Tr::tr("Executing test function %1").arg(m_testCase) - : Tr::tr("Executing test case %1").arg(m_className)); - const ITestTreeItem *testItem = testResult->findTestTreeItem(); + TestResult result = createDefaultResult(); + result.setResult(ResultType::TestStart); + result.setDescription(isFunction ? Tr::tr("Executing test function %1").arg(m_testCase) + : Tr::tr("Executing test case %1").arg(m_className)); + const ITestTreeItem *testItem = result.findTestTreeItem(); if (testItem && testItem->line()) { - testResult->setFileName(testItem->filePath()); - testResult->setLine(testItem->line()); + result.setFileName(testItem->filePath()); + result.setLine(testItem->line()); } - reportResult(testResult); + reportResult(result); } void QtTestOutputReader::sendFinishMessage(bool isFunction) { - TestResultPtr testResult = createDefaultResult(); - testResult->setResult(ResultType::TestEnd); + TestResult result = createDefaultResult(); + result.setResult(ResultType::TestEnd); if (!m_duration.isEmpty()) { - testResult->setDescription(isFunction ? Tr::tr("Execution took %1 ms.").arg(m_duration) - : Tr::tr("Test execution took %1 ms.").arg(m_duration)); + result.setDescription(isFunction ? Tr::tr("Execution took %1 ms.").arg(m_duration) + : Tr::tr("Test execution took %1 ms.").arg(m_duration)); } else { - testResult->setDescription(isFunction ? Tr::tr("Test function finished.") - : Tr::tr("Test finished.")); + result.setDescription(isFunction ? Tr::tr("Test function finished.") + : Tr::tr("Test finished.")); } - reportResult(testResult); + reportResult(result); } void QtTestOutputReader::handleAndSendConfigMessage(const QRegularExpressionMatch &config) { - TestResultPtr testResult = createDefaultResult(); - testResult->setResult(ResultType::MessageInternal); - testResult->setDescription(trQtVersion(config.captured(3))); - reportResult(testResult); - testResult = createDefaultResult(); - testResult->setResult(ResultType::MessageInternal); - testResult->setDescription(trQtBuild(config.captured(2))); - reportResult(testResult); - testResult = createDefaultResult(); - testResult->setResult(ResultType::MessageInternal); - testResult->setDescription(trQtestVersion(config.captured(1))); - reportResult(testResult); + TestResult result = createDefaultResult(); + result.setResult(ResultType::MessageInternal); + result.setDescription(trQtVersion(config.captured(3))); + reportResult(result); + + result = createDefaultResult(); + result.setResult(ResultType::MessageInternal); + result.setDescription(trQtBuild(config.captured(2))); + reportResult(result); + + result = createDefaultResult(); + result.setResult(ResultType::MessageInternal); + result.setDescription(trQtestVersion(config.captured(1))); + reportResult(result); } } // namespace Internal diff --git a/src/plugins/autotest/qtest/qttestoutputreader.h b/src/plugins/autotest/qtest/qttestoutputreader.h index 1995c816f36..6db9aa87810 100644 --- a/src/plugins/autotest/qtest/qttestoutputreader.h +++ b/src/plugins/autotest/qtest/qttestoutputreader.h @@ -22,12 +22,12 @@ public: PlainText }; - QtTestOutputReader(const QFutureInterface &futureInterface, + QtTestOutputReader(const QFutureInterface &futureInterface, Utils::QtcProcess *testApplication, const Utils::FilePath &buildDirectory, const Utils::FilePath &projectFile, OutputMode mode, TestType type); protected: void processOutputLine(const QByteArray &outputLine) override; - TestResultPtr createDefaultResult() const override; + TestResult createDefaultResult() const override; private: void processXMLOutput(const QByteArray &outputLine); diff --git a/src/plugins/autotest/qtest/qttestparser.cpp b/src/plugins/autotest/qtest/qttestparser.cpp index 637d571fd96..2b13133f03f 100644 --- a/src/plugins/autotest/qtest/qttestparser.cpp +++ b/src/plugins/autotest/qtest/qttestparser.cpp @@ -36,8 +36,7 @@ TestTreeItem *QtTestParseResult::createTestTreeItem() const static bool includesQtTest(const CPlusPlus::Document::Ptr &doc, const CPlusPlus::Snapshot &snapshot) { - static QStringList expectedHeaderPrefixes - = Utils::HostOsInfo::isMacHost() + static QStringList expectedHeaderPrefixes = HostOsInfo::isMacHost() ? QStringList({"QtTest.framework/Headers", "QtTest"}) : QStringList({"QtTest"}); const QList includes = doc->resolvedIncludes(); @@ -71,7 +70,7 @@ static bool includesQtTest(const CPlusPlus::Document::Ptr &doc, const CPlusPlus: return false; } -static bool qtTestLibDefined(const Utils::FilePath &fileName) +static bool qtTestLibDefined(const FilePath &fileName) { const QList parts = CppEditor::CppModelManager::instance()->projectPart(fileName); @@ -84,7 +83,7 @@ static bool qtTestLibDefined(const Utils::FilePath &fileName) } TestCases QtTestParser::testCases(const CppEditor::CppModelManager *modelManager, - const Utils::FilePath &filePath) const + const FilePath &filePath) const { const QByteArray &fileContent = getFileContent(filePath); CPlusPlus::Document::Ptr document = modelManager->document(filePath); @@ -141,7 +140,7 @@ TestCases QtTestParser::testCases(const CppEditor::CppModelManager *modelManager static CPlusPlus::Document::Ptr declaringDocument(CPlusPlus::Document::Ptr doc, const CPlusPlus::Snapshot &snapshot, const QString &testCaseName, - const Utils::FilePaths &alternativeFiles = {}, + const FilePaths &alternativeFiles = {}, int *line = nullptr, int *column = nullptr) { @@ -153,7 +152,7 @@ static CPlusPlus::Document::Ptr declaringDocument(CPlusPlus::Document::Ptr doc, doc->globalNamespace()); // fallback for inherited functions if (lookupItems.size() == 0 && !alternativeFiles.isEmpty()) { - for (const Utils::FilePath &alternativeFile : alternativeFiles) { + for (const FilePath &alternativeFile : alternativeFiles) { if (snapshot.contains(alternativeFile)) { CPlusPlus::Document::Ptr document = snapshot.document(alternativeFile); CPlusPlus::TypeOfExpression typeOfExpr; // we need a new one with no bindings @@ -179,10 +178,10 @@ static CPlusPlus::Document::Ptr declaringDocument(CPlusPlus::Document::Ptr doc, return declaringDoc; } -static QSet filesWithDataFunctionDefinitions( +static QSet filesWithDataFunctionDefinitions( const QMap &testFunctions) { - QSet result; + QSet result; QMap::ConstIterator it = testFunctions.begin(); const QMap::ConstIterator end = testFunctions.end(); @@ -195,7 +194,7 @@ static QSet filesWithDataFunctionDefinitions( } QHash QtTestParser::checkForDataTags( - const Utils::FilePath &fileName) const + const FilePath &fileName) const { const QByteArray fileContent = getFileContent(fileName); CPlusPlus::Document::Ptr document = m_cppSnapshot.preprocessedDocument(fileContent, fileName); @@ -289,12 +288,12 @@ static QtTestCodeLocationList tagLocationsFor(const QtTestParseResult *func, static bool isQObject(const CPlusPlus::Document::Ptr &declaringDoc) { const FilePath file = declaringDoc->filePath(); - return (Utils::HostOsInfo::isMacHost() && file.endsWith("QtCore.framework/Headers/qobject.h")) + return (HostOsInfo::isMacHost() && file.endsWith("QtCore.framework/Headers/qobject.h")) || file.endsWith("QtCore/qobject.h") || file.endsWith("kernel/qobject.h"); } bool QtTestParser::processDocument(QFutureInterface &futureInterface, - const Utils::FilePath &fileName) + const FilePath &fileName) { CPlusPlus::Document::Ptr doc = document(fileName); if (doc.isNull()) @@ -359,8 +358,8 @@ std::optional QtTestParser::fillTestCaseData( if (data.testFunctions.isEmpty() && testCaseName == "QObject" && isQObject(declaringDoc)) return true; // we did not handle it, but we do not expect any test defined there either - const QSet &files = filesWithDataFunctionDefinitions(data.testFunctions); - for (const Utils::FilePath &file : files) + const QSet &files = filesWithDataFunctionDefinitions(data.testFunctions); + for (const FilePath &file : files) Utils::addToHash(&(data.dataTags), checkForDataTags(file)); data.fileName = declaringDoc->filePath(); @@ -378,7 +377,7 @@ QtTestParseResult *QtTestParser::createParseResult( parseResult->displayName = testCaseName; parseResult->line = data.line; parseResult->column = data.column; - parseResult->proFile = Utils::FilePath::fromString(projectFile); + parseResult->proFile = FilePath::fromString(projectFile); parseResult->setRunsMultipleTestcases(data.multipleTestCases); QMap::ConstIterator it = data.testFunctions.begin(); const QMap::ConstIterator end = data.testFunctions.end(); @@ -414,7 +413,7 @@ QtTestParseResult *QtTestParser::createParseResult( return parseResult; } -void QtTestParser::init(const Utils::FilePaths &filesToParse, bool fullParse) +void QtTestParser::init(const FilePaths &filesToParse, bool fullParse) { if (!fullParse) { // in a full parse cached information might lead to wrong results m_testCases = QTestUtils::testCaseNamesForFiles(framework(), filesToParse); diff --git a/src/plugins/autotest/qtest/qttestsettings.cpp b/src/plugins/autotest/qtest/qttestsettings.cpp index b16d991726a..4be93d99379 100644 --- a/src/plugins/autotest/qtest/qttestsettings.cpp +++ b/src/plugins/autotest/qtest/qttestsettings.cpp @@ -86,7 +86,7 @@ QString QtTestSettings::metricsTypeToOption(const MetricsType type) { switch (type) { case MetricsType::Walltime: - return QString(); + return {}; case MetricsType::TickCounter: return QString("-tickcounter"); case MetricsType::EventCounter: @@ -96,7 +96,7 @@ QString QtTestSettings::metricsTypeToOption(const MetricsType type) case MetricsType::Perf: return QString("-perf"); } - return QString(); + return {}; } QtTestSettingsPage::QtTestSettingsPage(QtTestSettings *settings, Id settingsId) diff --git a/src/plugins/autotest/qtest/qttesttreeitem.cpp b/src/plugins/autotest/qtest/qttesttreeitem.cpp index 1b4e4d21d2a..2f846372822 100644 --- a/src/plugins/autotest/qtest/qttesttreeitem.cpp +++ b/src/plugins/autotest/qtest/qttesttreeitem.cpp @@ -12,11 +12,13 @@ #include #include +using namespace Utils; + namespace Autotest { namespace Internal { QtTestTreeItem::QtTestTreeItem(ITestFramework *testFramework, const QString &name, - const Utils::FilePath &filePath, TestTreeItem::Type type) + const FilePath &filePath, TestTreeItem::Type type) : TestTreeItem(testFramework, name, filePath, type) { if (type == TestDataTag) @@ -286,7 +288,7 @@ QList QtTestTreeItem::getFailedTestConfigurations() const return result; } -QList QtTestTreeItem::getTestConfigurationsForFile(const Utils::FilePath &fileName) const +QList QtTestTreeItem::getTestConfigurationsForFile(const FilePath &fileName) const { QList result; @@ -321,7 +323,7 @@ TestTreeItem *QtTestTreeItem::find(const TestParseResult *result) switch (type()) { case Root: if (result->framework->grouping()) { - const Utils::FilePath path = result->fileName.absolutePath(); + const FilePath path = result->fileName.absolutePath(); for (int row = 0; row < childCount(); ++row) { TestTreeItem *group = childItem(row); if (group->filePath() != path) @@ -394,7 +396,7 @@ bool QtTestTreeItem::modify(const TestParseResult *result) TestTreeItem *QtTestTreeItem::createParentGroupNode() const { - const Utils::FilePath &absPath = filePath().absolutePath(); + const FilePath &absPath = filePath().absolutePath(); return new QtTestTreeItem(framework(), absPath.baseName(), absPath, TestTreeItem::GroupNode); } @@ -403,7 +405,7 @@ bool QtTestTreeItem::isGroupable() const return type() == TestCase; } -TestTreeItem *QtTestTreeItem::findChildByFileNameAndType(const Utils::FilePath &file, +TestTreeItem *QtTestTreeItem::findChildByFileNameAndType(const FilePath &file, const QString &name, Type type) const { return findFirstLevelChildItem([file, name, type](const TestTreeItem *other) { diff --git a/src/plugins/autotest/qtest/qttesttreeitem.h b/src/plugins/autotest/qtest/qttesttreeitem.h index 086261af4ae..ebcc8b89dfe 100644 --- a/src/plugins/autotest/qtest/qttesttreeitem.h +++ b/src/plugins/autotest/qtest/qttesttreeitem.h @@ -11,8 +11,8 @@ namespace Internal { class QtTestTreeItem : public TestTreeItem { public: - explicit QtTestTreeItem(ITestFramework *framework, const QString &name = QString(), - const Utils::FilePath &filePath = Utils::FilePath(), Type type = Root); + explicit QtTestTreeItem(ITestFramework *framework, const QString &name = {}, + const Utils::FilePath &filePath = {}, Type type = Root); TestTreeItem *copyWithoutChildren() override; QVariant data(int column, int role) const override; diff --git a/src/plugins/autotest/qtest/qttestvisitors.cpp b/src/plugins/autotest/qtest/qttestvisitors.cpp index 8aa824c6e09..6f615c99321 100644 --- a/src/plugins/autotest/qtest/qttestvisitors.cpp +++ b/src/plugins/autotest/qtest/qttestvisitors.cpp @@ -12,6 +12,7 @@ #include using namespace CPlusPlus; +using namespace Utils; namespace Autotest { namespace Internal { @@ -36,7 +37,7 @@ bool TestVisitor::visit(Class *symbol) Symbol *member = symbol->memberAt(i); Type *type = member->type().type(); - const QString className = o.prettyName(CPlusPlus::LookupContext::fullyQualifiedName( + const QString className = o.prettyName(LookupContext::fullyQualifiedName( member->enclosingClass())); if (className != m_className) continue; @@ -51,12 +52,12 @@ bool TestVisitor::visit(Class *symbol) Function *functionDefinition = m_symbolFinder.findMatchingDefinition( func, m_snapshot, true); if (functionDefinition && functionDefinition->fileId()) { - locationAndType.m_filePath = Utils::FilePath::fromString( + locationAndType.m_filePath = FilePath::fromString( QString::fromUtf8(functionDefinition->fileName())); locationAndType.m_line = functionDefinition->line(); locationAndType.m_column = functionDefinition->column() - 1; } else { // if we cannot find the definition use declaration as fallback - locationAndType.m_filePath = Utils::FilePath::fromString( + locationAndType.m_filePath = FilePath::fromString( QString::fromUtf8(member->fileName())); locationAndType.m_line = member->line(); locationAndType.m_column = member->column() - 1; @@ -74,7 +75,7 @@ bool TestVisitor::visit(Class *symbol) } for (int counter = 0, end = symbol->baseClassCount(); counter < end; ++counter) { if (BaseClass *base = symbol->baseClassAt(counter)) { - const QString &baseClassName = o.prettyName(CPlusPlus::LookupContext::fullyQualifiedName(base)); + const QString &baseClassName = o.prettyName(LookupContext::fullyQualifiedName(base)); if (baseClassName != "QObject") m_baseClasses.insert(baseClassName); } @@ -171,7 +172,7 @@ bool TestDataFunctionVisitor::visit(FunctionDefinitionAST *ast) LookupContext lc; const QString prettyName = - m_overview.prettyName(CPlusPlus::LookupContext::fullyQualifiedName(ast->symbol)); + m_overview.prettyName(LookupContext::fullyQualifiedName(ast->symbol)); // do not handle functions that aren't real test data functions if (!prettyName.endsWith("_data")) return false; @@ -189,7 +190,7 @@ QString TestDataFunctionVisitor::extractNameFromAST(StringLiteralAST *ast, bool auto token = m_currentDoc->translationUnit()->tokenAt(ast->literal_token); if (!token.isStringLiteral()) { *ok = false; - return QString(); + return {}; } *ok = true; QString name = QString::fromUtf8(token.spell()); diff --git a/src/plugins/autotest/quick/quicktest_utils.cpp b/src/plugins/autotest/quick/quicktest_utils.cpp index 17202b538a9..09d2453c96a 100644 --- a/src/plugins/autotest/quick/quicktest_utils.cpp +++ b/src/plugins/autotest/quick/quicktest_utils.cpp @@ -9,6 +9,8 @@ #include +using namespace Utils; + namespace Autotest { namespace Internal { namespace QuickTestUtils { @@ -20,10 +22,9 @@ bool isQuickTestMacro(const QByteArray ¯o) return valid.contains(macro); } -QHash proFilesForQmlFiles(ITestFramework *framework, - const Utils::FilePaths &files) +QHash proFilesForQmlFiles(ITestFramework *framework, const FilePaths &files) { - QHash result; + QHash result; TestTreeItem *rootNode = framework->rootNode(); QTC_ASSERT(rootNode, return result); @@ -31,16 +32,16 @@ QHash proFilesForQmlFiles(ITestFramework *fram return result; rootNode->forFirstLevelChildItems([&result, &files](TestTreeItem *child) { - const Utils::FilePath &file = child->filePath(); + const FilePath &file = child->filePath(); if (!file.isEmpty() && files.contains(file)) { - const Utils::FilePath &proFile = child->proFile(); + const FilePath &proFile = child->proFile(); if (!proFile.isEmpty()) result.insert(file, proFile); } child->forFirstLevelChildItems([&result, &files](TestTreeItem *grandChild) { - const Utils::FilePath &file = grandChild->filePath(); + const FilePath &file = grandChild->filePath(); if (!file.isEmpty() && files.contains(file)) { - const Utils::FilePath &proFile = grandChild->proFile(); + const FilePath &proFile = grandChild->proFile(); if (!proFile.isEmpty()) result.insert(file, proFile); } diff --git a/src/plugins/autotest/quick/quicktestconfiguration.cpp b/src/plugins/autotest/quick/quicktestconfiguration.cpp index ffeecec99e6..c7cce31acf1 100644 --- a/src/plugins/autotest/quick/quicktestconfiguration.cpp +++ b/src/plugins/autotest/quick/quicktestconfiguration.cpp @@ -12,6 +12,8 @@ #include +using namespace Utils; + namespace Autotest { namespace Internal { @@ -22,7 +24,7 @@ QuickTestConfiguration::QuickTestConfiguration(ITestFramework *framework) } TestOutputReader *QuickTestConfiguration::createOutputReader( - const QFutureInterface &fi, Utils::QtcProcess *app) const + const QFutureInterface &fi, QtcProcess *app) const { auto qtSettings = static_cast(framework()->testSettings()); const QtTestOutputReader::OutputMode mode = qtSettings && qtSettings->useXMLOutput.value() @@ -64,7 +66,7 @@ QStringList QuickTestConfiguration::argumentsForTestRunner(QStringList *omitted) return arguments; } -Utils::Environment QuickTestConfiguration::filteredEnvironment(const Utils::Environment &original) const +Environment QuickTestConfiguration::filteredEnvironment(const Environment &original) const { return QTestUtils::prepareBasicEnvironment(original); } diff --git a/src/plugins/autotest/quick/quicktestconfiguration.h b/src/plugins/autotest/quick/quicktestconfiguration.h index 1b1eff7cb42..84e374ebe8a 100644 --- a/src/plugins/autotest/quick/quicktestconfiguration.h +++ b/src/plugins/autotest/quick/quicktestconfiguration.h @@ -12,7 +12,7 @@ class QuickTestConfiguration : public DebuggableTestConfiguration { public: explicit QuickTestConfiguration(ITestFramework *framework); - TestOutputReader *createOutputReader(const QFutureInterface &fi, + TestOutputReader *createOutputReader(const QFutureInterface &fi, Utils::QtcProcess *app) const override; QStringList argumentsForTestRunner(QStringList *omitted = nullptr) const override; Utils::Environment filteredEnvironment(const Utils::Environment &original) const override; diff --git a/src/plugins/autotest/quick/quicktestframework.cpp b/src/plugins/autotest/quick/quicktestframework.cpp index b68eebe87d1..a8289ad48e5 100644 --- a/src/plugins/autotest/quick/quicktestframework.cpp +++ b/src/plugins/autotest/quick/quicktestframework.cpp @@ -20,8 +20,7 @@ ITestParser *QuickTestFramework::createTestParser() ITestTreeItem *QuickTestFramework::createRootNode() { - return new QuickTestTreeItem(this, displayName(), - Utils::FilePath(), ITestTreeItem::Root); + return new QuickTestTreeItem(this, displayName(), {}, ITestTreeItem::Root); } const char *QuickTestFramework::name() const diff --git a/src/plugins/autotest/quick/quicktestparser.cpp b/src/plugins/autotest/quick/quicktestparser.cpp index c5f0524a53e..2c72cdf3f1a 100644 --- a/src/plugins/autotest/quick/quicktestparser.cpp +++ b/src/plugins/autotest/quick/quicktestparser.cpp @@ -43,8 +43,7 @@ TestTreeItem *QuickTestParseResult::createTestTreeItem() const static bool includesQtQuickTest(const CPlusPlus::Document::Ptr &doc, const CPlusPlus::Snapshot &snapshot) { - static QStringList expectedHeaderPrefixes - = Utils::HostOsInfo::isMacHost() + static QStringList expectedHeaderPrefixes = HostOsInfo::isMacHost() ? QStringList({"QtQuickTest.framework/Headers", "QtQuickTest"}) : QStringList({"QtQuickTest"}); @@ -78,8 +77,7 @@ static bool includesQtQuickTest(const CPlusPlus::Document::Ptr &doc, return false; } -static QString quickTestSrcDir(const CppEditor::CppModelManager *cppMM, - const Utils::FilePath &fileName) +static QString quickTestSrcDir(const CppEditor::CppModelManager *cppMM, const FilePath &fileName) { const QList parts = cppMM->projectPart(fileName); if (parts.size() > 0) { @@ -103,7 +101,7 @@ static QString quickTestSrcDir(const CppEditor::CppModelManager *cppMM, QString QuickTestParser::quickTestName(const CPlusPlus::Document::Ptr &doc) const { const QList macros = doc->macroUses(); - const Utils::FilePath filePath = doc->filePath(); + const FilePath filePath = doc->filePath(); for (const CPlusPlus::Document::MacroUse ¯o : macros) { if (!macro.isFunctionLike() || macro.arguments().isEmpty()) @@ -121,7 +119,7 @@ QString QuickTestParser::quickTestName(const CPlusPlus::Document::Ptr &doc) cons // check for using quick_test_main() directly CPlusPlus::Document::Ptr document = m_cppSnapshot.preprocessedDocument(fileContent, filePath); if (document.isNull()) - return QString(); + return {}; document->check(); CPlusPlus::AST *ast = document->translationUnit()->ast(); QuickTestAstVisitor astVisitor(document, m_cppSnapshot); @@ -130,8 +128,7 @@ QString QuickTestParser::quickTestName(const CPlusPlus::Document::Ptr &doc) cons return astVisitor.testBaseName(); // check for precompiled headers - static QStringList expectedHeaderPrefixes - = Utils::HostOsInfo::isMacHost() + static QStringList expectedHeaderPrefixes = HostOsInfo::isMacHost() ? QStringList({"QtQuickTest.framework/Headers", "QtQuickTest"}) : QStringList({"QtQuickTest"}); bool pchIncludes = false; @@ -152,9 +149,9 @@ QString QuickTestParser::quickTestName(const CPlusPlus::Document::Ptr &doc) cons return {}; } -QList QuickTestParser::scanDirectoryForQuickTestQmlFiles(const Utils::FilePath &srcDir) +QList QuickTestParser::scanDirectoryForQuickTestQmlFiles(const FilePath &srcDir) { - Utils::FilePaths dirs({srcDir}); + FilePaths dirs({srcDir}); QStringList dirsStr({srcDir.toString()}); ModelManagerInterface *qmlJsMM = QmlJSTools::Internal::ModelManager::instance(); // make sure even files not listed in pro file are available inside the snapshot @@ -171,7 +168,7 @@ QList QuickTestParser::scanDirectoryForQuickTestQmlFiles(const Ut QDirIterator::Subdirectories); while (it.hasNext()) { it.next(); - auto subDir = Utils::FilePath::fromFileInfo(it.fileInfo()).canonicalPath(); + auto subDir = FilePath::fromFileInfo(it.fileInfo()).canonicalPath(); dirs.append(subDir); dirsStr.append(subDir.toString()); } @@ -182,10 +179,10 @@ QList QuickTestParser::scanDirectoryForQuickTestQmlFiles(const Ut QList foundDocs; - for (const Utils::FilePath &path : std::as_const(dirs)) { + for (const FilePath &path : std::as_const(dirs)) { const QList docs = snapshot.documentsInDirectory(path); for (const Document::Ptr &doc : docs) { - Utils::FilePath fi = doc->fileName(); + const FilePath fi = doc->fileName(); //const QFileInfo fi(doc->fileName()); // using working copy above might provide no more existing files if (!fi.exists()) @@ -202,7 +199,7 @@ QList QuickTestParser::scanDirectoryForQuickTestQmlFiles(const Ut static bool checkQmlDocumentForQuickTestCode(QFutureInterface &futureInterface, const Document::Ptr &qmlJSDoc, ITestFramework *framework, - const Utils::FilePath &proFile = Utils::FilePath(), + const FilePath &proFile = {}, bool checkForDerivedTest = false) { if (qmlJSDoc.isNull()) @@ -260,10 +257,9 @@ bool QuickTestParser::handleQtQuickTest(QFutureInterface &fu if (ppList.isEmpty()) // happens if shutting down while parsing return false; const FilePath cppFileName = document->filePath(); - const FilePath proFile = Utils::FilePath::fromString(ppList.at(0)->projectFile); + const FilePath proFile = FilePath::fromString(ppList.at(0)->projectFile); m_mainCppFiles.insert(cppFileName, proFile); - const Utils::FilePath srcDir = Utils::FilePath::fromString( - quickTestSrcDir(modelManager, cppFileName)); + const FilePath srcDir = FilePath::fromString(quickTestSrcDir(modelManager, cppFileName)); if (srcDir.isEmpty()) return false; @@ -308,7 +304,7 @@ void QuickTestParser::handleDirectoryChanged(const QString &directory) if (timestampChanged) { m_watchedFiles[directory] = filesAndDates; PathsAndLanguages paths; - paths.maybeInsert(Utils::FilePath::fromString(directory), Dialect::Qml); + paths.maybeInsert(FilePath::fromString(directory), Dialect::Qml); QFutureInterface future; ModelManagerInterface *qmlJsMM = ModelManagerInterface::instance(); ModelManagerInterface::importScan(future, ModelManagerInterface::workingCopy(), paths, @@ -342,14 +338,14 @@ QuickTestParser::QuickTestParser(ITestFramework *framework) this, &QuickTestParser::handleDirectoryChanged); } -void QuickTestParser::init(const Utils::FilePaths &filesToParse, bool fullParse) +void QuickTestParser::init(const FilePaths &filesToParse, bool fullParse) { m_qmlSnapshot = QmlJSTools::Internal::ModelManager::instance()->snapshot(); if (!fullParse) { // in a full parse we get the correct entry points by the respective main m_proFilesForQmlFiles = QuickTestUtils::proFilesForQmlFiles(framework(), filesToParse); // get rid of cached main cpp files that are going to get processed anyhow - for (const Utils::FilePath &file : filesToParse) { + for (const FilePath &file : filesToParse) { if (m_mainCppFiles.contains(file)) { m_mainCppFiles.remove(file); if (m_mainCppFiles.isEmpty()) @@ -375,10 +371,10 @@ void QuickTestParser::release() } bool QuickTestParser::processDocument(QFutureInterface &futureInterface, - const Utils::FilePath &fileName) + const FilePath &fileName) { if (fileName.endsWith(".qml")) { - const Utils::FilePath &proFile = m_proFilesForQmlFiles.value(fileName); + const FilePath &proFile = m_proFilesForQmlFiles.value(fileName); if (proFile.isEmpty()) return false; Document::Ptr qmlJSDoc = m_qmlSnapshot.document(fileName); @@ -396,9 +392,9 @@ bool QuickTestParser::processDocument(QFutureInterface &futu return handleQtQuickTest(futureInterface, cppdoc, framework()); } -Utils::FilePath QuickTestParser::projectFileForMainCppFile(const Utils::FilePath &fileName) const +FilePath QuickTestParser::projectFileForMainCppFile(const FilePath &fileName) const { - return m_mainCppFiles.contains(fileName) ? m_mainCppFiles.value(fileName) : Utils::FilePath(); + return m_mainCppFiles.contains(fileName) ? m_mainCppFiles.value(fileName) : FilePath(); } } // namespace Internal diff --git a/src/plugins/autotest/quick/quicktesttreeitem.cpp b/src/plugins/autotest/quick/quicktesttreeitem.cpp index d07c14628e1..49358f49ebd 100644 --- a/src/plugins/autotest/quick/quicktesttreeitem.cpp +++ b/src/plugins/autotest/quick/quicktesttreeitem.cpp @@ -12,10 +12,12 @@ #include #include +using namespace Utils; + namespace Autotest { namespace Internal { -static QSet internalTargets(const Utils::FilePath &proFile); +static QSet internalTargets(const FilePath &proFile); TestTreeItem *QuickTestTreeItem::copyWithoutChildren() { @@ -149,8 +151,8 @@ static QList testConfigurationsFor( if (!project || rootNode->type() != TestTreeItem::Root) return {}; - QHash configurationForProFiles; - rootNode->forSelectedChildren([&predicate, &configurationForProFiles](Utils::TreeItem *it) { + QHash configurationForProFiles; + rootNode->forSelectedChildren([&predicate, &configurationForProFiles](TreeItem *it) { auto treeItem = static_cast(it); if (treeItem->type() == TestTreeItem::Root || treeItem->type() == TestTreeItem::GroupNode) return true; @@ -208,12 +210,12 @@ QList QuickTestTreeItem::getAllTestConfigurations() const if (!project || type() != Root) return result; - QHash testsForProfile; + QHash testsForProfile; forFirstLevelChildItems([&testsForProfile](TestTreeItem *child) { // unnamed Quick Tests must be handled separately if (child->name().isEmpty()) { child->forFirstLevelChildItems([&testsForProfile](TestTreeItem *grandChild) { - const Utils::FilePath &proFile = grandChild->proFile(); + const FilePath &proFile = grandChild->proFile(); ++(testsForProfile[proFile].testCount); testsForProfile[proFile].internalTargets = internalTargets(grandChild->proFile()); }); @@ -255,7 +257,7 @@ QList QuickTestTreeItem::getFailedTestConfigurations() con } QList QuickTestTreeItem::getTestConfigurationsForFile( - const Utils::FilePath &fileName) const + const FilePath &fileName) const { return testConfigurationsFor(this, [&fileName](TestTreeItem *it) { return it->filePath() == fileName; @@ -271,7 +273,7 @@ TestTreeItem *QuickTestTreeItem::find(const TestParseResult *result) if (result->name.isEmpty()) return unnamedQuickTests(); if (result->framework->grouping()) { - const Utils::FilePath path = result->fileName.absolutePath(); + const FilePath path = result->fileName.absolutePath(); TestTreeItem *group = findFirstLevelChildItem([path](TestTreeItem *group) { return group->filePath() == path; }); @@ -354,7 +356,7 @@ bool QuickTestTreeItem::removeOnSweepIfEmpty() const TestTreeItem *QuickTestTreeItem::createParentGroupNode() const { - const Utils::FilePath &absPath = filePath().absolutePath(); + const FilePath &absPath = filePath().absolutePath(); return new QuickTestTreeItem(framework(), absPath.baseName(), absPath, TestTreeItem::GroupNode); } @@ -363,7 +365,7 @@ bool QuickTestTreeItem::isGroupable() const return type() == TestCase && !name().isEmpty() && !filePath().isEmpty(); } -QSet internalTargets(const Utils::FilePath &proFile) +QSet internalTargets(const FilePath &proFile) { QSet result; const auto cppMM = CppEditor::CppModelManager::instance(); @@ -379,11 +381,11 @@ QSet internalTargets(const Utils::FilePath &proFile) return result; } -void QuickTestTreeItem::markForRemovalRecursively(const Utils::FilePath &filePath) +void QuickTestTreeItem::markForRemovalRecursively(const FilePath &filePath) { TestTreeItem::markForRemovalRecursively(filePath); auto parser = static_cast(framework()->testParser()); - const Utils::FilePath proFile = parser->projectFileForMainCppFile(filePath); + const FilePath proFile = parser->projectFileForMainCppFile(filePath); if (!proFile.isEmpty()) { TestTreeItem *root = framework()->rootNode(); root->forAllChildItems([proFile](TestTreeItem *it) { @@ -393,7 +395,7 @@ void QuickTestTreeItem::markForRemovalRecursively(const Utils::FilePath &filePat } } -TestTreeItem *QuickTestTreeItem::findChildByFileNameAndType(const Utils::FilePath &filePath, +TestTreeItem *QuickTestTreeItem::findChildByFileNameAndType(const FilePath &filePath, const QString &name, TestTreeItem::Type tType) @@ -404,7 +406,7 @@ TestTreeItem *QuickTestTreeItem::findChildByFileNameAndType(const Utils::FilePat } TestTreeItem *QuickTestTreeItem::findChildByNameFileAndLine(const QString &name, - const Utils::FilePath &filePath, + const FilePath &filePath, int line) { return findFirstLevelChildItem([name, filePath, line](const TestTreeItem *other) { diff --git a/src/plugins/autotest/quick/quicktesttreeitem.h b/src/plugins/autotest/quick/quicktesttreeitem.h index 7b7c0985b93..adbf95fa8bc 100644 --- a/src/plugins/autotest/quick/quicktesttreeitem.h +++ b/src/plugins/autotest/quick/quicktesttreeitem.h @@ -11,10 +11,8 @@ namespace Internal { class QuickTestTreeItem : public TestTreeItem { public: - explicit QuickTestTreeItem(ITestFramework *testFramework, - const QString &name = QString(), - const Utils::FilePath &filePath = Utils::FilePath(), - Type type = Root) + explicit QuickTestTreeItem(ITestFramework *testFramework, const QString &name = {}, + const Utils::FilePath &filePath = {}, Type type = Root) : TestTreeItem(testFramework, name, filePath, type) {} diff --git a/src/plugins/autotest/testcodeparser.cpp b/src/plugins/autotest/testcodeparser.cpp index c5dbcd6d51f..edf126f8d42 100644 --- a/src/plugins/autotest/testcodeparser.cpp +++ b/src/plugins/autotest/testcodeparser.cpp @@ -26,6 +26,8 @@ #include #include +using namespace Utils; + namespace Autotest { namespace Internal { @@ -146,12 +148,12 @@ void TestCodeParser::updateTestTree(const QSet &parsers) [](const ITestParser *lhs, const ITestParser *rhs) { return lhs->framework()->priority() < rhs->framework()->priority(); }); - scanForTests(Utils::FilePaths(), sortedParsers); + scanForTests({}, sortedParsers); } /****** threaded parsing stuff *******/ -void TestCodeParser::onDocumentUpdated(const Utils::FilePath &fileName, bool isQmlFile) +void TestCodeParser::onDocumentUpdated(const FilePath &fileName, bool isQmlFile) { if (isProjectParsing() || m_codeModelParsing || m_postponedUpdateType == UpdateType::FullUpdate) return; @@ -163,7 +165,7 @@ void TestCodeParser::onDocumentUpdated(const Utils::FilePath &fileName, bool isQ if (!isQmlFile && !project->isKnownFile(fileName)) return; - scanForTests(Utils::FilePaths{fileName}); + scanForTests({fileName}); } void TestCodeParser::onCppDocumentUpdated(const CPlusPlus::Document::Ptr &document) @@ -174,7 +176,7 @@ void TestCodeParser::onCppDocumentUpdated(const CPlusPlus::Document::Ptr &docume void TestCodeParser::onQmlDocumentUpdated(const QmlJS::Document::Ptr &document) { static const QStringList ignoredSuffixes{ "qbs", "ui.qml" }; - const Utils::FilePath fileName = document->fileName(); + const FilePath fileName = document->fileName(); if (!ignoredSuffixes.contains(fileName.suffix())) onDocumentUpdated(fileName, true); } @@ -211,7 +213,7 @@ void TestCodeParser::aboutToShutdown() } } -bool TestCodeParser::postponed(const Utils::FilePaths &fileList) +bool TestCodeParser::postponed(const FilePaths &fileList) { switch (m_parserState) { case Idle: @@ -253,7 +255,7 @@ bool TestCodeParser::postponed(const Utils::FilePaths &fileList) if (m_postponedUpdateType == UpdateType::FullUpdate) return true; // partial parse triggered, postpone or add current files to already postponed partial - for (const Utils::FilePath &file : fileList) + for (const FilePath &file : fileList) m_postponedFiles.insert(file); m_postponedUpdateType = UpdateType::PartialUpdate; } @@ -266,7 +268,7 @@ bool TestCodeParser::postponed(const Utils::FilePaths &fileList) static void parseFileForTests(const QList &parsers, QFutureInterface &futureInterface, - const Utils::FilePath &fileName) + const FilePath &fileName) { for (ITestParser *parser : parsers) { if (futureInterface.isCanceled()) @@ -276,8 +278,7 @@ static void parseFileForTests(const QList &parsers, } } -void TestCodeParser::scanForTests(const Utils::FilePaths &fileList, - const QList &parsers) +void TestCodeParser::scanForTests(const FilePaths &fileList, const QList &parsers) { if (m_parserState == Shutdown || m_testCodeParsers.isEmpty()) return; @@ -292,7 +293,7 @@ void TestCodeParser::scanForTests(const Utils::FilePaths &fileList, Project *project = SessionManager::startupProject(); if (!project) return; - Utils::FilePaths list; + FilePaths list; if (isFullParse) { list = project->files(Project::SourceFiles); if (list.isEmpty()) { @@ -318,7 +319,7 @@ void TestCodeParser::scanForTests(const Utils::FilePaths &fileList, TestTreeModel::instance()->updateCheckStateCache(); if (isFullParse) { // remove qml files as they will be found automatically by the referencing cpp file - list = Utils::filtered(list, [](const Utils::FilePath &fn) { + list = Utils::filtered(list, [](const FilePath &fn) { return !fn.endsWith(".qml"); }); if (!parsers.isEmpty()) { @@ -330,11 +331,11 @@ void TestCodeParser::scanForTests(const Utils::FilePaths &fileList, } } else if (!parsers.isEmpty()) { for (ITestParser *parser: parsers) { - for (const Utils::FilePath &filePath : std::as_const(list)) + for (const FilePath &filePath : std::as_const(list)) parser->framework()->rootNode()->markForRemovalRecursively(filePath); } } else { - for (const Utils::FilePath &filePath : std::as_const(list)) + for (const FilePath &filePath : std::as_const(list)) emit requestRemoval(filePath); } @@ -353,8 +354,8 @@ void TestCodeParser::scanForTests(const Utils::FilePaths &fileList, } // We are only interested in files that have been either parsed by the c++ parser, // or have an extension that one of the parsers is specifically interested in. - const Utils::FilePaths filteredList - = Utils::filtered(list, [&extensions, &cppSnapshot](const Utils::FilePath &fn) { + const FilePaths filteredList + = Utils::filtered(list, [&extensions, &cppSnapshot](const FilePath &fn) { const bool isSupportedExtension = Utils::anyOf(extensions, [&fn](const QString &ext) { return fn.suffix() == ext; }); @@ -366,12 +367,11 @@ void TestCodeParser::scanForTests(const Utils::FilePaths &fileList, qCDebug(LOG) << "Starting scan of" << filteredList.size() << "(" << list.size() << ")" << "files with" << codeParsers.size() << "parsers"; - QFuture future = Utils::map( - filteredList, - [codeParsers](QFutureInterface &fi, const Utils::FilePath &file) { + QFuture future = Utils::map(filteredList, + [codeParsers](QFutureInterface &fi, const FilePath &file) { parseFileForTests(codeParsers, fi, file); }, - Utils::MapReduceOption::Unordered, + MapReduceOption::Unordered, m_threadPool, QThread::LowestPriority); m_futureWatcher.setFuture(future); @@ -381,7 +381,7 @@ void TestCodeParser::scanForTests(const Utils::FilePaths &fileList, } } -void TestCodeParser::onTaskStarted(Utils::Id type) +void TestCodeParser::onTaskStarted(Id type) { if (type == CppEditor::Constants::TASK_INDEX) { m_codeModelParsing = true; @@ -395,7 +395,7 @@ void TestCodeParser::onTaskStarted(Utils::Id type) } } -void TestCodeParser::onAllTasksFinished(Utils::Id type) +void TestCodeParser::onAllTasksFinished(Id type) { // if we cancel parsing ensure that progress animation is canceled as well if (type == Constants::TASK_PARSE && m_parsingHasFailed) diff --git a/src/plugins/autotest/testconfiguration.cpp b/src/plugins/autotest/testconfiguration.cpp index cd424b016bd..764ede697d1 100644 --- a/src/plugins/autotest/testconfiguration.cpp +++ b/src/plugins/autotest/testconfiguration.cpp @@ -61,7 +61,7 @@ FilePath ITestConfiguration::executableFilePath() const if (!hasExecutable()) return {}; - const Environment env = m_runnable.environment.isValid() + const Environment env = m_runnable.environment.hasChanges() ? m_runnable.environment : Environment::systemEnvironment(); return env.searchInPath(m_runnable.command.executable().path()); } diff --git a/src/plugins/autotest/testconfiguration.h b/src/plugins/autotest/testconfiguration.h index 8df0ea10553..d57416d0164 100644 --- a/src/plugins/autotest/testconfiguration.h +++ b/src/plugins/autotest/testconfiguration.h @@ -26,8 +26,6 @@ class TestOutputReader; class TestResult; enum class TestRunMode; -using TestResultPtr = QSharedPointer; - class ITestConfiguration { public: @@ -40,8 +38,9 @@ public: Utils::FilePath workingDirectory() const; bool hasExecutable() const; Utils::FilePath executableFilePath() const; + virtual Utils::FilePath testExecutable() const { return executableFilePath(); }; - virtual TestOutputReader *createOutputReader(const QFutureInterface &fi, + virtual TestOutputReader *createOutputReader(const QFutureInterface &fi, Utils::QtcProcess *app) const = 0; virtual Utils::Environment filteredEnvironment(const Utils::Environment &original) const; @@ -126,6 +125,7 @@ public: explicit TestToolConfiguration(ITestBase *testBase) : ITestConfiguration(testBase) {} Utils::CommandLine commandLine() const { return m_commandLine; } void setCommandLine(const Utils::CommandLine &cmdline) { m_commandLine = cmdline; } + virtual Utils::FilePath testExecutable() const override { return m_commandLine.executable(); }; private: Utils::CommandLine m_commandLine; diff --git a/src/plugins/autotest/testnavigationwidget.cpp b/src/plugins/autotest/testnavigationwidget.cpp index 8e81068cc9b..100eed89ed9 100644 --- a/src/plugins/autotest/testnavigationwidget.cpp +++ b/src/plugins/autotest/testnavigationwidget.cpp @@ -32,6 +32,8 @@ #include #include +using namespace Utils; + namespace Autotest { namespace Internal { @@ -48,10 +50,8 @@ TestNavigationWidget::TestNavigationWidget(QWidget *parent) : m_view->setItemDelegate(new TestTreeItemDelegate(this)); QPalette pal; - pal.setColor(QPalette::Window, - Utils::creatorTheme()->color(Utils::Theme::InfoBarBackground)); - pal.setColor(QPalette::WindowText, - Utils::creatorTheme()->color(Utils::Theme::InfoBarText)); + pal.setColor(QPalette::Window, creatorTheme()->color(Theme::InfoBarBackground)); + pal.setColor(QPalette::WindowText, creatorTheme()->color(Theme::InfoBarText)); m_missingFrameworksWidget = new QFrame; m_missingFrameworksWidget->setPalette(pal); m_missingFrameworksWidget->setAutoFillBackground(true); @@ -70,7 +70,7 @@ TestNavigationWidget::TestNavigationWidget(QWidget *parent) : connect(m_view, &TestTreeView::activated, this, &TestNavigationWidget::onItemActivated); - m_progressIndicator = new Utils::ProgressIndicator(Utils::ProgressIndicatorSize::Medium, this); + m_progressIndicator = new ProgressIndicator(ProgressIndicatorSize::Medium, this); m_progressIndicator->attachToWidget(m_view); m_progressIndicator->hide(); @@ -94,8 +94,7 @@ TestNavigationWidget::TestNavigationWidget(QWidget *parent) : }); connect(m_model, &TestTreeModel::testTreeModelChanged, this, &TestNavigationWidget::reapplyCachedExpandedState); - connect(m_progressTimer, &QTimer::timeout, - m_progressIndicator, &Utils::ProgressIndicator::show); + connect(m_progressTimer, &QTimer::timeout, m_progressIndicator, &ProgressIndicator::show); connect(m_view, &TestTreeView::expanded, this, &TestNavigationWidget::updateExpandedStateCache); connect(m_view, &TestTreeView::collapsed, this, &TestNavigationWidget::updateExpandedStateCache); } @@ -233,8 +232,8 @@ void TestNavigationWidget::updateExpandedStateCache() { m_expandedStateCache.evolve(ITestBase::Framework); - for (Utils::TreeItem *rootNode : *m_model->rootItem()) { - rootNode->forAllChildren([this](Utils::TreeItem *child) { + for (TreeItem *rootNode : *m_model->rootItem()) { + rootNode->forAllChildren([this](TreeItem *child) { m_expandedStateCache.insert(static_cast(child), m_view->isExpanded(child->index())); }); @@ -243,7 +242,7 @@ void TestNavigationWidget::updateExpandedStateCache() void TestNavigationWidget::onItemActivated(const QModelIndex &index) { - const Utils::Link link = index.data(LinkRole).value(); + const Link link = index.data(LinkRole).value(); if (link.hasValidTarget()) Core::EditorManager::openEditorAt(link); } diff --git a/src/plugins/autotest/testoutputreader.cpp b/src/plugins/autotest/testoutputreader.cpp index 90fec5675f6..2f1dc75e485 100644 --- a/src/plugins/autotest/testoutputreader.cpp +++ b/src/plugins/autotest/testoutputreader.cpp @@ -16,20 +16,19 @@ #include #include +using namespace Utils; + namespace Autotest { -Utils::FilePath TestOutputReader::constructSourceFilePath(const Utils::FilePath &path, - const QString &file) +FilePath TestOutputReader::constructSourceFilePath(const FilePath &path, const QString &file) { - const Utils::FilePath filePath = path.resolvePath(file); - return filePath.isReadableFile() ? filePath : Utils::FilePath(); + const FilePath filePath = path.resolvePath(file); + return filePath.isReadableFile() ? filePath : FilePath(); } -TestOutputReader::TestOutputReader(const QFutureInterface &futureInterface, - Utils::QtcProcess *testApplication, - const Utils::FilePath &buildDirectory) +TestOutputReader::TestOutputReader(const QFutureInterface &futureInterface, + QtcProcess *testApplication, const FilePath &buildDirectory) : m_futureInterface(futureInterface) - , m_testApplication(testApplication) , m_buildDir(buildDirectory) , m_id(testApplication ? testApplication->commandLine().executable().toUserOutput() : QString()) { @@ -41,11 +40,11 @@ TestOutputReader::TestOutputReader(const QFutureInterface &future return line; }; - if (m_testApplication) { - m_testApplication->setStdOutLineCallback([this, &chopLineBreak](const QString &line) { + if (testApplication) { + testApplication->setStdOutLineCallback([this, &chopLineBreak](const QString &line) { processStdOutput(chopLineBreak(line.toUtf8())); }); - m_testApplication->setStdErrLineCallback([this, &chopLineBreak](const QString &line) { + testApplication->setStdErrLineCallback([this, &chopLineBreak](const QString &line) { processStdError(chopLineBreak(line.toUtf8())); }); } @@ -53,7 +52,7 @@ TestOutputReader::TestOutputReader(const QFutureInterface &future TestOutputReader::~TestOutputReader() { - if (m_sanitizerResult) + if (m_sanitizerResult.isValid()) sendAndResetSanitizerResult(); } @@ -71,17 +70,17 @@ void TestOutputReader::processStdError(const QByteArray &outputLine) void TestOutputReader::reportCrash() { - TestResultPtr result = createDefaultResult(); - result->setDescription(Tr::tr("Test executable crashed.")); - result->setResult(ResultType::MessageFatal); - m_futureInterface.reportResult(result); + TestResult result = createDefaultResult(); + result.setDescription(Tr::tr("Test executable crashed.")); + result.setResult(ResultType::MessageFatal); + emit newResult(result); } void TestOutputReader::createAndReportResult(const QString &message, ResultType type) { - TestResultPtr result = createDefaultResult(); - result->setDescription(message); - result->setResult(type); + TestResult result = createDefaultResult(); + result.setDescription(message); + result.setResult(type); reportResult(result); } @@ -105,11 +104,11 @@ QString TestOutputReader::removeCommandlineColors(const QString &original) return result; } -void TestOutputReader::reportResult(const TestResultPtr &result) +void TestOutputReader::reportResult(const TestResult &result) { - if (m_sanitizerResult) + if (m_sanitizerResult.isValid()) sendAndResetSanitizerResult(); - m_futureInterface.reportResult(result); + emit newResult(result); m_hadValidOutput = true; } @@ -141,7 +140,7 @@ void TestOutputReader::checkForSanitizerOutput(const QByteArray &line) mode = SanitizerOutputMode::Ubsan; } if (mode != SanitizerOutputMode::None) { - if (m_sanitizerResult) // we have a result that has not been reported yet + if (m_sanitizerResult.isValid()) // we have a result that has not been reported yet sendAndResetSanitizerResult(); m_sanitizerOutputMode = mode; @@ -149,34 +148,34 @@ void TestOutputReader::checkForSanitizerOutput(const QByteArray &line) m_sanitizerLines.append("Sanitizer Issue"); m_sanitizerLines.append(lineStr); if (m_sanitizerOutputMode == SanitizerOutputMode::Ubsan) { - const Utils::FilePath path = constructSourceFilePath(m_buildDir, match.captured(1)); + const FilePath path = constructSourceFilePath(m_buildDir, match.captured(1)); // path may be empty if not existing - so, provide at least what we have - m_sanitizerResult->setFileName( - path.exists() ? path : Utils::FilePath::fromString(match.captured(1))); - m_sanitizerResult->setLine(match.captured(2).toInt()); + m_sanitizerResult.setFileName( + path.exists() ? path : FilePath::fromString(match.captured(1))); + m_sanitizerResult.setLine(match.captured(2).toInt()); } } } void TestOutputReader::sendAndResetSanitizerResult() { - QTC_ASSERT(m_sanitizerResult, return); - m_sanitizerResult->setDescription(m_sanitizerLines.join('\n')); - m_sanitizerResult->setResult(m_sanitizerOutputMode == SanitizerOutputMode::Ubsan - ? ResultType::Fail : ResultType::MessageFatal); + QTC_ASSERT(m_sanitizerResult.isValid(), return); + m_sanitizerResult.setDescription(m_sanitizerLines.join('\n')); + m_sanitizerResult.setResult(m_sanitizerOutputMode == SanitizerOutputMode::Ubsan + ? ResultType::Fail : ResultType::MessageFatal); - if (m_sanitizerResult->fileName().isEmpty()) { - const ITestTreeItem *testItem = m_sanitizerResult->findTestTreeItem(); + if (m_sanitizerResult.fileName().isEmpty()) { + const ITestTreeItem *testItem = m_sanitizerResult.findTestTreeItem(); if (testItem && testItem->line()) { - m_sanitizerResult->setFileName(testItem->filePath()); - m_sanitizerResult->setLine(testItem->line()); + m_sanitizerResult.setFileName(testItem->filePath()); + m_sanitizerResult.setLine(testItem->line()); } } - m_futureInterface.reportResult(m_sanitizerResult); + emit newResult(m_sanitizerResult); m_hadValidOutput = true; m_sanitizerLines.clear(); - m_sanitizerResult.reset(); + m_sanitizerResult = {}; m_sanitizerOutputMode = SanitizerOutputMode::None; } diff --git a/src/plugins/autotest/testoutputreader.h b/src/plugins/autotest/testoutputreader.h index b56f4210516..55c645d87c4 100644 --- a/src/plugins/autotest/testoutputreader.h +++ b/src/plugins/autotest/testoutputreader.h @@ -17,7 +17,7 @@ class TestOutputReader : public QObject { Q_OBJECT public: - TestOutputReader(const QFutureInterface &futureInterface, + TestOutputReader(const QFutureInterface &futureInterface, Utils::QtcProcess *testApplication, const Utils::FilePath &buildDirectory); virtual ~TestOutputReader(); void processStdOutput(const QByteArray &outputLine); @@ -33,6 +33,7 @@ public: void resetCommandlineColor(); signals: + void newResult(const TestResult &result); void newOutputLineAvailable(const QByteArray &outputLine, OutputChannel channel); protected: static Utils::FilePath constructSourceFilePath(const Utils::FilePath &base, @@ -40,20 +41,19 @@ protected: QString removeCommandlineColors(const QString &original); virtual void processOutputLine(const QByteArray &outputLine) = 0; - virtual TestResultPtr createDefaultResult() const = 0; + virtual TestResult createDefaultResult() const = 0; void checkForSanitizerOutput(const QByteArray &line); void sendAndResetSanitizerResult(); - void reportResult(const TestResultPtr &result); - QFutureInterface m_futureInterface; - Utils::QtcProcess *m_testApplication; // not owned + void reportResult(const TestResult &result); + QFutureInterface m_futureInterface; Utils::FilePath m_buildDir; QString m_id; QHash m_summary; int m_disabled = -1; private: enum class SanitizerOutputMode { None, Asan, Ubsan}; - TestResultPtr m_sanitizerResult; + TestResult m_sanitizerResult; QStringList m_sanitizerLines; SanitizerOutputMode m_sanitizerOutputMode = SanitizerOutputMode::None; bool m_hadValidOutput = false; diff --git a/src/plugins/autotest/testprojectsettings.cpp b/src/plugins/autotest/testprojectsettings.cpp index 67ae9ddb003..862fc8a24f4 100644 --- a/src/plugins/autotest/testprojectsettings.cpp +++ b/src/plugins/autotest/testprojectsettings.cpp @@ -12,6 +12,8 @@ #include +using namespace Utils; + namespace Autotest { namespace Internal { @@ -43,7 +45,7 @@ void TestProjectSettings::setUseGlobalSettings(bool useGlobal) m_useGlobalSettings = useGlobal; } -void TestProjectSettings::activateFramework(const Utils::Id &id, bool activate) +void TestProjectSettings::activateFramework(const Id &id, bool activate) { ITestFramework *framework = TestFrameworkManager::frameworkForId(id); m_activeTestFrameworks[framework] = activate; @@ -51,7 +53,7 @@ void TestProjectSettings::activateFramework(const Utils::Id &id, bool activate) framework->resetRootNode(); } -void TestProjectSettings::activateTestTool(const Utils::Id &id, bool activate) +void TestProjectSettings::activateTestTool(const Id &id, bool activate) { ITestTool *testTool = TestFrameworkManager::testToolForId(id); m_activeTestTools[testTool] = activate; @@ -74,12 +76,12 @@ void TestProjectSettings::load() if (activeFrameworks.isValid()) { const QMap frameworksMap = activeFrameworks.toMap(); for (ITestFramework *framework : registeredFrameworks) { - const Utils::Id id = framework->id(); + const Id id = framework->id(); bool active = frameworksMap.value(id.toString(), framework->active()).toBool(); m_activeTestFrameworks.insert(framework, active); } for (ITestTool *testTool : registeredTestTools) { - const Utils::Id id = testTool->id(); + const Id id = testTool->id(); bool active = frameworksMap.value(id.toString(), testTool->active()).toBool(); m_activeTestTools.insert(testTool, active); } diff --git a/src/plugins/autotest/testresult.cpp b/src/plugins/autotest/testresult.cpp index b718b843dba..ae885d6b48c 100644 --- a/src/plugins/autotest/testresult.cpp +++ b/src/plugins/autotest/testresult.cpp @@ -6,6 +6,8 @@ #include #include +using namespace Utils; + namespace Autotest { TestResult::TestResult(const QString &id, const QString &name, const ResultHooks &hooks) @@ -15,6 +17,11 @@ TestResult::TestResult(const QString &id, const QString &name, const ResultHooks { } +bool TestResult::isValid() const +{ + return !m_id.isEmpty(); +} + const QString TestResult::outputString(bool selected) const { if (m_hooks.outputString) @@ -113,10 +120,10 @@ QString TestResult::resultToString(const ResultType type) return QString("BXFAIL"); case ResultType::MessageLocation: case ResultType::Application: - return QString(); + return {}; default: if (type >= ResultType::INTERNAL_MESSAGES_BEGIN && type <= ResultType::INTERNAL_MESSAGES_END) - return QString(); + return {}; return QString("UNKNOWN"); } } @@ -126,64 +133,60 @@ QColor TestResult::colorForType(const ResultType type) if (type >= ResultType::INTERNAL_MESSAGES_BEGIN && type <= ResultType::INTERNAL_MESSAGES_END) return QColor("transparent"); - Utils::Theme *creatorTheme = Utils::creatorTheme(); + const Theme *theme = creatorTheme(); switch (type) { case ResultType::Pass: - return creatorTheme->color(Utils::Theme::OutputPanes_TestPassTextColor); + return theme->color(Theme::OutputPanes_TestPassTextColor); case ResultType::Fail: - return creatorTheme->color(Utils::Theme::OutputPanes_TestFailTextColor); + return theme->color(Theme::OutputPanes_TestFailTextColor); case ResultType::ExpectedFail: - return creatorTheme->color(Utils::Theme::OutputPanes_TestXFailTextColor); + return theme->color(Theme::OutputPanes_TestXFailTextColor); case ResultType::UnexpectedPass: - return creatorTheme->color(Utils::Theme::OutputPanes_TestXPassTextColor); + return theme->color(Theme::OutputPanes_TestXPassTextColor); case ResultType::Skip: - return creatorTheme->color(Utils::Theme::OutputPanes_TestSkipTextColor); + return theme->color(Theme::OutputPanes_TestSkipTextColor); case ResultType::MessageDebug: case ResultType::MessageInfo: - return creatorTheme->color(Utils::Theme::OutputPanes_TestDebugTextColor); + return theme->color(Theme::OutputPanes_TestDebugTextColor); case ResultType::MessageWarn: - return creatorTheme->color(Utils::Theme::OutputPanes_TestWarnTextColor); + return theme->color(Theme::OutputPanes_TestWarnTextColor); case ResultType::MessageFatal: case ResultType::MessageSystem: case ResultType::MessageError: - return creatorTheme->color(Utils::Theme::OutputPanes_TestFatalTextColor); + return theme->color(Theme::OutputPanes_TestFatalTextColor); case ResultType::BlacklistedPass: case ResultType::BlacklistedFail: case ResultType::BlacklistedXPass: case ResultType::BlacklistedXFail: default: - return creatorTheme->color(Utils::Theme::OutputPanes_StdOutTextColor); + return theme->color(Theme::OutputPanes_StdOutTextColor); } } -bool TestResult::isDirectParentOf(const TestResult *other, bool *needsIntermediate) const +bool TestResult::isDirectParentOf(const TestResult &other, bool *needsIntermediate) const { - QTC_ASSERT(other, return false); - const bool ret = !m_id.isEmpty() && m_id == other->m_id && m_name == other->m_name; + QTC_ASSERT(other.isValid(), return false); + const bool ret = !m_id.isEmpty() && m_id == other.m_id && m_name == other.m_name; if (!ret) return false; if (m_hooks.directParent) - return m_hooks.directParent(*this, *other, needsIntermediate); + return m_hooks.directParent(*this, other, needsIntermediate); return true; } -bool TestResult::isIntermediateFor(const TestResult *other) const +bool TestResult::isIntermediateFor(const TestResult &other) const { - QTC_ASSERT(other, return false); + QTC_ASSERT(other.isValid(), return false); if (m_hooks.intermediate) - return m_hooks.intermediate(*this, *other); - return !m_id.isEmpty() && m_id == other->m_id && m_name == other->m_name; + return m_hooks.intermediate(*this, other); + return !m_id.isEmpty() && m_id == other.m_id && m_name == other.m_name; } -TestResult *TestResult::createIntermediateResult() const +TestResult TestResult::intermediateResult() const { - if (m_hooks.createResult) { - TestResult *newResult = new TestResult; - *newResult = m_hooks.createResult(*this); - return newResult; - } - TestResult *intermediate = new TestResult(m_id, m_name); - return intermediate; + if (m_hooks.createResult) + return m_hooks.createResult(*this); + return {m_id, m_name}; } } // namespace Autotest diff --git a/src/plugins/autotest/testresult.h b/src/plugins/autotest/testresult.h index 08136db6f54..423f76e1e24 100644 --- a/src/plugins/autotest/testresult.h +++ b/src/plugins/autotest/testresult.h @@ -5,12 +5,9 @@ #include "autotestconstants.h" -#include +#include #include -#include -#include -#include namespace Autotest { @@ -82,6 +79,7 @@ public: TestResult(const QString &id, const QString &name, const ResultHooks &hooks = {}); virtual ~TestResult() {} + bool isValid() const; const QString outputString(bool selected) const; const ITestTreeItem *findTestTreeItem() const; @@ -103,9 +101,9 @@ public: static QString resultToString(const ResultType type); static QColor colorForType(const ResultType type); - bool isDirectParentOf(const TestResult *other, bool *needsIntermediate) const; - bool isIntermediateFor(const TestResult *other) const; - TestResult *createIntermediateResult() const; + bool isDirectParentOf(const TestResult &other, bool *needsIntermediate) const; + bool isIntermediateFor(const TestResult &other) const; + TestResult intermediateResult() const; private: QString m_id; @@ -117,8 +115,6 @@ private: ResultHooks m_hooks; }; -using TestResultPtr = QSharedPointer; - } // namespace Autotest Q_DECLARE_METATYPE(Autotest::TestResult) diff --git a/src/plugins/autotest/testresultdelegate.cpp b/src/plugins/autotest/testresultdelegate.cpp index 48de155a39b..9b55623d572 100644 --- a/src/plugins/autotest/testresultdelegate.cpp +++ b/src/plugins/autotest/testresultdelegate.cpp @@ -50,9 +50,9 @@ void TestResultDelegate::paint(QPainter *painter, const QStyleOptionViewItem &op painter->fillRect(opt.rect, background); painter->setPen(foreground); - LayoutPositions positions(opt, resultFilterModel); - const TestResult *testResult = resultFilterModel->testResult(index); - QTC_ASSERT(testResult, painter->restore();return); + const LayoutPositions positions(opt, resultFilterModel); + const TestResult testResult = resultFilterModel->testResult(index); + QTC_ASSERT(testResult.isValid(), painter->restore(); return); const QWidget *widget = dynamic_cast(painter->device()); QWindow *window = widget ? widget->window()->windowHandle() : nullptr; @@ -69,15 +69,15 @@ void TestResultDelegate::paint(QPainter *painter, const QStyleOptionViewItem &op painter->drawText(positions.typeAreaLeft(), positions.top() + fm.ascent(), typeStr); } else { QPen tmp = painter->pen(); - if (testResult->result() == ResultType::TestStart) + if (testResult.result() == ResultType::TestStart) painter->setPen(opt.palette.mid().color()); else - painter->setPen(TestResult::colorForType(testResult->result())); + painter->setPen(TestResult::colorForType(testResult.result())); painter->drawText(positions.typeAreaLeft(), positions.top() + fm.ascent(), typeStr); painter->setPen(tmp); } - QString output = testResult->outputString(selected); + QString output = testResult.outputString(selected); if (selected) { limitTextOutput(output); @@ -92,12 +92,12 @@ void TestResultDelegate::paint(QPainter *painter, const QStyleOptionViewItem &op fm.elidedText(output.left(2000), Qt::ElideRight, positions.textAreaWidth())); } - const QString file = testResult->fileName().fileName(); + const QString file = testResult.fileName().fileName(); painter->setClipRect(positions.fileArea()); painter->drawText(positions.fileAreaLeft(), positions.top() + fm.ascent(), file); - if (testResult->line()) { - QString line = QString::number(testResult->line()); + if (testResult.line()) { + QString line = QString::number(testResult.line()); painter->setClipRect(positions.lineArea()); painter->drawText(positions.lineAreaLeft(), positions.top() + fm.ascent(), line); } @@ -129,9 +129,9 @@ QSize TestResultDelegate::sizeHint(const QStyleOptionViewItem &option, const QMo s.setWidth(opt.rect.width() - indentation); if (selected) { - const TestResult *testResult = resultFilterModel->testResult(index); - QTC_ASSERT(testResult, return QSize()); - QString output = testResult->outputString(selected); + const TestResult testResult = resultFilterModel->testResult(index); + QTC_ASSERT(testResult.isValid(), return {}); + QString output = testResult.outputString(selected); limitTextOutput(output); output.replace('\n', QChar::LineSeparator); recalculateTextLayout(index, output, opt.font, positions.textAreaWidth() - indentation); diff --git a/src/plugins/autotest/testresultmodel.cpp b/src/plugins/autotest/testresultmodel.cpp index 0eb9faf52e5..8a65a377da3 100644 --- a/src/plugins/autotest/testresultmodel.cpp +++ b/src/plugins/autotest/testresultmodel.cpp @@ -16,12 +16,14 @@ #include #include +using namespace Utils; + namespace Autotest { namespace Internal { /********************************* TestResultItem ******************************************/ -TestResultItem::TestResultItem(const TestResultPtr &testResult) +TestResultItem::TestResultItem(const TestResult &testResult) : m_testResult(testResult) { } @@ -71,9 +73,9 @@ QVariant TestResultItem::data(int column, int role) const { switch (role) { case Qt::DecorationRole: { - if (!m_testResult) - return QVariant(); - const ResultType result = m_testResult->result(); + if (!m_testResult.isValid()) + return {}; + const ResultType result = m_testResult.result(); if (result == ResultType::MessageLocation && parent()) return parent()->data(column, role); if (result == ResultType::TestStart) @@ -81,17 +83,16 @@ QVariant TestResultItem::data(int column, int role) const return testResultIcon(result); } case Qt::DisplayRole: - return m_testResult ? m_testResult->outputString(true) : QVariant(); + return m_testResult.isValid() ? m_testResult.outputString(true) : QVariant(); default: - return Utils::TreeItem::data(column, role); + return TreeItem::data(column, role); } } void TestResultItem::updateDescription(const QString &description) { - QTC_ASSERT(m_testResult, return); - - m_testResult->setDescription(description); + QTC_ASSERT(m_testResult.isValid(), return); + m_testResult.setDescription(description); } static bool isSignificant(ResultType type) @@ -117,7 +118,7 @@ void TestResultItem::updateResult(bool &changed, ResultType addedChildType, const std::optional &summary) { changed = false; - if (m_testResult->result() != ResultType::TestStart) + if (m_testResult.result() != ResultType::TestStart) return; if (!isSignificant(addedChildType) || (addedChildType == ResultType::TestStart && !summary)) @@ -165,13 +166,13 @@ void TestResultItem::updateResult(bool &changed, ResultType addedChildType, TestResultItem *TestResultItem::intermediateFor(const TestResultItem *item) const { QTC_ASSERT(item, return nullptr); - const TestResult *otherResult = item->testResult(); + const TestResult otherResult = item->testResult(); for (int row = childCount() - 1; row >= 0; --row) { TestResultItem *child = childAt(row); - const TestResult *testResult = child->testResult(); - if (testResult->result() != ResultType::TestStart) + const TestResult testResult = child->testResult(); + if (testResult.result() != ResultType::TestStart) continue; - if (testResult->isIntermediateFor(otherResult)) + if (testResult.isIntermediateFor(otherResult)) return child; } return nullptr; @@ -179,9 +180,9 @@ TestResultItem *TestResultItem::intermediateFor(const TestResultItem *item) cons TestResultItem *TestResultItem::createAndAddIntermediateFor(const TestResultItem *child) { - TestResultPtr result(child->testResult()->createIntermediateResult()); - QTC_ASSERT(!result.isNull(), return nullptr); - result->setResult(ResultType::TestStart); + TestResult result = child->testResult().intermediateResult(); + QTC_ASSERT(result.isValid(), return nullptr); + result.setResult(ResultType::TestStart); TestResultItem *intermediate = new TestResultItem(result); appendChild(intermediate); return intermediate; @@ -189,17 +190,17 @@ TestResultItem *TestResultItem::createAndAddIntermediateFor(const TestResultItem QString TestResultItem::resultString() const { - if (testResult()->result() != ResultType::TestStart) - return TestResult::resultToString(testResult()->result()); + if (testResult().result() != ResultType::TestStart) + return TestResult::resultToString(testResult().result()); if (!m_summaryResult) - return QString(); + return {}; return m_summaryResult->failed ? QString("FAIL") : QString("PASS"); } /********************************* TestResultModel *****************************************/ TestResultModel::TestResultModel(QObject *parent) - : Utils::TreeModel(new TestResultItem(TestResultPtr()), parent) + : TreeModel(new TestResultItem({}), parent) { connect(TestRunner::instance(), &TestRunner::reportSummary, this, [this](const QString &id, const QHash &summary){ @@ -210,12 +211,12 @@ TestResultModel::TestResultModel(QObject *parent) void TestResultModel::updateParent(const TestResultItem *item) { QTC_ASSERT(item, return); - QTC_ASSERT(item->testResult(), return); + QTC_ASSERT(item->testResult().isValid(), return); TestResultItem *parentItem = item->parent(); if (parentItem == rootItem()) // do not update invisible root item return; bool changed = false; - parentItem->updateResult(changed, item->testResult()->result(), item->summaryResult()); + parentItem->updateResult(changed, item->testResult().result(), item->summaryResult()); if (!changed) return; emit dataChanged(parentItem->index(), parentItem->index()); @@ -232,16 +233,16 @@ static bool isFailed(ResultType type) } } -void TestResultModel::addTestResult(const TestResultPtr &testResult, bool autoExpand) +void TestResultModel::addTestResult(const TestResult &testResult, bool autoExpand) { const int lastRow = rootItem()->childCount() - 1; - if (testResult->result() == ResultType::MessageCurrentTest) { + if (testResult.result() == ResultType::MessageCurrentTest) { // MessageCurrentTest should always be the last top level item if (lastRow >= 0) { TestResultItem *current = rootItem()->childAt(lastRow); - const TestResult *result = current->testResult(); - if (result && result->result() == ResultType::MessageCurrentTest) { - current->updateDescription(testResult->description()); + const TestResult result = current->testResult(); + if (result.isValid() && result.result() == ResultType::MessageCurrentTest) { + current->updateDescription(testResult.description()); emit dataChanged(current->index(), current->index()); return; } @@ -251,22 +252,22 @@ void TestResultModel::addTestResult(const TestResultPtr &testResult, bool autoEx return; } - m_testResultCount[testResult->id()][testResult->result()]++; + m_testResultCount[testResult.id()][testResult.result()]++; TestResultItem *newItem = new TestResultItem(testResult); TestResultItem *root = nullptr; if (AutotestPlugin::settings()->displayApplication) { - const QString application = testResult->id(); + const QString application = testResult.id(); if (!application.isEmpty()) { root = rootItem()->findFirstLevelChild([&application](TestResultItem *child) { QTC_ASSERT(child, return false); - return child->testResult()->id() == application; + return child->testResult().id() == application; }); if (!root) { - TestResult *tmpAppResult = new TestResult(application, application); - tmpAppResult->setResult(ResultType::Application); - root = new TestResultItem(TestResultPtr(tmpAppResult)); + TestResult tmpAppResult(application, application); + tmpAppResult.setResult(ResultType::Application); + root = new TestResultItem(tmpAppResult); if (lastRow >= 0) rootItem()->insertChild(lastRow, root); else @@ -276,20 +277,20 @@ void TestResultModel::addTestResult(const TestResultPtr &testResult, bool autoEx } TestResultItem *parentItem = findParentItemFor(newItem, root); - addFileName(testResult->fileName().fileName()); // ensure we calculate the results pane correctly + addFileName(testResult.fileName().fileName()); // ensure we calculate the results pane correctly if (parentItem) { parentItem->appendChild(newItem); if (autoExpand) { parentItem->expand(); newItem->expand(); - newItem->forAllChildren([](Utils::TreeItem *it) { it->expand(); }); + newItem->forAllChildren([](TreeItem *it) { it->expand(); }); } updateParent(newItem); } else { if (lastRow >= 0) { TestResultItem *current = rootItem()->childAt(lastRow); - const TestResult *result = current->testResult(); - if (result && result->result() == ResultType::MessageCurrentTest) { + const TestResult result = current->testResult(); + if (result.isValid() && result.result() == ResultType::MessageCurrentTest) { rootItem()->insertChild(current->index().row(), newItem); return; } @@ -298,8 +299,8 @@ void TestResultModel::addTestResult(const TestResultPtr &testResult, bool autoEx rootItem()->appendChild(newItem); } - if (isFailed(testResult->result())) { - if (const ITestTreeItem *it = testResult->findTestTreeItem()) { + if (isFailed(testResult.result())) { + if (const ITestTreeItem *it = testResult.findTestTreeItem()) { TestTreeModel *model = TestTreeModel::instance(); model->setData(model->indexForItem(it), true, FailedRole); } @@ -309,7 +310,7 @@ void TestResultModel::addTestResult(const TestResultPtr &testResult, bool autoEx void TestResultModel::removeCurrentTestMessage() { TestResultItem *currentMessageItem = rootItem()->findFirstLevelChild([](TestResultItem *it) { - return (it->testResult()->result() == ResultType::MessageCurrentTest); + return (it->testResult().result() == ResultType::MessageCurrentTest); }); if (currentMessageItem) destroyItem(currentMessageItem); @@ -326,12 +327,11 @@ void TestResultModel::clearTestResults() m_widthOfLineNumber = 0; } -const TestResult *TestResultModel::testResult(const QModelIndex &idx) +TestResult TestResultModel::testResult(const QModelIndex &idx) { if (idx.isValid()) return itemForIndex(idx)->testResult(); - - return nullptr; + return {}; } void TestResultModel::recalculateMaxWidthOfFileName(const QFont &font) @@ -383,15 +383,15 @@ TestResultItem *TestResultModel::findParentItemFor(const TestResultItem *item, { QTC_ASSERT(item, return nullptr); TestResultItem *root = startItem ? const_cast(startItem) : nullptr; - const TestResult *result = item->testResult(); - const QString &name = result->name(); - const QString &id = result->id(); + const TestResult result = item->testResult(); + const QString &name = result.name(); + const QString &id = result.id(); if (root == nullptr && !name.isEmpty()) { for (int row = rootItem()->childCount() - 1; row >= 0; --row) { TestResultItem *tmp = rootItem()->childAt(row); - auto tmpTestResult = tmp->testResult(); - if (tmpTestResult->id() == id && tmpTestResult->name() == name) { + const TestResult tmpTestResult = tmp->testResult(); + if (tmpTestResult.id() == id && tmpTestResult.name() == name) { root = tmp; break; } @@ -401,9 +401,9 @@ TestResultItem *TestResultModel::findParentItemFor(const TestResultItem *item, return root; bool needsIntermediate = false; - auto predicate = [result, &needsIntermediate](Utils::TreeItem *it) { + auto predicate = [result, &needsIntermediate](TreeItem *it) { TestResultItem *currentItem = static_cast(it); - return currentItem->testResult()->isDirectParentOf(result, &needsIntermediate); + return currentItem->testResult().isDirectParentOf(result, &needsIntermediate); }; TestResultItem *parent = root->reverseFindAnyChild(predicate); if (parent) { @@ -480,7 +480,7 @@ bool TestResultFilterModel::hasResults() return rowCount(QModelIndex()); } -const TestResult *TestResultFilterModel::testResult(const QModelIndex &index) const +TestResult TestResultFilterModel::testResult(const QModelIndex &index) const { return m_sourceModel->testResult(mapToSource(index)); } @@ -495,7 +495,7 @@ bool TestResultFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &s QModelIndex index = m_sourceModel->index(sourceRow, 0, sourceParent); if (!index.isValid()) return false; - ResultType resultType = m_sourceModel->testResult(index)->result(); + const ResultType resultType = m_sourceModel->testResult(index).result(); if (resultType == ResultType::TestStart) { TestResultItem *item = m_sourceModel->itemForIndex(index); QTC_ASSERT(item, return false); @@ -511,7 +511,7 @@ bool TestResultFilterModel::acceptTestCaseResult(const QModelIndex &srcIndex) co for (int row = 0, count = m_sourceModel->rowCount(srcIndex); row < count; ++row) { const QModelIndex &child = m_sourceModel->index(row, 0, srcIndex); TestResultItem *item = m_sourceModel->itemForIndex(child); - ResultType type = item->testResult()->result(); + const ResultType type = item->testResult().result(); if (type == ResultType::TestStart) { if (!item->summaryResult()) diff --git a/src/plugins/autotest/testresultmodel.h b/src/plugins/autotest/testresultmodel.h index f4de2e87f0b..97655ca57b9 100644 --- a/src/plugins/autotest/testresultmodel.h +++ b/src/plugins/autotest/testresultmodel.h @@ -20,9 +20,9 @@ namespace Internal { class TestResultItem : public Utils::TypedTreeItem { public: - explicit TestResultItem(const TestResultPtr &testResult); + explicit TestResultItem(const TestResult &testResult); QVariant data(int column, int role) const override; - const TestResult *testResult() const { return m_testResult.data(); } + TestResult testResult() const { return m_testResult; } void updateDescription(const QString &description); struct SummaryEvaluation @@ -45,7 +45,7 @@ public: std::optional summaryResult() const { return m_summaryResult; } private: - TestResultPtr m_testResult; + TestResult m_testResult; std::optional m_summaryResult; }; @@ -54,11 +54,11 @@ class TestResultModel : public Utils::TreeModel public: explicit TestResultModel(QObject *parent = nullptr); - void addTestResult(const TestResultPtr &testResult, bool autoExpand = false); + void addTestResult(const TestResult &testResult, bool autoExpand = false); void removeCurrentTestMessage(); void clearTestResults(); - const TestResult *testResult(const QModelIndex &idx); + TestResult testResult(const QModelIndex &idx); int maxWidthOfFileName(const QFont &font); int maxWidthOfLineNumber(const QFont &font); @@ -92,7 +92,7 @@ public: void toggleTestResultType(ResultType type); void clearTestResults(); bool hasResults(); - const TestResult *testResult(const QModelIndex &index) const; + TestResult testResult(const QModelIndex &index) const; TestResultItem *itemForIndex(const QModelIndex &index) const; protected: diff --git a/src/plugins/autotest/testresultspane.cpp b/src/plugins/autotest/testresultspane.cpp index 86200584322..aacd12adcff 100644 --- a/src/plugins/autotest/testresultspane.cpp +++ b/src/plugins/autotest/testresultspane.cpp @@ -56,7 +56,7 @@ namespace Autotest { namespace Internal { ResultsTreeView::ResultsTreeView(QWidget *parent) - : Utils::TreeView(parent) + : TreeView(parent) { setAttribute(Qt::WA_MacShowFocusRect, false); setFrameStyle(NoFrame); @@ -84,10 +84,8 @@ TestResultsPane::TestResultsPane(QObject *parent) : visualOutputWidget->setLayout(outputLayout); QPalette pal; - pal.setColor(QPalette::Window, - Utils::creatorTheme()->color(Utils::Theme::InfoBarBackground)); - pal.setColor(QPalette::WindowText, - Utils::creatorTheme()->color(Utils::Theme::InfoBarText)); + pal.setColor(QPalette::Window, creatorTheme()->color(Theme::InfoBarBackground)); + pal.setColor(QPalette::WindowText, creatorTheme()->color(Theme::InfoBarText)); m_summaryWidget = new QFrame; m_summaryWidget->setPalette(pal); m_summaryWidget->setAutoFillBackground(true); @@ -130,10 +128,10 @@ TestResultsPane::TestResultsPane(QObject *parent) : createToolButtons(); - connect(m_treeView, &Utils::TreeView::activated, this, &TestResultsPane::onItemActivated); + connect(m_treeView, &TreeView::activated, this, &TestResultsPane::onItemActivated); connect(m_treeView->selectionModel(), &QItemSelectionModel::currentChanged, trd, &TestResultDelegate::currentChanged); - connect(m_treeView, &Utils::TreeView::customContextMenuRequested, + connect(m_treeView, &TreeView::customContextMenuRequested, this, &TestResultsPane::onCustomContextMenuRequested); connect(m_treeView, &ResultsTreeView::copyShortcutTriggered, this, [this] { onCopyItemTriggered(getTestResult(m_treeView->currentIndex())); @@ -167,25 +165,21 @@ void TestResultsPane::createToolButtons() }); m_runAll = new QToolButton(m_treeView); - m_runAll->setDefaultAction( - Utils::ProxyAction::proxyActionWithIcon( + m_runAll->setDefaultAction(ProxyAction::proxyActionWithIcon( ActionManager::command(Constants::ACTION_RUN_ALL_ID)->action(), Utils::Icons::RUN_SMALL_TOOLBAR.icon())); m_runSelected = new QToolButton(m_treeView); - m_runSelected->setDefaultAction( - Utils::ProxyAction::proxyActionWithIcon( + m_runSelected->setDefaultAction(ProxyAction::proxyActionWithIcon( ActionManager::command(Constants::ACTION_RUN_SELECTED_ID)->action(), Utils::Icons::RUN_SELECTED_TOOLBAR.icon())); m_runFailed = new QToolButton(m_treeView); - m_runFailed->setDefaultAction( - Utils::ProxyAction::proxyActionWithIcon( + m_runFailed->setDefaultAction(ProxyAction::proxyActionWithIcon( ActionManager::command(Constants::ACTION_RUN_FAILED_ID)->action(), Icons::RUN_FAILED_TOOLBAR.icon())); m_runFile = new QToolButton(m_treeView); - m_runFile->setDefaultAction( - Utils::ProxyAction::proxyActionWithIcon( + m_runFile->setDefaultAction(ProxyAction::proxyActionWithIcon( ActionManager::command(Constants::ACTION_RUN_FILE_ID)->action(), Utils::Icons::RUN_FILE_TOOLBAR.icon())); @@ -228,7 +222,7 @@ TestResultsPane::~TestResultsPane() s_instance = nullptr; } -void TestResultsPane::addTestResult(const TestResultPtr &result) +void TestResultsPane::addTestResult(const TestResult &result) { const QScrollBar *scrollBar = m_treeView->verticalScrollBar(); m_atEnd = scrollBar ? scrollBar->value() == scrollBar->maximum() : true; @@ -248,22 +242,22 @@ static void checkAndFineTuneColors(QTextCharFormat *format) const QColor bgColor = format->background().color(); QColor fgColor = format->foreground().color(); - if (Utils::StyleHelper::isReadableOn(bgColor, fgColor)) + if (StyleHelper::isReadableOn(bgColor, fgColor)) return; int h, s, v; fgColor.getHsv(&h, &s, &v); // adjust the color value to ensure better readability - if (Utils::StyleHelper::luminance(bgColor) < .5) + if (StyleHelper::luminance(bgColor) < .5) v = v + 64; else v = v - 64; fgColor.setHsv(h, s, v); - if (!Utils::StyleHelper::isReadableOn(bgColor, fgColor)) { + if (!StyleHelper::isReadableOn(bgColor, fgColor)) { s = (s + 128) % 255; // adjust the saturation to ensure better readability fgColor.setHsv(h, s, v); - if (!Utils::StyleHelper::isReadableOn(bgColor, fgColor)) + if (!StyleHelper::isReadableOn(bgColor, fgColor)) return; } @@ -278,9 +272,9 @@ void TestResultsPane::addOutputLine(const QByteArray &outputLine, OutputChannel return; } - const Utils::FormattedText formattedText - = Utils::FormattedText{QString::fromUtf8(outputLine), m_defaultFormat}; - const QList formatted = channel == OutputChannel::StdOut + const FormattedText formattedText + = FormattedText{QString::fromUtf8(outputLine), m_defaultFormat}; + const QList formatted = channel == OutputChannel::StdOut ? m_stdOutHandler.parseText(formattedText) : m_stdErrHandler.parseText(formattedText); @@ -332,9 +326,9 @@ void TestResultsPane::clearContents() connect(m_treeView->verticalScrollBar(), &QScrollBar::rangeChanged, this, &TestResultsPane::onScrollBarRangeChanged, Qt::UniqueConnection); m_textOutput->clear(); - m_defaultFormat.setBackground(Utils::creatorTheme()->palette().color( + m_defaultFormat.setBackground(creatorTheme()->palette().color( m_textOutput->backgroundRole())); - m_defaultFormat.setForeground(Utils::creatorTheme()->palette().color( + m_defaultFormat.setForeground(creatorTheme()->palette().color( m_textOutput->foregroundRole())); // in case they had been forgotten to reset @@ -402,7 +396,7 @@ void TestResultsPane::goToNext() // if we have no current or could not find a next one, use the first item of the whole tree if (!nextCurrentIndex.isValid()) { - Utils::TreeItem *rootItem = m_model->itemForIndex(QModelIndex()); + TreeItem *rootItem = m_model->itemForIndex(QModelIndex()); // if the tree does not contain any item - don't do anything if (!rootItem || !rootItem->childCount()) return; @@ -457,9 +451,9 @@ void TestResultsPane::onItemActivated(const QModelIndex &index) if (!index.isValid()) return; - const TestResult *testResult = m_filterModel->testResult(index); - if (testResult && !testResult->fileName().isEmpty()) - EditorManager::openEditorAt(Utils::Link{testResult->fileName(), testResult->line(), 0}); + const TestResult testResult = m_filterModel->testResult(index); + if (testResult.isValid() && !testResult.fileName().isEmpty()) + EditorManager::openEditorAt(Link{testResult.fileName(), testResult.line(), 0}); } void TestResultsPane::onRunAllTriggered() @@ -609,12 +603,12 @@ void TestResultsPane::onCustomContextMenuRequested(const QPoint &pos) { const bool resultsAvailable = m_filterModel->hasResults(); const bool enabled = !m_testRunning && resultsAvailable; - const TestResult *clicked = getTestResult(m_treeView->indexAt(pos)); + const TestResult clicked = getTestResult(m_treeView->indexAt(pos)); QMenu menu; QAction *action = new QAction(Tr::tr("Copy"), &menu); action->setShortcut(QKeySequence(QKeySequence::Copy)); - action->setEnabled(resultsAvailable && clicked); + action->setEnabled(resultsAvailable && clicked.isValid()); connect(action, &QAction::triggered, this, [this, clicked] { onCopyItemTriggered(clicked); }); @@ -630,7 +624,7 @@ void TestResultsPane::onCustomContextMenuRequested(const QPoint &pos) connect(action, &QAction::triggered, this, &TestResultsPane::onSaveWholeTriggered); menu.addAction(action); - const auto correlatingItem = (enabled && clicked) ? clicked->findTestTreeItem() : nullptr; + const auto correlatingItem = (enabled && clicked.isValid()) ? clicked.findTestTreeItem() : nullptr; action = new QAction(Tr::tr("Run This Test"), &menu); action->setEnabled(correlatingItem && correlatingItem->canProvideTestConfiguration()); connect(action, &QAction::triggered, this, [this, clicked] { @@ -669,21 +663,19 @@ void TestResultsPane::onCustomContextMenuRequested(const QPoint &pos) menu.exec(m_treeView->mapToGlobal(pos)); } -const TestResult *TestResultsPane::getTestResult(const QModelIndex &idx) +TestResult TestResultsPane::getTestResult(const QModelIndex &idx) { if (!idx.isValid()) - return nullptr; - - const TestResult *result = m_filterModel->testResult(idx); - QTC_CHECK(result); - + return {}; + const TestResult result = m_filterModel->testResult(idx); + QTC_CHECK(result.isValid()); return result; } -void TestResultsPane::onCopyItemTriggered(const TestResult *result) +void TestResultsPane::onCopyItemTriggered(const TestResult &result) { - QTC_ASSERT(result, return); - setClipboardAndSelection(result->outputString(true)); + QTC_ASSERT(result.isValid(), return); + setClipboardAndSelection(result.outputString(true)); } void TestResultsPane::onCopyWholeTriggered() @@ -705,12 +697,11 @@ void TestResultsPane::onSaveWholeTriggered() } } -void TestResultsPane::onRunThisTestTriggered(TestRunMode runMode, const TestResult *result) +void TestResultsPane::onRunThisTestTriggered(TestRunMode runMode, const TestResult &result) { - QTC_ASSERT(result, return); - - const ITestTreeItem *item = result->findTestTreeItem(); + QTC_ASSERT(result.isValid(), return); + const ITestTreeItem *item = result.findTestTreeItem(); if (item) TestRunner::instance()->runTest(runMode, item); } @@ -729,11 +720,11 @@ QString TestResultsPane::getWholeOutput(const QModelIndex &parent) QString output; for (int row = 0, count = m_model->rowCount(parent); row < count; ++row) { QModelIndex current = m_model->index(row, 0, parent); - const TestResult *result = m_model->testResult(current); - QTC_ASSERT(result, continue); + const TestResult result = m_model->testResult(current); + QTC_ASSERT(result.isValid(), continue); if (auto item = m_model->itemForIndex(current)) output.append(item->resultString()).append('\t'); - output.append(result->outputString(true)).append('\n'); + output.append(result.outputString(true)).append('\n'); output.append(getWholeOutput(current)); } return output; @@ -741,25 +732,25 @@ QString TestResultsPane::getWholeOutput(const QModelIndex &parent) void TestResultsPane::createMarks(const QModelIndex &parent) { - const TestResult *parentResult = m_model->testResult(parent); - ResultType parentType = parentResult ? parentResult->result() : ResultType::Invalid; + const TestResult parentResult = m_model->testResult(parent); + const ResultType parentType = parentResult.isValid() ? parentResult.result() : ResultType::Invalid; const QVector interested{ResultType::Fail, ResultType::UnexpectedPass}; for (int row = 0, count = m_model->rowCount(parent); row < count; ++row) { const QModelIndex index = m_model->index(row, 0, parent); - const TestResult *result = m_model->testResult(index); - QTC_ASSERT(result, continue); + const TestResult result = m_model->testResult(index); + QTC_ASSERT(result.isValid(), continue); if (m_model->hasChildren(index)) createMarks(index); - bool isLocationItem = result->result() == ResultType::MessageLocation; - if (interested.contains(result->result()) + bool isLocationItem = result.result() == ResultType::MessageLocation; + if (interested.contains(result.result()) || (isLocationItem && interested.contains(parentType))) { - TestEditorMark *mark = new TestEditorMark(index, result->fileName(), result->line()); + TestEditorMark *mark = new TestEditorMark(index, result.fileName(), result.line()); mark->setIcon(index.data(Qt::DecorationRole).value()); - mark->setColor(Utils::Theme::OutputPanes_TestFailTextColor); + mark->setColor(Theme::OutputPanes_TestFailTextColor); mark->setPriority(TextEditor::TextMark::NormalPriority); - mark->setToolTip(result->description()); + mark->setToolTip(result.description()); m_marks << mark; } } diff --git a/src/plugins/autotest/testresultspane.h b/src/plugins/autotest/testresultspane.h index ab255d74b36..7cede72ad65 100644 --- a/src/plugins/autotest/testresultspane.h +++ b/src/plugins/autotest/testresultspane.h @@ -72,7 +72,7 @@ public: void goToNext() override; void goToPrev() override; - void addTestResult(const TestResultPtr &result); + void addTestResult(const TestResult &result); void addOutputLine(const QByteArray &outputLine, OutputChannel channel); void showTestResult(const QModelIndex &index); private: @@ -92,11 +92,11 @@ private: void onTestRunFinished(); void onScrollBarRangeChanged(int, int max); void onCustomContextMenuRequested(const QPoint &pos); - const TestResult *getTestResult(const QModelIndex &idx); - void onCopyItemTriggered(const TestResult *result); + TestResult getTestResult(const QModelIndex &idx); + void onCopyItemTriggered(const TestResult &result); void onCopyWholeTriggered(); void onSaveWholeTriggered(); - void onRunThisTestTriggered(TestRunMode runMode, const TestResult *result); + void onRunThisTestTriggered(TestRunMode runMode, const TestResult &result); void toggleOutputStyle(); QString getWholeOutput(const QModelIndex &parent = QModelIndex()); diff --git a/src/plugins/autotest/testrunner.cpp b/src/plugins/autotest/testrunner.cpp index 1ed54b63208..76138a8ea4b 100644 --- a/src/plugins/autotest/testrunner.cpp +++ b/src/plugins/autotest/testrunner.cpp @@ -72,13 +72,11 @@ TestRunner::TestRunner() m_cancelTimer.setSingleShot(true); connect(&m_cancelTimer, &QTimer::timeout, this, [this] { cancelCurrent(Timeout); }); - connect(&m_futureWatcher, &QFutureWatcher::resultReadyAt, - this, [this](int index) { emit testResultReady(m_futureWatcher.resultAt(index)); }); - connect(&m_futureWatcher, &QFutureWatcher::finished, + connect(&m_futureWatcher, &QFutureWatcher::finished, this, &TestRunner::onFinished); connect(this, &TestRunner::requestStopTestRun, - &m_futureWatcher, &QFutureWatcher::cancel); - connect(&m_futureWatcher, &QFutureWatcher::canceled, this, [this] { + &m_futureWatcher, &QFutureWatcher::cancel); + connect(&m_futureWatcher, &QFutureWatcher::canceled, this, [this] { cancelCurrent(UserCanceled); reportResult(ResultType::MessageFatal, Tr::tr("Test run canceled by user.")); }); @@ -148,42 +146,22 @@ static QString constructOmittedVariablesDetailsString(const EnvironmentItems &di bool TestRunner::currentConfigValid() { - FilePath commandFilePath; - if (m_currentConfig->testBase()->type() == ITestBase::Framework) { - TestConfiguration *current = static_cast(m_currentConfig); - commandFilePath = current->executableFilePath(); - } else { - TestToolConfiguration *current = static_cast(m_currentConfig); - commandFilePath = current->commandLine().executable(); - } - if (commandFilePath.isEmpty()) { - reportResult(ResultType::MessageFatal, - Tr::tr("Executable path is empty. (%1)").arg(m_currentConfig->displayName())); - delete m_currentConfig; - m_currentConfig = nullptr; - if (m_selectedTests.isEmpty()) { - if (m_fakeFutureInterface) - m_fakeFutureInterface->reportFinished(); - onFinished(); - } else { - onProcessDone(); - } - return false; - } - return true; -} + const FilePath commandFilePath = m_currentConfig->testExecutable(); + if (!commandFilePath.isEmpty()) + return true; -void TestRunner::setUpProcess() -{ - QTC_ASSERT(m_currentConfig, return); - m_currentProcess = new QtcProcess; - if (m_currentConfig->testBase()->type() == ITestBase::Framework) { - TestConfiguration *current = static_cast(m_currentConfig); - m_currentProcess->setCommand({current->executableFilePath(), {}}); + reportResult(ResultType::MessageFatal, + Tr::tr("Executable path is empty. (%1)").arg(m_currentConfig->displayName())); + delete m_currentConfig; + m_currentConfig = nullptr; + if (m_selectedTests.isEmpty()) { + if (m_fakeFutureInterface) + m_fakeFutureInterface->reportFinished(); + onFinished(); } else { - TestToolConfiguration *current = static_cast(m_currentConfig); - m_currentProcess->setCommand({current->commandLine().executable(), {}}); + onProcessDone(); } + return false; } void TestRunner::setUpProcessEnv() @@ -234,12 +212,13 @@ void TestRunner::scheduleNext() if (!m_currentConfig->project()) onProcessDone(); - setUpProcess(); - QTC_ASSERT(m_currentProcess, onProcessDone(); return); + m_currentProcess = new QtcProcess; + m_currentProcess->setCommand({m_currentConfig->testExecutable(), {}}); + QTC_ASSERT(!m_currentOutputReader, delete m_currentOutputReader); m_currentOutputReader = m_currentConfig->createOutputReader(*m_fakeFutureInterface, m_currentProcess); QTC_ASSERT(m_currentOutputReader, onProcessDone(); return); - + connect(m_currentOutputReader, &TestOutputReader::newResult, this, &TestRunner::testResultReady); connect(m_currentOutputReader, &TestOutputReader::newOutputLineAvailable, TestResultsPane::instance(), &TestResultsPane::addOutputLine); @@ -537,8 +516,8 @@ void TestRunner::runTestsHelper() int testCaseCount = precheckTestConfigurations(); // Fake future interface - destruction will be handled by QFuture/QFutureWatcher - m_fakeFutureInterface = new QFutureInterface(QFutureInterfaceBase::Running); - QFuture future = m_fakeFutureInterface->future(); + m_fakeFutureInterface = new QFutureInterface(QFutureInterfaceBase::Running); + QFuture future = m_fakeFutureInterface->future(); m_fakeFutureInterface->setProgressRange(0, testCaseCount); m_fakeFutureInterface->setProgressValue(0); m_futureWatcher.setFuture(future); @@ -648,12 +627,13 @@ void TestRunner::debugTests() } // We need a fake QFuture for the results. TODO: replace with QtConcurrent::run - QFutureInterface *futureInterface - = new QFutureInterface(QFutureInterfaceBase::Running); + QFutureInterface *futureInterface + = new QFutureInterface(QFutureInterfaceBase::Running); m_futureWatcher.setFuture(futureInterface->future()); if (useOutputProcessor) { TestOutputReader *outputreader = config->createOutputReader(*futureInterface, nullptr); + connect(outputreader, &TestOutputReader::newResult, this, &TestRunner::testResultReady); outputreader->setId(inferior.command.executable().toString()); connect(outputreader, &TestOutputReader::newOutputLineAvailable, TestResultsPane::instance(), &TestResultsPane::addOutputLine); @@ -804,9 +784,9 @@ void TestRunner::onFinished() void TestRunner::reportResult(ResultType type, const QString &description) { - TestResultPtr result(new TestResult); - result->setResult(type); - result->setDescription(description); + TestResult result("internal", {}); + result.setResult(type); + result.setDescription(description); emit testResultReady(result); } diff --git a/src/plugins/autotest/testrunner.h b/src/plugins/autotest/testrunner.h index 2bb8527479a..c96af56075f 100644 --- a/src/plugins/autotest/testrunner.h +++ b/src/plugins/autotest/testrunner.h @@ -50,7 +50,7 @@ signals: void testRunStarted(); void testRunFinished(); void requestStopTestRun(); - void testResultReady(const TestResultPtr &result); + void testResultReady(const TestResult &result); void hadDisabledTests(int disabled); void reportSummary(const QString &id, const QHash &summary); @@ -62,7 +62,6 @@ private: int precheckTestConfigurations(); bool currentConfigValid(); - void setUpProcess(); void setUpProcessEnv(); void scheduleNext(); void cancelCurrent(CancelReason reason); @@ -76,8 +75,8 @@ private: bool postponeTestRunWithEmptyExecutable(ProjectExplorer::Project *project); void onBuildSystemUpdated(); - QFutureWatcher m_futureWatcher; - QFutureInterface *m_fakeFutureInterface = nullptr; + QFutureWatcher m_futureWatcher; + QFutureInterface *m_fakeFutureInterface = nullptr; QList m_selectedTests; bool m_executingTests = false; bool m_canceled = false; diff --git a/src/plugins/autotest/testsettingspage.cpp b/src/plugins/autotest/testsettingspage.cpp index 588a58f7780..1cd6e2d1ae4 100644 --- a/src/plugins/autotest/testsettingspage.cpp +++ b/src/plugins/autotest/testsettingspage.cpp @@ -29,6 +29,8 @@ #include #include +using namespace Utils; + namespace Autotest { namespace Internal { @@ -41,8 +43,8 @@ public: TestSettings settings() const; private: - void populateFrameworksListWidget(const QHash &frameworks, - const QHash &testTools); + void populateFrameworksListWidget(const QHash &frameworks, + const QHash &testTools); void testSettings(TestSettings &settings) const; void testToolsSettings(TestSettings &settings) const; void onFrameworkItemChanged(); @@ -61,7 +63,7 @@ private: QComboBox *m_runAfterBuildCB; QSpinBox *m_timeoutSpin; QTreeWidget *m_frameworkTreeWidget; - Utils::InfoLabel *m_frameworksWarn; + InfoLabel *m_frameworksWarn; }; TestSettingsWidget::TestSettingsWidget(QWidget *parent) @@ -146,12 +148,12 @@ TestSettingsWidget::TestSettingsWidget(QWidget *parent) item->setText(1, Tr::tr("Group")); item->setToolTip(1, Tr::tr("Enables grouping of test cases.")); - m_frameworksWarn = new Utils::InfoLabel; + m_frameworksWarn = new InfoLabel; m_frameworksWarn->setVisible(false); m_frameworksWarn->setElideMode(Qt::ElideNone); - m_frameworksWarn->setType(Utils::InfoLabel::Warning); + m_frameworksWarn->setType(InfoLabel::Warning); - using namespace Utils::Layouting; + using namespace Layouting; PushButton resetChoicesButton { text(Tr::tr("Reset Cached Choices")), @@ -249,13 +251,13 @@ enum TestBaseInfo BaseType }; -void TestSettingsWidget::populateFrameworksListWidget(const QHash &frameworks, - const QHash &testTools) +void TestSettingsWidget::populateFrameworksListWidget(const QHash &frameworks, + const QHash &testTools) { const TestFrameworks ®istered = TestFrameworkManager::registeredFrameworks(); m_frameworkTreeWidget->clear(); for (const ITestFramework *framework : registered) { - const Utils::Id id = framework->id(); + const Id id = framework->id(); auto item = new QTreeWidgetItem(m_frameworkTreeWidget, {framework->displayName()}); item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable); item->setCheckState(0, frameworks.value(id) ? Qt::Checked : Qt::Unchecked); @@ -272,7 +274,7 @@ void TestSettingsWidget::populateFrameworksListWidget(const QHashid(); + const Id id = testTool->id(); auto item = new QTreeWidgetItem(m_frameworkTreeWidget, {testTool->displayName()}); item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable); item->setCheckState(0, testTools.value(id) ? Qt::Checked : Qt::Unchecked); @@ -289,7 +291,7 @@ void TestSettingsWidget::testSettings(TestSettings &settings) const QTC_ASSERT(itemCount <= model->rowCount(), return); for (int row = 0; row < itemCount; ++row) { QModelIndex idx = model->index(row, 0); - const Utils::Id id = Utils::Id::fromSetting(idx.data(BaseId)); + const Id id = Id::fromSetting(idx.data(BaseId)); settings.frameworks.insert(id, idx.data(Qt::CheckStateRole) == Qt::Checked); idx = model->index(row, 1); settings.frameworksGrouping.insert(id, idx.data(Qt::CheckStateRole) == Qt::Checked); @@ -306,7 +308,7 @@ void TestSettingsWidget::testToolsSettings(TestSettings &settings) const QTC_ASSERT(row <= end, return); for ( ; row < end; ++row) { const QModelIndex idx = model->index(row, 0); - const Utils::Id id = Utils::Id::fromSetting(idx.data(BaseId)); + const Id id = Id::fromSetting(idx.data(BaseId)); settings.tools.insert(id, idx.data(Qt::CheckStateRole) == Qt::Checked); } } @@ -365,8 +367,8 @@ void TestSettingsPage::apply() if (!m_widget) // page was not shown at all return; const TestSettings newSettings = m_widget->settings(); - const QList changedIds = Utils::filtered(newSettings.frameworksGrouping.keys(), - [newSettings, this](const Utils::Id &id) { + const QList changedIds = Utils::filtered(newSettings.frameworksGrouping.keys(), + [newSettings, this](const Id &id) { return newSettings.frameworksGrouping[id] != m_settings->frameworksGrouping[id]; }); *m_settings = newSettings; diff --git a/src/plugins/autotest/testtreeitem.cpp b/src/plugins/autotest/testtreeitem.cpp index 9196b2c6d4f..bd6a4a14824 100644 --- a/src/plugins/autotest/testtreeitem.cpp +++ b/src/plugins/autotest/testtreeitem.cpp @@ -14,16 +14,18 @@ #include +using namespace Utils; + namespace Autotest { static QIcon testTreeIcon(TestTreeItem::Type type) { static QIcon icons[] = { QIcon(), - Utils::Icons::OPENFILE.icon(), + Icons::OPENFILE.icon(), QIcon(":/autotest/images/suite.png"), - Utils::CodeModelIcon::iconForType(Utils::CodeModelIcon::Class), - Utils::CodeModelIcon::iconForType(Utils::CodeModelIcon::SlotPrivate), + CodeModelIcon::iconForType(CodeModelIcon::Class), + CodeModelIcon::iconForType(CodeModelIcon::SlotPrivate), QIcon(":/autotest/images/data.png") }; @@ -33,7 +35,7 @@ static QIcon testTreeIcon(TestTreeItem::Type type) } ITestTreeItem::ITestTreeItem(ITestBase *testBase, const QString &name, - const Utils::FilePath &filePath, Type type) + const FilePath &filePath, Type type) : m_testBase(testBase) , m_name(name) , m_filePath(filePath) @@ -115,8 +117,8 @@ bool ITestTreeItem::lessThan(const ITestTreeItem *other, ITestTreeItem::SortMode Qt::CaseInsensitive) > 0; } - const Utils::Link &leftLink = data(0, LinkRole).value(); - const Utils::Link &rightLink = other->data(0, LinkRole).value(); + const Link &leftLink = data(0, LinkRole).value(); + const Link &rightLink = other->data(0, LinkRole).value(); const int comparison = leftLink.targetFilePath.toString().compare( rightLink.targetFilePath.toString(), Qt::CaseInsensitive); if (comparison == 0) { @@ -144,7 +146,7 @@ ITestConfiguration *ITestTreeItem::asConfiguration(TestRunMode mode) const /****************************** TestTreeItem ********************************************/ TestTreeItem::TestTreeItem(ITestFramework *testFramework, const QString &name, - const Utils::FilePath &filePath, Type type) + const FilePath &filePath, Type type) : ITestTreeItem(testFramework, name, filePath, type) { switch (type) { @@ -168,7 +170,7 @@ QVariant TestTreeItem::data(int column, int role) const return QVariant(); QVariant itemLink; itemLink.setValue( - Utils::Link(filePath(), line(), int(m_column))); + Link(filePath(), line(), int(m_column))); return itemLink; } return ITestTreeItem::data(column, role); @@ -222,7 +224,7 @@ void TestTreeItem::markForRemovalRecursively(bool mark) childItem(row)->markForRemovalRecursively(mark); } -void TestTreeItem::markForRemovalRecursively(const Utils::FilePath &filepath) +void TestTreeItem::markForRemovalRecursively(const FilePath &filepath) { bool mark = filePath() == filepath; forFirstLevelChildItems([&mark, &filepath](TestTreeItem *child) { @@ -249,20 +251,20 @@ TestTreeItem *TestTreeItem::findChildByName(const QString &name) }); } -TestTreeItem *TestTreeItem::findChildByFile(const Utils::FilePath &filePath) +TestTreeItem *TestTreeItem::findChildByFile(const FilePath &filePath) { return findFirstLevelChildItem([filePath](const TestTreeItem *other) { return other->filePath() == filePath; }); } -TestTreeItem *TestTreeItem::findChildByFileAndType(const Utils::FilePath &filePath, Type tType) +TestTreeItem *TestTreeItem::findChildByFileAndType(const FilePath &filePath, Type tType) { return findFirstLevelChildItem([filePath, tType](const TestTreeItem *other) { return other->type() == tType && other->filePath() == filePath; });} -TestTreeItem *TestTreeItem::findChildByNameAndFile(const QString &name, const Utils::FilePath &filePath) +TestTreeItem *TestTreeItem::findChildByNameAndFile(const QString &name, const FilePath &filePath) { return findFirstLevelChildItem([name, filePath](const TestTreeItem *other) { return other->filePath() == filePath && other->name() == name; @@ -280,7 +282,7 @@ ITestConfiguration *TestTreeItem::asConfiguration(TestRunMode mode) const } } -QList TestTreeItem::getTestConfigurationsForFile(const Utils::FilePath &) const +QList TestTreeItem::getTestConfigurationsForFile(const FilePath &) const { return QList(); } @@ -342,7 +344,7 @@ void TestTreeItem::copyBasicDataFrom(const TestTreeItem *other) m_status = other->m_status; } -inline bool TestTreeItem::modifyFilePath(const Utils::FilePath &filepath) +inline bool TestTreeItem::modifyFilePath(const FilePath &filepath) { if (filePath() != filepath) { setFilePath(filepath); diff --git a/src/plugins/autotest/testtreeitem.h b/src/plugins/autotest/testtreeitem.h index 43d0b11b48a..f5f0723ca8c 100644 --- a/src/plugins/autotest/testtreeitem.h +++ b/src/plugins/autotest/testtreeitem.h @@ -51,8 +51,8 @@ public: }; explicit ITestTreeItem(ITestBase *testBase, - const QString &name = QString(), - const Utils::FilePath &filePath = Utils::FilePath(), + const QString &name = {}, + const Utils::FilePath &filePath = {}, Type type = Root); virtual QVariant data(int column, int role) const override; @@ -97,8 +97,8 @@ class TestTreeItem : public ITestTreeItem { public: explicit TestTreeItem(ITestFramework *testFramework, - const QString &name = QString(), - const Utils::FilePath &filePath = Utils::FilePath(), + const QString &name = {}, + const Utils::FilePath &filePath = {}, Type type = Root); virtual TestTreeItem *copyWithoutChildren() = 0; diff --git a/src/plugins/autotest/testtreemodel.cpp b/src/plugins/autotest/testtreemodel.cpp index 0500fd90cab..5a619ffe532 100644 --- a/src/plugins/autotest/testtreemodel.cpp +++ b/src/plugins/autotest/testtreemodel.cpp @@ -20,8 +20,9 @@ #include #include -using namespace ProjectExplorer; using namespace Autotest::Internal; +using namespace ProjectExplorer; +using namespace Utils; namespace Autotest { @@ -95,8 +96,7 @@ void TestTreeModel::setupParsingConnections() m_parser, &TestCodeParser::onCppDocumentUpdated, Qt::QueuedConnection); connect(cppMM, &CppEditor::CppModelManager::aboutToRemoveFiles, this, [this](const QStringList &files) { - const Utils::FilePaths filesToRemove - = Utils::FileUtils::toFilePathList(files); + const FilePaths filesToRemove = FileUtils::toFilePathList(files); removeFiles(filesToRemove); }, Qt::QueuedConnection); connect(cppMM, &CppEditor::CppModelManager::projectPartsUpdated, @@ -125,7 +125,7 @@ bool TestTreeModel::setData(const QModelIndex &index, const QVariant &value, int Qt::CheckState checked = item->checked(); if (item->hasChildren() && checked != Qt::PartiallyChecked) { // handle the new checkstate for children as well... - for (Utils::TreeItem *child : *item) { + for (TreeItem *child : *item) { const QModelIndex &idx = indexForItem(child); setData(idx, checked ? Qt::Checked : Qt::Unchecked, Qt::CheckStateRole); } @@ -155,7 +155,7 @@ Qt::ItemFlags TestTreeModel::flags(const QModelIndex &index) const bool TestTreeModel::hasTests() const { - for (Utils::TreeItem *frameworkRoot : *rootItem()) { + for (TreeItem *frameworkRoot : *rootItem()) { if (frameworkRoot->hasChildren()) return true; } @@ -189,7 +189,7 @@ QList TestTreeModel::getFailedTests() const return result; } -QList TestTreeModel::getTestsForFile(const Utils::FilePath &fileName) const +QList TestTreeModel::getTestsForFile(const FilePath &fileName) const { QList result; forItemsAtLevel<1>([&result, &fileName](ITestTreeItem *testRoot) { @@ -316,10 +316,10 @@ void TestTreeModel::synchronizeTestFrameworks() const auto sortedParsers = Utils::transform(sorted, &ITestFramework::testParser); // pre-check to avoid further processing when frameworks are unchanged - Utils::TreeItem *invisibleRoot = rootItem(); + TreeItem *invisibleRoot = rootItem(); QSet newlyAdded; QList oldFrameworkRoots; - for (Utils::TreeItem *oldFrameworkRoot : *invisibleRoot) + for (TreeItem *oldFrameworkRoot : *invisibleRoot) oldFrameworkRoots.append(static_cast(oldFrameworkRoot)); for (ITestTreeItem *oldFrameworkRoot : oldFrameworkRoots) @@ -362,10 +362,10 @@ void TestTreeModel::synchronizeTestTools() } // pre-check to avoid further processing when test tools are unchanged - Utils::TreeItem *invisibleRoot = rootItem(); + TreeItem *invisibleRoot = rootItem(); QSet newlyAdded; QList oldFrameworkRoots; - for (Utils::TreeItem *oldFrameworkRoot : *invisibleRoot) { + for (TreeItem *oldFrameworkRoot : *invisibleRoot) { auto item = static_cast(oldFrameworkRoot); if (item->testBase()->type() == ITestBase::Tool) oldFrameworkRoots.append(item); @@ -416,9 +416,9 @@ void TestTreeModel::filterAndInsert(TestTreeItem *item, TestTreeItem *root, bool insertItemInParent(filtered, root, groupingEnabled); } -void TestTreeModel::rebuild(const QList &frameworkIds) +void TestTreeModel::rebuild(const QList &frameworkIds) { - for (const Utils::Id &id : frameworkIds) { + for (const Id &id : frameworkIds) { ITestFramework *framework = TestFrameworkManager::frameworkForId(id); TestTreeItem *frameworkRoot = framework->rootNode(); const bool groupingEnabled = framework->grouping(); @@ -456,7 +456,7 @@ void TestTreeModel::updateCheckStateCache() bool TestTreeModel::hasFailedTests() const { - auto failedItem = rootItem()->findAnyChild([](Utils::TreeItem *it) { + auto failedItem = rootItem()->findAnyChild([](TreeItem *it) { return it->data(0, FailedRole).toBool(); }); return failedItem != nullptr; @@ -464,17 +464,17 @@ bool TestTreeModel::hasFailedTests() const void TestTreeModel::clearFailedMarks() { - for (Utils::TreeItem *rootNode : *rootItem()) { - rootNode->forAllChildren([](Utils::TreeItem *child) { + for (TreeItem *rootNode : *rootItem()) { + rootNode->forAllChildren([](TreeItem *child) { child->setData(0, false, FailedRole); }); } m_failedStateCache.clear(); } -void TestTreeModel::removeFiles(const Utils::FilePaths &files) +void TestTreeModel::removeFiles(const FilePaths &files) { - for (const Utils::FilePath &file : files) + for (const FilePath &file : files) markForRemoval(file); sweep(); } @@ -488,7 +488,7 @@ void TestTreeModel::markAllFrameworkItemsForRemoval() } } -void TestTreeModel::markForRemoval(const Utils::FilePath &filePath) +void TestTreeModel::markForRemoval(const FilePath &filePath) { if (filePath.isEmpty()) return; @@ -558,7 +558,7 @@ static void applyParentCheckState(ITestTreeItem *parent, ITestTreeItem *newItem) const Qt::CheckState checkState = parent->checked() == Qt::Unchecked ? Qt::Unchecked : Qt::Checked; newItem->setData(0, checkState, Qt::CheckStateRole); - newItem->forAllChildren([checkState](Utils::TreeItem *it) { + newItem->forAllChildren([checkState](TreeItem *it) { it->setData(0, checkState, Qt::CheckStateRole); }); } @@ -752,25 +752,25 @@ void TestTreeModel::removeAllTestToolItems() // we're inside tests - so use some internal knowledge to make testing easier static TestTreeItem *qtRootNode() { - auto id = Utils::Id(Constants::FRAMEWORK_PREFIX).withSuffix("QtTest"); + const Id id = Id(Constants::FRAMEWORK_PREFIX).withSuffix("QtTest"); return TestFrameworkManager::frameworkForId(id)->rootNode(); } static TestTreeItem *quickRootNode() { - auto id = Utils::Id(Constants::FRAMEWORK_PREFIX).withSuffix("QtQuickTest"); + const Id id = Id(Constants::FRAMEWORK_PREFIX).withSuffix("QtQuickTest"); return TestFrameworkManager::frameworkForId(id)->rootNode(); } static TestTreeItem *gtestRootNode() { - auto id = Utils::Id(Constants::FRAMEWORK_PREFIX).withSuffix("GTest"); + const Id id = Id(Constants::FRAMEWORK_PREFIX).withSuffix("GTest"); return TestFrameworkManager::frameworkForId(id)->rootNode(); } static TestTreeItem *boostTestRootNode() { - auto id = Utils::Id(Constants::FRAMEWORK_PREFIX).withSuffix("Boost"); + const Id id = Id(Constants::FRAMEWORK_PREFIX).withSuffix("Boost"); return TestFrameworkManager::frameworkForId(id)->rootNode(); } diff --git a/src/plugins/boot2qt/qdbmakedefaultappstep.cpp b/src/plugins/boot2qt/qdbmakedefaultappstep.cpp index f8b50b22e32..70c1c40acba 100644 --- a/src/plugins/boot2qt/qdbmakedefaultappstep.cpp +++ b/src/plugins/boot2qt/qdbmakedefaultappstep.cpp @@ -11,7 +11,6 @@ #include #include -#include #include #include diff --git a/src/plugins/boot2qt/qdbplugin.cpp b/src/plugins/boot2qt/qdbplugin.cpp index ee14e08a7bc..a73646b6c82 100644 --- a/src/plugins/boot2qt/qdbplugin.cpp +++ b/src/plugins/boot2qt/qdbplugin.cpp @@ -100,20 +100,6 @@ void registerFlashAction(QObject *parentForAction) toolsContainer->addAction(flashCommand, flashActionId); } -class QdbQtVersionFactory : public QtSupport::QtVersionFactory -{ -public: - QdbQtVersionFactory() - { - setQtVersionCreator([] { return new QdbQtVersion; }); - setSupportedType("Qdb.EmbeddedLinuxQt"); - setPriority(99); - setRestrictionChecker([](const SetupData &setup) { - return setup.platforms.contains("boot2qt"); - }); - } -}; - template class QdbDeployStepFactory : public ProjectExplorer::BuildStepFactory { diff --git a/src/plugins/boot2qt/qdbqtversion.cpp b/src/plugins/boot2qt/qdbqtversion.cpp index 7f15b32348b..3900220b7e7 100644 --- a/src/plugins/boot2qt/qdbqtversion.cpp +++ b/src/plugins/boot2qt/qdbqtversion.cpp @@ -6,17 +6,32 @@ #include "qdbconstants.h" #include "qdbtr.h" +#include +#include + namespace Qdb::Internal { -QString QdbQtVersion::description() const +class QdbQtVersion : public QtSupport::QtVersion { - return Tr::tr("Boot2Qt", "Qt version is used for Boot2Qt development"); -} +public: + QString description() const final + { + return QtSupport::Tr::tr("Boot2Qt", "Qt version is used for Boot2Qt development"); + } + QSet targetDeviceTypes() const final + { + return {Utils::Id(Constants::QdbLinuxOsType)}; + } +}; -QSet QdbQtVersion::targetDeviceTypes() const +QdbQtVersionFactory::QdbQtVersionFactory() { - return {Utils::Id(Constants::QdbLinuxOsType)}; - + setQtVersionCreator([] { return new QdbQtVersion; }); + setSupportedType("Qdb.EmbeddedLinuxQt"); + setPriority(99); + setRestrictionChecker([](const SetupData &setup) { + return setup.platforms.contains("boot2qt"); + }); } } // Qdb::Internal diff --git a/src/plugins/boot2qt/qdbqtversion.h b/src/plugins/boot2qt/qdbqtversion.h index 3488324074b..2b27277f48b 100644 --- a/src/plugins/boot2qt/qdbqtversion.h +++ b/src/plugins/boot2qt/qdbqtversion.h @@ -3,18 +3,14 @@ #pragma once -#include +#include namespace Qdb::Internal { -class QdbQtVersion : public QtSupport::QtVersion +class QdbQtVersionFactory : public QtSupport::QtVersionFactory { public: - QdbQtVersion() = default; - ~QdbQtVersion() = default; - - QString description() const final; - QSet targetDeviceTypes() const final; + QdbQtVersionFactory(); }; } // Qdb::Internal diff --git a/src/plugins/boot2qt/qdbstopapplicationstep.cpp b/src/plugins/boot2qt/qdbstopapplicationstep.cpp index 85b513e95af..60d891beafb 100644 --- a/src/plugins/boot2qt/qdbstopapplicationstep.cpp +++ b/src/plugins/boot2qt/qdbstopapplicationstep.cpp @@ -11,7 +11,6 @@ #include #include -#include #include #include diff --git a/src/plugins/clangcodemodel/clangdclient.cpp b/src/plugins/clangcodemodel/clangdclient.cpp index 6735b96aeb5..6d2d2fb95dc 100644 --- a/src/plugins/clangcodemodel/clangdclient.cpp +++ b/src/plugins/clangcodemodel/clangdclient.cpp @@ -292,7 +292,7 @@ public: void findUsages(TextDocument *document, const QTextCursor &cursor, const QString &searchTerm, const std::optional &replacement, - bool categorize); + const std::function &callback, bool categorize); void handleDeclDefSwitchReplies(); @@ -509,7 +509,8 @@ void ClangdClient::closeExtraFile(const Utils::FilePath &filePath) } void ClangdClient::findUsages(TextDocument *document, const QTextCursor &cursor, - const std::optional &replacement) + const std::optional &replacement, + const std::function &renameCallback) { // Quick check: Are we even on anything searchable? const QTextCursor adjustedCursor = d->adjustedCursor(cursor, document); @@ -519,7 +520,7 @@ void ClangdClient::findUsages(TextDocument *document, const QTextCursor &cursor, if (replacement && versionNumber() >= QVersionNumber(16) && Utils::qtcEnvironmentVariable("QTC_CLANGD_RENAMING") != "0") { - symbolSupport().renameSymbol(document, adjustedCursor, *replacement, + symbolSupport().renameSymbol(document, adjustedCursor, *replacement, renameCallback, CppEditor::preferLowerCaseFileNames()); return; } @@ -530,19 +531,20 @@ void ClangdClient::findUsages(TextDocument *document, const QTextCursor &cursor, if (searchTerm != "operator" && Utils::allOf(searchTerm, [](const QChar &c) { return c.isLetterOrNumber() || c == '_'; })) { - d->findUsages(document, adjustedCursor, searchTerm, replacement, categorize); + d->findUsages(document, adjustedCursor, searchTerm, replacement, renameCallback, categorize); return; } // Otherwise get the proper spelling of the search term from clang, so we can put it into the // search widget. - const auto symbolInfoHandler = [this, doc = QPointer(document), adjustedCursor, replacement, categorize] + const auto symbolInfoHandler = [this, doc = QPointer(document), adjustedCursor, replacement, + renameCallback, categorize] (const QString &name, const QString &, const MessageId &) { if (!doc) return; if (name.isEmpty()) return; - d->findUsages(doc.data(), adjustedCursor, name, replacement, categorize); + d->findUsages(doc.data(), adjustedCursor, name, replacement, renameCallback, categorize); }; requestSymbolInfo(document->filePath(), Range(adjustedCursor).start(), symbolInfoHandler); } @@ -704,10 +706,11 @@ CppEditor::ClangdSettings::Data ClangdClient::settingsData() const { return d->s void ClangdClient::Private::findUsages(TextDocument *document, const QTextCursor &cursor, const QString &searchTerm, - const std::optional &replacement, bool categorize) + const std::optional &replacement, const std::function &renameCallback, + bool categorize) { const auto findRefs = new ClangdFindReferences(q, document, cursor, searchTerm, replacement, - categorize); + renameCallback, categorize); if (isTesting) { connect(findRefs, &ClangdFindReferences::foundReferences, q, &ClangdClient::foundReferences); diff --git a/src/plugins/clangcodemodel/clangdclient.h b/src/plugins/clangcodemodel/clangdclient.h index ff64a9c3f54..772920b2776 100644 --- a/src/plugins/clangcodemodel/clangdclient.h +++ b/src/plugins/clangcodemodel/clangdclient.h @@ -54,7 +54,8 @@ public: void closeExtraFile(const Utils::FilePath &filePath); void findUsages(TextEditor::TextDocument *document, const QTextCursor &cursor, - const std::optional &replacement); + const std::optional &replacement, + const std::function &renameCallback); void checkUnused(const Utils::Link &link, Core::SearchResult *search, const Utils::LinkHandler &callback); void followSymbol(TextEditor::TextDocument *document, diff --git a/src/plugins/clangcodemodel/clangdfindreferences.cpp b/src/plugins/clangcodemodel/clangdfindreferences.cpp index 2458183164b..f2102325f06 100644 --- a/src/plugins/clangcodemodel/clangdfindreferences.cpp +++ b/src/plugins/clangcodemodel/clangdfindreferences.cpp @@ -109,7 +109,8 @@ public: ClangdFindReferences::ClangdFindReferences(ClangdClient *client, TextDocument *document, const QTextCursor &cursor, const QString &searchTerm, - const std::optional &replacement, bool categorize) + const std::optional &replacement, const std::function &callback, + bool categorize) : QObject(client), d(new ClangdFindReferences::Private(this)) { d->categorize = categorize; @@ -130,6 +131,7 @@ ClangdFindReferences::ClangdFindReferences(ClangdClient *client, TextDocument *d replacement ? SearchResultWindow::SearchAndReplace : SearchResultWindow::SearchOnly, SearchResultWindow::PreserveCaseDisabled, "CppEditor"); + d->search->makeNonInteractive(callback); if (categorize) d->search->setFilter(new CppSearchResultFilter); if (d->replacementData) { @@ -150,7 +152,8 @@ ClangdFindReferences::ClangdFindReferences(ClangdClient *client, TextDocument *d connect(d->search, &SearchResult::activated, [](const SearchResultItem& item) { EditorManager::openEditorAtSearchResult(item); }); - SearchResultWindow::instance()->popup(IOutputPane::ModeSwitch | IOutputPane::WithFocus); + if (d->search->isInteractive()) + SearchResultWindow::instance()->popup(IOutputPane::ModeSwitch | IOutputPane::WithFocus); const std::optional requestId = client->symbolSupport().findUsages( document, cursor, [self = QPointer(this)](const QList &locations) { diff --git a/src/plugins/clangcodemodel/clangdfindreferences.h b/src/plugins/clangcodemodel/clangdfindreferences.h index ce6d4726303..d904db31f41 100644 --- a/src/plugins/clangcodemodel/clangdfindreferences.h +++ b/src/plugins/clangcodemodel/clangdfindreferences.h @@ -27,7 +27,9 @@ class ClangdFindReferences : public QObject public: ClangdFindReferences(ClangdClient *client, TextEditor::TextDocument *document, const QTextCursor &cursor, const QString &searchTerm, - const std::optional &replacement, bool categorize); + const std::optional &replacement, + const std::function &callback, + bool categorize); ClangdFindReferences(ClangdClient *client, const Utils::Link &link, Core::SearchResult *search, const Utils::LinkHandler &callback); ~ClangdFindReferences(); diff --git a/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp b/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp index e9742195f8e..64a8dac7bdf 100644 --- a/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp +++ b/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp @@ -313,16 +313,17 @@ void ClangModelManagerSupport::startLocalRenaming(const CppEditor::CursorInEdito } void ClangModelManagerSupport::globalRename(const CppEditor::CursorInEditor &cursor, - const QString &replacement) + const QString &replacement, + const std::function &callback) { if (ClangdClient * const client = clientForFile(cursor.filePath()); client && client->isFullyIndexed()) { QTC_ASSERT(client->documentOpen(cursor.textDocument()), client->openDocument(cursor.textDocument())); - client->findUsages(cursor.textDocument(), cursor.cursor(), replacement); + client->findUsages(cursor.textDocument(), cursor.cursor(), replacement, callback); return; } - CppModelManager::globalRename(cursor, replacement, CppModelManager::Backend::Builtin); + CppModelManager::globalRename(cursor, replacement, callback, CppModelManager::Backend::Builtin); } void ClangModelManagerSupport::findUsages(const CppEditor::CursorInEditor &cursor) const @@ -331,7 +332,7 @@ void ClangModelManagerSupport::findUsages(const CppEditor::CursorInEditor &curso client && client->isFullyIndexed()) { QTC_ASSERT(client->documentOpen(cursor.textDocument()), client->openDocument(cursor.textDocument())); - client->findUsages(cursor.textDocument(), cursor.cursor(), {}); + client->findUsages(cursor.textDocument(), cursor.cursor(), {}, {}); return; } diff --git a/src/plugins/clangcodemodel/clangmodelmanagersupport.h b/src/plugins/clangcodemodel/clangmodelmanagersupport.h index 983d8e89eab..a901b4407e2 100644 --- a/src/plugins/clangcodemodel/clangmodelmanagersupport.h +++ b/src/plugins/clangcodemodel/clangmodelmanagersupport.h @@ -60,7 +60,8 @@ private: void startLocalRenaming(const CppEditor::CursorInEditor &data, const CppEditor::ProjectPart *projectPart, CppEditor::RenameCallback &&renameSymbolsCallback) override; - void globalRename(const CppEditor::CursorInEditor &cursor, const QString &replacement) override; + void globalRename(const CppEditor::CursorInEditor &cursor, const QString &replacement, + const std::function &callback) override; void findUsages(const CppEditor::CursorInEditor &cursor) const override; void switchHeaderSource(const Utils::FilePath &filePath, bool inNextSplit) override; void checkUnused(const Utils::Link &link, Core::SearchResult *search, diff --git a/src/plugins/clangcodemodel/test/clangdtests.cpp b/src/plugins/clangcodemodel/test/clangdtests.cpp index affeb8db5f1..c2517c13579 100644 --- a/src/plugins/clangcodemodel/test/clangdtests.cpp +++ b/src/plugins/clangcodemodel/test/clangdtests.cpp @@ -294,7 +294,7 @@ void ClangdTestFindReferences::test() QVERIFY(doc); QTextCursor cursor(doc->document()); cursor.setPosition(pos); - client()->findUsages(doc, cursor, {}); + client()->findUsages(doc, cursor, {}, {}); QVERIFY(waitForSignalOrTimeout(client(), &ClangdClient::findUsagesDone, timeOutInMs())); QCOMPARE(m_actualResults.size(), expectedResults.size()); diff --git a/src/plugins/clangtools/documentclangtoolrunner.cpp b/src/plugins/clangtools/documentclangtoolrunner.cpp index 11f01f31b6d..2afd12aa270 100644 --- a/src/plugins/clangtools/documentclangtoolrunner.cpp +++ b/src/plugins/clangtools/documentclangtoolrunner.cpp @@ -146,7 +146,7 @@ static Environment projectBuildEnvironment(Project *project) if (BuildConfiguration *buildConfig = target->activeBuildConfiguration()) env = buildConfig->environment(); } - if (!env.isValid()) + if (!env.hasChanges()) env = Environment::systemEnvironment(); return env; } diff --git a/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp b/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp index 37cc9ff29f2..9b834bc6263 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp +++ b/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp @@ -1401,4 +1401,9 @@ void CMakeBuildSystem::runGenerator(Id id) proc->start(); } +ExtraCompiler *CMakeBuildSystem::findExtraCompiler(const ExtraCompilerFilter &filter) const +{ + return Utils::findOrDefault(m_extraCompilers, filter); +} + } // CMakeProjectManager::Internal diff --git a/src/plugins/cmakeprojectmanager/cmakebuildsystem.h b/src/plugins/cmakeprojectmanager/cmakebuildsystem.h index e9b1acedc73..802921f04b8 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildsystem.h +++ b/src/plugins/cmakeprojectmanager/cmakebuildsystem.h @@ -122,6 +122,8 @@ signals: private: QList> generators() const override; void runGenerator(Utils::Id id) override; + ProjectExplorer::ExtraCompiler *findExtraCompiler( + const ExtraCompilerFilter &filter) const override; enum ForceEnabledChanged { False, True }; void clearError(ForceEnabledChanged fec = ForceEnabledChanged::False); diff --git a/src/plugins/coreplugin/find/searchresultwidget.cpp b/src/plugins/coreplugin/find/searchresultwidget.cpp index 0d590743b5c..bb16aaf23da 100644 --- a/src/plugins/coreplugin/find/searchresultwidget.cpp +++ b/src/plugins/coreplugin/find/searchresultwidget.cpp @@ -468,12 +468,16 @@ void SearchResultWidget::handleReplaceButton() { // check if button is actually enabled, because this is also triggered // by pressing return in replace line edit - if (m_replaceButton->isEnabled()) { - m_infoBar.clear(); - setShowReplaceUI(false); - emit replaceButtonClicked(m_replaceTextEdit->text(), checkedItems(), - m_preserveCaseSupported && m_preserveCaseCheck->isChecked()); - } + if (m_replaceButton->isEnabled()) + doReplace(); +} + +void SearchResultWidget::doReplace() +{ + m_infoBar.clear(); + setShowReplaceUI(false); + emit replaceButtonClicked(m_replaceTextEdit->text(), checkedItems(), + m_preserveCaseSupported && m_preserveCaseCheck->isChecked()); } void SearchResultWidget::cancel() diff --git a/src/plugins/coreplugin/find/searchresultwidget.h b/src/plugins/coreplugin/find/searchresultwidget.h index f761dde0e1d..f10bd5dd673 100644 --- a/src/plugins/coreplugin/find/searchresultwidget.h +++ b/src/plugins/coreplugin/find/searchresultwidget.h @@ -39,6 +39,7 @@ public: void setSupportsReplace(bool replaceSupported, const QString &group); bool supportsReplace() const; + void triggerReplace() { doReplace(); } void setTextToReplace(const QString &textToReplace); QString textToReplace() const; @@ -91,6 +92,7 @@ signals: private: void handleJumpToSearchResult(const SearchResultItem &item); void handleReplaceButton(); + void doReplace(); void cancel(); void searchAgain(); diff --git a/src/plugins/coreplugin/find/searchresultwindow.cpp b/src/plugins/coreplugin/find/searchresultwindow.cpp index 29c1953a084..3b2ea6e8ae9 100644 --- a/src/plugins/coreplugin/find/searchresultwindow.cpp +++ b/src/plugins/coreplugin/find/searchresultwindow.cpp @@ -850,6 +850,12 @@ void SearchResult::setFilter(SearchResultFilter *filter) void SearchResult::finishSearch(bool canceled, const QString &reason) { m_widget->finishSearch(canceled, reason); + if (m_finishedHandler) { + if (!canceled) + m_widget->triggerReplace(); + m_finishedHandler(); + m_finishedHandler = {}; + } } /*! @@ -893,6 +899,13 @@ void SearchResult::popup() m_widget->sendRequestPopup(); } +void Core::SearchResult::makeNonInteractive(const std::function &callback) +{ + QTC_ASSERT(callback, return); + m_widget->setEnabled(false); + m_finishedHandler = callback; +} + } // namespace Core #include "searchresultwindow.moc" diff --git a/src/plugins/coreplugin/find/searchresultwindow.h b/src/plugins/coreplugin/find/searchresultwindow.h index e8758e657ce..da1bcf67e41 100644 --- a/src/plugins/coreplugin/find/searchresultwindow.h +++ b/src/plugins/coreplugin/find/searchresultwindow.h @@ -12,6 +12,8 @@ #include #include +#include + QT_BEGIN_NAMESPACE class QFont; QT_END_NAMESPACE @@ -53,6 +55,8 @@ public: void setSearchAgainSupported(bool supported); QWidget *additionalReplaceWidget() const; void setAdditionalReplaceWidget(QWidget *widget); + void makeNonInteractive(const std::function &callback); + bool isInteractive() const { return !m_finishedHandler; } public slots: void addResult(const SearchResultItem &item); @@ -83,6 +87,7 @@ private: private: Internal::SearchResultWidget *m_widget; QVariant m_userData; + std::function m_finishedHandler; }; class CORE_EXPORT SearchResultWindow : public IOutputPane diff --git a/src/plugins/coreplugin/progressmanager/processprogress.cpp b/src/plugins/coreplugin/progressmanager/processprogress.cpp index f52204197e3..9a545561a78 100644 --- a/src/plugins/coreplugin/progressmanager/processprogress.cpp +++ b/src/plugins/coreplugin/progressmanager/processprogress.cpp @@ -82,7 +82,7 @@ ProcessProgress::ProcessProgress(QtcProcess *process) , d(new ProcessProgressPrivate(this, process)) { connect(&d->m_watcher, &QFutureWatcher::canceled, this, [this] { - d->m_process->stop(); // TODO: should we have different cancel policies? + d->m_process->stop(); // TODO: See TaskProgress::setAutoStopOnCancel }); connect(d->m_process, &QtcProcess::starting, this, [this] { d->m_futureInterface = QFutureInterface(); diff --git a/src/plugins/coreplugin/progressmanager/taskprogress.cpp b/src/plugins/coreplugin/progressmanager/taskprogress.cpp index 044b4ddf190..6c7ee0bbb79 100644 --- a/src/plugins/coreplugin/progressmanager/taskprogress.cpp +++ b/src/plugins/coreplugin/progressmanager/taskprogress.cpp @@ -39,6 +39,8 @@ public: QFutureWatcher m_watcher; QFutureInterface m_futureInterface; QPointer m_futureProgress; + Id m_id; + bool m_isAutoStopOnCancel = true; int m_halfLifeTimePerTask = 1000; // 1000 ms QString m_displayName; FutureProgress::KeepOnFinishType m_keep = FutureProgress::HideOnFinish; @@ -99,7 +101,9 @@ TaskProgress::TaskProgress(TaskTree *taskTree) , d(new TaskProgressPrivate(this, taskTree)) { connect(&d->m_watcher, &QFutureWatcher::canceled, this, [this] { - d->m_taskTree->stop(); // TODO: should we have different cancel policies? + emit canceled(); + if (d->m_isAutoStopOnCancel) + d->m_taskTree->stop(); }); connect(d->m_taskTree, &TaskTree::started, this, [this] { d->m_futureInterface = QFutureInterface(); @@ -109,7 +113,7 @@ TaskProgress::TaskProgress(TaskTree *taskTree) d->m_futureInterface.reportStarted(); d->advanceProgress(0); - const auto id = Id::fromString(d->m_displayName + ".action"); + const Id id = d->m_id.isValid() ? d->m_id : Id::fromString(d->m_displayName + ".action"); d->m_futureProgress = ProgressManager::addTask(d->m_futureInterface.future(), d->m_displayName, id); d->m_futureProgress->setKeepOnFinish(d->m_keep); @@ -133,6 +137,16 @@ TaskProgress::TaskProgress(TaskTree *taskTree) TaskProgress::~TaskProgress() = default; +void TaskProgress::setId(Utils::Id id) +{ + d->m_id = id; +} + +void TaskProgress::setAutoStopOnCancel(bool enable) +{ + d->m_isAutoStopOnCancel = enable; +} + void TaskProgress::setHalfLifeTimePerTask(int msecs) { d->m_halfLifeTimePerTask = msecs; diff --git a/src/plugins/coreplugin/progressmanager/taskprogress.h b/src/plugins/coreplugin/progressmanager/taskprogress.h index c1fa41cbd06..e15967fa2b8 100644 --- a/src/plugins/coreplugin/progressmanager/taskprogress.h +++ b/src/plugins/coreplugin/progressmanager/taskprogress.h @@ -17,16 +17,23 @@ class TaskProgressPrivate; class CORE_EXPORT TaskProgress : public QObject { + Q_OBJECT + public: TaskProgress(Utils::TaskTree *taskTree); // Makes TaskProgress a child of task tree ~TaskProgress() override; + void setId(Utils::Id id); + void setAutoStopOnCancel(bool enable); // Default is true void setHalfLifeTimePerTask(int msecs); // Default is 1000 ms void setDisplayName(const QString &name); void setKeepOnFinish(FutureProgress::KeepOnFinishType keepType); void setSubtitleVisibleInStatusBar(bool visible); void setSubtitle(const QString &subtitle); +signals: + void canceled(); + private: std::unique_ptr d; }; diff --git a/src/plugins/cppeditor/cppbuiltinmodelmanagersupport.cpp b/src/plugins/cppeditor/cppbuiltinmodelmanagersupport.cpp index 3c6900e58f8..c59fd475bda 100644 --- a/src/plugins/cppeditor/cppbuiltinmodelmanagersupport.cpp +++ b/src/plugins/cppeditor/cppbuiltinmodelmanagersupport.cpp @@ -142,7 +142,8 @@ void BuiltinModelManagerSupport::startLocalRenaming(const CursorInEditor &data, } void BuiltinModelManagerSupport::globalRename(const CursorInEditor &data, - const QString &replacement) + const QString &replacement, + const std::function &callback) { CppModelManager *modelManager = CppModelManager::instance(); if (!modelManager) @@ -161,7 +162,7 @@ void BuiltinModelManagerSupport::globalRename(const CursorInEditor &data, Internal::CanonicalSymbol cs(info.doc, info.snapshot); CPlusPlus::Symbol *canonicalSymbol = cs(cursor); if (canonicalSymbol) - modelManager->renameUsages(canonicalSymbol, cs.context(), replacement); + modelManager->renameUsages(canonicalSymbol, cs.context(), replacement, callback); } } diff --git a/src/plugins/cppeditor/cppbuiltinmodelmanagersupport.h b/src/plugins/cppeditor/cppbuiltinmodelmanagersupport.h index 3518c33401e..04de866d5ce 100644 --- a/src/plugins/cppeditor/cppbuiltinmodelmanagersupport.h +++ b/src/plugins/cppeditor/cppbuiltinmodelmanagersupport.h @@ -38,7 +38,8 @@ private: void startLocalRenaming(const CursorInEditor &data, const ProjectPart *projectPart, RenameCallback &&renameSymbolsCallback) override; - void globalRename(const CursorInEditor &data, const QString &replacement) override; + void globalRename(const CursorInEditor &data, const QString &replacement, + const std::function &callback) override; void findUsages(const CursorInEditor &data) const override; void switchHeaderSource(const Utils::FilePath &filePath, bool inNextSplit) override; void checkUnused(const Utils::Link &link, Core::SearchResult *search, diff --git a/src/plugins/cppeditor/cppeditorwidget.cpp b/src/plugins/cppeditor/cppeditorwidget.cpp index acf4e88fef3..fe693bbbfc9 100644 --- a/src/plugins/cppeditor/cppeditorwidget.cpp +++ b/src/plugins/cppeditor/cppeditorwidget.cpp @@ -619,6 +619,16 @@ void CppEditorWidget::renameUsages(const QString &replacement, QTextCursor curso d->m_modelManager->globalRename(cursorInEditor, replacement); } +void CppEditorWidget::renameUsages(const Utils::FilePath &filePath, const QString &replacement, + QTextCursor cursor, const std::function &callback) +{ + if (cursor.isNull()) + cursor = textCursor(); + CursorInEditor cursorInEditor{cursor, filePath, this, textDocument()}; + QPointer cppEditorWidget = this; + d->m_modelManager->globalRename(cursorInEditor, replacement, callback); +} + bool CppEditorWidget::selectBlockUp() { if (!behaviorSettings().m_smartSelectionChanging) @@ -1160,7 +1170,7 @@ void CppEditorWidget::updateSemanticInfo() void CppEditorWidget::updateSemanticInfo(const SemanticInfo &semanticInfo, bool updateUseSelectionSynchronously) { - if (semanticInfo.revision != documentRevision()) + if (semanticInfo.revision < documentRevision()) return; d->m_lastSemanticInfo = semanticInfo; diff --git a/src/plugins/cppeditor/cppeditorwidget.h b/src/plugins/cppeditor/cppeditorwidget.h index 32706872011..a42d786ead9 100644 --- a/src/plugins/cppeditor/cppeditorwidget.h +++ b/src/plugins/cppeditor/cppeditorwidget.h @@ -10,6 +10,8 @@ #include +#include + namespace TextEditor { class IAssistProposal; class IAssistProvider; @@ -61,6 +63,10 @@ public: void findUsages(QTextCursor cursor); void renameUsages(const QString &replacement = QString(), QTextCursor cursor = QTextCursor()); + void renameUsages(const Utils::FilePath &filePath, + const QString &replacement = QString(), + QTextCursor cursor = QTextCursor(), + const std::function &callback = {}); void renameSymbolUnderCursor() override; bool selectBlockUp() override; diff --git a/src/plugins/cppeditor/cppfindreferences.cpp b/src/plugins/cppeditor/cppfindreferences.cpp index f6116f62d1a..940eb358e93 100644 --- a/src/plugins/cppeditor/cppfindreferences.cpp +++ b/src/plugins/cppeditor/cppfindreferences.cpp @@ -368,12 +368,13 @@ static void find_helper(QFutureInterface &future, void CppFindReferences::findUsages(CPlusPlus::Symbol *symbol, const CPlusPlus::LookupContext &context) { - findUsages(symbol, context, QString(), false); + findUsages(symbol, context, QString(), {}, false); } void CppFindReferences::findUsages(CPlusPlus::Symbol *symbol, const CPlusPlus::LookupContext &context, const QString &replacement, + const std::function &callback, bool replace) { CPlusPlus::Overview overview; @@ -385,6 +386,8 @@ void CppFindReferences::findUsages(CPlusPlus::Symbol *symbol, SearchResultWindow::PreserveCaseDisabled, QLatin1String("CppEditor")); search->setTextToReplace(replacement); + if (callback) + search->makeNonInteractive(callback); if (codeModelSettings()->categorizeFindReferences()) search->setFilter(new CppSearchResultFilter); setupSearch(search); @@ -408,12 +411,13 @@ void CppFindReferences::findUsages(CPlusPlus::Symbol *symbol, void CppFindReferences::renameUsages(CPlusPlus::Symbol *symbol, const CPlusPlus::LookupContext &context, - const QString &replacement) + const QString &replacement, + const std::function &callback) { if (const CPlusPlus::Identifier *id = symbol->identifier()) { const QString textToReplace = replacement.isEmpty() ? QString::fromUtf8(id->chars(), id->size()) : replacement; - findUsages(symbol, context, textToReplace, true); + findUsages(symbol, context, textToReplace, callback, true); } } @@ -429,7 +433,8 @@ void CppFindReferences::findAll_helper(SearchResult *search, CPlusPlus::Symbol * Core::EditorManager::openEditorAtSearchResult(item); }); - SearchResultWindow::instance()->popup(IOutputPane::ModeSwitch | IOutputPane::WithFocus); + if (search->isInteractive()) + SearchResultWindow::instance()->popup(IOutputPane::ModeSwitch | IOutputPane::WithFocus); const WorkingCopy workingCopy = m_modelManager->workingCopy(); QFuture result; result = Utils::runAsync(m_modelManager->sharedThreadPool(), find_helper, diff --git a/src/plugins/cppeditor/cppfindreferences.h b/src/plugins/cppeditor/cppfindreferences.h index fc76888ad07..54f40bf4cfe 100644 --- a/src/plugins/cppeditor/cppfindreferences.h +++ b/src/plugins/cppeditor/cppfindreferences.h @@ -14,6 +14,8 @@ #include #include +#include + QT_FORWARD_DECLARE_CLASS(QTimer) namespace Core { @@ -65,7 +67,8 @@ public: public: void findUsages(CPlusPlus::Symbol *symbol, const CPlusPlus::LookupContext &context); void renameUsages(CPlusPlus::Symbol *symbol, const CPlusPlus::LookupContext &context, - const QString &replacement = QString()); + const QString &replacement = QString(), + const std::function &callback = {}); void findMacroUses(const CPlusPlus::Macro ¯o); void renameMacroUses(const CPlusPlus::Macro ¯o, const QString &replacement = QString()); @@ -80,7 +83,8 @@ private: void searchAgain(Core::SearchResult *search); void findUsages(CPlusPlus::Symbol *symbol, const CPlusPlus::LookupContext &context, - const QString &replacement, bool replace); + const QString &replacement, const std::function &callback, + bool replace); void findMacroUses(const CPlusPlus::Macro ¯o, const QString &replacement, bool replace); void findAll_helper(Core::SearchResult *search, CPlusPlus::Symbol *symbol, diff --git a/src/plugins/cppeditor/cppmodelmanager.cpp b/src/plugins/cppeditor/cppmodelmanager.cpp index f8dc3173c20..9c7500a6c45 100644 --- a/src/plugins/cppeditor/cppmodelmanager.cpp +++ b/src/plugins/cppeditor/cppmodelmanager.cpp @@ -6,6 +6,7 @@ #include "abstracteditorsupport.h" #include "baseeditordocumentprocessor.h" #include "compileroptionsbuilder.h" +#include "cppcanonicalsymbol.h" #include "cppcodemodelinspectordumper.h" #include "cppcodemodelsettings.h" #include "cppcurrentdocumentfilter.h" @@ -325,9 +326,9 @@ void CppModelManager::startLocalRenaming(const CursorInEditor &data, } void CppModelManager::globalRename(const CursorInEditor &data, const QString &replacement, - Backend backend) + const std::function &callback, Backend backend) { - instance()->modelManagerSupport(backend)->globalRename(data, replacement); + instance()->modelManagerSupport(backend)->globalRename(data, replacement, callback); } void CppModelManager::findUsages(const CursorInEditor &data, Backend backend) @@ -1170,10 +1171,21 @@ void CppModelManager::findUsages(Symbol *symbol, const LookupContext &context) void CppModelManager::renameUsages(Symbol *symbol, const LookupContext &context, - const QString &replacement) + const QString &replacement, + const std::function &callback) { if (symbol->identifier()) - d->m_findReferences->renameUsages(symbol, context, replacement); + d->m_findReferences->renameUsages(symbol, context, replacement, callback); +} + +void CppModelManager::renameUsages(const Document::Ptr &doc, const QTextCursor &cursor, + const Snapshot &snapshot, const QString &replacement, + const std::function &callback) +{ + Internal::CanonicalSymbol cs(doc, snapshot); + CPlusPlus::Symbol *canonicalSymbol = cs(cursor); + if (canonicalSymbol) + renameUsages(canonicalSymbol, cs.context(), replacement, callback); } void CppModelManager::findMacroUsages(const CPlusPlus::Macro ¯o) diff --git a/src/plugins/cppeditor/cppmodelmanager.h b/src/plugins/cppeditor/cppmodelmanager.h index 3d3cc2eca12..2929d615e62 100644 --- a/src/plugins/cppeditor/cppmodelmanager.h +++ b/src/plugins/cppeditor/cppmodelmanager.h @@ -19,6 +19,7 @@ #include #include +#include #include namespace Core { @@ -149,7 +150,13 @@ public: int position) const; void renameUsages(CPlusPlus::Symbol *symbol, const CPlusPlus::LookupContext &context, - const QString &replacement = QString()); + const QString &replacement = QString(), + const std::function &callback = {}); + void renameUsages(const CPlusPlus::Document::Ptr &doc, + const QTextCursor &cursor, + const CPlusPlus::Snapshot &snapshot, + const QString &replacement, + const std::function &callback); void findUsages(CPlusPlus::Symbol *symbol, const CPlusPlus::LookupContext &context); void findMacroUsages(const CPlusPlus::Macro ¯o); @@ -178,6 +185,7 @@ public: RenameCallback &&renameSymbolsCallback, Backend backend = Backend::Best); static void globalRename(const CursorInEditor &data, const QString &replacement, + const std::function &callback = {}, Backend backend = Backend::Best); static void findUsages(const CursorInEditor &data, Backend backend = Backend::Best); static void switchHeaderSource(bool inNextSplit, Backend backend = Backend::Best); diff --git a/src/plugins/cppeditor/cppmodelmanagersupport.h b/src/plugins/cppeditor/cppmodelmanagersupport.h index 387d86272e0..392712d86ae 100644 --- a/src/plugins/cppeditor/cppmodelmanagersupport.h +++ b/src/plugins/cppeditor/cppmodelmanagersupport.h @@ -11,6 +11,7 @@ #include #include +#include #include namespace Core { class SearchResult; } @@ -49,7 +50,8 @@ public: virtual void startLocalRenaming(const CursorInEditor &data, const ProjectPart *projectPart, RenameCallback &&renameSymbolsCallback) = 0; - virtual void globalRename(const CursorInEditor &data, const QString &replacement) = 0; + virtual void globalRename(const CursorInEditor &data, const QString &replacement, + const std::function &callback) = 0; virtual void findUsages(const CursorInEditor &data) const = 0; virtual void switchHeaderSource(const Utils::FilePath &filePath, bool inNextSplit) = 0; diff --git a/src/plugins/debugger/cdb/cdbengine.cpp b/src/plugins/debugger/cdb/cdbengine.cpp index 8b2d2683952..42e25035df6 100644 --- a/src/plugins/debugger/cdb/cdbengine.cpp +++ b/src/plugins/debugger/cdb/cdbengine.cpp @@ -411,7 +411,7 @@ void CdbEngine::setupEngine() m_autoBreakPointCorrection = false; - Environment inferiorEnvironment = sp.inferior.environment.isValid() + Environment inferiorEnvironment = sp.inferior.environment.hasChanges() ? sp.inferior.environment : Environment::systemEnvironment(); // Make sure that QTestLib uses OutputDebugString for logging. diff --git a/src/plugins/designer/qtcreatorintegration.cpp b/src/plugins/designer/qtcreatorintegration.cpp index c2dd4ed45ab..db96523c360 100644 --- a/src/plugins/designer/qtcreatorintegration.cpp +++ b/src/plugins/designer/qtcreatorintegration.cpp @@ -2,7 +2,6 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "designertr.h" -#include "editordata.h" #include "formeditorw.h" #include "formwindoweditor.h" #include "qtcreatorintegration.h" @@ -10,7 +9,9 @@ #include #include +#include #include +#include #include #include #include @@ -19,28 +20,38 @@ #include #include #include +#include #include #include +#include +#include #include #include #include +#include +#include #include #include #include +#include #include #include - -#include - -#include -#include #include +#include +#include +#include +#include +#include #include +#include + enum { indentation = 4 }; +Q_LOGGING_CATEGORY(log, "qtc.designer", QtWarningMsg); + using namespace Designer::Internal; using namespace CPlusPlus; using namespace TextEditor; @@ -60,8 +71,23 @@ static QString msgClassNotFound(const QString &uiClassName, const QList> extraCompilers; + std::optional showPropertyEditorRenameWarning = false; +}; + QtCreatorIntegration::QtCreatorIntegration(QDesignerFormEditorInterface *core, QObject *parent) - : QDesignerIntegration(core, parent) + : QDesignerIntegration(core, parent), d(new Private) { setResourceFileWatcherBehaviour(ReloadResourceFileSilently); Feature f = features(); @@ -77,6 +103,44 @@ QtCreatorIntegration::QtCreatorIntegration(QDesignerFormEditorInterface *core, Q slotSyncSettingsToDesigner(); connect(Core::ICore::instance(), &Core::ICore::saveSettingsRequested, this, &QtCreatorIntegration::slotSyncSettingsToDesigner); + + // The problem is as follows: + // - If the user edits the object name in the property editor, the objectNameChanged() signal + // is emitted for every keystroke (QTCREATORBUG-19141). We should not try to rename + // in that case, because the signals will likely come in faster than the renaming + // procedure takes, putting the code model in some non-deterministic state. + // - Unfortunately, this condition is not trivial to detect, because the propertyChanged() + // signal is (somewhat surprisingly) emitted *after* objectNameChanged(). + // - We can also not simply use a queued connection for objectNameChanged(), because then + // the ExtraCompiler might have run before our handler, and we won't find the old + // object name in the source code anymore. + // The solution is as follows: + // - Upon receiving objectNameChanged(), we retrieve the corresponding ExtraCompiler, + // block it and store it away. Then we invoke the actual handler delayed. + // - Upon receiving propertyChanged(), we check whether it refers to an object name change. + // If it does, we unblock the ExtraCompiler and remove it from our map. + // - When the real handler runs, it first checks for the ExtraCompiler. If it is not found, + // we don't do anything. Otherwise the actual renaming procedure is run. + connect(this, &QtCreatorIntegration::objectNameChanged, + this, &QtCreatorIntegration::handleSymbolRenameStage1); + connect(this, &QtCreatorIntegration::propertyChanged, + this, [this](QDesignerFormWindowInterface *formWindow, const QString &name, + const QVariant &) { + if (name == "objectName") { + if (const auto extraCompiler = d->extraCompilers.find(formWindow); + extraCompiler != d->extraCompilers.end()) { + (*extraCompiler)->unblock(); + d->extraCompilers.erase(extraCompiler); + if (d->showPropertyEditorRenameWarning) + d->showPropertyEditorRenameWarning = true; + } + } + }); +} + +QtCreatorIntegration::~QtCreatorIntegration() +{ + delete d; } void QtCreatorIntegration::slotDesignerHelpRequested(const QString &manual, const QString &document) @@ -565,6 +629,162 @@ bool QtCreatorIntegration::navigateToSlot(const QString &objectName, return false; } +void QtCreatorIntegration::handleSymbolRenameStage1( + QDesignerFormWindowInterface *formWindow, QObject *object, + const QString &newName, const QString &oldName) +{ + const FilePath uiFile = FilePath::fromString(formWindow->fileName()); + qCDebug(log) << Q_FUNC_INFO << uiFile << object << oldName << newName; + if (newName.isEmpty() || newName == oldName) + return; + + // Get ExtraCompiler. + const Project * const project = SessionManager::projectForFile(uiFile); + if (!project) { + return reportRenamingError(oldName, Designer::Tr::tr("File \"%1\" not found in project.") + .arg(uiFile.toUserOutput())); + } + const Target * const target = project->activeTarget(); + if (!target) + return reportRenamingError(oldName, Designer::Tr::tr("No active target.")); + BuildSystem * const buildSystem = target->buildSystem(); + if (!buildSystem) + return reportRenamingError(oldName, Designer::Tr::tr("No active build system.")); + ExtraCompiler * const ec = buildSystem->extraCompilerForSource(uiFile); + if (!ec) + return reportRenamingError(oldName, Designer::Tr::tr("Failed to find the ui header.")); + ec->block(); + d->extraCompilers.insert(formWindow, ec); + qCDebug(log) << "\tfound extra compiler, scheduling stage 2"; + QMetaObject::invokeMethod(this, [this, formWindow, newName, oldName] { + handleSymbolRenameStage2(formWindow, newName, oldName); + }, Qt::QueuedConnection); +} + +void QtCreatorIntegration::handleSymbolRenameStage2( + QDesignerFormWindowInterface *formWindow, const QString &newName, const QString &oldName) +{ + // Retrieve and check previously stored ExtraCompiler. + ExtraCompiler * const ec = d->extraCompilers.take(formWindow); + if (!ec) { + qCDebug(log) << "\tchange came from property editor, ignoring"; + if (d->showPropertyEditorRenameWarning && *d->showPropertyEditorRenameWarning) { + d->showPropertyEditorRenameWarning.reset(); + reportRenamingError(oldName, Designer::Tr::tr("Renaming via the property editor " + "cannot be synced with C++ code; see QTCREATORBUG-19141." + " This message will not be repeated.")); + } + return; + } + + class ResourceHandler { + public: + ResourceHandler(ExtraCompiler *ec) : m_ec(ec) {} + void setEditor(BaseTextEditor *editorToClose) { m_editorToClose = editorToClose; } + void setTempFile(std::unique_ptr &&tempFile) { + m_tempFile = std::move(tempFile); + } + ~ResourceHandler() + { + if (m_ec) + m_ec->unblock(); + if (m_editorToClose) + Core::EditorManager::closeEditors({m_editorToClose}, false); + } + private: + const QPointer m_ec; + QPointer m_editorToClose; + std::unique_ptr m_tempFile; + }; + const auto resourceHandler = std::make_shared(ec); + + QTC_ASSERT(ec->targets().size() == 1, return); + const FilePath uiHeader = ec->targets().first(); + qCDebug(log) << '\t' << uiHeader; + const QByteArray virtualContent = ec->content(uiHeader); + if (virtualContent.isEmpty()) { + qCDebug(log) << "\textra compiler unexpectedly has no contents"; + return reportRenamingError(oldName, + Designer::Tr::tr("Failed to retrieve ui header contents.")); + } + + // Secretly open ui header file contents in editor. + // Use a temp file rather than the actual ui header path. + const auto openFlags = Core::EditorManager::DoNotMakeVisible + | Core::EditorManager::DoNotChangeCurrentEditor; + std::unique_ptr tempFile + = std::make_unique("XXXXXX" + uiHeader.fileName()); + QTC_ASSERT(tempFile->open(), return); + qCDebug(log) << '\t' << tempFile->fileName(); + const auto editor = qobject_cast( + Core::EditorManager::openEditor(FilePath::fromString(tempFile->fileName()), {}, + openFlags)); + QTC_ASSERT(editor, return); + resourceHandler->setTempFile(std::move(tempFile)); + resourceHandler->setEditor(editor); + + const auto editorWidget = qobject_cast(editor->editorWidget()); + QTC_ASSERT(editorWidget && editorWidget->textDocument(), return); + + // Parse temp file with built-in code model. Pretend it's the real ui header. + // In the case of clangd, this entails doing a "virtual rename" on the TextDocument, + // as the LanguageClient cannot be forced into taking a document and assuming a different + // file path. + const bool usesClangd = CppEditor::CppModelManager::usesClangd(editorWidget->textDocument()); + if (usesClangd) + editorWidget->textDocument()->setFilePath(uiHeader); + editorWidget->textDocument()->setPlainText(QString::fromUtf8(virtualContent)); + Snapshot snapshot = CppEditor::CppModelManager::instance()->snapshot(); + snapshot.remove(uiHeader); + snapshot.remove(editor->textDocument()->filePath()); + const Document::Ptr cppDoc = snapshot.preprocessedDocument(virtualContent, uiHeader); + cppDoc->check(); + QTC_ASSERT(cppDoc && cppDoc->isParsed(), return); + + // Locate old identifier in ui header. + const QByteArray oldNameBa = oldName.toUtf8(); + const Identifier oldIdentifier(oldNameBa.constData(), oldNameBa.size()); + QList scopes{cppDoc->globalNamespace()}; + while (!scopes.isEmpty()) { + const Scope * const scope = scopes.takeFirst(); + qCDebug(log) << '\t' << scope->memberCount(); + for (int i = 0; i < scope->memberCount(); ++i) { + Symbol * const symbol = scope->memberAt(i); + if (const Scope * const s = symbol->asScope()) + scopes << s; + if (symbol->asNamespace()) + continue; + qCDebug(log) << '\t' << Overview().prettyName(symbol->name()); + if (!symbol->name()->match(&oldIdentifier)) + continue; + QTextCursor cursor(editorWidget->textCursor()); + cursor.setPosition(cppDoc->translationUnit()->getTokenPositionInDocument( + symbol->sourceLocation(), editorWidget->document())); + qCDebug(log) << '\t' << cursor.position() << cursor.blockNumber() + << cursor.positionInBlock(); + + // Trigger non-interactive renaming. The callback is destructed after invocation, + // closing the editor, removing the temp file and unblocking the extra compiler. + // For the built-in code model, we must access the model manager directly, + // as otherwise our file path trickery would be found out. + const auto callback = [resourceHandler] { }; + if (usesClangd) { + qCDebug(log) << "renaming with clangd"; + editorWidget->renameUsages(uiHeader, newName, cursor, callback); + } else { + qCDebug(log) << "renaming with built-in code model"; + snapshot.insert(cppDoc); + snapshot.updateDependencyTable(); + CppEditor::CppModelManager::instance()->renameUsages(cppDoc, cursor, snapshot, + newName, callback); + } + return; + } + } + reportRenamingError(oldName, + Designer::Tr::tr("Failed to locate corresponding symbol in ui header.")); +} + void QtCreatorIntegration::slotSyncSettingsToDesigner() { // Set promotion-relevant parameters on integration. diff --git a/src/plugins/designer/qtcreatorintegration.h b/src/plugins/designer/qtcreatorintegration.h index 4789d28bfc2..c27d296ab0b 100644 --- a/src/plugins/designer/qtcreatorintegration.h +++ b/src/plugins/designer/qtcreatorintegration.h @@ -17,6 +17,7 @@ class QtCreatorIntegration : public QDesignerIntegration public: explicit QtCreatorIntegration(QDesignerFormEditorInterface *core, QObject *parent = nullptr); + ~QtCreatorIntegration(); QWidget *containerWindow(QWidget *widget) const override; @@ -36,6 +37,13 @@ private: const QString &signalSignature, const QStringList ¶meterNames, QString *errorMessage); + void handleSymbolRenameStage1(QDesignerFormWindowInterface *formWindow, QObject *object, + const QString &newName, const QString &oldName); + void handleSymbolRenameStage2(QDesignerFormWindowInterface *formWindow, + const QString &newName, const QString &oldName); + + class Private; + Private * const d; }; } // namespace Internal diff --git a/src/plugins/docker/dockerdevice.cpp b/src/plugins/docker/dockerdevice.cpp index 6ab31035487..aaa9fd3a1ea 100644 --- a/src/plugins/docker/dockerdevice.cpp +++ b/src/plugins/docker/dockerdevice.cpp @@ -198,7 +198,7 @@ public: QString m_container; - Environment m_cachedEnviroment; + std::optional m_cachedEnviroment; bool m_isShutdown = false; DockerDeviceFileAccess m_fileAccess{this}; }; @@ -500,7 +500,7 @@ void DockerDevicePrivate::stopCurrentContainer() proc.runBlocking(); - m_cachedEnviroment.clear(); + m_cachedEnviroment.reset(); } bool DockerDevicePrivate::prepareForBuild(const Target *target) @@ -1137,11 +1137,11 @@ bool DockerDevicePrivate::addTemporaryMount(const FilePath &path, const FilePath Environment DockerDevicePrivate::environment() { - if (!m_cachedEnviroment.isValid()) + if (!m_cachedEnviroment) fetchSystemEnviroment(); - QTC_CHECK(m_cachedEnviroment.isValid()); - return m_cachedEnviroment; + QTC_ASSERT(m_cachedEnviroment, return {}); + return m_cachedEnviroment.value(); } void DockerDevicePrivate::shutdown() diff --git a/src/plugins/haskell/CMakeLists.txt b/src/plugins/haskell/CMakeLists.txt new file mode 100644 index 00000000000..8f6cc4f1d85 --- /dev/null +++ b/src/plugins/haskell/CMakeLists.txt @@ -0,0 +1,27 @@ +add_qtc_plugin(Haskell + PLUGIN_DEPENDS + QtCreator::Core QtCreator::TextEditor QtCreator::ProjectExplorer + DEPENDS Qt5::Widgets + SOURCES + haskell.qrc + haskell_global.h + haskellbuildconfiguration.cpp haskellbuildconfiguration.h + haskellconstants.h + haskelleditorfactory.cpp haskelleditorfactory.h + haskellhighlighter.cpp haskellhighlighter.h + haskellmanager.cpp haskellmanager.h + haskellplugin.cpp haskellplugin.h + haskellproject.cpp haskellproject.h + haskellrunconfiguration.cpp haskellrunconfiguration.h + haskelltokenizer.cpp haskelltokenizer.h + optionspage.cpp optionspage.h + stackbuildstep.cpp stackbuildstep.h +) + +qtc_add_resources(Haskell haskell_wizards + PREFIX "/haskell" + BASE share/wizards + FILES + module/file.hs + module/wizard.json +) diff --git a/src/plugins/haskell/Haskell.json.in b/src/plugins/haskell/Haskell.json.in new file mode 100644 index 00000000000..f9062c88b27 --- /dev/null +++ b/src/plugins/haskell/Haskell.json.in @@ -0,0 +1,23 @@ +{ + \"Name\" : \"Haskell\", + \"Version\" : \"$$QTCREATOR_VERSION\", + \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", + \"DisabledByDefault\" : true, + \"Vendor\" : \"Eike Ziller\", + \"Copyright\" : \"(C) Eike Ziller\", + \"License\" : \"MIT\", + \"Description\" : \"Haskell support\", + \"Url\" : \"https://haskell.org\", + $$dependencyList, + + \"Mimetypes\" : [ + \"\", + \"\", + \" \", + \" \", + \" Haskell Cabal project file\", + \" \", + \" \", + \"\" + ] +} diff --git a/src/plugins/haskell/haskell.qbs b/src/plugins/haskell/haskell.qbs new file mode 100644 index 00000000000..30eb8328f1a --- /dev/null +++ b/src/plugins/haskell/haskell.qbs @@ -0,0 +1,28 @@ +import qbs 1.0 + +QtcPlugin { + name: "Haskell" + + Depends { name: "Qt.widgets" } + Depends { name: "Utils" } + + Depends { name: "Core" } + Depends { name: "TextEditor" } + Depends { name: "ProjectExplorer" } + + files: [ + "haskell.qrc", + "haskellbuildconfiguration.cpp", "haskellbuildconfiguration.h", + "haskellconstants.h", + "haskelleditorfactory.cpp", "haskelleditorfactory.h", + "haskell_global.h", + "haskellhighlighter.cpp", "haskellhighlighter.h", + "haskellmanager.cpp", "haskellmanager.h", + "haskellplugin.cpp", "haskellplugin.h", + "haskellproject.cpp", "haskellproject.h", + "haskellrunconfiguration.cpp", "haskellrunconfiguration.h", + "haskelltokenizer.cpp", "haskelltokenizer.h", + "optionspage.cpp", "optionspage.h", + "stackbuildstep.cpp", "stackbuildstep.h" + ] +} diff --git a/src/plugins/haskell/haskell.qrc b/src/plugins/haskell/haskell.qrc new file mode 100644 index 00000000000..654f45818c3 --- /dev/null +++ b/src/plugins/haskell/haskell.qrc @@ -0,0 +1,5 @@ + + + images/category_haskell.png + + diff --git a/src/plugins/haskell/haskell_global.h b/src/plugins/haskell/haskell_global.h new file mode 100644 index 00000000000..9452e44653c --- /dev/null +++ b/src/plugins/haskell/haskell_global.h @@ -0,0 +1,34 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include + +#if defined(HASKELL_LIBRARY) +# define HASKELLSHARED_EXPORT Q_DECL_EXPORT +#else +# define HASKELLSHARED_EXPORT Q_DECL_IMPORT +#endif diff --git a/src/plugins/haskell/haskellbuildconfiguration.cpp b/src/plugins/haskell/haskellbuildconfiguration.cpp new file mode 100644 index 00000000000..77bee060a77 --- /dev/null +++ b/src/plugins/haskell/haskellbuildconfiguration.cpp @@ -0,0 +1,132 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "haskellbuildconfiguration.h" + +#include "haskellconstants.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace ProjectExplorer; + +const char C_HASKELL_BUILDCONFIGURATION_ID[] = "Haskell.BuildConfiguration"; + +namespace Haskell { +namespace Internal { + +HaskellBuildConfigurationFactory::HaskellBuildConfigurationFactory() +{ + registerBuildConfiguration(C_HASKELL_BUILDCONFIGURATION_ID); + setSupportedProjectType(Constants::C_HASKELL_PROJECT_ID); + setSupportedProjectMimeTypeName(Constants::C_HASKELL_PROJECT_MIMETYPE); + + setBuildGenerator([](const Kit *k, const Utils::FilePath &projectPath, bool forSetup) { + BuildInfo info; + info.typeName = HaskellBuildConfiguration::tr("Release"); + if (forSetup) { + info.displayName = info.typeName; + info.buildDirectory = projectPath.parentDir().pathAppended(".stack-work"); + } + info.kitId = k->id(); + info.buildType = BuildConfiguration::BuildType::Release; + return QList{info}; + }); +} + +HaskellBuildConfiguration::HaskellBuildConfiguration(Target *target, Utils::Id id) + : BuildConfiguration(target, id) +{ + setInitializer([this](const BuildInfo &info) { + setBuildDirectory(info.buildDirectory); + setBuildType(info.buildType); + setDisplayName(info.displayName); + }); + appendInitialBuildStep(Constants::C_STACK_BUILD_STEP_ID); +} + +NamedWidget *HaskellBuildConfiguration::createConfigWidget() +{ + return new HaskellBuildConfigurationWidget(this); +} + +BuildConfiguration::BuildType HaskellBuildConfiguration::buildType() const +{ + return m_buildType; +} + +void HaskellBuildConfiguration::setBuildType(BuildConfiguration::BuildType type) +{ + m_buildType = type; +} + +HaskellBuildConfigurationWidget::HaskellBuildConfigurationWidget(HaskellBuildConfiguration *bc) + : NamedWidget(tr("General")) + , m_buildConfiguration(bc) +{ + setLayout(new QVBoxLayout); + layout()->setContentsMargins(0, 0, 0, 0); + auto box = new Utils::DetailsWidget; + box->setState(Utils::DetailsWidget::NoSummary); + layout()->addWidget(box); + auto details = new QWidget; + box->setWidget(details); + details->setLayout(new QHBoxLayout); + details->layout()->setContentsMargins(0, 0, 0, 0); + details->layout()->addWidget(new QLabel(tr("Build directory:"))); + + auto buildDirectoryInput = new Utils::PathChooser; + buildDirectoryInput->setExpectedKind(Utils::PathChooser::Directory); + buildDirectoryInput->setFilePath(m_buildConfiguration->buildDirectory()); + details->layout()->addWidget(buildDirectoryInput); + + connect(m_buildConfiguration, + &BuildConfiguration::buildDirectoryChanged, + buildDirectoryInput, + [this, buildDirectoryInput] { + buildDirectoryInput->setFilePath(m_buildConfiguration->buildDirectory()); + }); + connect(buildDirectoryInput, + &Utils::PathChooser::textChanged, + m_buildConfiguration, + [this, buildDirectoryInput](const QString &) { + m_buildConfiguration->setBuildDirectory(buildDirectoryInput->rawFilePath()); + }); +} + +} // namespace Internal +} // namespace Haskell diff --git a/src/plugins/haskell/haskellbuildconfiguration.h b/src/plugins/haskell/haskellbuildconfiguration.h new file mode 100644 index 00000000000..c93161e5353 --- /dev/null +++ b/src/plugins/haskell/haskellbuildconfiguration.h @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include +#include + +namespace Haskell { +namespace Internal { + +class HaskellBuildConfigurationFactory : public ProjectExplorer::BuildConfigurationFactory +{ +public: + HaskellBuildConfigurationFactory(); +}; + +class HaskellBuildConfiguration : public ProjectExplorer::BuildConfiguration +{ + Q_OBJECT + +public: + HaskellBuildConfiguration(ProjectExplorer::Target *target, Utils::Id id); + + ProjectExplorer::NamedWidget *createConfigWidget() override; + BuildType buildType() const override; + void setBuildType(BuildType type); + +private: + BuildType m_buildType = BuildType::Release; +}; + +class HaskellBuildConfigurationWidget : public ProjectExplorer::NamedWidget +{ + Q_OBJECT + +public: + HaskellBuildConfigurationWidget(HaskellBuildConfiguration *bc); + +private: + HaskellBuildConfiguration *m_buildConfiguration; +}; + +} // namespace Internal +} // namespace Haskell diff --git a/src/plugins/haskell/haskellconstants.h b/src/plugins/haskell/haskellconstants.h new file mode 100644 index 00000000000..8b63761ada0 --- /dev/null +++ b/src/plugins/haskell/haskellconstants.h @@ -0,0 +1,41 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +namespace Haskell { +namespace Constants { + +const char C_HASKELLEDITOR_ID[] = "Haskell.HaskellEditor"; +const char C_HASKELLSNIPPETSGROUP_ID[] = "Haskell"; +const char C_HASKELL_PROJECT_MIMETYPE[] = "text/x-haskell-project"; +const char C_HASKELL_PROJECT_ID[] = "Haskell.Project"; +const char C_HASKELL_RUNCONFIG_ID[] = "Haskell.RunConfiguration"; +const char C_STACK_BUILD_STEP_ID[] = "Haskell.Stack.Build"; +const char OPTIONS_GENERAL[] = "Haskell.A.General"; +const char A_RUN_GHCI[] = "Haskell.RunGHCi"; + +} // namespace Haskell +} // namespace Constants diff --git a/src/plugins/haskell/haskelleditorfactory.cpp b/src/plugins/haskell/haskelleditorfactory.cpp new file mode 100644 index 00000000000..206677915ba --- /dev/null +++ b/src/plugins/haskell/haskelleditorfactory.cpp @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "haskelleditorfactory.h" + +#include "haskellconstants.h" +#include "haskellhighlighter.h" +#include "haskellmanager.h" + +#include +#include +#include +#include + +#include + +namespace Haskell { +namespace Internal { + +static QWidget *createEditorWidget() +{ + auto widget = new TextEditor::TextEditorWidget; + auto ghciButton = new Core::CommandButton(Constants::A_RUN_GHCI, widget); + ghciButton->setText(HaskellManager::tr("GHCi")); + QObject::connect(ghciButton, &QToolButton::clicked, HaskellManager::instance(), [widget] { + HaskellManager::openGhci(widget->textDocument()->filePath()); + }); + widget->insertExtraToolBarWidget(TextEditor::TextEditorWidget::Left, ghciButton); + return widget; +} + +HaskellEditorFactory::HaskellEditorFactory() +{ + setId(Constants::C_HASKELLEDITOR_ID); + setDisplayName(QCoreApplication::translate("OpenWith::Editors", "Haskell Editor")); + addMimeType("text/x-haskell"); + setEditorActionHandlers(TextEditor::TextEditorActionHandler::UnCommentSelection + | TextEditor::TextEditorActionHandler::FollowSymbolUnderCursor); + setDocumentCreator([] { return new TextEditor::TextDocument(Constants::C_HASKELLEDITOR_ID); }); + setIndenterCreator([](QTextDocument *doc) { return new TextEditor::TextIndenter(doc); }); + setEditorWidgetCreator(createEditorWidget); + setCommentDefinition(Utils::CommentDefinition("--", "{-", "-}")); + setParenthesesMatchingEnabled(true); + setMarksVisible(true); + setSyntaxHighlighterCreator([] { return new HaskellHighlighter(); }); +} + +} // Internal +} // Haskell diff --git a/src/plugins/haskell/haskelleditorfactory.h b/src/plugins/haskell/haskelleditorfactory.h new file mode 100644 index 00000000000..a48632d289d --- /dev/null +++ b/src/plugins/haskell/haskelleditorfactory.h @@ -0,0 +1,40 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include + +namespace Haskell { +namespace Internal { + +class HaskellEditorFactory : public TextEditor::TextEditorFactory +{ +public: + HaskellEditorFactory(); +}; + +} // Internal +} // Haskell diff --git a/src/plugins/haskell/haskellhighlighter.cpp b/src/plugins/haskell/haskellhighlighter.cpp new file mode 100644 index 00000000000..8a5b6cae76e --- /dev/null +++ b/src/plugins/haskell/haskellhighlighter.cpp @@ -0,0 +1,152 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "haskellhighlighter.h" + +#include "haskelltokenizer.h" + +#include +#include +#include + +#include +#include + +Q_GLOBAL_STATIC_WITH_ARGS(QSet, IMPORT_HIGHLIGHTS, ({ + "qualified", + "as", + "hiding" +})); + +using namespace TextEditor; + +namespace Haskell { +namespace Internal { + +HaskellHighlighter::HaskellHighlighter() +{ + setDefaultTextFormatCategories(); + updateFormats(TextEditorSettings::fontSettings()); +} + +void HaskellHighlighter::highlightBlock(const QString &text) +{ + const Tokens tokens = HaskellTokenizer::tokenize(text, previousBlockState()); + setCurrentBlockState(tokens.state); + const Token *firstNonWS = 0; + const Token *secondNonWS = 0; + bool inType = false; + bool inImport = false; + for (const Token & token : tokens) { + switch (token.type) { + case TokenType::Variable: + if (inType) + setTokenFormat(token, C_LOCAL); + else if (inImport && IMPORT_HIGHLIGHTS->contains(token.text.toString())) + setTokenFormat(token, C_KEYWORD); +// else +// setTokenFormat(token, C_TEXT); + break; + case TokenType::Constructor: + case TokenType::OperatorConstructor: + setTokenFormat(token, C_TYPE); + break; + case TokenType::Operator: + setTokenFormat(token, C_OPERATOR); + break; + case TokenType::Whitespace: + setTokenFormat(token, C_VISUAL_WHITESPACE); + break; + case TokenType::Keyword: + if (token.text == QLatin1String("::") && firstNonWS && !secondNonWS) { // toplevel declaration + setFormat(firstNonWS->startCol, firstNonWS->length, m_toplevelDeclFormat); + inType = true; + } else if (token.text == QLatin1String("import")) { + inImport = true; + } + setTokenFormat(token, C_KEYWORD); + break; + case TokenType::Integer: + case TokenType::Float: + setTokenFormat(token, C_NUMBER); + break; + case TokenType::String: + setTokenFormatWithSpaces(text, token, C_STRING); + break; + case TokenType::Char: + setTokenFormatWithSpaces(text, token, C_STRING); + break; + case TokenType::EscapeSequence: + setTokenFormat(token, C_PRIMITIVE_TYPE); + break; + case TokenType::SingleLineComment: + setTokenFormatWithSpaces(text, token, C_COMMENT); + break; + case TokenType::MultiLineComment: + setTokenFormatWithSpaces(text, token, C_COMMENT); + break; + case TokenType::Special: +// setTokenFormat(token, C_TEXT); + break; + case TokenType::StringError: + case TokenType::CharError: + case TokenType::Unknown: + setTokenFormat(token, C_PARENTHESES_MISMATCH); + break; + } + if (token.type != TokenType::Whitespace) { + if (!firstNonWS) + firstNonWS = &token; + else if (!secondNonWS) + secondNonWS = &token; + } + } +} + +void HaskellHighlighter::setFontSettings(const FontSettings &fontSettings) +{ + SyntaxHighlighter::setFontSettings(fontSettings); + updateFormats(fontSettings); +} + +void HaskellHighlighter::updateFormats(const FontSettings &fontSettings) +{ + m_toplevelDeclFormat = fontSettings.toTextCharFormat( + TextStyles::mixinStyle(C_FUNCTION, C_DECLARATION)); +} + +void HaskellHighlighter::setTokenFormat(const Token &token, TextStyle style) +{ + setFormat(token.startCol, token.length, formatForCategory(style)); +} + +void HaskellHighlighter::setTokenFormatWithSpaces(const QString &text, const Token &token, + TextStyle style) +{ + setFormatWithSpaces(text, token.startCol, token.length, formatForCategory(style)); +} + +} // Internal +} // Haskell diff --git a/src/plugins/haskell/haskellhighlighter.h b/src/plugins/haskell/haskellhighlighter.h new file mode 100644 index 00000000000..621333341bc --- /dev/null +++ b/src/plugins/haskell/haskellhighlighter.h @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include + +#include +#include + +namespace Haskell { +namespace Internal { + +class Token; + +class HaskellHighlighter : public TextEditor::SyntaxHighlighter +{ + Q_OBJECT + +public: + HaskellHighlighter(); + +protected: + void highlightBlock(const QString &text) override; + +private: + void setFontSettings(const TextEditor::FontSettings &fontSettings) override; + void updateFormats(const TextEditor::FontSettings &fontSettings); + void setTokenFormat(const Token &token, TextEditor::TextStyle style); + void setTokenFormatWithSpaces(const QString &text, const Token &token, + TextEditor::TextStyle style); + QTextCharFormat m_toplevelDeclFormat; +}; + +} // Internal +} // Haskell diff --git a/src/plugins/haskell/haskellmanager.cpp b/src/plugins/haskell/haskellmanager.cpp new file mode 100644 index 00000000000..dd6a3712b95 --- /dev/null +++ b/src/plugins/haskell/haskellmanager.cpp @@ -0,0 +1,141 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "haskellmanager.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +static const char kStackExecutableKey[] = "Haskell/StackExecutable"; + +using namespace Utils; + +namespace Haskell { +namespace Internal { + +class HaskellManagerPrivate +{ +public: + FilePath stackExecutable; +}; + +Q_GLOBAL_STATIC(HaskellManagerPrivate, m_d) +Q_GLOBAL_STATIC(HaskellManager, m_instance) + +HaskellManager *HaskellManager::instance() +{ + return m_instance; +} + +FilePath HaskellManager::findProjectDirectory(const FilePath &filePath) +{ + if (filePath.isEmpty()) + return {}; + + QDir directory(filePath.toFileInfo().isDir() ? filePath.toString() + : filePath.parentDir().toString()); + directory.setNameFilters({"stack.yaml", "*.cabal"}); + directory.setFilter(QDir::Files | QDir::Readable); + do { + if (!directory.entryList().isEmpty()) + return FilePath::fromString(directory.path()); + } while (!directory.isRoot() && directory.cdUp()); + return {}; +} + +FilePath defaultStackExecutable() +{ + // stack from brew or the installer script from https://docs.haskellstack.org + // install to /usr/local/bin. + if (HostOsInfo::isAnyUnixHost()) + return FilePath::fromString("/usr/local/bin/stack"); + return FilePath::fromString("stack"); +} + +FilePath HaskellManager::stackExecutable() +{ + return m_d->stackExecutable; +} + +void HaskellManager::setStackExecutable(const FilePath &filePath) +{ + if (filePath == m_d->stackExecutable) + return; + m_d->stackExecutable = filePath; + emit m_instance->stackExecutableChanged(m_d->stackExecutable); +} + +void HaskellManager::openGhci(const FilePath &haskellFile) +{ + const QList mimeTypes = mimeTypesForFileName(haskellFile.toString()); + const bool isHaskell = Utils::anyOf(mimeTypes, [](const MimeType &mt) { + return mt.inherits("text/x-haskell") || mt.inherits("text/x-literate-haskell"); + }); + const auto args = QStringList{"ghci"} + + (isHaskell ? QStringList{haskellFile.fileName()} : QStringList()); + auto p = new QtcProcess(m_instance); + p->setTerminalMode(TerminalMode::On); + p->setCommand({stackExecutable(), args}); + p->setWorkingDirectory(haskellFile.absolutePath()); + connect(p, &QtcProcess::done, p, [p] { + if (p->result() != ProcessResult::FinishedWithSuccess) { + Core::MessageManager::writeDisrupting( + tr("Failed to run GHCi: \"%1\".").arg(p->errorString())); + } + p->deleteLater(); + }); + p->start(); +} + +void HaskellManager::readSettings(QSettings *settings) +{ + m_d->stackExecutable = FilePath::fromString( + settings->value(kStackExecutableKey, + defaultStackExecutable().toString()).toString()); + emit m_instance->stackExecutableChanged(m_d->stackExecutable); +} + +void HaskellManager::writeSettings(QSettings *settings) +{ + if (m_d->stackExecutable == defaultStackExecutable()) + settings->remove(kStackExecutableKey); + else + settings->setValue(kStackExecutableKey, m_d->stackExecutable.toString()); +} + +} // namespace Internal +} // namespace Haskell diff --git a/src/plugins/haskell/haskellmanager.h b/src/plugins/haskell/haskellmanager.h new file mode 100644 index 00000000000..361b8f8e966 --- /dev/null +++ b/src/plugins/haskell/haskellmanager.h @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include + +#include + +QT_BEGIN_NAMESPACE +class QSettings; +QT_END_NAMESPACE + +namespace Haskell { +namespace Internal { + +class HaskellManager : public QObject +{ + Q_OBJECT + +public: + static HaskellManager *instance(); + + static Utils::FilePath findProjectDirectory(const Utils::FilePath &filePath); + static Utils::FilePath stackExecutable(); + static void setStackExecutable(const Utils::FilePath &filePath); + static void openGhci(const Utils::FilePath &haskellFile); + static void readSettings(QSettings *settings); + static void writeSettings(QSettings *settings); + +signals: + void stackExecutableChanged(const Utils::FilePath &filePath); +}; + +} // namespace Internal +} // namespace Haskell diff --git a/src/plugins/haskell/haskellplugin.cpp b/src/plugins/haskell/haskellplugin.cpp new file mode 100644 index 00000000000..c3ae400629c --- /dev/null +++ b/src/plugins/haskell/haskellplugin.cpp @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "haskellplugin.h" + +#include "haskellbuildconfiguration.h" +#include "haskellconstants.h" +#include "haskelleditorfactory.h" +#include "haskellmanager.h" +#include "haskellproject.h" +#include "haskellrunconfiguration.h" +#include "optionspage.h" +#include "stackbuildstep.h" + +#include +#include +#include +#include +#include +#include + +#include + +namespace Haskell { +namespace Internal { + +class HaskellPluginPrivate +{ +public: + HaskellEditorFactory editorFactory; + OptionsPage optionsPage; + HaskellBuildConfigurationFactory buildConfigFactory; + StackBuildStepFactory stackBuildStepFactory; + HaskellRunConfigurationFactory runConfigFactory; + ProjectExplorer::SimpleTargetRunnerFactory runWorkerFactory{{Constants::C_HASKELL_RUNCONFIG_ID}}; +}; + +HaskellPlugin::~HaskellPlugin() +{ + delete d; +} + +static void registerGhciAction() +{ + QAction *action = new QAction(HaskellManager::tr("Run GHCi"), HaskellManager::instance()); + Core::ActionManager::registerAction(action, Constants::A_RUN_GHCI); + QObject::connect(action, &QAction::triggered, HaskellManager::instance(), [] { + if (Core::IDocument *doc = Core::EditorManager::currentDocument()) + HaskellManager::openGhci(doc->filePath()); + }); +} + +bool HaskellPlugin::initialize(const QStringList &arguments, QString *errorString) +{ + Q_UNUSED(arguments) + Q_UNUSED(errorString) + + d = new HaskellPluginPrivate; + + ProjectExplorer::ProjectManager::registerProjectType( + Constants::C_HASKELL_PROJECT_MIMETYPE); + TextEditor::SnippetProvider::registerGroup(Constants::C_HASKELLSNIPPETSGROUP_ID, + tr("Haskell", "SnippetProvider")); + + connect(Core::ICore::instance(), &Core::ICore::saveSettingsRequested, this, [] { + HaskellManager::writeSettings(Core::ICore::settings()); + }); + + registerGhciAction(); + + HaskellManager::readSettings(Core::ICore::settings()); + + ProjectExplorer::JsonWizardFactory::addWizardPath(":/haskell/share/wizards/"); + return true; +} + +} // namespace Internal +} // namespace Haskell diff --git a/src/plugins/haskell/haskellplugin.h b/src/plugins/haskell/haskellplugin.h new file mode 100644 index 00000000000..163f40b959b --- /dev/null +++ b/src/plugins/haskell/haskellplugin.h @@ -0,0 +1,52 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include "haskell_global.h" + +#include + +namespace Haskell { +namespace Internal { + +class HaskellPlugin : public ExtensionSystem::IPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "Haskell.json") + +public: + HaskellPlugin() = default; + ~HaskellPlugin() final; + +private: + bool initialize(const QStringList &arguments, QString *errorString) final; + void extensionsInitialized() final {} + + class HaskellPluginPrivate *d = nullptr; +}; + +} // namespace Internal +} // namespace Haskell diff --git a/src/plugins/haskell/haskellproject.cpp b/src/plugins/haskell/haskellproject.cpp new file mode 100644 index 00000000000..248f898aeed --- /dev/null +++ b/src/plugins/haskell/haskellproject.cpp @@ -0,0 +1,135 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "haskellproject.h" + +#include "haskellconstants.h" + +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include + +using namespace ProjectExplorer; +using namespace Utils; + +namespace Haskell { +namespace Internal { + +static QVector parseExecutableNames(const FilePath &projectFilePath) +{ + static const QString EXECUTABLE = "executable"; + static const int EXECUTABLE_LEN = EXECUTABLE.length(); + QVector result; + QFile file(projectFilePath.toString()); + if (file.open(QFile::ReadOnly)) { + QTextStream stream(&file); + while (!stream.atEnd()) { + const QString line = stream.readLine().trimmed(); + if (line.length() > EXECUTABLE_LEN && line.startsWith(EXECUTABLE) + && line.at(EXECUTABLE_LEN).isSpace()) + result.append(line.mid(EXECUTABLE_LEN + 1).trimmed()); + } + } + return result; +} + +HaskellProject::HaskellProject(const Utils::FilePath &fileName) + : Project(Constants::C_HASKELL_PROJECT_MIMETYPE, fileName) +{ + setId(Constants::C_HASKELL_PROJECT_ID); + setDisplayName(fileName.toFileInfo().completeBaseName()); + setBuildSystemCreator([](Target *t) { return new HaskellBuildSystem(t); }); +} + +bool HaskellProject::isHaskellProject(Project *project) +{ + return project && project->id() == Constants::C_HASKELL_PROJECT_ID; +} + +HaskellBuildSystem::HaskellBuildSystem(Target *t) + : BuildSystem(t) +{ + connect(&m_scanner, &TreeScanner::finished, this, [this] { + auto root = std::make_unique(projectDirectory()); + root->setDisplayName(target()->project()->displayName()); + std::vector> nodePtrs + = Utils::transform(m_scanner.release().allFiles, [](FileNode *fn) { + return std::unique_ptr(fn); + }); + root->addNestedNodes(std::move(nodePtrs)); + setRootProjectNode(std::move(root)); + + updateApplicationTargets(); + + m_parseGuard.markAsSuccess(); + m_parseGuard = {}; + + emitBuildSystemUpdated(); + }); + + connect(target()->project(), + &Project::projectFileIsDirty, + this, + &BuildSystem::requestDelayedParse); + + requestDelayedParse(); +} + +void HaskellBuildSystem::triggerParsing() +{ + m_parseGuard = guardParsingRun(); + m_scanner.asyncScanForFiles(target()->project()->projectDirectory()); +} + +void HaskellBuildSystem::updateApplicationTargets() +{ + const QVector executables = parseExecutableNames(projectFilePath()); + const Utils::FilePath projFilePath = projectFilePath(); + const QList appTargets + = Utils::transform(executables, [projFilePath](const QString &executable) { + BuildTargetInfo bti; + bti.displayName = executable; + bti.buildKey = executable; + bti.targetFilePath = FilePath::fromString(executable); + bti.projectFilePath = projFilePath; + bti.isQtcRunnable = true; + return bti; + }); + setApplicationTargets(appTargets); + target()->updateDefaultRunConfigurations(); +} + +} // namespace Internal +} // namespace Haskell diff --git a/src/plugins/haskell/haskellproject.h b/src/plugins/haskell/haskellproject.h new file mode 100644 index 00000000000..0ee05d90333 --- /dev/null +++ b/src/plugins/haskell/haskellproject.h @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include +#include +#include +#include + +namespace Haskell { +namespace Internal { + +class HaskellProject : public ProjectExplorer::Project +{ + Q_OBJECT + +public: + explicit HaskellProject(const Utils::FilePath &fileName); + + static bool isHaskellProject(Project *project); +}; + +class HaskellBuildSystem : public ProjectExplorer::BuildSystem +{ + Q_OBJECT + +public: + HaskellBuildSystem(ProjectExplorer::Target *t); + + void triggerParsing() override; + QString name() const final { return QLatin1String("haskell"); } + +private: + void updateApplicationTargets(); + void refresh(); + +private: + ParseGuard m_parseGuard; + ProjectExplorer::TreeScanner m_scanner; +}; + +} // namespace Internal +} // namespace Haskell diff --git a/src/plugins/haskell/haskellrunconfiguration.cpp b/src/plugins/haskell/haskellrunconfiguration.cpp new file mode 100644 index 00000000000..74574710f6f --- /dev/null +++ b/src/plugins/haskell/haskellrunconfiguration.cpp @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "haskellrunconfiguration.h" + +#include "haskellconstants.h" +#include "haskellmanager.h" +#include "haskellproject.h" + +#include +#include +#include +#include +#include + +using namespace ProjectExplorer; + +namespace Haskell { +namespace Internal { + +HaskellRunConfigurationFactory::HaskellRunConfigurationFactory() +{ + registerRunConfiguration(Constants::C_HASKELL_RUNCONFIG_ID); + addSupportedProjectType(Constants::C_HASKELL_PROJECT_ID); + addSupportedTargetDeviceType(ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE); +} + +HaskellExecutableAspect::HaskellExecutableAspect() +{ + setSettingsKey("Haskell.Executable"); + setLabelText(tr("Executable")); +} + +HaskellRunConfiguration::HaskellRunConfiguration(Target *target, Utils::Id id) + : RunConfiguration(target, id) +{ + auto envAspect = addAspect(target); + + addAspect(); + addAspect(macroExpander()); + + auto workingDirAspect = addAspect(macroExpander(), envAspect); + workingDirAspect->setDefaultWorkingDirectory(target->project()->projectDirectory()); + workingDirAspect->setVisible(false); + + addAspect(); + + setUpdater([this] { aspect()->setValue(buildTargetInfo().buildKey); }); + connect(target, &Target::buildSystemUpdated, this, &RunConfiguration::update); + update(); +} + +Runnable HaskellRunConfiguration::runnable() const +{ + const Utils::FilePath projectDirectory = target()->project()->projectDirectory(); + Runnable r; + QStringList args; + if (BuildConfiguration *buildConfiguration = target()->activeBuildConfiguration()) { + args << "--work-dir" + << QDir(projectDirectory.toString()).relativeFilePath( + buildConfiguration->buildDirectory().toString()); + } + args << "exec" << aspect()->value(); + const QString arguments = aspect()->arguments(); + if (!arguments.isEmpty()) + args << "--" << arguments; + + r.workingDirectory = projectDirectory; + r.environment = aspect()->environment(); + r.command = {r.environment.searchInPath(HaskellManager::stackExecutable().toString()), args}; + return r; +} + +} // namespace Internal +} // namespace Haskell diff --git a/src/plugins/haskell/haskellrunconfiguration.h b/src/plugins/haskell/haskellrunconfiguration.h new file mode 100644 index 00000000000..27e4a83f9ad --- /dev/null +++ b/src/plugins/haskell/haskellrunconfiguration.h @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include +#include +#include +#include + +namespace Haskell { +namespace Internal { + +class HaskellRunConfiguration : public ProjectExplorer::RunConfiguration +{ + Q_OBJECT + +public: + HaskellRunConfiguration(ProjectExplorer::Target *target, Utils::Id id); + +private: + ProjectExplorer::Runnable runnable() const final; +}; + +class HaskellRunConfigurationFactory : public ProjectExplorer::RunConfigurationFactory +{ +public: + HaskellRunConfigurationFactory(); +}; + +class HaskellExecutableAspect : public Utils::StringAspect +{ + Q_OBJECT + +public: + HaskellExecutableAspect(); +}; + +} // namespace Internal +} // namespace Haskell diff --git a/src/plugins/haskell/haskelltokenizer.cpp b/src/plugins/haskell/haskelltokenizer.cpp new file mode 100644 index 00000000000..6f2556d2475 --- /dev/null +++ b/src/plugins/haskell/haskelltokenizer.cpp @@ -0,0 +1,643 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "haskelltokenizer.h" + +#include + +#include +#include + +Q_GLOBAL_STATIC_WITH_ARGS(QSet, RESERVED_OP, ({ + "..", + ":", + "::", + "=", + "\\", + "|", + "<-", + "->", + "@", + "~", + "=>", + + // Arrows GHC extension + "-<", + "-<<", + ">-", + ">>-", + "(|", + "|)" +})); + +Q_GLOBAL_STATIC_WITH_ARGS(QSet, RESERVED_ID, ({ + "case", + "class", + "data", + "default", + "deriving", + "do", + "else", + "foreign", + "if", + "import", + "in", + "infix", + "infixl", + "infixr", + "instance", + "let", + "module", + "newtype", + "of", + "then", + "type", + "where", + "_", + + // from GHC extensions + "family", + "forall", + "mdo", + "proc", + "rec" +})); + +Q_GLOBAL_STATIC_WITH_ARGS(QSet, SPECIAL, ({ + '(', + ')', + ',', + ';', + '[', + ']', + '`', + '{', + '}', +})); + +Q_GLOBAL_STATIC_WITH_ARGS(QSet, CHAR_ESCAPES, + ({'a', 'b', 'f', 'n', 'r', 't', 'v', '\\', '"', '\'', '&'})); + +Q_GLOBAL_STATIC_WITH_ARGS(QVector, ASCII_ESCAPES, ({ + "NUL", + "SOH", // must be before "SO" to match + "STX", + "ETX", + "EOT", + "ENQ", + "ACK", + "BEL", + "BS", + "HT", + "LF", + "VT", + "FF", + "CR", + "SO", + "SI", + "DLE", + "DC1", + "DC2", + "DC3", + "DC4", + "NAK", + "SYN", + "ETB", + "CAN", + "EM", + "SUB", + "ESC", + "FS", + "GS", + "RS", + "US", + "SP", + "DEL" +})); + +namespace Haskell { +namespace Internal { + +Token token(TokenType type, std::shared_ptr line, int start, int end) +{ + return {type, start, end - start, QStringView(*line).mid(start, end - start), line}; +} + +Tokens::Tokens(std::shared_ptr source) + : source(source) +{ +} + +Token Tokens::tokenAtColumn(int col) const +{ + auto it = std::upper_bound(begin(), end(), col, [](int c, const Token &i) { + return c < i.startCol; + }); + if (it == begin()) + return Token(); + --it; + if (it->startCol + it->length > col) + return *it; + return Token(); +} + +static int grab(const QString &line, int begin, + const std::function &test) +{ + const int length = line.length(); + int current = begin; + while (current < length && test(line.at(current))) + ++current; + return current - begin; +}; + +static bool isIdentifierChar(const QChar &c) +{ + return c.isLetterOrNumber() || c == '\'' || c == '_'; +} + +static bool isVariableIdentifierStart(const QChar &c) +{ + return c == '_' || c.isLower(); +} + +static bool isAscSymbol(const QChar &c) +{ + return c == '!' + || c == '#' + || c == '$' + || c == '%' + || c == '&' + || c == '*' + || c == '+' + || c == '.' + || c == '/' + || c == '<' + || c == '=' + || c == '>' + || c == '?' + || c == '@' + || c == '\\' + || c == '^' + || c == '|' + || c == '-' + || c == '~' + || c == ':'; +} + +static bool isSymbol(const QChar &c) +{ + return isAscSymbol(c) + || ((c.isSymbol() || c.isPunct()) && c != '_' && c != '"' && c != '\'' + && !SPECIAL->contains(c)); +} + +static bool isDigit(const QChar &c) +{ + return c.isDigit(); +} + +static bool isOctit(const QChar &c) +{ + return c >= '0' && c <= '7'; +} + +static bool isHexit(const QChar &c) +{ + return c.isDigit() + || (c >= 'A' && c <= 'F') + || (c >= 'a' && c <= 'f'); +} + +static bool isCntrl(const QChar &c) +{ + return (c >= 'A' && c <= 'Z') + || c == '@' + || c == '[' + || c == '\\' + || c == ']' + || c == '^' + || c == '_'; +} + +static QVector getSpace(std::shared_ptr line, int start) +{ + const auto lineEnd = line->cend(); + const auto tokenStart = line->cbegin() + start; + auto current = tokenStart; + while (current != lineEnd && (*current).isSpace()) + ++current; + const int length = int(std::distance(tokenStart, current)); + if (current > tokenStart) + return {{TokenType::Whitespace, start, length, QStringView(*line).mid(start, length), line}}; + return {}; +} + +static QVector getNumber(std::shared_ptr line, int start) +{ + const QChar &startC = line->at(start); + if (!startC.isDigit()) + return {}; + const int length = line->length(); + int current = start + 1; + TokenType type = TokenType::Integer; + if (current < length) { + if (startC == '0') { + // check for octal or hexadecimal + const QChar &secondC = line->at(current); + if (secondC == 'o' || secondC == 'O') { + const int numLen = grab(*line, current + 1, isOctit); + if (numLen > 0) + return {token(TokenType::Integer, line, start, current + numLen + 1)}; + } else if (secondC == 'x' || secondC == 'X') { + const int numLen = grab(*line, current + 1, isHexit); + if (numLen > 0) + return {token(TokenType::Integer, line, start, current + numLen + 1)}; + } + } + // starts with decimal + const int numLen = grab(*line, start, isDigit); + current = start + numLen; + // check for floating point + if (current < length && line->at(current) == '.') { + const int numLen = grab(*line, current + 1, isDigit); + if (numLen > 0) { + current += numLen + 1; + type = TokenType::Float; + } + } + // check for exponent + if (current + 1 < length /*for at least 'e' and digit*/ + && (line->at(current) == 'e' || line->at(current) == 'E')) { + int expEnd = current + 1; + if (line->at(expEnd) == '+' || line->at(expEnd) == '-') + ++expEnd; + const int numLen = grab(*line, expEnd, isDigit); + if (numLen > 0) { + current = expEnd + numLen; + type = TokenType::Float; + } + } + } + return {token(type, line, start, current)}; +} + +static QVector getIdOrOpOrSingleLineComment(std::shared_ptr line, int start) +{ + const int length = line->length(); + if (start >= length) + return {}; + int current = start; + // check for {conid.}conid + int conidEnd = start; + bool canOnlyBeConstructor = false; + while (current < length && line->at(current).isUpper()) { + current += grab(*line, current, isIdentifierChar); + conidEnd = current; + // it is definitely a constructor id if it is not followed by a '.' + canOnlyBeConstructor = current >= length || line->at(current) != '.'; + // otherwise it might be a module id, and we skip the dot to check for qualified thing + if (!canOnlyBeConstructor) + ++current; + } + if (canOnlyBeConstructor) + return {token(TokenType::Constructor, line, start, conidEnd)}; + + // check for variable or reserved id + if (current < length && isVariableIdentifierStart(line->at(current))) { + const int varLen = grab(*line, current, isIdentifierChar); + // check for reserved id + if (RESERVED_ID->contains(line->mid(current, varLen))) { + QVector result; + // possibly add constructor + op '.' + if (conidEnd > start) { + result.append(token(TokenType::Constructor, line, start, conidEnd)); + result.append(token(TokenType::Operator, line, conidEnd, current)); + } + result.append(token(TokenType::Keyword, line, current, current + varLen)); + return result; + } + return {token(TokenType::Variable, line, start, current + varLen)}; + } + // check for operator + if (current < length && isSymbol(line->at(current))) { + const int opLen = grab(*line, current, isSymbol); + // check for reserved op + if (RESERVED_OP->contains(line->mid(current, opLen))) { + // because of the case of F... (constructor + op '...') etc + // we only add conid if we have one, handling the rest in next iteration + if (conidEnd > start) + return {token(TokenType::Constructor, line, start, conidEnd)}; + return {token(TokenType::Keyword, line, start, current + opLen)}; + } + // check for single line comment + if (opLen >= 2 && std::all_of(line->begin() + current, line->begin() + current + opLen, + [](const QChar c) { return c == '-'; })) { + QVector result; + // possibly add constructor + op '.' + if (conidEnd > start) { + result.append(token(TokenType::Constructor, line, start, conidEnd)); + result.append(token(TokenType::Operator, line, conidEnd, current)); + } + // rest is comment + result.append(token(TokenType::SingleLineComment, line, current, length)); + return result; + } + // check for (qualified?) operator constructor + if (line->at(current) == ':') + return {token(TokenType::OperatorConstructor, line, start, current + opLen)}; + return {token(TokenType::Operator, line, start, current + opLen)}; + } + // Foo.Blah. + if (conidEnd > start) + return {token(TokenType::Constructor, line, start, conidEnd)}; + return {}; +} + +static int getEscape(const QString &line, int start) +{ + if (CHAR_ESCAPES->contains(line.at(start))) + return 1; + + // decimal + if (line.at(start).isDigit()) + return grab(line, start + 1, isDigit) + 1; + // octal + if (line.at(start) == 'o') { + const int count = grab(line, start + 1, isOctit); + if (count < 1) // no octal number after 'o' + return 0; + return count + 1; + } + // hexadecimal + if (line.at(start) == 'x') { + const int count = grab(line, start + 1, isHexit); + if (count < 1) // no octal number after 'o' + return 0; + return count + 1; + } + // ascii cntrl + if (line.at(start) == '^') { + const int count = grab(line, start + 1, isCntrl); + if (count < 1) // no octal number after 'o' + return 0; + return count + 1; + } + const QStringView s = QStringView(line).mid(start); + for (const QString &esc : *ASCII_ESCAPES) { + if (s.startsWith(esc)) + return esc.length(); + } + return 0; +} + +static QVector getString(std::shared_ptr line, int start, bool *inStringGap/*in-out*/) +{ + // Haskell has the specialty of using \\ within strings for multiline strings + const int length = line->length(); + if (start >= length) + return {}; + QVector result; + int tokenStart = start; + int current = tokenStart; + bool inString = *inStringGap; + do { + const QChar c = line->at(current); + if (*inStringGap && !c.isSpace() && c != '\\') { + // invalid non-whitespace in string gap + // add previous string as token, this is at least a whitespace + result.append(token(TokenType::String, line, tokenStart, current)); + // then add wrong non-whitespace + tokenStart = current; + do { ++current; } while (current < length && !line->at(current).isSpace()); + result.append(token(TokenType::StringError, line, tokenStart, current)); + tokenStart = current; + } else if (c == '"') { + inString = !inString; + ++current; + } else if (inString) { + if (c == '\\') { + ++current; + if (*inStringGap) { + // ending string gap + *inStringGap = false; + } else if (current >= length || line->at(current).isSpace()) { + // starting string gap + *inStringGap = true; + current = std::min(current + 1, length); + } else { // there is at least one character after current + const int escapeLength = getEscape(*line, current); + if (escapeLength > 0) { + // valid escape + // add previous string as token without backslash, if necessary + if (tokenStart < current - 1/*backslash*/) + result.append(token(TokenType::String, line, tokenStart, current - 1)); + tokenStart = current - 1; // backslash + current += escapeLength; + result.append(token(TokenType::EscapeSequence, line, tokenStart, current)); + tokenStart = current; + } else { // invalid escape sequence + // add previous string as token, this is at least backslash + result.append(token(TokenType::String, line, tokenStart, current)); + result.append(token(TokenType::StringError, line, current, current + 1)); + ++current; + tokenStart = current; + } + } + } else { + ++current; + } + } + } while (current < length && inString); + if (current > tokenStart) + result.append(token(TokenType::String, line, tokenStart, current)); + if (inString && !*inStringGap) { // unterminated string + // mark last character of last token as Unknown as an error hint + if (!result.isEmpty()) { // should actually never be different + Token &lastRef = result.last(); + if (lastRef.length == 1) { + lastRef.type = TokenType::StringError; + } else { + --lastRef.length; + lastRef.text = QStringView(*line).mid(lastRef.startCol, lastRef.length); + result.append(token(TokenType::StringError, line, current - 1, current)); + } + } + } + return result; +} + +static QVector getMultiLineComment(std::shared_ptr line, int start, + int *commentLevel/*in_out*/) +{ + // Haskell multiline comments can be nested {- foo {- bar -} blah -} + const int length = line->length(); + int current = start; + do { + const QStringView test = QStringView(*line).mid(current, 2); + if (test == QLatin1String("{-")) { + ++(*commentLevel); + current += 2; + } else if (test == QLatin1String("-}") && *commentLevel > 0) { + --(*commentLevel); + current += 2; + } else if (*commentLevel > 0) { + ++current; + } + } while (current < length && *commentLevel > 0); + if (current > start) { + return {token(TokenType::MultiLineComment, line, start, current)}; + } + return {}; +} + +static QVector getChar(std::shared_ptr line, int start) +{ + if (line->at(start) != '\'') + return {}; + QVector result; + const int length = line->length(); + int tokenStart = start; + int current = tokenStart + 1; + bool inChar = true; + int count = 0; + while (current < length && inChar) { + if (line->at(current) == '\'') { + inChar = false; + ++current; + } else if (count == 1) { + // we already have one character, so start Unknown token + if (current > tokenStart) + result.append(token(TokenType::Char, line, tokenStart, current)); + tokenStart = current; + ++count; + ++current; + } else if (count > 1) { + ++count; + ++current; + } else if (line->at(current) == '\\') { + if (current + 1 < length) { + ++current; + ++count; + const int escapeLength = getEscape(*line, current); + if (line->at(current) != '&' && escapeLength > 0) { // no & escape for chars + // valid escape + // add previous string as token without backslash, if necessary + if (tokenStart < current - 1/*backslash*/) + result.append(token(TokenType::Char, line, tokenStart, current - 1)); + tokenStart = current - 1; // backslash + current += escapeLength; + result.append(token(TokenType::EscapeSequence, line, tokenStart, current)); + tokenStart = current; + } else { // invalid escape sequence + // add previous string as token, this is at least backslash + result.append(token(TokenType::Char, line, tokenStart, current)); + result.append(token(TokenType::CharError, line, current, current + 1)); + ++current; + tokenStart = current; + } + } else { + ++current; + } + } else { + ++count; + ++current; + } + } + if (count > 1 && inChar) { + // too long and unterminated, just add Unknown token till end + result.append(token(TokenType::CharError, line, tokenStart, current)); + } else if (count > 1) { + // too long but terminated, add Unknown up to ending quote, then quote + result.append(token(TokenType::CharError, line, tokenStart, current - 1)); + result.append(token(TokenType::Char, line, current - 1, current)); + } else if (inChar || count < 1) { + // unterminated, or no character inside, mark last character as error + if (current > tokenStart + 1) + result.append(token(TokenType::Char, line, tokenStart, current - 1)); + result.append(token(TokenType::CharError, line, current - 1, current)); + } else { + result.append(token(TokenType::Char, line, tokenStart, current)); + } + return result; +} + +static QVector getSpecial(std::shared_ptr line, int start) +{ + if (SPECIAL->contains(line->at(start))) + return {{TokenType::Special, start, 1, QStringView(*line).mid(start, 1), line}}; + return {}; +} + +Tokens HaskellTokenizer::tokenize(const QString &line, int startState) +{ + Tokens result(std::make_shared(line)); + const int length = result.source->length(); + bool inStringGap = startState == int(Tokens::State::StringGap); + int multiLineCommentLevel = std::max(startState - int(Tokens::State::MultiLineCommentGuard), 0); + int currentStart = 0; + QVector tokens; + while (currentStart < length) { + if (multiLineCommentLevel <= 0 && + !(tokens = getString(result.source, currentStart, &inStringGap)).isEmpty()) { + result.append(tokens); + } else if (!(tokens = getMultiLineComment(result.source, currentStart, + &multiLineCommentLevel)).isEmpty()) { + result.append(tokens); + } else if (!(tokens = getChar(result.source, currentStart)).isEmpty()) { + result.append(tokens); + } else if (!(tokens = getSpace(result.source, currentStart)).isEmpty()) { + result.append(tokens); + } else if (!(tokens = getNumber(result.source, currentStart)).isEmpty()) { + result.append(tokens); + } else if (!(tokens = getIdOrOpOrSingleLineComment(result.source, currentStart)).isEmpty()) { + result.append(tokens); + } else if (!(tokens = getSpecial(result.source, currentStart)).isEmpty()) { + result.append(tokens); + } else { + tokens = {{TokenType::Unknown, + currentStart, + 1, + QStringView(*result.source).mid(currentStart, 1), + result.source}}; + result.append(tokens); + } + currentStart += std::accumulate(tokens.cbegin(), tokens.cend(), 0, + [](int s, const Token &t) { return s + t.length; }); + } + if (inStringGap) + result.state = int(Tokens::State::StringGap); + else if (multiLineCommentLevel > 0) + result.state = int(Tokens::State::MultiLineCommentGuard) + multiLineCommentLevel; + return result; +} + +bool Token::isValid() const +{ + return type != TokenType::Unknown; +} + +} // Internal +} // Haskell diff --git a/src/plugins/haskell/haskelltokenizer.h b/src/plugins/haskell/haskelltokenizer.h new file mode 100644 index 00000000000..63d82d960b0 --- /dev/null +++ b/src/plugins/haskell/haskelltokenizer.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include +#include +#include + +#include + +namespace Haskell { +namespace Internal { + +enum class TokenType { + Variable, + Constructor, + Operator, + OperatorConstructor, + Whitespace, + String, + StringError, + Char, + CharError, + EscapeSequence, + Integer, + Float, + Keyword, + Special, + SingleLineComment, + MultiLineComment, + Unknown +}; + +class Token { +public: + bool isValid() const; + + TokenType type = TokenType::Unknown; + int startCol = -1; + int length = -1; + QStringView text; + + std::shared_ptr source; // keep the string ref alive +}; + +class Tokens : public QVector +{ +public: + enum class State { + None = -1, + StringGap = 0, // gap == two backslashes enclosing only whitespace + MultiLineCommentGuard // nothing may follow that + }; + + Tokens(std::shared_ptr source); + + Token tokenAtColumn(int col) const; + + std::shared_ptr source; + int state = int(State::None); +}; + +class HaskellTokenizer +{ +public: + static Tokens tokenize(const QString &line, int startState); +}; + +} // Internal +} // Haskell diff --git a/src/plugins/haskell/images/category_haskell.png b/src/plugins/haskell/images/category_haskell.png new file mode 100644 index 00000000000..947f948bd91 Binary files /dev/null and b/src/plugins/haskell/images/category_haskell.png differ diff --git a/src/plugins/haskell/optionspage.cpp b/src/plugins/haskell/optionspage.cpp new file mode 100644 index 00000000000..63d159443b0 --- /dev/null +++ b/src/plugins/haskell/optionspage.cpp @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "optionspage.h" + +#include "haskellconstants.h" +#include "haskellmanager.h" + +#include +#include +#include +#include +#include + +namespace Haskell { +namespace Internal { + +OptionsPage::OptionsPage() +{ + setId(Constants::OPTIONS_GENERAL); + setDisplayName(tr("General")); + setCategory("J.Z.Haskell"); + setDisplayCategory(tr("Haskell")); + setCategoryIcon(Utils::Icon(":/haskell/images/category_haskell.png")); +} + +QWidget *OptionsPage::widget() +{ + using namespace Utils; + if (!m_widget) { + m_widget = new QWidget; + auto topLayout = new QVBoxLayout; + m_widget->setLayout(topLayout); + auto generalBox = new QGroupBox(tr("General")); + topLayout->addWidget(generalBox); + topLayout->addStretch(10); + auto boxLayout = new QHBoxLayout; + generalBox->setLayout(boxLayout); + boxLayout->addWidget(new QLabel(tr("Stack executable:"))); + m_stackPath = new PathChooser(); + m_stackPath->setExpectedKind(PathChooser::ExistingCommand); + m_stackPath->setPromptDialogTitle(tr("Choose Stack Executable")); + m_stackPath->setFilePath(HaskellManager::stackExecutable()); + m_stackPath->setCommandVersionArguments({"--version"}); + boxLayout->addWidget(m_stackPath); + } + return m_widget; +} + +void OptionsPage::apply() +{ + if (!m_widget) + return; + HaskellManager::setStackExecutable(m_stackPath->rawFilePath()); +} + +void OptionsPage::finish() +{ +} + +} // namespace Internal +} // namespace Haskell diff --git a/src/plugins/haskell/optionspage.h b/src/plugins/haskell/optionspage.h new file mode 100644 index 00000000000..c1663c7a47e --- /dev/null +++ b/src/plugins/haskell/optionspage.h @@ -0,0 +1,53 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include +#include + +#include + +namespace Haskell { +namespace Internal { + +class OptionsPage : public Core::IOptionsPage +{ + Q_OBJECT + +public: + OptionsPage(); + + QWidget *widget() override; + void apply() override; + void finish() override; + +private: + QPointer m_widget; + QPointer m_stackPath; +}; + +} // namespace Internal +} // namespace Haskell diff --git a/src/plugins/haskell/share/wizards/module/file.hs b/src/plugins/haskell/share/wizards/module/file.hs new file mode 100644 index 00000000000..e3f85b3f775 --- /dev/null +++ b/src/plugins/haskell/share/wizards/module/file.hs @@ -0,0 +1 @@ +module %{BaseName} where diff --git a/src/plugins/haskell/share/wizards/module/wizard.json b/src/plugins/haskell/share/wizards/module/wizard.json new file mode 100644 index 00000000000..37f6ebf975b --- /dev/null +++ b/src/plugins/haskell/share/wizards/module/wizard.json @@ -0,0 +1,42 @@ +{ + "version": 1, + "supportedProjectTypes": [ ], + "id": "C.Module", + "category": "S.Haskell", + "trDescription": "Creates a Haskell module.", + "trDisplayName": "Module", + "trDisplayCategory": "Haskell", + "iconText": "hs", + "enabled": "%{JS: value('Plugins').indexOf('Haskell') >= 0}", + + "options": [ + { "key": "FileName", "value": "%{JS: Util.fileName(value('TargetPath'), 'hs')}" }, + { "key": "BaseName", "value": "%{JS: Util.baseName(value('FileName'))}" } + ], + + "pages" : + [ + { + "trDisplayName": "Location", + "trShortTitle": "Location", + "typeId": "File" + }, + { + "trDisplayName": "Project Management", + "trShortTitle": "Summary", + "typeId": "Summary" + } + ], + "generators" : + [ + { + "typeId": "File", + "data": + { + "source": "file.hs", + "target": "%{FileName}", + "openInEditor": true + } + } + ] +} diff --git a/src/plugins/haskell/stackbuildstep.cpp b/src/plugins/haskell/stackbuildstep.cpp new file mode 100644 index 00000000000..52ed64f2834 --- /dev/null +++ b/src/plugins/haskell/stackbuildstep.cpp @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "stackbuildstep.h" + +#include "haskellconstants.h" +#include "haskellmanager.h" + +#include +#include +#include +#include + +using namespace ProjectExplorer; + +namespace Haskell { +namespace Internal { + +StackBuildStep::StackBuildStep(ProjectExplorer::BuildStepList *bsl, Utils::Id id) + : AbstractProcessStep(bsl, id) +{ + setDefaultDisplayName(trDisplayName()); +} + +QWidget *StackBuildStep::createConfigWidget() +{ + return new QWidget; +} + +QString StackBuildStep::trDisplayName() +{ + return tr("Stack Build"); +} + +bool StackBuildStep::init() +{ + if (AbstractProcessStep::init()) { + const auto projectDir = QDir(project()->projectDirectory().toString()); + processParameters()->setCommandLine( + {HaskellManager::stackExecutable(), + {"build", "--work-dir", projectDir.relativeFilePath(buildDirectory().toString())}}); + processParameters()->setEnvironment(buildEnvironment()); + } + return true; +} + +StackBuildStepFactory::StackBuildStepFactory() +{ + registerStep(Constants::C_STACK_BUILD_STEP_ID); + setDisplayName(StackBuildStep::StackBuildStep::trDisplayName()); + setSupportedStepList(ProjectExplorer::Constants::BUILDSTEPS_BUILD); +} + +} // namespace Internal +} // namespace Haskell diff --git a/src/plugins/haskell/stackbuildstep.h b/src/plugins/haskell/stackbuildstep.h new file mode 100644 index 00000000000..d74d155b708 --- /dev/null +++ b/src/plugins/haskell/stackbuildstep.h @@ -0,0 +1,55 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include + +namespace Haskell { +namespace Internal { + +class StackBuildStep : public ProjectExplorer::AbstractProcessStep +{ + Q_OBJECT + +public: + StackBuildStep(ProjectExplorer::BuildStepList *bsl, Utils::Id id); + + QWidget *createConfigWidget() override; + + static QString trDisplayName(); + +protected: + bool init() override; +}; + +class StackBuildStepFactory : public ProjectExplorer::BuildStepFactory +{ +public: + StackBuildStepFactory(); +}; + +} // namespace Internal +} // namespace Haskell diff --git a/src/plugins/languageclient/languageclientinterface.cpp b/src/plugins/languageclient/languageclientinterface.cpp index 517e466196b..541d1c3ab05 100644 --- a/src/plugins/languageclient/languageclientinterface.cpp +++ b/src/plugins/languageclient/languageclientinterface.cpp @@ -113,7 +113,7 @@ void StdIOClientInterface::startImpl() m_logFile.write(QString("Starting server: %1\nOutput:\n\n").arg(m_cmd.toUserOutput()).toUtf8()); m_process->setCommand(m_cmd); m_process->setWorkingDirectory(m_workingDirectory); - if (m_env.isValid()) + if (m_env.hasChanges()) m_process->setEnvironment(m_env); m_process->start(); } diff --git a/src/plugins/languageclient/languageclientsymbolsupport.cpp b/src/plugins/languageclient/languageclientsymbolsupport.cpp index a70b75b5474..995ca770f3d 100644 --- a/src/plugins/languageclient/languageclientsymbolsupport.cpp +++ b/src/plugins/languageclient/languageclientsymbolsupport.cpp @@ -295,7 +295,8 @@ void SymbolSupport::handleFindReferencesResponse(const FindReferencesRequest::Re Core::EditorManager::openEditorAtSearchResult(item); }); search->finishSearch(false); - search->popup(); + if (search->isInteractive()) + search->popup(); } } @@ -365,6 +366,7 @@ bool SymbolSupport::supportsRename(TextEditor::TextDocument *document) void SymbolSupport::renameSymbol(TextEditor::TextDocument *document, const QTextCursor &cursor, const QString &newSymbolName, + const std::function &callback, bool preferLowerCaseFileNames) { const TextDocumentPositionParams params = generateDocPosParams(document, cursor, m_client); @@ -376,17 +378,19 @@ void SymbolSupport::renameSymbol(TextEditor::TextDocument *document, if (!LanguageClient::supportsRename(m_client, document, prepareSupported)) { const QString error = Tr::tr("Renaming is not supported with %1").arg(m_client->name()); createSearch(params, derivePlaceholder(oldSymbolName, newSymbolName), - {}, {})->finishSearch(true, error); + {}, callback, {})->finishSearch(true, error); } else if (prepareSupported) { requestPrepareRename(document, generateDocPosParams(document, cursor, m_client), newSymbolName, oldSymbolName, + callback, preferLowerCaseFileNames); } else { startRenameSymbol(generateDocPosParams(document, cursor, m_client), newSymbolName, oldSymbolName, + callback, preferLowerCaseFileNames); } } @@ -395,6 +399,7 @@ void SymbolSupport::requestPrepareRename(TextEditor::TextDocument *document, const TextDocumentPositionParams ¶ms, const QString &placeholder, const QString &oldSymbolName, + const std::function &callback, bool preferLowerCaseFileNames) { PrepareRenameRequest request(params); @@ -402,13 +407,15 @@ void SymbolSupport::requestPrepareRename(TextEditor::TextDocument *document, params, placeholder, oldSymbolName, + callback, preferLowerCaseFileNames, document = QPointer(document)]( const PrepareRenameRequest::Response &response) { const std::optional &error = response.error(); if (error.has_value()) { m_client->log(*error); - createSearch(params, placeholder, {}, {})->finishSearch(true, error->toString()); + createSearch(params, placeholder, {}, callback, {}) + ->finishSearch(true, error->toString()); } const std::optional &result = response.result(); @@ -419,6 +426,7 @@ void SymbolSupport::requestPrepareRename(TextEditor::TextDocument *document, placeholder.isEmpty() ? placeHolderResult.placeHolder() : placeholder, oldSymbolName, + callback, preferLowerCaseFileNames); } else if (std::holds_alternative(*result)) { auto range = std::get(*result); @@ -429,9 +437,11 @@ void SymbolSupport::requestPrepareRename(TextEditor::TextDocument *document, startRenameSymbol(params, derivePlaceholder(reportedSymbolName, placeholder), reportedSymbolName, + callback, preferLowerCaseFileNames); } else { - startRenameSymbol(params, placeholder, oldSymbolName, preferLowerCaseFileNames); + startRenameSymbol(params, placeholder, oldSymbolName, callback, + preferLowerCaseFileNames); } } } @@ -449,7 +459,8 @@ void SymbolSupport::requestRename(const TextDocumentPositionParams &positionPara handleRenameResponse(search, response); }); m_client->sendMessage(request); - search->popup(); + if (search->isInteractive()) + search->popup(); } QList generateReplaceItems(const WorkspaceEdit &edits, @@ -480,6 +491,7 @@ QList generateReplaceItems(const WorkspaceEdit &edits, Core::SearchResult *SymbolSupport::createSearch(const TextDocumentPositionParams &positionParams, const QString &placeholder, const QString &oldSymbolName, + const std::function &callback, bool preferLowerCaseFileNames) { Core::SearchResult *search = Core::SearchResultWindow::instance()->startNewSearch( @@ -491,6 +503,8 @@ Core::SearchResult *SymbolSupport::createSearch(const TextDocumentPositionParams const auto extraWidget = new ReplaceWidget; search->setAdditionalReplaceWidget(extraWidget); search->setTextToReplace(placeholder); + if (callback) + search->makeNonInteractive(callback); connect(search, &Core::SearchResult::activated, [](const Core::SearchResultItem &item) { Core::EditorManager::openEditorAtSearchResult(item); @@ -521,10 +535,12 @@ Core::SearchResult *SymbolSupport::createSearch(const TextDocumentPositionParams void SymbolSupport::startRenameSymbol(const TextDocumentPositionParams &positionParams, const QString &placeholder, const QString &oldSymbolName, + const std::function &callback, bool preferLowerCaseFileNames) { requestRename(positionParams, - createSearch(positionParams, placeholder, oldSymbolName, preferLowerCaseFileNames)); + createSearch(positionParams, placeholder, oldSymbolName, callback, + preferLowerCaseFileNames)); } void SymbolSupport::handleRenameResponse(Core::SearchResult *search, diff --git a/src/plugins/languageclient/languageclientsymbolsupport.h b/src/plugins/languageclient/languageclientsymbolsupport.h index 864c963b8b9..a4b910a9dbd 100644 --- a/src/plugins/languageclient/languageclientsymbolsupport.h +++ b/src/plugins/languageclient/languageclientsymbolsupport.h @@ -42,7 +42,9 @@ public: bool supportsRename(TextEditor::TextDocument *document); void renameSymbol(TextEditor::TextDocument *document, const QTextCursor &cursor, - const QString &newSymbolName = {}, bool preferLowerCaseFileNames = true); + const QString &newSymbolName = {}, + const std::function &callback = {}, + bool preferLowerCaseFileNames = true); static Core::Search::TextRange convertRange(const LanguageServerProtocol::Range &range); static QStringList getFileContents(const Utils::FilePath &filePath); @@ -61,16 +63,17 @@ private: void requestPrepareRename(TextEditor::TextDocument *document, const LanguageServerProtocol::TextDocumentPositionParams ¶ms, const QString &placeholder, - const QString &oldSymbolName, + const QString &oldSymbolName, const std::function &callback, bool preferLowerCaseFileNames); void requestRename(const LanguageServerProtocol::TextDocumentPositionParams &positionParams, Core::SearchResult *search); Core::SearchResult *createSearch(const LanguageServerProtocol::TextDocumentPositionParams &positionParams, const QString &placeholder, const QString &oldSymbolName, + const std::function &callback, bool preferLowerCaseFileNames); void startRenameSymbol(const LanguageServerProtocol::TextDocumentPositionParams ¶ms, const QString &placeholder, const QString &oldSymbolName, - bool preferLowerCaseFileNames); + const std::function &callback, bool preferLowerCaseFileNames); void handleRenameResponse(Core::SearchResult *search, const LanguageServerProtocol::RenameRequest::Response &response); void applyRename(const QList &checkedItems, Core::SearchResult *search); diff --git a/src/plugins/projectexplorer/buildconfiguration.cpp b/src/plugins/projectexplorer/buildconfiguration.cpp index 21d0c24ff39..ff14b7ab07b 100644 --- a/src/plugins/projectexplorer/buildconfiguration.cpp +++ b/src/plugins/projectexplorer/buildconfiguration.cpp @@ -188,12 +188,12 @@ BuildConfiguration::BuildConfiguration(Target *target, Utils::Id id) d->m_buildDirectoryAspect = addAspect(this); d->m_buildDirectoryAspect->setBaseFileName(target->project()->projectDirectory()); - d->m_buildDirectoryAspect->setEnvironmentChange(EnvironmentChange::fromFixedEnvironment(environment())); + d->m_buildDirectoryAspect->setEnvironment(environment()); d->m_buildDirectoryAspect->setMacroExpanderProvider([this] { return macroExpander(); }); connect(d->m_buildDirectoryAspect, &StringAspect::changed, this, &BuildConfiguration::emitBuildDirectoryChanged); connect(this, &BuildConfiguration::environmentChanged, this, [this] { - d->m_buildDirectoryAspect->setEnvironmentChange(EnvironmentChange::fromFixedEnvironment(environment())); + d->m_buildDirectoryAspect->setEnvironment(environment()); emit this->target()->buildEnvironmentChanged(this); }); diff --git a/src/plugins/projectexplorer/buildsystem.cpp b/src/plugins/projectexplorer/buildsystem.cpp index e0e6f93dcd3..b103d974683 100644 --- a/src/plugins/projectexplorer/buildsystem.cpp +++ b/src/plugins/projectexplorer/buildsystem.cpp @@ -4,6 +4,7 @@ #include "buildsystem.h" #include "buildconfiguration.h" +#include "extracompiler.h" #include "projectexplorer.h" #include "projectexplorertr.h" #include "runconfiguration.h" @@ -190,6 +191,12 @@ void BuildSystem::requestParseHelper(int delay) d->m_delayedParsingTimer.start(); } +ExtraCompiler *BuildSystem::findExtraCompiler( + const std::function &) const +{ + return nullptr; +} + bool BuildSystem::addFiles(Node *, const FilePaths &filePaths, FilePaths *notAdded) { Q_UNUSED(filePaths) @@ -236,6 +243,11 @@ bool BuildSystem::supportsAction(Node *, ProjectAction, const Node *) const return false; } +ExtraCompiler *BuildSystem::extraCompilerForSource(const Utils::FilePath &source) const +{ + return findExtraCompiler([source](const ExtraCompiler *ec) { return ec->source() == source; }); +} + MakeInstallCommand BuildSystem::makeInstallCommand(const FilePath &installRoot) const { QTC_ASSERT(target()->project()->hasMakeInstallEquivalent(), return {}); diff --git a/src/plugins/projectexplorer/buildsystem.h b/src/plugins/projectexplorer/buildsystem.h index b60cb2f6e4a..1fcbdbff18f 100644 --- a/src/plugins/projectexplorer/buildsystem.h +++ b/src/plugins/projectexplorer/buildsystem.h @@ -19,6 +19,7 @@ namespace ProjectExplorer { class BuildConfiguration; class BuildStepList; +class ExtraCompiler; class Node; struct TestCaseInfo @@ -82,6 +83,9 @@ public: virtual bool supportsAction(Node *context, ProjectAction action, const Node *node) const; virtual QString name() const = 0; + // Owned by the build system. Use only in main thread. Can go away at any time. + ExtraCompiler *extraCompilerForSource(const Utils::FilePath &source) const; + virtual MakeInstallCommand makeInstallCommand(const Utils::FilePath &installRoot) const; virtual Utils::FilePaths filesGeneratedFrom(const Utils::FilePath &sourceFile) const; @@ -157,9 +161,12 @@ protected: // Call in GUI thread right after the actual parsing is done void emitParsingFinished(bool success); + using ExtraCompilerFilter = std::function; private: void requestParseHelper(int delay); // request a (delayed!) parser run. + virtual ExtraCompiler *findExtraCompiler(const ExtraCompilerFilter &filter) const; + class BuildSystemPrivate *d = nullptr; }; diff --git a/src/plugins/projectexplorer/customexecutablerunconfiguration.cpp b/src/plugins/projectexplorer/customexecutablerunconfiguration.cpp index 6f333c9b112..9d25bb7dfe6 100644 --- a/src/plugins/projectexplorer/customexecutablerunconfiguration.cpp +++ b/src/plugins/projectexplorer/customexecutablerunconfiguration.cpp @@ -31,14 +31,14 @@ CustomExecutableRunConfiguration::CustomExecutableRunConfiguration(Target *targe exeAspect->setDisplayStyle(StringAspect::PathChooserDisplay); exeAspect->setHistoryCompleter("Qt.CustomExecutable.History"); exeAspect->setExpectedKind(PathChooser::ExistingCommand); - exeAspect->setEnvironmentChange(EnvironmentChange::fromFixedEnvironment(envAspect->environment())); + exeAspect->setEnvironment(envAspect->environment()); addAspect(macroExpander()); addAspect(macroExpander(), envAspect); addAspect(); connect(envAspect, &EnvironmentAspect::environmentChanged, this, [exeAspect, envAspect] { - exeAspect->setEnvironmentChange(EnvironmentChange::fromFixedEnvironment(envAspect->environment())); + exeAspect->setEnvironment(envAspect->environment()); }); setDefaultDisplayName(defaultDisplayName()); diff --git a/src/plugins/projectexplorer/devicesupport/sshparameters.cpp b/src/plugins/projectexplorer/devicesupport/sshparameters.cpp index f9a868b1f6d..3cc6364a677 100644 --- a/src/plugins/projectexplorer/devicesupport/sshparameters.cpp +++ b/src/plugins/projectexplorer/devicesupport/sshparameters.cpp @@ -64,7 +64,7 @@ QStringList SshParameters::connectionOptions(const FilePath &binary) const bool SshParameters::setupSshEnvironment(QtcProcess *process) { Environment env = process->controlEnvironment(); - if (!env.isValid()) + if (!env.hasChanges()) env = Environment::systemEnvironment(); const bool hasDisplay = env.hasKey("DISPLAY") && (env.value("DISPLAY") != QString(":0")); if (SshSettings::askpassFilePath().exists()) { diff --git a/src/plugins/projectexplorer/extracompiler.cpp b/src/plugins/projectexplorer/extracompiler.cpp index 9fb7cd3ad78..ff7c061790e 100644 --- a/src/plugins/projectexplorer/extracompiler.cpp +++ b/src/plugins/projectexplorer/extracompiler.cpp @@ -13,10 +13,12 @@ #include #include +#include #include #include #include +#include #include #include @@ -26,6 +28,7 @@ namespace ProjectExplorer { Q_GLOBAL_STATIC(QThreadPool, s_extraCompilerThreadPool); Q_GLOBAL_STATIC(QList, factories); +Q_LOGGING_CATEGORY(log, "qtc.projectexplorer.extracompiler", QtWarningMsg); class ExtraCompilerPrivate { @@ -37,6 +40,7 @@ public: Core::IEditor *lastEditor = nullptr; QMetaObject::Connection activeBuildConfigConnection; QMetaObject::Connection activeEnvironmentConnection; + Utils::Guard lock; bool dirty = false; QTimer timer; @@ -55,13 +59,7 @@ ExtraCompiler::ExtraCompiler(const Project *project, const FilePath &source, d->contents.insert(target, QByteArray()); d->timer.setSingleShot(true); - connect(&d->timer, &QTimer::timeout, this, [this] { - if (d->dirty && d->lastEditor) { - d->dirty = false; - compileContent(d->lastEditor->document()->contents()); - } - }); - + connect(&d->timer, &QTimer::timeout, this, &ExtraCompiler::compileIfDirty); connect(BuildManager::instance(), &BuildManager::buildStateChanged, this, &ExtraCompiler::onTargetsBuilt); @@ -163,6 +161,16 @@ void ExtraCompiler::compileImpl(const ContentProvider &provider) d->m_taskTree->start(); } +void ExtraCompiler::compileIfDirty() +{ + qCDebug(log) << Q_FUNC_INFO; + if (!d->lock.isLocked() && d->dirty && d->lastEditor) { + qCDebug(log) << '\t' << "about to compile"; + d->dirty = false; + compileContent(d->lastEditor->document()->contents()); + } +} + ExtraCompiler::ContentProvider ExtraCompiler::fromFileProvider() const { const auto provider = [fileName = source()] { @@ -179,6 +187,20 @@ bool ExtraCompiler::isDirty() const return d->dirty; } +void ExtraCompiler::block() +{ + qCDebug(log) << Q_FUNC_INFO; + d->lock.lock(); +} + +void ExtraCompiler::unblock() +{ + qCDebug(log) << Q_FUNC_INFO; + d->lock.unlock(); + if (!d->lock.isLocked() && !d->timer.isActive()) + d->timer.start(); +} + void ExtraCompiler::onTargetsBuilt(Project *project) { if (project != d->project || BuildManager::isBuilding(project)) @@ -278,6 +300,7 @@ Utils::FutureSynchronizer *ExtraCompiler::futureSynchronizer() const void ExtraCompiler::setContent(const FilePath &file, const QByteArray &contents) { + qCDebug(log).noquote() << Q_FUNC_INFO << contents; auto it = d->contents.find(file); if (it != d->contents.end()) { if (it.value() != contents) { diff --git a/src/plugins/projectexplorer/extracompiler.h b/src/plugins/projectexplorer/extracompiler.h index 7d595b87260..34993f39406 100644 --- a/src/plugins/projectexplorer/extracompiler.h +++ b/src/plugins/projectexplorer/extracompiler.h @@ -55,6 +55,8 @@ public: Utils::Tasking::TaskItem compileFileItem(); void compileFile(); bool isDirty() const; + void block(); + void unblock(); signals: void contentsChanged(const Utils::FilePath &file); @@ -76,6 +78,7 @@ private: ContentProvider fromFileProvider() const; void compileContent(const QByteArray &content); void compileImpl(const ContentProvider &provider); + void compileIfDirty(); virtual Utils::Tasking::TaskItem taskItemImpl(const ContentProvider &provider) = 0; const std::unique_ptr d; diff --git a/src/plugins/projectexplorer/msvctoolchain.cpp b/src/plugins/projectexplorer/msvctoolchain.cpp index 704bc92d12b..d0856e29d44 100644 --- a/src/plugins/projectexplorer/msvctoolchain.cpp +++ b/src/plugins/projectexplorer/msvctoolchain.cpp @@ -1154,7 +1154,7 @@ ToolChain::BuiltInHeaderPathsRunner MsvcToolChain::createBuiltInHeaderPathsRunne void MsvcToolChain::addToEnvironment(Utils::Environment &env) const { // We cache the full environment (incoming + modifications by setup script). - if (!m_resultEnvironment.isValid() || env != m_lastEnvironment) { + if (!m_resultEnvironment.hasChanges() || env != m_lastEnvironment) { qCDebug(Log) << "addToEnvironment: " << displayName(); m_lastEnvironment = env; m_resultEnvironment = readEnvironmentSetting(env); diff --git a/src/plugins/projectexplorer/runconfigurationaspects.cpp b/src/plugins/projectexplorer/runconfigurationaspects.cpp index c8f3836e46f..9ba24c04672 100644 --- a/src/plugins/projectexplorer/runconfigurationaspects.cpp +++ b/src/plugins/projectexplorer/runconfigurationaspects.cpp @@ -187,9 +187,9 @@ void WorkingDirectoryAspect::addToLayout(LayoutBuilder &builder) if (m_envAspect) { connect(m_envAspect, &EnvironmentAspect::environmentChanged, m_chooser.data(), [this] { - m_chooser->setEnvironmentChange(EnvironmentChange::fromFixedEnvironment(m_envAspect->environment())); + m_chooser->setEnvironment(m_envAspect->environment()); }); - m_chooser->setEnvironmentChange(EnvironmentChange::fromFixedEnvironment(m_envAspect->environment())); + m_chooser->setEnvironment(m_envAspect->environment()); } builder.addItems({Tr::tr("Working directory:"), m_chooser.data(), m_resetButton.data()}); @@ -580,6 +580,11 @@ void ExecutableAspect::setEnvironmentChange(const EnvironmentChange &change) m_alternativeExecutable->setEnvironmentChange(change); } +void ExecutableAspect::setEnvironment(const Environment &env) +{ + setEnvironmentChange(EnvironmentChange::fromDictionary(env.toDictionary())); +} + /*! Sets the display \a style for aspect. diff --git a/src/plugins/projectexplorer/runconfigurationaspects.h b/src/plugins/projectexplorer/runconfigurationaspects.h index 0290b837af2..9e948bc6ecf 100644 --- a/src/plugins/projectexplorer/runconfigurationaspects.h +++ b/src/plugins/projectexplorer/runconfigurationaspects.h @@ -169,6 +169,7 @@ public: void setHistoryCompleter(const QString &historyCompleterKey); void setExpectedKind(const Utils::PathChooser::Kind expectedKind); void setEnvironmentChange(const Utils::EnvironmentChange &change); + void setEnvironment(const Utils::Environment &env); void setDisplayStyle(Utils::StringAspect::DisplayStyle style); struct Data : BaseAspect::Data diff --git a/src/plugins/qbsprojectmanager/qbsproject.cpp b/src/plugins/qbsprojectmanager/qbsproject.cpp index ef22f02c8f1..dc72660e7a3 100644 --- a/src/plugins/qbsprojectmanager/qbsproject.cpp +++ b/src/plugins/qbsprojectmanager/qbsproject.cpp @@ -586,6 +586,11 @@ void QbsBuildSystem::delayParsing() requestDelayedParse(); } +ExtraCompiler *QbsBuildSystem::findExtraCompiler(const ExtraCompilerFilter &filter) const +{ + return Utils::findOrDefault(m_extraCompilers, filter); +} + void QbsBuildSystem::parseCurrentBuildConfiguration() { m_parsingScheduled = false; diff --git a/src/plugins/qbsprojectmanager/qbsproject.h b/src/plugins/qbsprojectmanager/qbsproject.h index f33d725daa5..ab4728ae749 100644 --- a/src/plugins/qbsprojectmanager/qbsproject.h +++ b/src/plugins/qbsprojectmanager/qbsproject.h @@ -105,6 +105,9 @@ public: private: friend class QbsProject; + ProjectExplorer::ExtraCompiler *findExtraCompiler( + const ExtraCompilerFilter &filter) const override; + void handleQbsParsingDone(bool success); void changeActiveTarget(ProjectExplorer::Target *t); void prepareForParsing(); diff --git a/src/plugins/qmakeprojectmanager/qmakeparsernodes.cpp b/src/plugins/qmakeprojectmanager/qmakeparsernodes.cpp index a1c34a528d6..14fecd00d93 100644 --- a/src/plugins/qmakeprojectmanager/qmakeparsernodes.cpp +++ b/src/plugins/qmakeprojectmanager/qmakeparsernodes.cpp @@ -2080,6 +2080,22 @@ QList QmakeProFile::extraCompilers() const return m_extraCompilers; } +ExtraCompiler *QmakeProFile::findExtraCompiler( + const std::function &filter) +{ + for (ExtraCompiler * const ec : std::as_const(m_extraCompilers)) { + if (filter(ec)) + return ec; + } + for (QmakePriFile * const priFile : std::as_const(m_children)) { + if (const auto proFile = dynamic_cast(priFile)) { + if (ExtraCompiler * const ec = proFile->findExtraCompiler(filter)) + return ec; + } + } + return nullptr; +} + void QmakeProFile::setupExtraCompiler(const FilePath &buildDir, const FileType &fileType, ExtraCompilerFactory *factory) { diff --git a/src/plugins/qmakeprojectmanager/qmakeparsernodes.h b/src/plugins/qmakeprojectmanager/qmakeparsernodes.h index 86a3a97b49f..34c4958564b 100644 --- a/src/plugins/qmakeprojectmanager/qmakeparsernodes.h +++ b/src/plugins/qmakeprojectmanager/qmakeparsernodes.h @@ -301,6 +301,8 @@ public: const Utils::FilePath &sourceFile, const ProjectExplorer::FileType &sourceFileType) const; QList extraCompilers() const; + ProjectExplorer::ExtraCompiler *findExtraCompiler( + const std::function &filter); TargetInformation targetInformation() const; InstallsList installsList() const; diff --git a/src/plugins/qmakeprojectmanager/qmakeproject.cpp b/src/plugins/qmakeprojectmanager/qmakeproject.cpp index 5d021f9c56d..6d840187251 100644 --- a/src/plugins/qmakeprojectmanager/qmakeproject.cpp +++ b/src/plugins/qmakeprojectmanager/qmakeproject.cpp @@ -532,6 +532,11 @@ void QmakeBuildSystem::scheduleUpdateAllNowOrLater() scheduleUpdateAll(QmakeProFile::ParseLater); } +ExtraCompiler *QmakeBuildSystem::findExtraCompiler(const ExtraCompilerFilter &filter) const +{ + return m_rootProFile->findExtraCompiler(filter); +} + QmakeBuildConfiguration *QmakeBuildSystem::qmakeBuildConfiguration() const { return static_cast(BuildSystem::buildConfiguration()); diff --git a/src/plugins/qmakeprojectmanager/qmakeproject.h b/src/plugins/qmakeprojectmanager/qmakeproject.h index b83468a1082..6615baaaca5 100644 --- a/src/plugins/qmakeprojectmanager/qmakeproject.h +++ b/src/plugins/qmakeprojectmanager/qmakeproject.h @@ -163,6 +163,9 @@ public: void scheduleUpdateAllNowOrLater(); private: + ProjectExplorer::ExtraCompiler *findExtraCompiler( + const ExtraCompilerFilter &filter) const override; + void scheduleUpdateAll(QmakeProFile::AsyncUpdateDelay delay); void scheduleUpdateAllLater() { scheduleUpdateAll(QmakeProFile::ParseLater); } diff --git a/src/plugins/remotelinux/CMakeLists.txt b/src/plugins/remotelinux/CMakeLists.txt index 7f486510e97..2899475f13d 100644 --- a/src/plugins/remotelinux/CMakeLists.txt +++ b/src/plugins/remotelinux/CMakeLists.txt @@ -2,7 +2,6 @@ add_qtc_plugin(RemoteLinux DEPENDS QmlDebug PLUGIN_DEPENDS Core Debugger ProjectExplorer SOURCES - abstractremotelinuxdeployservice.cpp abstractremotelinuxdeployservice.h abstractremotelinuxdeploystep.cpp abstractremotelinuxdeploystep.h customcommanddeploystep.cpp customcommanddeploystep.h deploymenttimeinfo.cpp deploymenttimeinfo.h diff --git a/src/plugins/remotelinux/abstractremotelinuxdeployservice.cpp b/src/plugins/remotelinux/abstractremotelinuxdeployservice.cpp deleted file mode 100644 index 57485c3d33b..00000000000 --- a/src/plugins/remotelinux/abstractremotelinuxdeployservice.cpp +++ /dev/null @@ -1,146 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "abstractremotelinuxdeployservice.h" - -#include "deploymenttimeinfo.h" -#include "remotelinuxtr.h" - -#include -#include -#include -#include - -#include -#include - -#include -#include - -using namespace ProjectExplorer; -using namespace Utils; - -namespace RemoteLinux { -namespace Internal { - -class AbstractRemoteLinuxDeployServicePrivate -{ -public: - IDevice::ConstPtr deviceConfiguration; - QPointer target; - - DeploymentTimeInfo deployTimes; - std::unique_ptr m_taskTree; -}; - -} // namespace Internal - -using namespace Internal; - -AbstractRemoteLinuxDeployService::AbstractRemoteLinuxDeployService(QObject *parent) - : QObject(parent), d(new AbstractRemoteLinuxDeployServicePrivate) -{ -} - -AbstractRemoteLinuxDeployService::~AbstractRemoteLinuxDeployService() -{ - delete d; -} - -const Target *AbstractRemoteLinuxDeployService::target() const -{ - return d->target; -} - -const Kit *AbstractRemoteLinuxDeployService::kit() const -{ - return d->target ? d->target->kit() : nullptr; -} - -IDevice::ConstPtr AbstractRemoteLinuxDeployService::deviceConfiguration() const -{ - return d->deviceConfiguration; -} - -void AbstractRemoteLinuxDeployService::saveDeploymentTimeStamp(const DeployableFile &deployableFile, - const QDateTime &remoteTimestamp) -{ - d->deployTimes.saveDeploymentTimeStamp(deployableFile, kit(), remoteTimestamp); -} - -bool AbstractRemoteLinuxDeployService::hasLocalFileChanged( - const DeployableFile &deployableFile) const -{ - return d->deployTimes.hasLocalFileChanged(deployableFile, kit()); -} - -bool AbstractRemoteLinuxDeployService::hasRemoteFileChanged( - const DeployableFile &deployableFile, const QDateTime &remoteTimestamp) const -{ - return d->deployTimes.hasRemoteFileChanged(deployableFile, kit(), remoteTimestamp); -} - -void AbstractRemoteLinuxDeployService::setTarget(Target *target) -{ - d->target = target; - d->deviceConfiguration = DeviceKitAspect::device(kit()); -} - -void AbstractRemoteLinuxDeployService::setDevice(const IDevice::ConstPtr &device) -{ - d->deviceConfiguration = device; -} - -void AbstractRemoteLinuxDeployService::start() -{ - QTC_ASSERT(!d->m_taskTree, return); - - const CheckResult check = isDeploymentPossible(); - if (!check) { - emit errorMessage(check.errorMessage()); - emit finished(); - return; - } - - if (!isDeploymentNecessary()) { - emit progressMessage(Tr::tr("No deployment action necessary. Skipping.")); - emit finished(); - return; - } - - d->m_taskTree.reset(new TaskTree(deployRecipe())); - const auto endHandler = [this] { - d->m_taskTree.release()->deleteLater(); - emit finished(); - }; - connect(d->m_taskTree.get(), &TaskTree::done, this, endHandler); - connect(d->m_taskTree.get(), &TaskTree::errorOccurred, this, endHandler); - d->m_taskTree->start(); -} - -void AbstractRemoteLinuxDeployService::stop() -{ - if (!d->m_taskTree) - return; - d->m_taskTree.reset(); - emit finished(); -} - -CheckResult AbstractRemoteLinuxDeployService::isDeploymentPossible() const -{ - if (!deviceConfiguration()) - return CheckResult::failure(Tr::tr("No device configuration set.")); - return CheckResult::success(); -} - -QVariantMap AbstractRemoteLinuxDeployService::exportDeployTimes() const -{ - return d->deployTimes.exportDeployTimes(); -} - -void AbstractRemoteLinuxDeployService::importDeployTimes(const QVariantMap &map) -{ - d->deployTimes.importDeployTimes(map); -} - -} // namespace RemoteLinux diff --git a/src/plugins/remotelinux/abstractremotelinuxdeployservice.h b/src/plugins/remotelinux/abstractremotelinuxdeployservice.h deleted file mode 100644 index f0b7fa6c0a8..00000000000 --- a/src/plugins/remotelinux/abstractremotelinuxdeployservice.h +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include "remotelinux_export.h" - -#include - -#include -#include - -namespace ProjectExplorer { -class DeployableFile; -class Kit; -class Target; -} - -namespace Utils::Tasking { class Group; } - -namespace RemoteLinux { -namespace Internal { class AbstractRemoteLinuxDeployServicePrivate; } - -class REMOTELINUX_EXPORT CheckResult -{ -public: - static CheckResult success() { return {true, {}}; } - static CheckResult failure(const QString &error = {}) { return {false, error}; } - - operator bool() const { return m_ok; } - QString errorMessage() const { return m_error; } - -private: - CheckResult(bool ok, const QString &error) : m_ok(ok), m_error(error) {} - - bool m_ok = false; - QString m_error; -}; - -class REMOTELINUX_EXPORT AbstractRemoteLinuxDeployService : public QObject -{ - Q_OBJECT - Q_DISABLE_COPY(AbstractRemoteLinuxDeployService) -public: - explicit AbstractRemoteLinuxDeployService(QObject *parent = nullptr); - ~AbstractRemoteLinuxDeployService() override; - - void setTarget(ProjectExplorer::Target *bc); - // Only use setDevice() as fallback if no target is available - void setDevice(const ProjectExplorer::IDeviceConstPtr &device); - void start(); - void stop(); - - QVariantMap exportDeployTimes() const; - void importDeployTimes(const QVariantMap &map); - - virtual CheckResult isDeploymentPossible() const; - -signals: - void errorMessage(const QString &message); - void progressMessage(const QString &message); - void warningMessage(const QString &message); - void stdOutData(const QString &data); - void stdErrData(const QString &data); - void finished(); // Used by Qnx. - -protected: - const ProjectExplorer::Target *target() const; - const ProjectExplorer::Kit *kit() const; - ProjectExplorer::IDeviceConstPtr deviceConfiguration() const; - - void saveDeploymentTimeStamp(const ProjectExplorer::DeployableFile &deployableFile, - const QDateTime &remoteTimestamp); - bool hasLocalFileChanged(const ProjectExplorer::DeployableFile &deployableFile) const; - bool hasRemoteFileChanged(const ProjectExplorer::DeployableFile &deployableFile, - const QDateTime &remoteTimestamp) const; - -private: - virtual bool isDeploymentNecessary() const = 0; - virtual Utils::Tasking::Group deployRecipe() = 0; - - Internal::AbstractRemoteLinuxDeployServicePrivate * const d; -}; - -} // namespace RemoteLinux diff --git a/src/plugins/remotelinux/abstractremotelinuxdeploystep.cpp b/src/plugins/remotelinux/abstractremotelinuxdeploystep.cpp index b97ffcb2698..270b95eb229 100644 --- a/src/plugins/remotelinux/abstractremotelinuxdeploystep.cpp +++ b/src/plugins/remotelinux/abstractremotelinuxdeploystep.cpp @@ -3,17 +3,37 @@ #include "abstractremotelinuxdeploystep.h" -#include "abstractremotelinuxdeployservice.h" +#include "deploymenttimeinfo.h" #include "remotelinuxtr.h" -#include +#include +#include #include +#include +#include + +#include +#include + +#include +#include using namespace ProjectExplorer; +using namespace Utils; namespace RemoteLinux { namespace Internal { +class AbstractRemoteLinuxDeployServicePrivate +{ +public: + IDevice::ConstPtr deviceConfiguration; + QPointer target; + + DeploymentTimeInfo deployTimes; + std::unique_ptr m_taskTree; +}; + class AbstractRemoteLinuxDeployStepPrivate { public: @@ -23,7 +43,117 @@ public: AbstractRemoteLinuxDeployService *deployService = nullptr; }; -} // namespace Internal +} // Internal + +using namespace Internal; + +AbstractRemoteLinuxDeployService::AbstractRemoteLinuxDeployService(QObject *parent) + : QObject(parent), d(new AbstractRemoteLinuxDeployServicePrivate) +{ +} + +AbstractRemoteLinuxDeployService::~AbstractRemoteLinuxDeployService() +{ + delete d; +} + +const Target *AbstractRemoteLinuxDeployService::target() const +{ + return d->target; +} + +const Kit *AbstractRemoteLinuxDeployService::kit() const +{ + return d->target ? d->target->kit() : nullptr; +} + +IDevice::ConstPtr AbstractRemoteLinuxDeployService::deviceConfiguration() const +{ + return d->deviceConfiguration; +} + +void AbstractRemoteLinuxDeployService::saveDeploymentTimeStamp(const DeployableFile &deployableFile, + const QDateTime &remoteTimestamp) +{ + d->deployTimes.saveDeploymentTimeStamp(deployableFile, kit(), remoteTimestamp); +} + +bool AbstractRemoteLinuxDeployService::hasLocalFileChanged( + const DeployableFile &deployableFile) const +{ + return d->deployTimes.hasLocalFileChanged(deployableFile, kit()); +} + +bool AbstractRemoteLinuxDeployService::hasRemoteFileChanged( + const DeployableFile &deployableFile, const QDateTime &remoteTimestamp) const +{ + return d->deployTimes.hasRemoteFileChanged(deployableFile, kit(), remoteTimestamp); +} + +void AbstractRemoteLinuxDeployService::setTarget(Target *target) +{ + d->target = target; + d->deviceConfiguration = DeviceKitAspect::device(kit()); +} + +void AbstractRemoteLinuxDeployService::setDevice(const IDevice::ConstPtr &device) +{ + d->deviceConfiguration = device; +} + +void AbstractRemoteLinuxDeployService::start() +{ + QTC_ASSERT(!d->m_taskTree, return); + + const CheckResult check = isDeploymentPossible(); + if (!check) { + emit errorMessage(check.errorMessage()); + emit finished(); + return; + } + + if (!isDeploymentNecessary()) { + emit progressMessage(Tr::tr("No deployment action necessary. Skipping.")); + emit finished(); + return; + } + + d->m_taskTree.reset(new TaskTree(deployRecipe())); + const auto endHandler = [this] { + d->m_taskTree.release()->deleteLater(); + emit finished(); + }; + connect(d->m_taskTree.get(), &TaskTree::done, this, endHandler); + connect(d->m_taskTree.get(), &TaskTree::errorOccurred, this, endHandler); + d->m_taskTree->start(); +} + +void AbstractRemoteLinuxDeployService::stop() +{ + if (!d->m_taskTree) + return; + d->m_taskTree.reset(); + emit finished(); +} + +CheckResult AbstractRemoteLinuxDeployService::isDeploymentPossible() const +{ + if (!deviceConfiguration()) + return CheckResult::failure(Tr::tr("No device configuration set.")); + return CheckResult::success(); +} + +QVariantMap AbstractRemoteLinuxDeployService::exportDeployTimes() const +{ + return d->deployTimes.exportDeployTimes(); +} + +void AbstractRemoteLinuxDeployService::importDeployTimes(const QVariantMap &map) +{ + d->deployTimes.importDeployTimes(map); +} + + AbstractRemoteLinuxDeployStep::AbstractRemoteLinuxDeployStep(BuildStepList *bsl, Utils::Id id) : BuildStep(bsl, id), d(new Internal::AbstractRemoteLinuxDeployStepPrivate) diff --git a/src/plugins/remotelinux/abstractremotelinuxdeploystep.h b/src/plugins/remotelinux/abstractremotelinuxdeploystep.h index e866c511184..258d4b9e7f9 100644 --- a/src/plugins/remotelinux/abstractremotelinuxdeploystep.h +++ b/src/plugins/remotelinux/abstractremotelinuxdeploystep.h @@ -6,6 +6,18 @@ #include "remotelinux_export.h" #include +#include + +#include +#include + +namespace ProjectExplorer { +class DeployableFile; +class Kit; +class Target; +} + +namespace Utils::Tasking { class Group; } namespace RemoteLinux { @@ -13,6 +25,7 @@ class AbstractRemoteLinuxDeployService; class CheckResult; namespace Internal { class AbstractRemoteLinuxDeployStepPrivate; } +namespace Internal { class AbstractRemoteLinuxDeployServicePrivate; } class REMOTELINUX_EXPORT AbstractRemoteLinuxDeployStep : public ProjectExplorer::BuildStep { @@ -45,4 +58,65 @@ private: Internal::AbstractRemoteLinuxDeployStepPrivate *d; }; -} // namespace RemoteLinux +class REMOTELINUX_EXPORT CheckResult +{ +public: + static CheckResult success() { return {true, {}}; } + static CheckResult failure(const QString &error = {}) { return {false, error}; } + + operator bool() const { return m_ok; } + QString errorMessage() const { return m_error; } + +private: + CheckResult(bool ok, const QString &error) : m_ok(ok), m_error(error) {} + + bool m_ok = false; + QString m_error; +}; + +class REMOTELINUX_EXPORT AbstractRemoteLinuxDeployService : public QObject +{ + Q_OBJECT + Q_DISABLE_COPY(AbstractRemoteLinuxDeployService) +public: + explicit AbstractRemoteLinuxDeployService(QObject *parent = nullptr); + ~AbstractRemoteLinuxDeployService() override; + + void setTarget(ProjectExplorer::Target *bc); + // Only use setDevice() as fallback if no target is available + void setDevice(const ProjectExplorer::IDeviceConstPtr &device); + void start(); + void stop(); + + QVariantMap exportDeployTimes() const; + void importDeployTimes(const QVariantMap &map); + + virtual CheckResult isDeploymentPossible() const; + +signals: + void errorMessage(const QString &message); + void progressMessage(const QString &message); + void warningMessage(const QString &message); + void stdOutData(const QString &data); + void stdErrData(const QString &data); + void finished(); // Used by Qnx. + +protected: + const ProjectExplorer::Target *target() const; + const ProjectExplorer::Kit *kit() const; + ProjectExplorer::IDeviceConstPtr deviceConfiguration() const; + + void saveDeploymentTimeStamp(const ProjectExplorer::DeployableFile &deployableFile, + const QDateTime &remoteTimestamp); + bool hasLocalFileChanged(const ProjectExplorer::DeployableFile &deployableFile) const; + bool hasRemoteFileChanged(const ProjectExplorer::DeployableFile &deployableFile, + const QDateTime &remoteTimestamp) const; + +private: + virtual bool isDeploymentNecessary() const = 0; + virtual Utils::Tasking::Group deployRecipe() = 0; + + Internal::AbstractRemoteLinuxDeployServicePrivate * const d; +}; + +} // RemoteLinux diff --git a/src/plugins/remotelinux/customcommanddeploystep.cpp b/src/plugins/remotelinux/customcommanddeploystep.cpp index 7c538bb51b1..b7abdd83044 100644 --- a/src/plugins/remotelinux/customcommanddeploystep.cpp +++ b/src/plugins/remotelinux/customcommanddeploystep.cpp @@ -3,7 +3,6 @@ #include "customcommanddeploystep.h" -#include "abstractremotelinuxdeployservice.h" #include "abstractremotelinuxdeploystep.h" #include "remotelinux_constants.h" #include "remotelinuxtr.h" diff --git a/src/plugins/remotelinux/genericdirectuploadservice.h b/src/plugins/remotelinux/genericdirectuploadservice.h index a584bfec89e..31799f063ad 100644 --- a/src/plugins/remotelinux/genericdirectuploadservice.h +++ b/src/plugins/remotelinux/genericdirectuploadservice.h @@ -5,7 +5,7 @@ #include "remotelinux_export.h" -#include "abstractremotelinuxdeployservice.h" +#include "abstractremotelinuxdeploystep.h" #include diff --git a/src/plugins/remotelinux/killappstep.cpp b/src/plugins/remotelinux/killappstep.cpp index e719108252f..03473d95a23 100644 --- a/src/plugins/remotelinux/killappstep.cpp +++ b/src/plugins/remotelinux/killappstep.cpp @@ -4,7 +4,6 @@ #include "killappstep.h" #include "abstractremotelinuxdeploystep.h" -#include "abstractremotelinuxdeployservice.h" #include "remotelinux_constants.h" #include "remotelinuxtr.h" diff --git a/src/plugins/remotelinux/makeinstallstep.cpp b/src/plugins/remotelinux/makeinstallstep.cpp index 21c9cc2c906..ea7b6364586 100644 --- a/src/plugins/remotelinux/makeinstallstep.cpp +++ b/src/plugins/remotelinux/makeinstallstep.cpp @@ -170,7 +170,7 @@ bool MakeInstallStep::init() } const MakeInstallCommand cmd = buildSystem()->makeInstallCommand(rootDir); - if (cmd.environment.isValid()) { + if (cmd.environment.hasChanges()) { Environment env = processParameters()->environment(); for (auto it = cmd.environment.constBegin(); it != cmd.environment.constEnd(); ++it) { if (cmd.environment.isEnabled(it)) { diff --git a/src/plugins/remotelinux/remotelinux.qbs b/src/plugins/remotelinux/remotelinux.qbs index cea281f43c1..3dad98eb88c 100644 --- a/src/plugins/remotelinux/remotelinux.qbs +++ b/src/plugins/remotelinux/remotelinux.qbs @@ -13,8 +13,6 @@ Project { Depends { name: "ProjectExplorer" } files: [ - "abstractremotelinuxdeployservice.cpp", - "abstractremotelinuxdeployservice.h", "abstractremotelinuxdeploystep.cpp", "abstractremotelinuxdeploystep.h", "deploymenttimeinfo.cpp", diff --git a/src/plugins/remotelinux/rsyncdeploystep.cpp b/src/plugins/remotelinux/rsyncdeploystep.cpp index e19ef52d1d8..b20eaf90499 100644 --- a/src/plugins/remotelinux/rsyncdeploystep.cpp +++ b/src/plugins/remotelinux/rsyncdeploystep.cpp @@ -3,8 +3,6 @@ #include "rsyncdeploystep.h" -#include "abstractremotelinuxdeploystep.h" -#include "abstractremotelinuxdeployservice.h" #include "abstractremotelinuxdeploystep.h" #include "remotelinux_constants.h" #include "remotelinuxtr.h" diff --git a/src/plugins/remotelinux/tarpackagedeploystep.cpp b/src/plugins/remotelinux/tarpackagedeploystep.cpp index ef7e1dc166b..ee276666ac8 100644 --- a/src/plugins/remotelinux/tarpackagedeploystep.cpp +++ b/src/plugins/remotelinux/tarpackagedeploystep.cpp @@ -3,7 +3,6 @@ #include "tarpackagedeploystep.h" -#include "abstractremotelinuxdeployservice.h" #include "abstractremotelinuxdeploystep.h" #include "remotelinux_constants.h" #include "remotelinuxtr.h" diff --git a/src/plugins/squish/squishresultmodel.cpp b/src/plugins/squish/squishresultmodel.cpp index 6c1049cf84b..15bca8d91ae 100644 --- a/src/plugins/squish/squishresultmodel.cpp +++ b/src/plugins/squish/squishresultmodel.cpp @@ -160,8 +160,7 @@ TestResult SquishResultFilterModel::testResult(const QModelIndex &idx) const { if (auto item = static_cast(m_sourceModel->itemForIndex(mapToSource(idx)))) return item->result(); - - return TestResult(); + return {}; } bool SquishResultFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const diff --git a/src/plugins/squish/squishxmloutputhandler.cpp b/src/plugins/squish/squishxmloutputhandler.cpp index b35b7a834fd..a9f59af44a3 100644 --- a/src/plugins/squish/squishxmloutputhandler.cpp +++ b/src/plugins/squish/squishxmloutputhandler.cpp @@ -244,7 +244,7 @@ void SquishXmlOutputHandler::outputAvailable(const QByteArray &output) // SquishReport tags will be ignored completely if (currentName == "epilog") { QTC_ASSERT(testCaseRootItem, break); - TestResult result(Result::End, QString(), time); + const TestResult result(Result::End, QString(), time); SquishResultItem *item = new SquishResultItem(result); testCaseRootItem->appendChild(item); emit updateStatus(result.text()); @@ -283,7 +283,7 @@ void SquishXmlOutputHandler::outputAvailable(const QByteArray &output) if (!logDetailsList.isEmpty()) { for (const QString &detail : std::as_const(logDetailsList)) { - TestResult childResult(Result::Detail, detail); + const TestResult childResult(Result::Detail, detail); SquishResultItem *childItem = new SquishResultItem(childResult); item->appendChild(childItem); emit updateStatus(childResult.text()); diff --git a/src/tools/perfparser b/src/tools/perfparser index 310d7f81d8f..6c475b413b3 160000 --- a/src/tools/perfparser +++ b/src/tools/perfparser @@ -1 +1 @@ -Subproject commit 310d7f81d8ff59dfc9e3afc23181d1c3f51930bb +Subproject commit 6c475b413b3edb26897cbc1bd19296d497aaee79 diff --git a/tests/auto/CMakeLists.txt b/tests/auto/CMakeLists.txt index 36c9c2c59cf..c71cbafdb2d 100644 --- a/tests/auto/CMakeLists.txt +++ b/tests/auto/CMakeLists.txt @@ -9,6 +9,7 @@ add_subdirectory(environment) add_subdirectory(extensionsystem) add_subdirectory(externaltool) add_subdirectory(filesearch) +add_subdirectory(haskell) add_subdirectory(json) add_subdirectory(languageserverprotocol) add_subdirectory(mapreduce) diff --git a/tests/auto/haskell/CMakeLists.txt b/tests/auto/haskell/CMakeLists.txt new file mode 100644 index 00000000000..cdcccae5edb --- /dev/null +++ b/tests/auto/haskell/CMakeLists.txt @@ -0,0 +1,8 @@ +add_qtc_test(tst_tokenizer + DEPENDS Qt::Core Qt::Test + INCLUDES ../../../src/plugins/haskell + SOURCES + tst_tokenizer.cpp + ../../../src/plugins/haskell/haskelltokenizer.cpp + ../../../src/plugins/haskell/haskelltokenizer.h +) diff --git a/tests/auto/haskell/tst_tokenizer.cpp b/tests/auto/haskell/tst_tokenizer.cpp new file mode 100644 index 00000000000..ffa34b205b8 --- /dev/null +++ b/tests/auto/haskell/tst_tokenizer.cpp @@ -0,0 +1,730 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include + +#include +#include + +using namespace Haskell::Internal; + +const QSet escapes{'a', 'b', 'f', 'n', 'r', 't', 'v', '\\', '"', '\'', '&'}; + +struct TokenInfo +{ + TokenType type; + int column; + QString text; +}; + +Q_DECLARE_METATYPE(TokenInfo) + +bool operator==(const TokenInfo &info, const Token &token) +{ + return info.type == token.type + && info.column == token.startCol + && info.text.length() == token.length + && info.text == token.text.toString(); +} + +bool operator==(const Token &token, const TokenInfo &info) +{ + return info == token; +} + +class tst_Tokenizer : public QObject +{ + Q_OBJECT + +private slots: + void singleLineComment_data(); + void singleLineComment(); + + void multiLineComment_data(); + void multiLineComment(); + + void string_data(); + void string(); + + void character_data(); + void character(); + + void number_data(); + void number(); + + void keyword_data(); + void keyword(); + + void variable_data(); + void variable(); + + void constructor_data(); + void constructor(); + + void op_data(); + void op(); + +private: + void setupData(); + void addRow(const char *name, + const QString &input, + const QList &tokens, + Tokens::State startState = Tokens::State::None, + Tokens::State endState = Tokens::State::None); + void checkData(); +}; + +void tst_Tokenizer::setupData() +{ + QTest::addColumn("input"); + QTest::addColumn>("output"); + QTest::addColumn("startState"); + QTest::addColumn("endState"); +} + +void tst_Tokenizer::addRow(const char *name, + const QString &input, + const QList &tokens, + Tokens::State startState, + Tokens::State endState) +{ + QTest::newRow(name) << input << tokens << int(startState) << int(endState); +} + +void tst_Tokenizer::checkData() +{ + QFETCH(QString, input); + QFETCH(QList, output); + QFETCH(int, startState); + QFETCH(int, endState); + const Tokens tokens = HaskellTokenizer::tokenize(input, startState); + QCOMPARE(tokens.length(), output.length()); + QCOMPARE(tokens.state, endState); + for (int i = 0; i < tokens.length(); ++i) { + const Token t = tokens.at(i); + const TokenInfo ti = output.at(i); + QVERIFY2(t == ti, QString("Token at index %1 does not match, {%2, %3, \"%4\"} != {%5, %6, \"%7\"}") + .arg(i) + .arg(int(t.type)).arg(t.startCol).arg(t.text.toString()) + .arg(int(ti.type)).arg(ti.column).arg(ti.text) + .toUtf8().constData()); + } +} + +void tst_Tokenizer::singleLineComment_data() +{ + setupData(); + + addRow("simple", " -- foo", { + {TokenType::Whitespace, 0, " "}, + {TokenType::SingleLineComment, 1, "-- foo"} + }); + addRow("dash, id", "--foo", { + {TokenType::SingleLineComment, 0, "--foo"} + }); + addRow("dash, space, op", "-- |foo", { + {TokenType::SingleLineComment, 0, "-- |foo"} + }); + addRow("multi-dash, space", "---- foo", { + {TokenType::SingleLineComment, 0, "---- foo"} + }); + addRow("dash, op", "--| foo", { + {TokenType::Operator, 0, "--|"}, + {TokenType::Whitespace, 3, " "}, + {TokenType::Variable, 4, "foo"} + }); + addRow("dash, special", "--(foo", { + {TokenType::SingleLineComment, 0, "--(foo"} + }); + addRow("not a qualified varsym", "F.-- foo", { + {TokenType::Constructor, 0, "F"}, + {TokenType::Operator, 1, "."}, + {TokenType::SingleLineComment, 2, "-- foo"} + }); +} + +void tst_Tokenizer::singleLineComment() +{ + checkData(); +} + +void tst_Tokenizer::multiLineComment_data() +{ + setupData(); + + addRow("trailing dashes", "{---foo -}", { + {TokenType::MultiLineComment, 0, "{---foo -}"} + }); + addRow("multiline", "{- foo", { + {TokenType::MultiLineComment, 0, "{- foo"} + }, + Tokens::State::None, + Tokens::State(int(Tokens::State::MultiLineCommentGuard) + 1)); + addRow("multiline2", "bar -}", { + {TokenType::MultiLineComment, 0, "bar -}"} + }, + Tokens::State(int(Tokens::State::MultiLineCommentGuard) + 1), + Tokens::State::None); + addRow("nested", "{- fo{-o", { + {TokenType::MultiLineComment, 0, "{- fo{-o"} + }, + Tokens::State::None, + Tokens::State(int(Tokens::State::MultiLineCommentGuard) + 2)); + addRow("nested2", "bar -}", { + {TokenType::MultiLineComment, 0, "bar -}"} + }, + Tokens::State(int(Tokens::State::MultiLineCommentGuard) + 2), + Tokens::State(int(Tokens::State::MultiLineCommentGuard) + 1)); + addRow("nested3", "bar -}", { + {TokenType::MultiLineComment, 0, "bar -}"} + }, + Tokens::State(int(Tokens::State::MultiLineCommentGuard) + 1), + Tokens::State::None); +} + +void tst_Tokenizer::multiLineComment() +{ + checkData(); +} + +void tst_Tokenizer::string_data() +{ + setupData(); + + addRow("simple", "\"foo\"", { + {TokenType::String, 0, "\"foo\""} + }); + + addRow("unterminated", "\"", { + {TokenType::StringError, 0, "\""} + }); + addRow("unterminated2", "\"foo", { + {TokenType::String, 0, "\"fo"}, + {TokenType::StringError, 3, "o"} + }); + addRow("unterminated with escape", "\"\\\\", { + {TokenType::String, 0, "\""}, + {TokenType::EscapeSequence, 1, "\\"}, + {TokenType::StringError, 2, "\\"} + }); + + // gaps + addRow("gap", "\" \\ \\\"", { + {TokenType::String, 0, "\" \\ \\\""} + }); + addRow("gap over endline", "\"foo\\", { + {TokenType::String, 0, "\"foo\\"} + }, + Tokens::State::None, Tokens::State::StringGap); + addRow("gap over endline2", "\\foo\"", { + {TokenType::String, 0, "\\foo\""} + }, + Tokens::State::StringGap, Tokens::State::None); + addRow("gap error", "\"\\ ab \\\"", { + {TokenType::String, 0, "\"\\ "}, + {TokenType::StringError, 3, "ab"}, + {TokenType::String, 5, " \\\""} + }); + addRow("gap error with quote", "\"\\ \"", { + {TokenType::String, 0, "\"\\ "}, + {TokenType::StringError, 3, "\""} + }, + Tokens::State::None, Tokens::State::StringGap); + + // char escapes (including wrong ones) + for (char c = '!'; c <= '~'; ++c) { + // skip uppercase and '^', since these can be part of ascii escapes + // and 'o' and 'x' since they start octal and hex escapes + // and digits as part of decimal escape + if ((c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '^' || c == 'o' || c == 'x') + continue; + const QChar qc(c); + const QByteArray name = QString("charesc '%1'").arg(qc).toUtf8(); + const QString input = QString("\"\\%1\"").arg(qc); + if (escapes.contains(c)) { + addRow(name.constData(), input, { + {TokenType::String, 0, "\""}, + {TokenType::EscapeSequence, 1, QLatin1String("\\") + qc}, + {TokenType::String, 3, "\""} + }); + } else { + addRow(name.constData(), input, { + {TokenType::String, 0, "\"\\"}, + {TokenType::StringError, 2, qc}, + {TokenType::String, 3, "\""} + }); + } + } + + addRow("decimal escape", "\"\\1234a\"", { + {TokenType::String, 0, "\""}, + {TokenType::EscapeSequence, 1, "\\1234"}, + {TokenType::String, 6, "a\""} + }); + + addRow("octal escape", "\"\\o0678a\"", { + {TokenType::String, 0, "\""}, + {TokenType::EscapeSequence, 1, "\\o067"}, + {TokenType::String, 6, "8a\""} + }); + addRow("octal escape error", "\"\\o8a\"", { + {TokenType::String, 0, "\"\\"}, + {TokenType::StringError, 2, "o"}, + {TokenType::String, 3, "8a\""} + }); + + addRow("hexadecimal escape", "\"\\x0678Abg\"", { + {TokenType::String, 0, "\""}, + {TokenType::EscapeSequence, 1, "\\x0678Ab"}, + {TokenType::String, 9, "g\""} + }); + addRow("hexadecimal escape error", "\"\\xg\"", { + {TokenType::String, 0, "\"\\"}, + {TokenType::StringError, 2, "x"}, + {TokenType::String, 3, "g\""} + }); + + // ascii cntrl escapes (including wrong ones) + for (char c = '!'; c <= '~'; ++c) { + if (c == '"') // is special because it also ends the string + continue; + const QChar qc(c); + const QByteArray name = QString("ascii cntrl '^%1'").arg(qc).toUtf8(); + const QString input = QString("\"\\^%1\"").arg(qc); + if ((qc >= 'A' && qc <= 'Z') || qc == '@' || qc == '[' || qc == '\\' || qc == ']' + || qc == '^' || qc == '_') { + addRow(name.constData(), input, { + {TokenType::String, 0, "\""}, + {TokenType::EscapeSequence, 1, QLatin1String("\\^") + qc}, + {TokenType::String, 4, "\""} + }); + } else { + addRow(name.constData(), input, { + {TokenType::String, 0, "\"\\"}, + {TokenType::StringError, 2, "^"}, + {TokenType::String, 3, QString(qc) + "\""} + }); + } + } + + addRow("ascii escape SOH", "\"\\SOHN\"", { + {TokenType::String, 0, "\""}, + {TokenType::EscapeSequence, 1, "\\SOH"}, + {TokenType::String, 5, "N\""} + }); + addRow("ascii escape SO", "\"\\SON\"", { + {TokenType::String, 0, "\""}, + {TokenType::EscapeSequence, 1, "\\SO"}, + {TokenType::String, 4, "N\""} + }); + addRow("ascii escape error", "\"\\TON\"", { + {TokenType::String, 0, "\"\\"}, + {TokenType::StringError, 2, "T"}, + {TokenType::String, 3, "ON\""} + }); + addRow("ascii escape error 2", "\"\\STO\"", { + {TokenType::String, 0, "\"\\"}, + {TokenType::StringError, 2, "S"}, + {TokenType::String, 3, "TO\""} + }); +} + +void tst_Tokenizer::string() +{ + checkData(); +} + +void tst_Tokenizer::character_data() +{ + setupData(); + + addRow("simple", "'a'", { + {TokenType::Char, 0, "'a'"} + }); + addRow("too many", "'abc'", { + {TokenType::Char, 0, "'a"}, + {TokenType::CharError, 2, "bc"}, + {TokenType::Char, 4, "'"} + }); + addRow("too few", "''", { + {TokenType::Char, 0, "'"}, + {TokenType::CharError, 1, "'"} + }); + addRow("only quote", "'", { + {TokenType::CharError, 0, "'"} + }); + addRow("unterminated", "'a", { + {TokenType::Char, 0, "'"}, + {TokenType::CharError, 1, "a"} + }); + addRow("unterminated too many", "'abc", { + {TokenType::Char, 0, "'a"}, + {TokenType::CharError, 2, "bc"} + }); + addRow("unterminated backslash", "'\\", { + {TokenType::Char, 0, "'"}, + {TokenType::CharError, 1, "\\"} + }); + + // char escapes (including wrong ones) + for (char c = '!'; c <= '~'; ++c) { + // skip uppercase and '^', since these can be part of ascii escapes + // and 'o' and 'x' since they start octal and hex escapes + // and digits as part of decimal escape + if ((c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '^' || c == 'o' || c == 'x') + continue; + const QChar qc(c); + const QByteArray name = QString("charesc '%1'").arg(qc).toUtf8(); + const QString input = QString("'\\%1'").arg(qc); + if (c != '&' && escapes.contains(c)) { + addRow(name.constData(), input, { + {TokenType::Char, 0, "'"}, + {TokenType::EscapeSequence, 1, QLatin1String("\\") + qc}, + {TokenType::Char, 3, "'"} + }); + } else { + addRow(name.constData(), input, { + {TokenType::Char, 0, "'\\"}, + {TokenType::CharError, 2, qc}, + {TokenType::Char, 3, "'"} + }); + } + } + + addRow("decimal escape", "'\\1234'", { + {TokenType::Char, 0, "'"}, + {TokenType::EscapeSequence, 1, "\\1234"}, + {TokenType::Char, 6, "'"} + }); + addRow("decimal escape too long", "'\\1234a'", { + {TokenType::Char, 0, "'"}, + {TokenType::EscapeSequence, 1, "\\1234"}, + {TokenType::CharError, 6, "a"}, + {TokenType::Char, 7, "'"} + }); + + addRow("octal escape", "'\\o067'", { + {TokenType::Char, 0, "'"}, + {TokenType::EscapeSequence, 1, "\\o067"}, + {TokenType::Char, 6, "'"} + }); + addRow("octal escape error", "'\\o8'", { + {TokenType::Char, 0, "'\\"}, + {TokenType::CharError, 2, "o"}, + {TokenType::CharError, 3, "8"}, + {TokenType::Char, 4, "'"} + }); + + addRow("hexadecimal escape", "'\\x0678Ab'", { + {TokenType::Char, 0, "'"}, + {TokenType::EscapeSequence, 1, "\\x0678Ab"}, + {TokenType::Char, 9, "'"} + }); + addRow("hexadecimal escape error", "'\\xg'", { + {TokenType::Char, 0, "'\\"}, + {TokenType::CharError, 2, "x"}, + {TokenType::CharError, 3, "g"}, + {TokenType::Char, 4, "'"} + }); + + // ascii cntrl escapes (including wrong ones) + for (char c = '!'; c <= '~'; ++c) { + if (c == '\'') // is special because it also ends the string + continue; + const QChar qc(c); + const QByteArray name = QString("ascii cntrl '^%1'").arg(qc).toUtf8(); + const QString input = QString("'\\^%1'").arg(qc); + if ((qc >= 'A' && qc <= 'Z') || qc == '@' || qc == '[' || qc == '\\' || qc == ']' + || qc == '^' || qc == '_') { + addRow(name.constData(), input, { + {TokenType::Char, 0, "'"}, + {TokenType::EscapeSequence, 1, QLatin1String("\\^") + qc}, + {TokenType::Char, 4, "'"} + }); + } else { + addRow(name.constData(), input, { + {TokenType::Char, 0, "'\\"}, + {TokenType::CharError, 2, "^"}, + {TokenType::CharError, 3, qc}, + {TokenType::Char, 4, "'"} + }); + } + } + + addRow("ascii escape SOH", "'\\SOH'", { + {TokenType::Char, 0, "'"}, + {TokenType::EscapeSequence, 1, "\\SOH"}, + {TokenType::Char, 5, "'"} + }); + addRow("ascii escape SO, too long", "'\\SON'", { + {TokenType::Char, 0, "'"}, + {TokenType::EscapeSequence, 1, "\\SO"}, + {TokenType::CharError, 4, "N"}, + {TokenType::Char, 5, "'"} + }); + addRow("ascii escape error", "'\\TON'", { + {TokenType::Char, 0, "'\\"}, + {TokenType::CharError, 2, "T"}, + {TokenType::CharError, 3, "ON"}, + {TokenType::Char, 5, "'"} + }); +} + +void tst_Tokenizer::character() +{ + checkData(); +} + +void tst_Tokenizer::number_data() +{ + setupData(); + + addRow("decimal", "012345", { + {TokenType::Integer, 0, "012345"} + }); + addRow("single digit decimal", "0", { + {TokenType::Integer, 0, "0"} + }); + addRow("octal", "0o1234", { + {TokenType::Integer, 0, "0o1234"} + }); + // this is a bit weird, but correct: octal 1 followed by decimal 8 + addRow("number after octal", "0O18", { + {TokenType::Integer, 0, "0O1"}, + {TokenType::Integer, 3, "8"} + }); + addRow("not octal", "0o9", { + {TokenType::Integer, 0, "0"}, + {TokenType::Variable, 1, "o9"}, + }); + addRow("hexadecimal", "0x9fA", { + {TokenType::Integer, 0, "0x9fA"} + }); + // hex number followed by identifier 'g' + addRow("hexadecimal", "0X9fg", { + {TokenType::Integer, 0, "0X9f"}, + {TokenType::Variable, 4, "g"} + }); + + // 0 followed by identifier + addRow("decimal followed by identifier", "0z6", { + {TokenType::Integer, 0, "0"}, + {TokenType::Variable, 1, "z6"} + }); + + addRow("float", "0123.45", { + {TokenType::Float, 0, "0123.45"} + }); + addRow("decimal + operator '.'", "0123.", { + {TokenType::Integer, 0, "0123"}, + {TokenType::Operator, 4, "."} + }); + addRow("operator '.' + decimal", ".0123", { + {TokenType::Operator, 0, "."}, + {TokenType::Integer, 1, "0123"} + }); + addRow("without '.', with exp 'e'", "0123e45", { + {TokenType::Float, 0, "0123e45"} + }); + addRow("without '.', with exp 'E'", "0123E45", { + {TokenType::Float, 0, "0123E45"} + }); + addRow("without '.', with '+'", "0123e+45", { + {TokenType::Float, 0, "0123e+45"} + }); + addRow("without '.', with '-'", "0123e-45", { + {TokenType::Float, 0, "0123e-45"} + }); + addRow("without '.', with '+', missing decimal", "0123e+", { + {TokenType::Integer, 0, "0123"}, + {TokenType::Variable, 4, "e"}, + {TokenType::Operator, 5, "+"} + }); + addRow("without '.', missing decimal", "0123e", { + {TokenType::Integer, 0, "0123"}, + {TokenType::Variable, 4, "e"} + }); + addRow("exp 'e'", "01.23e45", { + {TokenType::Float, 0, "01.23e45"} + }); + addRow("exp 'E'", "01.23E45", { + {TokenType::Float, 0, "01.23E45"} + }); + addRow("with '+'", "01.23e+45", { + {TokenType::Float, 0, "01.23e+45"} + }); + addRow("with '-'", "01.23e-45", { + {TokenType::Float, 0, "01.23e-45"} + }); + addRow("with '+', missing decimal", "01.23e+", { + {TokenType::Float, 0, "01.23"}, + {TokenType::Variable, 5, "e"}, + {TokenType::Operator, 6, "+"} + }); + addRow("missing decimal", "01.23e", { + {TokenType::Float, 0, "01.23"}, + {TokenType::Variable, 5, "e"} + }); +} + +void tst_Tokenizer::number() +{ + checkData(); +} + +void tst_Tokenizer::keyword_data() +{ + setupData(); + + addRow("data", "data", { + {TokenType::Keyword, 0, "data"} + }); + addRow("not a qualified varid", "Foo.case", { + {TokenType::Constructor, 0, "Foo"}, + {TokenType::Operator, 3, "."}, + {TokenType::Keyword, 4, "case"} + }); + addRow(":", ":", { + {TokenType::Keyword, 0, ":"} + }); + addRow("->", "->", { + {TokenType::Keyword, 0, "->"} + }); + addRow("not a qualified varsym", "Foo...", { + {TokenType::Constructor, 0, "Foo"}, + {TokenType::Operator, 3, "..."} + }); +} + +void tst_Tokenizer::keyword() +{ + checkData(); +} + +void tst_Tokenizer::variable_data() +{ + setupData(); + + addRow("simple", "fOo_1'", { + {TokenType::Variable, 0, "fOo_1'"} + }); + addRow("start with '_'", "_1", { + {TokenType::Variable, 0, "_1"} + }); + addRow("not a keyword", "cases", { + {TokenType::Variable, 0, "cases"} + }); + addRow("not a keyword 2", "qualified", { + {TokenType::Variable, 0, "qualified"} + }); + addRow("not a keyword 3", "as", { + {TokenType::Variable, 0, "as"} + }); + addRow("not a keyword 4", "hiding", { + {TokenType::Variable, 0, "hiding"} + }); + addRow(".variable", ".foo", { + {TokenType::Operator, 0, "."}, + {TokenType::Variable, 1, "foo"} + }); + addRow("variable.", "foo.", { + {TokenType::Variable, 0, "foo"}, + {TokenType::Operator, 3, "."} + }); + addRow("variable.variable", "blah.foo", { + {TokenType::Variable, 0, "blah"}, + {TokenType::Operator, 4, "."}, + {TokenType::Variable, 5, "foo"} + }); + addRow("qualified", "Blah.foo", { + {TokenType::Variable, 0, "Blah.foo"} + }); + addRow("qualified2", "Goo.Blah.foo", { + {TokenType::Variable, 0, "Goo.Blah.foo"} + }); + addRow("variable + op '..'", "foo..", { + {TokenType::Variable, 0, "foo"}, + {TokenType::Keyword, 3, ".."} + }); + addRow("variable + op '...'", "foo...", { + {TokenType::Variable, 0, "foo"}, + {TokenType::Operator, 3, "..."} + }); +} + +void tst_Tokenizer::variable() +{ + checkData(); +} + +void tst_Tokenizer::constructor_data() +{ + setupData(); + + addRow("simple", "Foo", { + {TokenType::Constructor, 0, "Foo"} + }); + addRow("qualified", "Foo.Bar", { + {TokenType::Constructor, 0, "Foo.Bar"} + }); + addRow("followed by op '.'", "Foo.Bar.", { + {TokenType::Constructor, 0, "Foo.Bar"}, + {TokenType::Operator, 7, "."} + }); + +} + +void tst_Tokenizer::constructor() +{ + checkData(); +} + +void tst_Tokenizer::op_data() +{ + setupData(); + + addRow("simple", "+-=", { + {TokenType::Operator, 0, "+-="} + }); + addRow("qualified", "Foo.+-=", { + {TokenType::Operator, 0, "Foo.+-="} + }); + addRow("qualified '.'", "Foo..", { + {TokenType::Operator, 0, "Foo.."} + }); + addRow("constructor plus op", "Foo+", { + {TokenType::Constructor, 0, "Foo"}, + {TokenType::Operator, 3, "+"} + }); +} + +void tst_Tokenizer::op() +{ + checkData(); +} + +QTEST_MAIN(tst_Tokenizer) + +#include "tst_tokenizer.moc" diff --git a/tests/manual/haskell/simple/README.md b/tests/manual/haskell/simple/README.md new file mode 100644 index 00000000000..7011e6ed4e9 --- /dev/null +++ b/tests/manual/haskell/simple/README.md @@ -0,0 +1 @@ +# simple test project diff --git a/tests/manual/haskell/simple/Setup.hs b/tests/manual/haskell/simple/Setup.hs new file mode 100644 index 00000000000..9a994af677b --- /dev/null +++ b/tests/manual/haskell/simple/Setup.hs @@ -0,0 +1,2 @@ +import Distribution.Simple +main = defaultMain diff --git a/tests/manual/haskell/simple/simple.cabal b/tests/manual/haskell/simple/simple.cabal new file mode 100644 index 00000000000..dd2a85e6001 --- /dev/null +++ b/tests/manual/haskell/simple/simple.cabal @@ -0,0 +1,14 @@ +name: simple +version: 0.1.0.0 +-- synopsis: +-- description: +category: Test +build-type: Simple +cabal-version: >=1.10 +extra-source-files: README.md + +executable simple + hs-source-dirs: src + main-is: Main.hs + default-language: Haskell2010 + build-depends: base >= 4.7 && < 5 diff --git a/tests/manual/haskell/simple/src/Main.hs b/tests/manual/haskell/simple/src/Main.hs new file mode 100644 index 00000000000..9cd992d9e53 --- /dev/null +++ b/tests/manual/haskell/simple/src/Main.hs @@ -0,0 +1,5 @@ +module Main where + +main :: IO () +main = do + putStrLn "hello world" diff --git a/tests/manual/haskell/simple/stack.yaml b/tests/manual/haskell/simple/stack.yaml new file mode 100644 index 00000000000..9f5c8c628b2 --- /dev/null +++ b/tests/manual/haskell/simple/stack.yaml @@ -0,0 +1,66 @@ +# This file was automatically generated by 'stack init' +# +# Some commonly used options have been documented as comments in this file. +# For advanced use and comprehensive documentation of the format, please see: +# https://docs.haskellstack.org/en/stable/yaml_configuration/ + +# Resolver to choose a 'specific' stackage snapshot or a compiler version. +# A snapshot resolver dictates the compiler version and the set of packages +# to be used for project dependencies. For example: +# +# resolver: lts-3.5 +# resolver: nightly-2015-09-21 +# resolver: ghc-7.10.2 +# resolver: ghcjs-0.1.0_ghc-7.10.2 +# resolver: +# name: custom-snapshot +# location: "./custom-snapshot.yaml" +resolver: lts-7.24 + +# User packages to be built. +# Various formats can be used as shown in the example below. +# +# packages: +# - some-directory +# - https://example.com/foo/bar/baz-0.0.2.tar.gz +# - location: +# git: https://github.com/commercialhaskell/stack.git +# commit: e7b331f14bcffb8367cd58fbfc8b40ec7642100a +# - location: https://github.com/commercialhaskell/stack/commit/e7b331f14bcffb8367cd58fbfc8b40ec7642100a +# extra-dep: true +# subdirs: +# - auto-update +# - wai +# +# A package marked 'extra-dep: true' will only be built if demanded by a +# non-dependency (i.e. a user package), and its test suites and benchmarks +# will not be run. This is useful for tweaking upstream packages. +packages: +- . +# Dependency packages to be pulled from upstream that are not in the resolver +# (e.g., acme-missiles-0.3) +# extra-deps: [] + +# Override default flag values for local packages and extra-deps +# flags: {} + +# Extra package databases containing global packages +# extra-package-dbs: [] + +# Control whether we use the GHC we find on the path +# system-ghc: true +# +# Require a specific version of stack, using version ranges +# require-stack-version: -any # Default +# require-stack-version: ">=1.6" +# +# Override the architecture used by stack, especially useful on Windows +# arch: i386 +# arch: x86_64 +# +# Extra directories used by stack for building +# extra-include-dirs: [/path/to/dir] +# extra-lib-dirs: [/path/to/dir] +# +# Allow a newer minor version of GHC than the snapshot specifies +# compiler-check: newer-minor