Commit 4843e887 authored by Christoph Cullmann's avatar Christoph Cullmann

Merge branch 'revive-externaltools-plugin' into 'master'

Revive externaltools plugin

See merge request kde/kate!15
parents 7ffcda6e 89d1400b
......@@ -3,6 +3,7 @@ find_package(KF5TextEditor QUIET REQUIRED)
ecm_optional_add_subdirectory(backtracebrowser)
ecm_optional_add_subdirectory(close-except-like) # Close all documents except this one (or similar).
ecm_optional_add_subdirectory(externaltools)
ecm_optional_add_subdirectory(filebrowser)
ecm_optional_add_subdirectory(filetree)
ecm_optional_add_subdirectory(gdbplugin)
......
#find_package(KF5TextEditor QUIET REQUIRED)
if(KF5TextEditor_VERSION VERSION_LESS 5.63.0)
return()
endif()
find_package(KF5I18n QUIET REQUIRED)
if(NOT KF5I18n_Found)
return()
endif()
find_package(KF5IconThemes QUIET)
set_package_properties(KF5IconThemes PROPERTIES PURPOSE "Required to build the externaltools addon")
if(NOT KF5IconThemes_FOUND)
return()
endif()
find_package(Qt5Test QUIET REQUIRED)
add_library(externaltoolsplugin MODULE "")
target_compile_definitions(externaltoolsplugin PRIVATE TRANSLATION_DOMAIN="kateexternaltoolsplugin")
target_link_libraries(externaltoolsplugin PRIVATE
KF5::CoreAddons
KF5::IconThemes
KF5::TextEditor
KF5::I18n
)
ki18n_wrap_ui(UI_SOURCES configwidget.ui
tooldialog.ui
toolview.ui
)
target_sources(
externaltoolsplugin
PRIVATE
externaltoolsplugin.cpp
kateexternaltoolsview.cpp
katetoolrunner.cpp
kateexternaltool.cpp
kateexternaltoolscommand.cpp
kateexternaltoolsconfigwidget.cpp
plugin.qrc
${UI_SOURCES}
)
kcoreaddons_desktop_to_json (externaltoolsplugin externaltoolsplugin.desktop)
install(TARGETS externaltoolsplugin DESTINATION ${PLUGIN_INSTALL_DIR}/ktexteditor )
if (BUILD_TESTING)
add_subdirectory(autotests)
endif()
#! /bin/sh
$EXTRACTRC *.rc *.ui >> rc.cpp
$XGETTEXT *.cpp -o $podir/kateexternaltoolsplugin.pot
include(ECMMarkAsTest)
find_package(KF5Config QUIET) # KConfig, KConfigGroup
if(NOT KF5Config_FOUND)
return()
endif()
find_package(KF5CoreAddons QUIET) # KShell
if(NOT KF5CoreAddons_FOUND)
return()
endif()
# Project Plugin
add_executable(externaltools_test
externaltooltest.cpp
../kateexternaltool.cpp
../katetoolrunner.cpp
)
add_test(plugin-externaltools_test externaltools_test)
target_link_libraries(externaltools_test PRIVATE Qt5::Test KF5::ConfigCore KF5::CoreAddons KF5::TextEditor)
ecm_mark_as_test(externaltools_test)
/* This file is part of the KDE project
*
* Copyright 2019 Dominik Haumann <dhaumann@kde.org>
*
* 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 <QtTest>
#include <QString>
#include <KConfig>
#include <KConfigGroup>
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()
{
std::unique_ptr<KateExternalTool> 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;
std::unique_ptr<KateExternalTool> tool2(new KateExternalTool(*tool));
// 1. /tmp $ ls /usr
KateToolRunner runner1(std::move(tool), nullptr);
runner1.run();
runner1.waitForFinished();
QVERIFY(runner1.outputData().contains(QStringLiteral("bin")));
// 2. /usr $ ls
tool2->arguments.clear();
tool2->workingDir = QStringLiteral("/usr");
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()
{
std::unique_ptr<KateExternalTool> 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(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;
/* This file is part of the KDE project
*
* Copyright 2019 Dominik Haumann <dhaumann@kde.org>
*
* 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 KATE_TOOLRUNNER_TEST_H
#define KATE_TOOLRUNNER_TEST_H
#include <QObject>
class ExternalToolTest : public QObject
{
Q_OBJECT
public Q_SLOTS:
void initTestCase();
void cleanupTestCase();
private Q_SLOTS:
void testLoadSave();
void testRunListDirectory();
void testRunTac();
};
#endif
// kate: space-indent on; indent-width 4; replace-tabs on;
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ExternalToolsConfigWidget</class>
<widget class="QWidget" name="ExternalToolsConfigWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>433</width>
<height>296</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QTreeView" name="lbTools">
<property name="whatsThis">
<string>This list shows all the configured tools, represented by their menu text.</string>
</property>
<property name="headerHidden">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="btnAdd">
<property name="text">
<string>&amp;Add</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnEdit">
<property name="text">
<string>&amp;Edit...</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnRemove">
<property name="text">
<string>&amp;Remove</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
[Global]
tools=10
version=1
[Tool 0]
actionName=externaltool_RunShellScript
arguments=-e sh -c "cd %{Document:Path} && pwd && chmod -vc a+x %{Document:FileName} && ./%{Document:FileName} ; echo Press any key to continue. && read -n 1"
category=
cmdname=run-script
executable=konsole
icon=system-run
input=
mimetypes=
name=Run Shell Script
output=Ignore
reload=false
save=CurrentDocument
workingDir=%{Document:Path}
[Tool 1]
actionName=externaltool_GoogleSelectedText
arguments="https://www.google.com/search?q=%{Document:Selection:Text}"
category=
cmdname=google
executable=xdg-open
icon=globe
input=
mimetypes=
name=Google Selected Text
output=Ignore
reload=false
save=None
workingDir=
[Tool 10]
actionName=externaltool_TestExternalTools
arguments=
category=Tools
cmdname=external-tool-test
executable=tac
icon=
input=Document:FileBaseName: %{Document:FileBaseName}\nDocument:FileName: %{Document:FileName}\nDocument:FilePath: %{Document:FilePath}\nDocument:FileExtension: %{Document:FileExtension}\nDocument:Text: %{Document:Text}\nDocument:Path: %{Document:Path}\nDocument:NativeFilePath: %{Document:NativeFilePath}\nDocument:NativePath: %{Document:NativePath}\nDocument:Cursor:Line: %{Document:Cursor:Line}\nDocument:Cursor:Column: %{Document:Cursor:Column}\nDocument:Cursor:XPos: %{Document:Cursor:XPos}\nDocument:Cursor:YPos: %{Document:Cursor:YPos}\nDocument:Selection:Text: %{Document:Selection:Text}\nDocument:Selection:StartLine: %{Document:Selection:StartLine}\nDocument:Selection:StartColumn: %{Document:Selection:StartColumn}\nDocument:Selection:EndLine: %{Document:Selection:EndLine}\nDocument:Selection:EndColumn: %{CurrentDocument:Selection:EndColumn}\nDocument:RowCount: %{CurrentDocument:RowCount}\nDate:dd.MM.yyyy: %{Date:dd.MM.yyyy}\nDate:Locale: %{Date:Locale}\nDate:ISO: %{Date:ISO}\nTime:hh:mm:ss.zzz: %{Time:hh:mm:ss.zzz}\nTime:Locale: %{Time:Locale}\nTime:ISO: %{Time:ISO}\nENV:KATE_PID: %{ENV:KATE_PID}\nUUID: %{UUID}\n
mimetypes=
name=Test External Tools
output=DisplayInPane
reload=false
save=None
workingDir=
[Tool 2]
actionName=externaltool_gitcola
arguments=-r %{Document:Path}
category=Git
cmdname=git-cola
executable=git-cola
icon=git-cola
input=
mimetypes=
name=git-cola
output=Ignore
reload=false
save=None
workingDir=
[Tool 3]
actionName=externaltool_gitk
arguments=
category=Git
cmdname=gitk
executable=gitk
icon=git-gui
input=
mimetypes=
name=gitk
output=Ignore
reload=false
save=None
workingDir=%{Document:Path}
[Tool 4]
actionName=externaltool_gitblame
arguments=gui blame %{Document:FileName}
category=Git
cmdname=git-blame
executable=git
icon=
input=
mimetypes=
name=git blame
output=Ignore
reload=false
save=CurrentDocument
workingDir=%{Document:Path}
[Tool 5]
actionName=externaltool_QtQuick2Previewqmlscene
arguments=%{Document:FileName}
category=Tools
cmdname=qml-preview
executable=qmlscene
icon=
input=
mimetypes=text/x-qml
name=Qt Quick 2 Preview (qmlscene)
output=Ignore
reload=false
save=CurrentDocument
workingDir=%{Document:Path}
[Tool 6]
actionName=externaltool_InsertUUID
arguments=%{UUID}
category=Tools
cmdname=uuid
executable=echo
icon=
input=
mimetypes=
name=Insert UUID
output=InsertAtCursor
reload=false
save=None
workingDir=
[Tool 7]
actionName=externaltool_ClangFormatFullFile
arguments=-i %{Document:FileName}
category=Tools
cmdname=clang-format-file
executable=clang-format
icon=
input=
mimetypes=
name=Clang Format Full File
output=Ignore
reload=true
save=CurrentDocument
workingDir=%{Document:Path}
[Tool 8]
actionName=externaltool_ClangFormatSelectedText
arguments=-assume-filename=%{Document:FileName}
category=Tools
cmdname=clang-format-selection
executable=clang-format
icon=
input=\s%{Document:Selection:Text}
mimetypes=
name=Clang Format Selected Text
output=ReplaceSelectedText
reload=false
save=None
workingDir=%{Document:Path}
[Tool 9]
actionName=externaltool_perl
arguments=%{ENV:KATE_PID
category=Tools
cmdname=perl
executable=echo
icon=
input=
mimetypes=
name=perl
output=DisplayInPane
reload=false
save=None
workingDir=
/* This file is part of the KDE project
*
* Copyright 2019 Dominik Haumann <dhaumann@kde.org>
*
* 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 "katetoolrunner.h"
#include "kateexternaltoolsconfigwidget.h"
#include <KTextEditor/Editor>
#include <KTextEditor/Document>
#include <KTextEditor/View>
#include <KActionCollection>
#include <KLocalizedString>
#include <QAction>
#include <kparts/part.h>
#include <KAboutData>
#include <KAuthorized>
#include <KConfig>
#include <KConfigGroup>
#include <KPluginFactory>
#include <KXMLGUIFactory>
K_PLUGIN_FACTORY_WITH_JSON(KateExternalToolsFactory, "externaltoolsplugin.json",
registerPlugin<KateExternalToolsPlugin>();)
KateExternalToolsPlugin::KateExternalToolsPlugin(QObject* parent, const QList<QVariant>&)
: 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<KateExternalTool*> & 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
std::unique_ptr<KateExternalTool> copy(new KateExternalTool(tool));
// clear previous toolview data
auto pluginView = viewForMainWindow(mw);
pluginView->clearToolView();
pluginView->addToolStatus(i18n("Running external tool: %1", copy->name));
pluginView->addToolStatus(i18n("- Executable: %1", copy->executable));
pluginView->addToolStatus(i18n("- Arguments : %1", copy->arguments));
pluginView->addToolStatus(i18n("- Input : %1", copy->input));
pluginView->addToolStatus(QString());
// expand macros
auto editor = KTextEditor::Editor::instance();
editor->expandText(copy->executable, view, copy->executable);
editor->expandText(copy->arguments, view, copy->arguments);
editor->expandText(copy->workingDir, view, copy->workingDir);
editor->expandText(copy->input, view, copy->input);
// 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(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, int exitCode, bool crashed)
{
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: {