diff --git a/addons/externaltools/autotests/externaltooltest.cpp b/addons/externaltools/autotests/externaltooltest.cpp index dbda7ea07..7ac2fe9b6 100644 --- a/addons/externaltools/autotests/externaltooltest.cpp +++ b/addons/externaltools/autotests/externaltooltest.cpp @@ -1,116 +1,116 @@ /* This file is part of the KDE project * * Copyright 2019 Dominik Haumann * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "externaltooltest.h" #include "../kateexternaltool.h" #include "../katetoolrunner.h" #include #include #include #include QTEST_MAIN(ExternalToolTest) void ExternalToolTest::initTestCase() { } void ExternalToolTest::cleanupTestCase() { } void ExternalToolTest::testLoadSave() { KConfig config; KConfigGroup cg(&config, "tool"); KateExternalTool tool; tool.category = QStringLiteral("Git Tools"); tool.name = QStringLiteral("git cola"); tool.icon = QStringLiteral("git-cola"); tool.executable = QStringLiteral("git-cola"); tool.arguments = QStringLiteral("none"); tool.input = QStringLiteral("in"); tool.workingDir = QStringLiteral("/usr/bin"); tool.mimetypes = QStringList{ QStringLiteral("everything") }; tool.hasexec = true; tool.actionName = QStringLiteral("asdf"); tool.cmdname = QStringLiteral("git-cola"); tool.saveMode = KateExternalTool::SaveMode::None; tool.save(cg); KateExternalTool clonedTool; clonedTool.load(cg); QCOMPARE(tool, clonedTool); } void ExternalToolTest::testRunListDirectory() { - auto tool = new KateExternalTool(); + std::unique_ptr tool(new KateExternalTool()); tool->category = QStringLiteral("Tools"); tool->name = QStringLiteral("ls"); tool->icon = QStringLiteral("none"); tool->executable = QStringLiteral("ls"); tool->arguments = QStringLiteral("/usr"); tool->workingDir = QStringLiteral("/tmp"); tool->mimetypes = QStringList{}; tool->hasexec = true; tool->actionName = QStringLiteral("ls"); tool->cmdname = QStringLiteral("ls"); tool->saveMode = KateExternalTool::SaveMode::None; // 1. /tmp $ ls /usr - KateToolRunner runner1(tool, nullptr); + KateToolRunner runner1(std::move(tool), nullptr); runner1.run(); runner1.waitForFinished(); QVERIFY(runner1.outputData().contains(QStringLiteral("bin"))); // 2. /usr $ ls - auto tool2 = new KateExternalTool(*tool); + std::unique_ptr tool2(new KateExternalTool(*tool)); tool2->arguments.clear(); tool2->workingDir = QStringLiteral("/usr"); - KateToolRunner runner2(tool2, nullptr); + KateToolRunner runner2(std::move(tool2), nullptr); runner2.run(); runner2.waitForFinished(); QVERIFY(runner2.outputData().contains(QStringLiteral("bin"))); // 1. and 2. must give the same result QCOMPARE(runner1.outputData(), runner2.outputData()); } void ExternalToolTest::testRunTac() { - auto tool = new KateExternalTool(); + std::unique_ptr tool(new KateExternalTool()); tool->name = QStringLiteral("tac"); tool->executable = QStringLiteral("tac"); tool->input = QStringLiteral("a\nb\nc\n"); tool->saveMode = KateExternalTool::SaveMode::None; // run tac to reverse order - KateToolRunner runner(tool, nullptr); + KateToolRunner runner(std::move(tool), nullptr); runner.run(); runner.waitForFinished(); QCOMPARE(runner.outputData(), QStringLiteral("c\nb\na\n")); } // kate: space-indent on; indent-width 4; replace-tabs on; diff --git a/addons/externaltools/externaltoolsplugin.cpp b/addons/externaltools/externaltoolsplugin.cpp index 9badcd9b7..90cdd90f7 100644 --- a/addons/externaltools/externaltoolsplugin.cpp +++ b/addons/externaltools/externaltoolsplugin.cpp @@ -1,238 +1,238 @@ /* This file is part of the KDE project * * Copyright 2019 Dominik Haumann * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "externaltoolsplugin.h" #include "kateexternaltoolsview.h" #include "kateexternaltool.h" #include "kateexternaltoolscommand.h" #include "katemacroexpander.h" #include "katetoolrunner.h" #include "kateexternaltoolsconfigwidget.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include K_PLUGIN_FACTORY_WITH_JSON(KateExternalToolsFactory, "externaltoolsplugin.json", registerPlugin();) KateExternalToolsPlugin::KateExternalToolsPlugin(QObject* parent, const QList&) : KTextEditor::Plugin(parent) { reload(); } KateExternalToolsPlugin::~KateExternalToolsPlugin() { delete m_command; m_command = nullptr; } QObject* KateExternalToolsPlugin::createView(KTextEditor::MainWindow* mainWindow) { KateExternalToolsPluginView* view = new KateExternalToolsPluginView(mainWindow, this); connect(this, &KateExternalToolsPlugin::externalToolsChanged, view, &KateExternalToolsPluginView::rebuildMenu); return view; } void KateExternalToolsPlugin::reload() { delete m_command; m_command = nullptr; m_commands.clear(); qDeleteAll(m_tools); m_tools.clear(); KConfig _config(QStringLiteral("externaltools"), KConfig::NoGlobals, QStandardPaths::ApplicationsLocation); KConfigGroup config(&_config, "Global"); const int toolCount = config.readEntry("tools", 0); for (int i = 0; i < toolCount; ++i) { config = KConfigGroup(&_config, QStringLiteral("Tool %1").arg(i)); auto t = new KateExternalTool(); t->load(config); m_tools.push_back(t); // FIXME test for a command name first! if (t->hasexec && (!t->cmdname.isEmpty())) { m_commands.push_back(t->cmdname); } } if (KAuthorized::authorizeAction(QStringLiteral("shell_access"))) { m_command = new KateExternalToolsCommand(this); } Q_EMIT externalToolsChanged(); } QStringList KateExternalToolsPlugin::commands() const { return m_commands; } const KateExternalTool* KateExternalToolsPlugin::toolForCommand(const QString& cmd) const { for (auto tool : m_tools) { if (tool->cmdname == cmd) { return tool; } } return nullptr; } const QVector & KateExternalToolsPlugin::tools() const { return m_tools; } void KateExternalToolsPlugin::runTool(const KateExternalTool& tool, KTextEditor::View* view) { // expand the macros in command if any, // and construct a command with an absolute path auto mw = view->mainWindow(); // save documents if requested if (tool.saveMode == KateExternalTool::SaveMode::CurrentDocument) { // only save if modified, to avoid unnecessary recompiles if (view->document()->isModified()) { view->document()->save(); } } else if (tool.saveMode == KateExternalTool::SaveMode::AllDocuments) { foreach (KXMLGUIClient* client, mw->guiFactory()->clients()) { if (QAction* a = client->actionCollection()->action(QStringLiteral("file_save_all"))) { a->trigger(); break; } } } // copy tool - auto copy = new KateExternalTool(tool); + std::unique_ptr copy(new KateExternalTool(tool)); MacroExpander macroExpander(view); if (!macroExpander.expandMacrosShellQuote(copy->executable)) { KMessageBox::sorry(view, i18n("Failed to expand the executable '%1'.", copy->executable), i18n("Kate External Tools")); return; } if (!macroExpander.expandMacrosShellQuote(copy->arguments)) { KMessageBox::sorry(view, i18n("Failed to expand the arguments '%1'.", copy->arguments), i18n("Kate External Tools")); return; } if (!macroExpander.expandMacrosShellQuote(copy->workingDir)) { KMessageBox::sorry(view, i18n("Failed to expand the working directory '%1'.", copy->workingDir), i18n("Kate External Tools")); return; } if (!macroExpander.expandMacrosShellQuote(copy->input)) { KMessageBox::sorry(view, i18n("Failed to expand the input '%1'.", copy->input), i18n("Kate External Tools")); return; } // Allocate runner on heap such that it lives as long as the child // process is running and does not block the main thread. - auto runner = new KateToolRunner(copy, view, this); + auto runner = new KateToolRunner(std::move(copy), view, this); // use QueuedConnection, since handleToolFinished deletes the runner connect(runner, &KateToolRunner::toolFinished, this, &KateExternalToolsPlugin::handleToolFinished, Qt::QueuedConnection); runner->run(); } void KateExternalToolsPlugin::handleToolFinished(KateToolRunner* runner) { auto view = runner->view(); if (view && !runner->outputData().isEmpty()) { switch (runner->tool()->outputMode) { case KateExternalTool::OutputMode::InsertAtCursor: { KTextEditor::Document::EditingTransaction transaction(view->document()); view->removeSelection(); view->insertText(runner->outputData()); break; } case KateExternalTool::OutputMode::ReplaceSelectedText: { KTextEditor::Document::EditingTransaction transaction(view->document()); view->removeSelectionText(); view->insertText(runner->outputData()); break; } case KateExternalTool::OutputMode::ReplaceCurrentDocument: { KTextEditor::Document::EditingTransaction transaction(view->document()); view->document()->clear(); view->insertText(runner->outputData()); break; } case KateExternalTool::OutputMode::AppendToCurrentDocument: { view->document()->insertText(view->document()->documentEnd(), runner->outputData()); break; } case KateExternalTool::OutputMode::InsertInNewDocument: { auto mainWindow = view->mainWindow(); auto newView = mainWindow->openUrl({}); newView->insertText(runner->outputData()); mainWindow->activateView(newView->document()); break; } default: break; } } // TODO: case KateExternalTool::OutputMode::DisplayInPane: break; // create a toolview with the contents. QTextEdit with fixed font? Something else? delete runner; } int KateExternalToolsPlugin::configPages() const { return 1; } KTextEditor::ConfigPage* KateExternalToolsPlugin::configPage(int number, QWidget* parent) { if (number == 0) { return new KateExternalToolsConfigWidget(parent, this); } return nullptr; } #include "externaltoolsplugin.moc" // kate: space-indent on; indent-width 4; replace-tabs on; diff --git a/addons/externaltools/katetoolrunner.cpp b/addons/externaltools/katetoolrunner.cpp index 396714f66..259a47aaf 100644 --- a/addons/externaltools/katetoolrunner.cpp +++ b/addons/externaltools/katetoolrunner.cpp @@ -1,112 +1,107 @@ /* This file is part of the KDE project * * Copyright 2019 Dominik Haumann * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "katetoolrunner.h" #include "kateexternaltool.h" #include #include #include -KateToolRunner::KateToolRunner(KateExternalTool* tool, KTextEditor::View * view, QObject* parent) +KateToolRunner::KateToolRunner(std::unique_ptr tool, KTextEditor::View * view, QObject* parent) : QObject(parent) , m_view(view) - , m_tool(tool) + , m_tool(std::move(tool)) , m_process(new QProcess()) { } KateToolRunner::~KateToolRunner() { - delete m_tool; - m_tool = nullptr; - - delete m_process; - m_process = nullptr; } KTextEditor::View* KateToolRunner::view() const { return m_view; } KateExternalTool* KateToolRunner::tool() const { - return m_tool; + return m_tool.get(); } void KateToolRunner::run() { if (m_tool->includeStderr) { m_process->setProcessChannelMode(QProcess::MergedChannels); } if (!m_tool->workingDir.isEmpty()) { m_process->setWorkingDirectory(m_tool->workingDir); } - QObject::connect(m_process, &QProcess::readyRead, this, &KateToolRunner::slotReadyRead); - QObject::connect(m_process, static_cast(&QProcess::finished), this, + QObject::connect(m_process.get(), &QProcess::readyRead, this, &KateToolRunner::slotReadyRead); + QObject::connect(m_process.get(), static_cast(&QProcess::finished), this, &KateToolRunner::handleToolFinished); // Write stdin to process, if applicable, then close write channel - QObject::connect(m_process, &QProcess::started, [this]() { + QObject::connect(m_process.get(), &QProcess::started, [this]() { if (!m_tool->input.isEmpty()) { m_process->write(m_tool->input.toLocal8Bit()); } m_process->closeWriteChannel(); }); const QStringList args = KShell::splitArgs(m_tool->arguments); m_process->start(m_tool->executable, args); } void KateToolRunner::waitForFinished() { m_process->waitForFinished(); } QString KateToolRunner::outputData() const { return QString::fromLocal8Bit(m_output); } void KateToolRunner::slotReadyRead() { m_output += m_process->readAll(); } void KateToolRunner::handleToolFinished(int exitCode, QProcess::ExitStatus exitStatus) { if (exitCode != 0) { // FIXME: somehow tell user qWarning() << i18n("External tool %1 finished with non-zero exit code: %2", m_tool->name, exitCode); } if (exitStatus != QProcess::NormalExit) { // FIXME: somehow tell user qWarning() << i18n("External tool crashed: %1", m_tool->name); } Q_EMIT toolFinished(this); } // kate: space-indent on; indent-width 4; replace-tabs on; diff --git a/addons/externaltools/katetoolrunner.h b/addons/externaltools/katetoolrunner.h index 5eca2ffa9..e6db54ee5 100644 --- a/addons/externaltools/katetoolrunner.h +++ b/addons/externaltools/katetoolrunner.h @@ -1,118 +1,120 @@ /* This file is part of the KDE project * * Copyright 2019 Dominik Haumann * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KTEXTEDITOR_EXTERNALTOOLRUNNER_H #define KTEXTEDITOR_EXTERNALTOOLRUNNER_H #include #include #include #include #include +#include + class KateExternalTool; class QProcess; namespace KTextEditor { class View; } /** * Helper class to run a KateExternalTool. */ class KateToolRunner : public QObject { Q_OBJECT public: /** * Constructor that will run @p tool in the run() method. * The @p view can later be retrieved again with view() to process the data when the tool is finished. */ - KateToolRunner(KateExternalTool* tool, KTextEditor::View * view, QObject* parent = nullptr); + KateToolRunner(std::unique_ptr tool, KTextEditor::View * view, QObject* parent = nullptr); KateToolRunner(const KateToolRunner&) = delete; void operator=(const KateToolRunner&) = delete; ~KateToolRunner(); /** * Returns the view that was active when running the tool. * @warning May be a nullptr, since the view could have been closed in the meantime. */ KTextEditor::View* view() const; /** * Returns the tool that was passed in the constructor. */ KateExternalTool* tool() const; /** * Starts a child process that executes the tool. */ void run(); /** * Blocking call that waits until the tool is finised. * Used internally for unit testing. */ void waitForFinished(); /** * Returns the data that was collected on stdout. * stderr is also included if includeStderr was set. */ QString outputData() const; Q_SIGNALS: /** * This signal is emitted when the tool is finished. */ void toolFinished(KateToolRunner* runner); private Q_SLOTS: /** * More tool output is available */ void slotReadyRead(); /** * Analysis finished * @param exitCode analyzer process exit code * @param exitStatus analyzer process exit status */ void handleToolFinished(int exitCode, QProcess::ExitStatus exitStatus); private: //! Use QPointer here, since the View may be closed in the meantime. QPointer m_view; //! We are the owner of the tool (it was copied) - KateExternalTool* m_tool; + std::unique_ptr m_tool; //! Child process that runs the tool - QProcess* m_process = nullptr; + std::unique_ptr m_process; //! Collect stdout, and optionally also stderr QByteArray m_output; }; #endif // KTEXTEDITOR_EXTERNALTOOLRUNNER_H // kate: space-indent on; indent-width 4; replace-tabs on;