diff --git a/plugins/externalscript/externalscriptplugin.cpp b/plugins/externalscript/externalscriptplugin.cpp index 451e0b3afa..d0389586e1 100644 --- a/plugins/externalscript/externalscriptplugin.cpp +++ b/plugins/externalscript/externalscriptplugin.cpp @@ -1,376 +1,385 @@ /* This plugin is part of KDevelop. Copyright (C) 2010 Milian Wolff This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "externalscriptplugin.h" #include "externalscriptview.h" #include "externalscriptitem.h" #include "externalscriptjob.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include K_PLUGIN_FACTORY_WITH_JSON(ExternalScriptFactory, "kdevexternalscript.json", registerPlugin();) class ExternalScriptViewFactory: public KDevelop::IToolViewFactory { public: explicit ExternalScriptViewFactory( ExternalScriptPlugin *plugin ): m_plugin( plugin ) {} QWidget* create( QWidget *parent = nullptr ) override { return new ExternalScriptView( m_plugin, parent ); } Qt::DockWidgetArea defaultPosition() override { return Qt::RightDockWidgetArea; } QString id() const override { return QStringLiteral("org.kdevelop.ExternalScriptView"); } private: ExternalScriptPlugin *m_plugin; }; // We extend ExternalScriptJob so that it deletes the temporarily created item on destruction class ExternalScriptJobOwningItem : public ExternalScriptJob { Q_OBJECT public: ExternalScriptJobOwningItem( ExternalScriptItem* item, const QUrl &url, ExternalScriptPlugin* parent ) : ExternalScriptJob(item, url, parent), m_item(item) { } ~ExternalScriptJobOwningItem() override { delete m_item; } private: ExternalScriptItem* m_item; }; ExternalScriptPlugin* ExternalScriptPlugin::m_self = nullptr; ExternalScriptPlugin::ExternalScriptPlugin( QObject* parent, const QVariantList& /*args*/ ) : IPlugin( QStringLiteral("kdevexternalscript"), parent ), m_model( new QStandardItemModel( this ) ), m_factory( new ExternalScriptViewFactory( this ) ) { Q_ASSERT( !m_self ); m_self = this; QDBusConnection::sessionBus().registerObject( QStringLiteral("/org/kdevelop/ExternalScriptPlugin"), this, QDBusConnection::ExportScriptableSlots ); setXMLFile( QStringLiteral("kdevexternalscript.rc") ); //BEGIN load config KConfigGroup config = getConfig(); foreach( const QString& group, config.groupList() ) { KConfigGroup script = config.group( group ); if ( script.hasKey( "name" ) && script.hasKey( "command" ) ) { ExternalScriptItem* item = new ExternalScriptItem; item->setText( script.readEntry( "name" ) ); item->setCommand( script.readEntry( "command" )); item->setInputMode( static_cast( script.readEntry( "inputMode", 0u ) ) ); item->setOutputMode( static_cast( script.readEntry( "outputMode", 0u ) ) ); item->setErrorMode( static_cast( script.readEntry( "errorMode", 0u ) ) ); item->setSaveMode( static_cast( script.readEntry( "saveMode", 0u ) ) ); item->setFilterMode( script.readEntry( "filterMode", 0u )); item->action()->setShortcut( QKeySequence( script.readEntry( "shortcuts" ) ) ); item->setShowOutput( script.readEntry( "showOutput", true ) ); m_model->appendRow( item ); } } //END load config core()->uiController()->addToolView( i18n( "External Scripts" ), m_factory ); connect( m_model, &QStandardItemModel::rowsRemoved, this, &ExternalScriptPlugin::rowsRemoved ); connect( m_model, &QStandardItemModel::rowsInserted, this, &ExternalScriptPlugin::rowsInserted ); const bool firstUse = config.readEntry( "firstUse", true ); if ( firstUse ) { // some example scripts ExternalScriptItem* item = new ExternalScriptItem; item->setText( i18n("Quick Compile") ); item->setCommand( QStringLiteral("g++ -o %b %f && ./%b") ); m_model->appendRow( item ); + #ifndef Q_OS_WIN + item = new ExternalScriptItem; + item->setText( i18n("Sort Selection") ); + item->setCommand( QStringLiteral("sort") ); + item->setInputMode( ExternalScriptItem::InputSelectionOrDocument ); + item->setOutputMode( ExternalScriptItem::OutputReplaceSelectionOrDocument ); + item->setShowOutput( false ); + m_model->appendRow( item ); + item = new ExternalScriptItem; item->setText( i18n("Google Selection") ); item->setCommand( QStringLiteral("xdg-open \"https://www.google.com/search?q=%s\"") ); item->setShowOutput( false ); m_model->appendRow( item ); item = new ExternalScriptItem; - item->setText( i18n("Sort Selection") ); - item->setCommand( QStringLiteral("sort") ); + item->setText( i18n("Paste to Hastebin") ); + item->setCommand( QStringLiteral("a=$(cat); curl -X POST -s -d \"$a\" https://hastebin.com/documents | awk -F '\"' '{print \"https://hastebin.com/\"$4}' | xargs xdg-open ;") ); item->setInputMode( ExternalScriptItem::InputSelectionOrDocument ); - item->setOutputMode( ExternalScriptItem::OutputReplaceSelectionOrDocument ); item->setShowOutput( false ); m_model->appendRow( item ); + #endif config.writeEntry( "firstUse", false ); config.sync(); } } ExternalScriptPlugin* ExternalScriptPlugin::self() { return m_self; } ExternalScriptPlugin::~ExternalScriptPlugin() { m_self = nullptr; } KDevelop::ContextMenuExtension ExternalScriptPlugin::contextMenuExtension(KDevelop::Context* context, QWidget* parent) { m_urls.clear(); int folderCount = 0; if ( context->type() == KDevelop::Context::FileContext ) { KDevelop::FileContext* filectx = static_cast(context); m_urls = filectx->urls(); } else if ( context->type() == KDevelop::Context::ProjectItemContext ) { KDevelop::ProjectItemContext* projctx = static_cast(context); foreach( KDevelop::ProjectBaseItem* item, projctx->items() ) { if ( item->file() ) { m_urls << item->file()->path().toUrl(); } else if ( item->folder() ) { m_urls << item->folder()->path().toUrl(); folderCount++; } } } else if ( context->type() == KDevelop::Context::EditorContext ) { KDevelop::EditorContext* econtext = static_cast(context); m_urls << econtext->url(); } if ( !m_urls.isEmpty() ) { KDevelop::ContextMenuExtension ext; QMenu* menu = nullptr; for ( int row = 0; row < m_model->rowCount(); ++row ) { ExternalScriptItem* item = dynamic_cast( m_model->item( row ) ); Q_ASSERT( item ); if (context->type() != KDevelop::Context::EditorContext) { // filter scripts that depend on an opened document // if the context menu was not requested inside the editor if (item->performParameterReplacement() && item->command().contains(QStringLiteral("%s"))) { continue; } else if (item->inputMode() == ExternalScriptItem::InputSelectionOrNone) { continue; } } if ( folderCount == m_urls.count() ) { // when only folders filter items that don't have %d parameter (or another parameter) if (item->performParameterReplacement() && (!item->command().contains(QStringLiteral("%d")) || item->command().contains(QStringLiteral("%s")) || item->command().contains(QStringLiteral("%u")) || item->command().contains(QStringLiteral("%f")) || item->command().contains(QStringLiteral("%b")) || item->command().contains(QStringLiteral("%n")) ) ) { continue; } } if (!menu) { menu = new QMenu(i18n("External Scripts"), parent); } QAction* scriptAction = new QAction(item->text(), menu); scriptAction->setData( QVariant::fromValue( item )); connect( scriptAction, &QAction::triggered, this, &ExternalScriptPlugin::executeScriptFromContextMenu ); menu->addAction( scriptAction ); } if (menu) { ext.addAction( KDevelop::ContextMenuExtension::ExtensionGroup, menu->menuAction() ); } return ext; } return KDevelop::IPlugin::contextMenuExtension(context, parent); } void ExternalScriptPlugin::unload() { core()->uiController()->removeToolView( m_factory ); KDevelop::IPlugin::unload(); } KConfigGroup ExternalScriptPlugin::getConfig() const { return KSharedConfig::openConfig()->group("External Scripts"); } QStandardItemModel* ExternalScriptPlugin::model() const { return m_model; } void ExternalScriptPlugin::execute( ExternalScriptItem* item, const QUrl& url ) const { ExternalScriptJob* job = new ExternalScriptJob( item, url, const_cast(this) ); KDevelop::ICore::self()->runController()->registerJob( job ); } void ExternalScriptPlugin::execute(ExternalScriptItem* item) const { auto document = KDevelop::ICore::self()->documentController()->activeDocument(); execute( item, document ? document->url() : QUrl() ); } bool ExternalScriptPlugin::executeCommand ( const QString& command, const QString& workingDirectory ) const { ExternalScriptItem* item = new ExternalScriptItem; item->setCommand(command); item->setWorkingDirectory(workingDirectory); item->setPerformParameterReplacement(false); qCDebug(PLUGIN_EXTERNALSCRIPT) << "executing command " << command << " in dir " << workingDirectory << " as external script"; ExternalScriptJobOwningItem* job = new ExternalScriptJobOwningItem( item, QUrl(), const_cast(this) ); // When a command is executed, for example through the terminal, we don't want the command output to be risen job->setVerbosity(KDevelop::OutputJob::Silent); KDevelop::ICore::self()->runController()->registerJob( job ); return true; } QString ExternalScriptPlugin::executeCommandSync ( const QString& command, const QString& workingDirectory ) const { qCDebug(PLUGIN_EXTERNALSCRIPT) << "executing command " << command << " in working-dir " << workingDirectory; KProcess process; process.setWorkingDirectory( workingDirectory ); process.setShellCommand( command ); process.setOutputChannelMode( KProcess::OnlyStdoutChannel ); process.execute(); return QString::fromLocal8Bit(process.readAll()); } void ExternalScriptPlugin::executeScriptFromActionData() const { QAction* action = dynamic_cast( sender() ); Q_ASSERT( action ); ExternalScriptItem* item = action->data().value(); Q_ASSERT( item ); execute( item ); } void ExternalScriptPlugin::executeScriptFromContextMenu() const { QAction* action = dynamic_cast( sender() ); Q_ASSERT( action ); ExternalScriptItem* item = action->data().value(); Q_ASSERT( item ); foreach( const QUrl& url, m_urls) { KDevelop::ICore::self()->documentController()->openDocument( url ); execute( item, url ); } } void ExternalScriptPlugin::rowsInserted( const QModelIndex& /*parent*/, int start, int end ) { for ( int i = start; i <= end; ++i ) { saveItemForRow( i ); } } void ExternalScriptPlugin::rowsRemoved( const QModelIndex& /*parent*/, int start, int end ) { KConfigGroup config = getConfig(); for ( int i = start; i <= end; ++i ) { KConfigGroup child = config.group( QStringLiteral("script %1").arg(i) ); qCDebug(PLUGIN_EXTERNALSCRIPT) << "removing config group:" << child.name(); child.deleteGroup(); } config.sync(); } void ExternalScriptPlugin::saveItem( const ExternalScriptItem* item ) { const QModelIndex index = m_model->indexFromItem( item ); Q_ASSERT( index.isValid() ); saveItemForRow( index.row() ); } void ExternalScriptPlugin::saveItemForRow( int row ) { const QModelIndex idx = m_model->index( row, 0 ); Q_ASSERT( idx.isValid() ); ExternalScriptItem* item = dynamic_cast( m_model->item( row ) ); Q_ASSERT( item ); qCDebug(PLUGIN_EXTERNALSCRIPT) << "save extern script:" << item << idx; KConfigGroup config = getConfig().group( QStringLiteral("script %1").arg( row ) ); config.writeEntry( "name", item->text() ); config.writeEntry( "command", item->command() ); config.writeEntry( "inputMode", (uint) item->inputMode() ); config.writeEntry( "outputMode", (uint) item->outputMode() ); config.writeEntry( "errorMode", (uint) item->errorMode() ); config.writeEntry( "saveMode", (uint) item->saveMode() ); config.writeEntry( "shortcuts", item->action()->shortcut().toString() ); config.writeEntry( "showOutput", item->showOutput() ); config.writeEntry( "filterMode", item->filterMode()); config.sync(); } #include "externalscriptplugin.moc" // kate: indent-mode cstyle; space-indent on; indent-width 2; replace-tabs on;