diff --git a/kdevplatform/language/duchain/navigation/abstractnavigationcontext.cpp b/kdevplatform/language/duchain/navigation/abstractnavigationcontext.cpp index 58baf175a6..d83ac93d27 100644 --- a/kdevplatform/language/duchain/navigation/abstractnavigationcontext.cpp +++ b/kdevplatform/language/duchain/navigation/abstractnavigationcontext.cpp @@ -1,620 +1,620 @@ /* Copyright 2007 David Nolden This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. 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 "abstractnavigationcontext.h" #include #include #include "abstractdeclarationnavigationcontext.h" #include "abstractnavigationwidget.h" #include "usesnavigationcontext.h" #include "../../../interfaces/icore.h" #include "../../../interfaces/idocumentcontroller.h" #include "../functiondeclaration.h" #include "../namespacealiasdeclaration.h" #include "../types/functiontype.h" #include "../types/structuretype.h" #include #include #include #include namespace KDevelop { class AbstractNavigationContextPrivate { public: QVector m_children; //Used to keep alive all children until this is deleted int m_selectedLink = 0; //The link currently selected NavigationAction m_selectedLinkAction; //Target of the currently selected link bool m_shorten = false; //A counter used while building the html-code to count the used links. int m_linkCount = -1; //Something else than -1 if the current position is represented by a line-number, not a link. int m_currentLine = 0; int m_currentPositionLine = 0; QMap m_links; QMap m_linkLines; //Holds the line for each link QMap m_intLinks; AbstractNavigationContext* m_previousContext; TopDUContextPointer m_topContext; QString m_currentText; //Here the text is built }; void AbstractNavigationContext::setTopContext(const TopDUContextPointer& context) { Q_D(AbstractNavigationContext); d->m_topContext = context; } TopDUContextPointer AbstractNavigationContext::topContext() const { Q_D(const AbstractNavigationContext); return d->m_topContext; } AbstractNavigationContext::AbstractNavigationContext(const TopDUContextPointer& topContext, AbstractNavigationContext* previousContext) : d_ptr(new AbstractNavigationContextPrivate) { Q_D(AbstractNavigationContext); d->m_previousContext = previousContext; d->m_topContext = topContext; qRegisterMetaType("KTextEditor::Cursor"); qRegisterMetaType("IDocumentation::Ptr"); } AbstractNavigationContext::~AbstractNavigationContext() { } void AbstractNavigationContext::makeLink(const QString& name, const DeclarationPointer& declaration, NavigationAction::Type actionType) { NavigationAction action(declaration, actionType); makeLink(name, QString(), action); } QString AbstractNavigationContext::createLink(const QString& name, const QString&, const NavigationAction& action) { Q_D(AbstractNavigationContext); if (d->m_shorten) { //Do not create links in shortened mode, it's only for viewing return typeHighlight(name.toHtmlEscaped()); } // NOTE: Since the by definition in the HTML standard some uri components // are case-insensitive, we define a new lowercase link-id for each // link. Otherwise Qt 5 seems to mess up the casing and the link // cannot be matched when it's executed. QString hrefId = QStringLiteral("link_%1").arg(d->m_links.count()); d->m_links[hrefId] = action; d->m_intLinks[d->m_linkCount] = action; d->m_linkLines[d->m_linkCount] = d->m_currentLine; if (d->m_currentPositionLine == d->m_currentLine) { d->m_currentPositionLine = -1; d->m_selectedLink = d->m_linkCount; } QString str = name.toHtmlEscaped(); if (d->m_linkCount == d->m_selectedLink) str = QLatin1String("") + str + QLatin1String( ""); QString ret = QLatin1String("m_linkCount == d->m_selectedLink && d->m_currentPositionLine == -1) ? QStringLiteral(" name = \"currentPosition\"") : QString()) + QLatin1Char('>') + str + QLatin1String(""); if (d->m_selectedLink == d->m_linkCount) d->m_selectedLinkAction = action; ++d->m_linkCount; return ret; } void AbstractNavigationContext::makeLink(const QString& name, const QString& targetId, const NavigationAction& action) { modifyHtml() += createLink(name, targetId, action); } void AbstractNavigationContext::clear() { Q_D(AbstractNavigationContext); d->m_linkCount = 0; d->m_currentLine = 0; d->m_currentText.clear(); d->m_links.clear(); d->m_intLinks.clear(); d->m_linkLines.clear(); } void AbstractNavigationContext::executeLink(const QString& link) { Q_D(AbstractNavigationContext); const auto actionIt = d->m_links.constFind(link); if (actionIt == d->m_links.constEnd()) return; execute(*actionIt); } NavigationContextPointer AbstractNavigationContext::executeKeyAction(const QString& key) { Q_UNUSED(key); return NavigationContextPointer(this); } NavigationContextPointer AbstractNavigationContext::execute(const NavigationAction& action) { Q_D(AbstractNavigationContext); if (action.targetContext) return NavigationContextPointer(action.targetContext); if (action.type == NavigationAction::ExecuteKey) return executeKeyAction(action.key); if (!action.decl && (action.type != NavigationAction::JumpToSource || action.document.isEmpty())) { - qCDebug(LANGUAGE) << "Navigation-action has invalid declaration" << endl; + qCDebug(LANGUAGE) << "Navigation-action has invalid declaration"; return NavigationContextPointer(this); } switch (action.type) { case NavigationAction::ExecuteKey: break; case NavigationAction::None: - qCDebug(LANGUAGE) << "Tried to execute an invalid action in navigation-widget" << endl; + qCDebug(LANGUAGE) << "Tried to execute an invalid action in navigation-widget"; break; case NavigationAction::NavigateDeclaration: { auto ctx = dynamic_cast(d->m_previousContext); if (ctx && ctx->declaration() == action.decl) return NavigationContextPointer(d->m_previousContext); return registerChild(action.decl); } case NavigationAction::NavigateUses: { auto* browser = ICore::self()->pluginController()->extensionForPlugin(); if (browser) { browser->showUses(action.decl); return NavigationContextPointer(this); } Q_FALLTHROUGH(); } case NavigationAction::ShowUses: { return registerChild(new UsesNavigationContext(action.decl.data(), this)); } case NavigationAction::JumpToSource: { QUrl doc = action.document; KTextEditor::Cursor cursor = action.cursor; { DUChainReadLocker lock(DUChain::lock()); if (action.decl) { if (doc.isEmpty()) { doc = action.decl->url().toUrl(); /* if(action.decl->internalContext()) cursor = action.decl->internalContext()->range().start() + KTextEditor::Cursor(0, 1); else*/ cursor = action.decl->rangeInCurrentRevision().start(); } action.decl->activateSpecialization(); } } //This is used to execute the slot delayed in the event-loop, so crashes are avoided QMetaObject::invokeMethod(ICore::self()->documentController(), "openDocument", Qt::QueuedConnection, Q_ARG(QUrl, doc), Q_ARG(KTextEditor::Cursor, cursor)); break; } case NavigationAction::ShowDocumentation: { auto doc = ICore::self()->documentationController()->documentationForDeclaration(action.decl.data()); // This is used to execute the slot delayed in the event-loop, so crashes are avoided // which can happen e.g. due to focus change events resulting in tooltip destruction and thus this object QMetaObject::invokeMethod( ICore::self()->documentationController(), "showDocumentation", Qt::QueuedConnection, Q_ARG(IDocumentation::Ptr, doc)); } break; } return NavigationContextPointer(this); } AbstractNavigationContext* AbstractNavigationContext::previousContext() const { Q_D(const AbstractNavigationContext); return d->m_previousContext; } void AbstractNavigationContext::setPreviousContext(AbstractNavigationContext* previous) { Q_D(AbstractNavigationContext); d->m_previousContext = previous; } NavigationContextPointer AbstractNavigationContext::registerChild(AbstractNavigationContext* context) { Q_D(AbstractNavigationContext); d->m_children << NavigationContextPointer(context); return d->m_children.last(); } NavigationContextPointer AbstractNavigationContext::registerChild(const DeclarationPointer& declaration) { Q_D(AbstractNavigationContext); //We create a navigation-widget here, and steal its context.. evil ;) QScopedPointer navigationWidget( declaration->context()->createNavigationWidget(declaration.data())); if (navigationWidget) { NavigationContextPointer ret = navigationWidget->context(); ret->setPreviousContext(this); d->m_children << ret; return ret; } else { return NavigationContextPointer(this); } } const int lineJump = 3; bool AbstractNavigationContext::down() { Q_D(AbstractNavigationContext); //Make sure link-count is valid if (d->m_linkCount == -1) { DUChainReadLocker lock; html(); } // select first link when we enter via down if (d->m_selectedLink == -1 && d->m_linkCount) { d->m_selectedLink = 0; d->m_currentPositionLine = -1; return true; } int fromLine = d->m_currentPositionLine; // try to select the next link within our lineJump distance if (d->m_selectedLink >= 0 && d->m_selectedLink < d->m_linkCount) { if (fromLine == -1) fromLine = d->m_linkLines[d->m_selectedLink]; for (int newSelectedLink = d->m_selectedLink + 1; newSelectedLink < d->m_linkCount; ++newSelectedLink) { if (d->m_linkLines[newSelectedLink] > fromLine && d->m_linkLines[newSelectedLink] - fromLine <= lineJump) { d->m_selectedLink = newSelectedLink; d->m_currentPositionLine = -1; return true; } } } if (fromLine == d->m_currentLine - 1) // nothing to do, we are at the end of the document return false; // scroll down by applying the lineJump if (fromLine == -1) fromLine = 0; d->m_currentPositionLine = fromLine + lineJump; if (d->m_currentPositionLine >= d->m_currentLine) { d->m_currentPositionLine = d->m_currentLine - 1; } return fromLine != d->m_currentPositionLine; } bool AbstractNavigationContext::up() { Q_D(AbstractNavigationContext); //Make sure link-count is valid if (d->m_linkCount == -1) { DUChainReadLocker lock; html(); } // select last link when we enter via up if (d->m_selectedLink == -1 && d->m_linkCount) { d->m_selectedLink = d->m_linkCount - 1; d->m_currentPositionLine = -1; return true; } int fromLine = d->m_currentPositionLine; if (d->m_selectedLink >= 0 && d->m_selectedLink < d->m_linkCount) { if (fromLine == -1) fromLine = d->m_linkLines[d->m_selectedLink]; for (int newSelectedLink = d->m_selectedLink - 1; newSelectedLink >= 0; --newSelectedLink) { if (d->m_linkLines[newSelectedLink] < fromLine && fromLine - d->m_linkLines[newSelectedLink] <= lineJump) { d->m_selectedLink = newSelectedLink; d->m_currentPositionLine = -1; return true; } } } if (fromLine == -1) fromLine = d->m_currentLine - 1; d->m_currentPositionLine = fromLine - lineJump; if (d->m_currentPositionLine < 0) d->m_currentPositionLine = 0; return fromLine || d->m_currentPositionLine; } bool AbstractNavigationContext::nextLink() { Q_D(AbstractNavigationContext); //Make sure link-count is valid if (d->m_linkCount == -1) { DUChainReadLocker lock; html(); } if (!d->m_linkCount) return false; d->m_currentPositionLine = -1; d->m_selectedLink++; if (d->m_selectedLink >= d->m_linkCount) { d->m_selectedLink = 0; return false; } return true; } bool AbstractNavigationContext::previousLink() { Q_D(AbstractNavigationContext); //Make sure link-count is valid if (d->m_linkCount == -1) { DUChainReadLocker lock; html(); } if (!d->m_linkCount) return false; d->m_currentPositionLine = -1; d->m_selectedLink--; if (d->m_selectedLink < 0) { d->m_selectedLink = d->m_linkCount - 1; return false; } return true; } int AbstractNavigationContext::linkCount() const { Q_D(const AbstractNavigationContext); return d->m_linkCount; } void AbstractNavigationContext::resetNavigation() { Q_D(AbstractNavigationContext); d->m_currentPositionLine = -1; d->m_selectedLink = -1; d->m_selectedLinkAction = {}; } NavigationContextPointer AbstractNavigationContext::back() { Q_D(AbstractNavigationContext); if (d->m_previousContext) return NavigationContextPointer(d->m_previousContext); else return NavigationContextPointer(this); } NavigationContextPointer AbstractNavigationContext::accept() { Q_D(AbstractNavigationContext); if (d->m_selectedLink >= 0 && d->m_selectedLink < d->m_linkCount) { NavigationAction action = d->m_intLinks[d->m_selectedLink]; return execute(action); } return NavigationContextPointer(this); } NavigationContextPointer AbstractNavigationContext::accept(IndexedDeclaration decl) { if (decl.data()) { NavigationAction action(DeclarationPointer(decl.data()), NavigationAction::NavigateDeclaration); return execute(action); } else { return NavigationContextPointer(this); } } NavigationContextPointer AbstractNavigationContext::acceptLink(const QString& link) { Q_D(AbstractNavigationContext); const auto actionIt = d->m_links.constFind(link); if (actionIt == d->m_links.constEnd()) { - qCDebug(LANGUAGE) << "Executed unregistered link " << link << endl; + qCDebug(LANGUAGE) << "Executed unregistered link " << link; return NavigationContextPointer(this); } return execute(*actionIt); } NavigationAction AbstractNavigationContext::currentAction() const { Q_D(const AbstractNavigationContext); return d->m_selectedLinkAction; } QString AbstractNavigationContext::declarationKind(const DeclarationPointer& decl) { const auto* function = dynamic_cast(decl.data()); QString kind; if (decl->isTypeAlias()) kind = i18n("Typedef"); else if (decl->kind() == Declaration::Type) { if (decl->type()) kind = i18n("Class"); } else if (decl->kind() == Declaration::Instance) { kind = i18n("Variable"); } else if (decl->kind() == Declaration::Namespace) { kind = i18n("Namespace"); } if (auto* alias = dynamic_cast(decl.data())) { if (alias->identifier().isEmpty()) kind = i18n("Namespace import"); else kind = i18n("Namespace alias"); } if (function) kind = i18n("Function"); if (decl->isForwardDeclaration()) kind = i18n("Forward Declaration"); return kind; } QString AbstractNavigationContext::html(bool shorten) { Q_D(AbstractNavigationContext); d->m_shorten = shorten; return QString(); } bool AbstractNavigationContext::alreadyComputed() const { Q_D(const AbstractNavigationContext); return !d->m_currentText.isEmpty(); } bool AbstractNavigationContext::isWidgetMaximized() const { return true; } QWidget* AbstractNavigationContext::widget() const { return nullptr; } ///Splits the string by the given regular expression, but keeps the split-matches at the end of each line static QStringList splitAndKeep(QString str, const QRegExp& regExp) { QStringList ret; int place = regExp.indexIn(str); while (place != -1) { ret << str.left(place + regExp.matchedLength()); str.remove(0, place + regExp.matchedLength()); place = regExp.indexIn(str); } ret << str; return ret; } void AbstractNavigationContext::addHtml(const QString& html) { Q_D(AbstractNavigationContext); QRegExp newLineRegExp(QStringLiteral("
|
|

")); const auto lines = splitAndKeep(html, newLineRegExp); for (const QString& line : lines) { d->m_currentText += line; if (line.indexOf(newLineRegExp) != -1) { ++d->m_currentLine; if (d->m_currentLine == d->m_currentPositionLine) { d->m_currentText += QLatin1String( " <-> "); // ><-> is <-> } } } } QString AbstractNavigationContext::currentHtml() const { Q_D(const AbstractNavigationContext); return d->m_currentText; } QString Colorizer::operator()(const QString& str) const { QString ret = QLatin1String("") + str + QLatin1String(""); if (m_formatting & Fixed) ret = QLatin1String("") + ret + QLatin1String(""); if (m_formatting & Bold) ret = QLatin1String("") + ret + QLatin1String(""); if (m_formatting & Italic) ret = QLatin1String("") + ret + QLatin1String(""); return ret; } const Colorizer AbstractNavigationContext::typeHighlight(QStringLiteral("006000")); const Colorizer AbstractNavigationContext::errorHighlight(QStringLiteral("990000")); const Colorizer AbstractNavigationContext::labelHighlight(QStringLiteral("000000")); const Colorizer AbstractNavigationContext::codeHighlight(QStringLiteral("005000")); const Colorizer AbstractNavigationContext::propertyHighlight(QStringLiteral("009900")); const Colorizer AbstractNavigationContext::navigationHighlight(QStringLiteral("000099")); const Colorizer AbstractNavigationContext::importantHighlight(QStringLiteral( "000000"), Colorizer::Bold | Colorizer::Italic); const Colorizer AbstractNavigationContext::commentHighlight(QStringLiteral("303030")); const Colorizer AbstractNavigationContext::nameHighlight(QStringLiteral("000000"), Colorizer::Bold); } diff --git a/kdevplatform/shell/sourceformattercontroller.cpp b/kdevplatform/shell/sourceformattercontroller.cpp index 71fd07ded5..7e8226034c 100644 --- a/kdevplatform/shell/sourceformattercontroller.cpp +++ b/kdevplatform/shell/sourceformattercontroller.cpp @@ -1,830 +1,830 @@ /* This file is part of KDevelop Copyright 2009 Andreas Pakulat Copyright (C) 2008 Cédric Pasteur 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 "sourceformattercontroller.h" #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 #include #include #include "core.h" #include "debug.h" #include "plugincontroller.h" #include "sourceformatterjob.h" #include "textdocument.h" namespace { namespace Strings { QString SourceFormatter() { return QStringLiteral("SourceFormatter"); } QString UseDefault() { return QStringLiteral("UseDefault"); } } } namespace KDevelop { class SourceFormatterControllerPrivate { public: // cache of formatter plugins, to avoid querying plugincontroller QVector sourceFormatters; // GUI actions QAction* formatTextAction; QAction* formatFilesAction; QAction* formatLine; QList prjItems; QList urls; bool enabled = true; ISourceFormatter* formatterForConfigEntry(const QString& entry, const QString& mimename) const; }; ISourceFormatter* SourceFormatterControllerPrivate::formatterForConfigEntry(const QString& entry, const QString& mimename) const { QStringList formatterinfo = entry.split( QStringLiteral("||"), QString::SkipEmptyParts ); if( formatterinfo.size() != 2 ) { qCDebug(SHELL) << "Broken formatting entry for mime:" << mimename << "current value:" << entry; } auto it = std::find_if(sourceFormatters.begin(), sourceFormatters.end(), [&](ISourceFormatter* iformatter) { return (iformatter->name() == formatterinfo.first()); }); return (it != sourceFormatters.end()) ? *it : nullptr; } QString SourceFormatterController::kateModeLineConfigKey() { return QStringLiteral("ModelinesEnabled"); } QString SourceFormatterController::kateOverrideIndentationConfigKey() { return QStringLiteral("OverrideKateIndentation"); } QString SourceFormatterController::styleCaptionKey() { return QStringLiteral("Caption"); } QString SourceFormatterController::styleContentKey() { return QStringLiteral("Content"); } QString SourceFormatterController::styleMimeTypesKey() { return QStringLiteral("MimeTypes"); } QString SourceFormatterController::styleSampleKey() { return QStringLiteral("StyleSample"); } SourceFormatterController::SourceFormatterController(QObject *parent) : ISourceFormatterController(parent) , d_ptr(new SourceFormatterControllerPrivate) { Q_D(SourceFormatterController); setObjectName(QStringLiteral("SourceFormatterController")); setComponentName(QStringLiteral("kdevsourceformatter"), i18n("Source Formatter")); setXMLFile(QStringLiteral("kdevsourceformatter.rc")); if (Core::self()->setupFlags() & Core::NoUi) return; d->formatTextAction = actionCollection()->addAction(QStringLiteral("edit_reformat_source")); d->formatTextAction->setText(i18n("&Reformat Source")); d->formatTextAction->setToolTip(i18n("Reformat source using AStyle")); d->formatTextAction->setWhatsThis(i18n("Source reformatting functionality using astyle library.")); d->formatTextAction->setEnabled(false); connect(d->formatTextAction, &QAction::triggered, this, &SourceFormatterController::beautifySource); d->formatLine = actionCollection()->addAction(QStringLiteral("edit_reformat_line")); d->formatLine->setText(i18n("Reformat Line")); d->formatLine->setToolTip(i18n("Reformat current line using AStyle")); d->formatLine->setWhatsThis(i18n("Source reformatting of line under cursor using astyle library.")); d->formatLine->setEnabled(false); connect(d->formatLine, &QAction::triggered, this, &SourceFormatterController::beautifyLine); d->formatFilesAction = actionCollection()->addAction(QStringLiteral("tools_astyle")); d->formatFilesAction->setText(i18n("Reformat Files...")); d->formatFilesAction->setToolTip(i18n("Format file(s) using the current theme")); d->formatFilesAction->setWhatsThis(i18n("Formatting functionality using astyle library.")); d->formatFilesAction->setEnabled(false); connect(d->formatFilesAction, &QAction::triggered, this, QOverload<>::of(&SourceFormatterController::formatFiles)); connect(Core::self()->pluginController(), &IPluginController::pluginLoaded, this, &SourceFormatterController::pluginLoaded); connect(Core::self()->pluginController(), &IPluginController::unloadingPlugin, this, &SourceFormatterController::unloadingPlugin); // connect to both documentActivated & documentClosed, // otherwise we miss when the last document was closed connect(Core::self()->documentController(), &IDocumentController::documentActivated, this, &SourceFormatterController::updateFormatTextAction); connect(Core::self()->documentController(), &IDocumentController::documentClosed, this, &SourceFormatterController::updateFormatTextAction); qRegisterMetaType>(); connect(Core::self()->documentController(), &IDocumentController::documentLoaded, // Use a queued connection, because otherwise the view is not yet fully set up // but wrap the document in a smart pointer to guard against crashes when it // gets deleted in the meantime this, [this](IDocument *doc) { const auto textDoc = QPointer(dynamic_cast(doc)); QMetaObject::invokeMethod(this, "documentLoaded", Qt::QueuedConnection, Q_ARG(QPointer, textDoc)); }); connect(Core::self()->projectController(), &IProjectController::projectOpened, this, &SourceFormatterController::projectOpened); updateFormatTextAction(); } void SourceFormatterController::documentLoaded(const QPointer& doc) { // NOTE: explicitly check this here to prevent crashes on shutdown // when this slot gets called (note: delayed connection) // but the text document was already destroyed // there have been unit tests that failed due to that... if (!doc || !doc->textDocument()) { return; } const auto url = doc->url(); const auto mime = QMimeDatabase().mimeTypeForUrl(url); adaptEditorIndentationMode(doc->textDocument(), formatterForUrl(url, mime), url); } void SourceFormatterController::projectOpened(const IProject* project) { Q_D(SourceFormatterController); // Adapt the indentation mode if a project was just opened. Otherwise if a document // is loaded before its project, it might not have the correct indentation mode set. auto config = project->projectConfiguration()->group(Strings::SourceFormatter()); if (!config.isValid() || config.readEntry(Strings::UseDefault(), true)) { return; } QHash formatters; const auto documents = ICore::self()->documentController()->openDocuments(); for (const KDevelop::IDocument* doc : documents) { if (project->inProject(IndexedString(doc->url()))) { const QString mimename = QMimeDatabase().mimeTypeForUrl(doc->url()).name(); auto it = formatters.find(mimename); if (it == formatters.end()) { const auto entry = config.readEntry(mimename, QString()); it = formatters.insert(mimename, entry.isEmpty() ? nullptr : d->formatterForConfigEntry(entry, mimename)); } if (it.value()) { adaptEditorIndentationMode(doc->textDocument(), it.value(), doc->url()); } } } } void SourceFormatterController::pluginLoaded(IPlugin* plugin) { Q_D(SourceFormatterController); auto* sourceFormatter = plugin->extension(); if (!sourceFormatter) { return; } d->sourceFormatters << sourceFormatter; resetUi(); emit formatterLoaded(sourceFormatter); // with one plugin now added, hasFormatters turned to true, so report to listeners if (d->sourceFormatters.size() == 1) { emit hasFormattersChanged(true); } } void SourceFormatterController::unloadingPlugin(IPlugin* plugin) { Q_D(SourceFormatterController); auto* sourceFormatter = plugin->extension(); if (!sourceFormatter) { return; } const int idx = d->sourceFormatters.indexOf(sourceFormatter); Q_ASSERT(idx != -1); d->sourceFormatters.remove(idx); resetUi(); emit formatterUnloading(sourceFormatter); if (d->sourceFormatters.isEmpty()) { emit hasFormattersChanged(false); } } void SourceFormatterController::initialize() { } SourceFormatterController::~SourceFormatterController() { } ISourceFormatter* SourceFormatterController::formatterForUrl(const QUrl &url) { QMimeType mime = QMimeDatabase().mimeTypeForUrl(url); return formatterForUrl(url, mime); } KConfigGroup SourceFormatterController::configForUrl(const QUrl& url) const { auto core = KDevelop::Core::self(); auto project = core->projectController()->findProjectForUrl(url); if (project) { auto config = project->projectConfiguration()->group(Strings::SourceFormatter()); if (config.isValid() && !config.readEntry(Strings::UseDefault(), true)) { return config; } } return core->activeSession()->config()->group( Strings::SourceFormatter() ); } KConfigGroup SourceFormatterController::sessionConfig() const { return KDevelop::Core::self()->activeSession()->config()->group( Strings::SourceFormatter() ); } KConfigGroup SourceFormatterController::globalConfig() const { return KSharedConfig::openConfig()->group( Strings::SourceFormatter() ); } ISourceFormatter* SourceFormatterController::findFirstFormatterForMimeType(const QMimeType& mime ) const { Q_D(const SourceFormatterController); static QHash knownFormatters; const auto formatterIt = knownFormatters.constFind(mime.name()); if (formatterIt != knownFormatters.constEnd()) return *formatterIt; auto it = std::find_if(d->sourceFormatters.constBegin(), d->sourceFormatters.constEnd(), [&](ISourceFormatter* iformatter) { QSharedPointer formatter(createFormatterForPlugin(iformatter)); return (formatter->supportedMimeTypes().contains(mime.name())); }); ISourceFormatter* iformatter = (it != d->sourceFormatters.constEnd()) ? *it : nullptr; // cache result in any case knownFormatters.insert(mime.name(), iformatter); return iformatter; } static void populateStyleFromConfigGroup(SourceFormatterStyle* s, const KConfigGroup& stylegrp) { s->setCaption( stylegrp.readEntry( SourceFormatterController::styleCaptionKey(), QString() ) ); s->setContent( stylegrp.readEntry( SourceFormatterController::styleContentKey(), QString() ) ); s->setMimeTypes( stylegrp.readEntry( SourceFormatterController::styleMimeTypesKey(), QStringList() ) ); s->setOverrideSample( stylegrp.readEntry( SourceFormatterController::styleSampleKey(), QString() ) ); } SourceFormatter* SourceFormatterController::createFormatterForPlugin(ISourceFormatter *ifmt) const { auto* formatter = new SourceFormatter(); formatter->formatter = ifmt; // Inserted a new formatter. Now fill it with styles const auto predefinedStyles = ifmt->predefinedStyles(); for (const KDevelop::SourceFormatterStyle& style : predefinedStyles) { formatter->styles[ style.name() ] = new SourceFormatterStyle(style); } KConfigGroup grp = globalConfig(); if( grp.hasGroup( ifmt->name() ) ) { KConfigGroup fmtgrp = grp.group( ifmt->name() ); const auto subgroups = fmtgrp.groupList(); for (const QString& subgroup : subgroups) { auto* s = new SourceFormatterStyle( subgroup ); KConfigGroup stylegrp = fmtgrp.group( subgroup ); populateStyleFromConfigGroup(s, stylegrp); formatter->styles[ s->name() ] = s; } } return formatter; } ISourceFormatter* SourceFormatterController::formatterForUrl(const QUrl& url, const QMimeType& mime) { Q_D(SourceFormatterController); if (!d->enabled || !isMimeTypeSupported(mime)) { return nullptr; } const auto formatter = configForUrl(url).readEntry(mime.name(), QString()); if( formatter.isEmpty() ) { return findFirstFormatterForMimeType( mime ); } return d->formatterForConfigEntry(formatter, mime.name()); } bool SourceFormatterController::isMimeTypeSupported(const QMimeType& mime) { if( findFirstFormatterForMimeType( mime ) ) { return true; } return false; } QString SourceFormatterController::indentationMode(const QMimeType& mime) { if (mime.inherits(QStringLiteral("text/x-c++src")) || mime.inherits(QStringLiteral("text/x-chdr")) || mime.inherits(QStringLiteral("text/x-c++hdr")) || mime.inherits(QStringLiteral("text/x-csrc")) || mime.inherits(QStringLiteral("text/x-java")) || mime.inherits(QStringLiteral("text/x-csharp"))) { return QStringLiteral("cstyle"); } return QStringLiteral("none"); } QString SourceFormatterController::addModelineForCurrentLang(QString input, const QUrl& url, const QMimeType& mime) { if( !isMimeTypeSupported(mime) ) return input; QRegExp kateModelineWithNewline(QStringLiteral("\\s*\\n//\\s*kate:(.*)$")); // If there already is a modeline in the document, adapt it while formatting, even // if "add modeline" is disabled. if (!configForUrl(url).readEntry(SourceFormatterController::kateModeLineConfigKey(), false) && kateModelineWithNewline.indexIn( input ) == -1 ) return input; ISourceFormatter* fmt = formatterForUrl(url, mime); ISourceFormatter::Indentation indentation = fmt->indentation(url); if( !indentation.isValid() ) return input; QString output; QTextStream os(&output, QIODevice::WriteOnly); QTextStream is(&input, QIODevice::ReadOnly); Q_ASSERT(fmt); QString modeline(QStringLiteral("// kate: ") + QLatin1String("indent-mode ") + indentationMode(mime) + QLatin1String("; ")); if(indentation.indentWidth) // We know something about indentation-width modeline.append(QStringLiteral("indent-width %1; ").arg(indentation.indentWidth)); if(indentation.indentationTabWidth != 0) // We know something about tab-usage { const auto state = (indentation.indentationTabWidth == -1) ? QLatin1String("on") : QLatin1String("off"); modeline += QLatin1String("replace-tabs ") + state + QLatin1String("; "); if(indentation.indentationTabWidth > 0) modeline.append(QStringLiteral("tab-width %1; ").arg(indentation.indentationTabWidth)); } - qCDebug(SHELL) << "created modeline: " << modeline << endl; + qCDebug(SHELL) << "created modeline: " << modeline; QRegExp kateModeline(QStringLiteral("^\\s*//\\s*kate:(.*)$")); bool modelinefound = false; QRegExp knownOptions(QStringLiteral("\\s*(indent-width|space-indent|tab-width|indent-mode|replace-tabs)")); while (!is.atEnd()) { QString line = is.readLine(); // replace only the options we care about if (kateModeline.indexIn(line) >= 0) { // match - qCDebug(SHELL) << "Found a kate modeline: " << line << endl; + qCDebug(SHELL) << "Found a kate modeline: " << line; modelinefound = true; QString options = kateModeline.cap(1); const QStringList optionList = options.split(QLatin1Char(';'), QString::SkipEmptyParts); os << modeline; for (QString s : optionList) { if (knownOptions.indexIn(s) < 0) { // unknown option, add it if(s.startsWith(QLatin1Char(' '))) s.remove(0, 1); os << s << ";"; - qCDebug(SHELL) << "Found unknown option: " << s << endl; + qCDebug(SHELL) << "Found unknown option: " << s; } } os << endl; } else os << line << endl; } if (!modelinefound) os << modeline << endl; return output; } void SourceFormatterController::cleanup() { } void SourceFormatterController::updateFormatTextAction() { Q_D(SourceFormatterController); bool enabled = false; if (!d->sourceFormatters.isEmpty()) { IDocument* doc = KDevelop::ICore::self()->documentController()->activeDocument(); if (doc) { QMimeType mime = QMimeDatabase().mimeTypeForUrl(doc->url()); if (isMimeTypeSupported(mime)) enabled = true; } } d->formatLine->setEnabled(enabled); d->formatTextAction->setEnabled(enabled); } void SourceFormatterController::beautifySource() { IDocument* idoc = KDevelop::ICore::self()->documentController()->activeDocument(); if (!idoc) return; KTextEditor::View* view = idoc->activeTextView(); if (!view) return; KTextEditor::Document* doc = view->document(); // load the appropriate formatter const auto url = idoc->url(); const auto mime = QMimeDatabase().mimeTypeForUrl(url); ISourceFormatter* formatter = formatterForUrl(url, mime); if( !formatter ) { qCDebug(SHELL) << "no formatter available for" << mime.name(); return; } // Ignore the modeline, as the modeline will be changed anyway adaptEditorIndentationMode(doc, formatter, url, true); bool has_selection = view->selection(); if (has_selection) { QString original = view->selectionText(); QString output = formatter->formatSource(view->selectionText(), url, mime, doc->text(KTextEditor::Range(KTextEditor::Cursor(0,0),view->selectionRange().start())), doc->text(KTextEditor::Range(view->selectionRange().end(), doc->documentRange().end()))); //remove the final newline character, unless it should be there if (!original.endsWith(QLatin1Char('\n')) && output.endsWith(QLatin1Char('\n'))) output.resize(output.length() - 1); //there was a selection, so only change the part of the text related to it // We don't use KTextEditor::Document directly, because CodeRepresentation transparently works // around a possible tab-replacement incompatibility between kate and kdevelop DynamicCodeRepresentation::Ptr code( dynamic_cast( KDevelop::createCodeRepresentation( IndexedString( doc->url() ) ).data() ) ); Q_ASSERT( code ); code->replace( view->selectionRange(), original, output ); } else { formatDocument(idoc, formatter, mime); } } void SourceFormatterController::beautifyLine() { KDevelop::IDocumentController *docController = KDevelop::ICore::self()->documentController(); KDevelop::IDocument *doc = docController->activeDocument(); if (!doc || !doc->isTextDocument()) return; KTextEditor::Document *tDoc = doc->textDocument(); KTextEditor::View* view = doc->activeTextView(); if (!view) return; // load the appropriate formatter const auto url = doc->url(); const auto mime = QMimeDatabase().mimeTypeForUrl(url); ISourceFormatter* formatter = formatterForUrl(url, mime); if( !formatter ) { qCDebug(SHELL) << "no formatter available for" << mime.name(); return; } const KTextEditor::Cursor cursor = view->cursorPosition(); const QString line = tDoc->line(cursor.line()); const QString prev = tDoc->text(KTextEditor::Range(0, 0, cursor.line(), 0)); const QString post = QLatin1Char('\n') + tDoc->text(KTextEditor::Range(KTextEditor::Cursor(cursor.line() + 1, 0), tDoc->documentEnd())); const QString formatted = formatter->formatSource(line, doc->url(), mime, prev, post); // We don't use KTextEditor::Document directly, because CodeRepresentation transparently works // around a possible tab-replacement incompatibility between kate and kdevelop DynamicCodeRepresentation::Ptr code(dynamic_cast( KDevelop::createCodeRepresentation( IndexedString( doc->url() ) ).data() ) ); Q_ASSERT( code ); code->replace( KTextEditor::Range(cursor.line(), 0, cursor.line(), line.length()), line, formatted ); // advance cursor one line view->setCursorPosition(KTextEditor::Cursor(cursor.line() + 1, 0)); } void SourceFormatterController::formatDocument(KDevelop::IDocument* doc, ISourceFormatter* formatter, const QMimeType& mime) { Q_ASSERT(doc); Q_ASSERT(formatter); qCDebug(SHELL) << "Running" << formatter->name() << "on" << doc->url(); // We don't use KTextEditor::Document directly, because CodeRepresentation transparently works // around a possible tab-replacement incompatibility between kate and kdevelop CodeRepresentation::Ptr code = KDevelop::createCodeRepresentation( IndexedString( doc->url() ) ); KTextEditor::Cursor cursor = doc->cursorPosition(); QString text = formatter->formatSource(code->text(), doc->url(), mime); text = addModelineForCurrentLang(text, doc->url(), mime); code->setText(text); doc->setCursorPosition(cursor); } void SourceFormatterController::settingsChanged() { const auto documents = ICore::self()->documentController()->openDocuments(); for (KDevelop::IDocument* doc : documents) { adaptEditorIndentationMode(doc->textDocument(), formatterForUrl(doc->url()), doc->url()); } } /** * Kate commands: * Use spaces for indentation: * "set-replace-tabs 1" * Use tabs for indentation (eventually mixed): * "set-replace-tabs 0" * Indent width: * "set-indent-width X" * Tab width: * "set-tab-width X" * */ void SourceFormatterController::adaptEditorIndentationMode(KTextEditor::Document *doc, ISourceFormatter *formatter, const QUrl& url, bool ignoreModeline) { if (!formatter || !configForUrl(url).readEntry(SourceFormatterController::kateOverrideIndentationConfigKey(), false) || !doc) return; qCDebug(SHELL) << "adapting mode for" << url; QRegExp kateModelineWithNewline(QStringLiteral("\\s*\\n//\\s*kate:(.*)$")); // modelines should always take precedence if( !ignoreModeline && kateModelineWithNewline.indexIn( doc->text() ) != -1 ) { qCDebug(SHELL) << "ignoring because a kate modeline was found"; return; } ISourceFormatter::Indentation indentation = formatter->indentation(url); if(indentation.isValid()) { struct CommandCaller { explicit CommandCaller(KTextEditor::Document* _doc) : doc(_doc), editor(KTextEditor::Editor::instance()) { Q_ASSERT(editor); } void operator()(const QString& cmd) { KTextEditor::Command* command = editor->queryCommand( cmd ); Q_ASSERT(command); QString msg; qCDebug(SHELL) << "calling" << cmd; const auto views = doc->views(); for (KTextEditor::View* view : views) { if (!command->exec(view, cmd, msg)) qCWarning(SHELL) << "setting indentation width failed: " << msg; } } KTextEditor::Document* doc; KTextEditor::Editor* editor; } call(doc); if( indentation.indentWidth ) // We know something about indentation-width call( QStringLiteral("set-indent-width %1").arg(indentation.indentWidth ) ); if( indentation.indentationTabWidth != 0 ) // We know something about tab-usage { call( QStringLiteral("set-replace-tabs %1").arg( (indentation.indentationTabWidth == -1) ? 1 : 0 ) ); if( indentation.indentationTabWidth > 0 ) call( QStringLiteral("set-tab-width %1").arg(indentation.indentationTabWidth ) ); } }else{ qCDebug(SHELL) << "found no valid indentation"; } } void SourceFormatterController::formatFiles() { Q_D(SourceFormatterController); if (d->prjItems.isEmpty() && d->urls.isEmpty()) return; //get a list of all files in this folder recursively QList folders; for (KDevelop::ProjectBaseItem* item : qAsConst(d->prjItems)) { if (!item) continue; if (item->folder()) folders.append(item->folder()); else if (item->file()) d->urls.append(item->file()->path().toUrl()); else if (item->target()) { const auto files = item->fileList(); for (KDevelop::ProjectFileItem* f : files) { d->urls.append(f->path().toUrl()); } } } while (!folders.isEmpty()) { KDevelop::ProjectFolderItem *item = folders.takeFirst(); const auto folderList = item->folderList(); for (KDevelop::ProjectFolderItem* f : folderList) { folders.append(f); } const auto targets = item->targetList(); for (KDevelop::ProjectTargetItem* f : targets) { const auto childs = f->fileList(); for (KDevelop::ProjectFileItem* child : childs) { d->urls.append(child->path().toUrl()); } } const auto files = item->fileList(); for (KDevelop::ProjectFileItem* f : files) { d->urls.append(f->path().toUrl()); } } auto win = ICore::self()->uiController()->activeMainWindow()->window(); QMessageBox msgBox(QMessageBox::Question, i18n("Reformat files?"), i18n("Reformat all files in the selected folder?"), QMessageBox::Ok|QMessageBox::Cancel, win); msgBox.setDefaultButton(QMessageBox::Cancel); auto okButton = msgBox.button(QMessageBox::Ok); okButton->setText(i18n("Reformat")); msgBox.exec(); if (msgBox.clickedButton() == okButton) { auto formatterJob = new SourceFormatterJob(this); formatterJob->setFiles(d->urls); ICore::self()->runController()->registerJob(formatterJob); } } KDevelop::ContextMenuExtension SourceFormatterController::contextMenuExtension(KDevelop::Context* context, QWidget* parent) { Q_D(SourceFormatterController); Q_UNUSED(parent); KDevelop::ContextMenuExtension ext; d->urls.clear(); d->prjItems.clear(); if (d->sourceFormatters.isEmpty()) { return ext; } if (context->hasType(KDevelop::Context::EditorContext)) { if (d->formatTextAction->isEnabled()) ext.addAction(KDevelop::ContextMenuExtension::EditGroup, d->formatTextAction); } else if (context->hasType(KDevelop::Context::FileContext)) { auto* filectx = static_cast(context); d->urls = filectx->urls(); ext.addAction(KDevelop::ContextMenuExtension::EditGroup, d->formatFilesAction); } else if (context->hasType(KDevelop::Context::CodeContext)) { } else if (context->hasType(KDevelop::Context::ProjectItemContext)) { auto* prjctx = static_cast(context); d->prjItems = prjctx->items(); if (!d->prjItems.isEmpty()) { ext.addAction(KDevelop::ContextMenuExtension::ExtensionGroup, d->formatFilesAction); } } return ext; } SourceFormatterStyle SourceFormatterController::styleForUrl(const QUrl& url, const QMimeType& mime) { const auto formatter = configForUrl(url).readEntry(mime.name(), QString()).split(QStringLiteral("||"), QString::SkipEmptyParts); if( formatter.count() == 2 ) { SourceFormatterStyle s( formatter.at( 1 ) ); KConfigGroup fmtgrp = globalConfig().group( formatter.at(0) ); if( fmtgrp.hasGroup( formatter.at(1) ) ) { KConfigGroup stylegrp = fmtgrp.group( formatter.at(1) ); populateStyleFromConfigGroup(&s, stylegrp); } return s; } return SourceFormatterStyle(); } void SourceFormatterController::disableSourceFormatting(bool disable) { Q_D(SourceFormatterController); d->enabled = !disable; } bool SourceFormatterController::sourceFormattingEnabled() { Q_D(SourceFormatterController); return d->enabled; } bool SourceFormatterController::hasFormatters() const { Q_D(const SourceFormatterController); return !d->sourceFormatters.isEmpty(); } QVector SourceFormatterController::formatters() const { Q_D(const SourceFormatterController); return d->sourceFormatters; } void SourceFormatterController::resetUi() { Q_D(SourceFormatterController); d->formatFilesAction->setEnabled(!d->sourceFormatters.isEmpty()); updateFormatTextAction(); } } diff --git a/kdevplatform/shell/sourceformatterjob.cpp b/kdevplatform/shell/sourceformatterjob.cpp index a5dd06beb1..17059036c8 100644 --- a/kdevplatform/shell/sourceformatterjob.cpp +++ b/kdevplatform/shell/sourceformatterjob.cpp @@ -1,153 +1,153 @@ /* * This file is part of KDevelop * * Copyright (C) 2008 Cédric Pasteur * Copyright 2009 Andreas Pakulat * Copyright 2017 Friedrich W. H. Kossebau * * This program 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 program 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 General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "sourceformatterjob.h" #include "sourceformattercontroller.h" #include #include #include #include #include #include #include #include #include #include using namespace KDevelop; SourceFormatterJob::SourceFormatterJob(SourceFormatterController* sourceFormatterController) : KJob(sourceFormatterController) , m_sourceFormatterController(sourceFormatterController) , m_workState(WorkIdle) , m_fileIndex(0) { setCapabilities(Killable); // set name for job listing setObjectName(i18n("Reformatting")); KDevelop::ICore::self()->uiController()->registerStatus(this); connect(this, &SourceFormatterJob::finished, this, [this]() { emit hideProgress(this); }); } QString SourceFormatterJob::statusName() const { return i18n("Reformat Files"); } void SourceFormatterJob::doWork() { // TODO: consider to use ExecuteCompositeJob, with every file a separate subjob switch (m_workState) { case WorkIdle: m_workState = WorkFormat; m_fileIndex = 0; emit showProgress(this, 0, 0, 0); emit showMessage(this, i18np("Reformatting one file", "Reformatting %1 files", m_fileList.length())); QMetaObject::invokeMethod(this, "doWork", Qt::QueuedConnection); break; case WorkFormat: if (m_fileIndex < m_fileList.length()) { emit showProgress(this, 0, m_fileList.length(), m_fileIndex); formatFile(m_fileList[m_fileIndex]); // trigger formatting of next file ++m_fileIndex; QMetaObject::invokeMethod(this, "doWork", Qt::QueuedConnection); } else { m_workState = WorkIdle; emitResult(); } break; case WorkCancelled: break; } } void SourceFormatterJob::start() { if (m_workState != WorkIdle) return; m_workState = WorkIdle; QMetaObject::invokeMethod(this, "doWork", Qt::QueuedConnection); } bool SourceFormatterJob::doKill() { m_workState = WorkCancelled; return true; } void SourceFormatterJob::setFiles(const QList& fileList) { m_fileList = fileList; } void SourceFormatterJob::formatFile(const QUrl& url) { // check mimetype QMimeType mime = QMimeDatabase().mimeTypeForUrl(url); - qCDebug(SHELL) << "Checking file " << url << " of mime type " << mime.name() << endl; + qCDebug(SHELL) << "Checking file " << url << " of mime type " << mime.name(); auto formatter = m_sourceFormatterController->formatterForUrl(url, mime); if (!formatter) // unsupported mime type return; // if the file is opened in the editor, format the text in the editor without saving it auto doc = ICore::self()->documentController()->documentForUrl(url); if (doc) { - qCDebug(SHELL) << "Processing file " << url << "opened in editor" << endl; + qCDebug(SHELL) << "Processing file " << url << "opened in editor"; m_sourceFormatterController->formatDocument(doc, formatter, mime); return; } - qCDebug(SHELL) << "Processing file " << url << endl; + qCDebug(SHELL) << "Processing file " << url; auto getJob = KIO::storedGet(url); // TODO: make also async and use start() and integrate using setError and setErrorString. if (getJob->exec()) { // TODO: really fromLocal8Bit/toLocal8Bit? no encoding detection? added in b8062f736a2bf2eec098af531a7fda6ebcdc7cde QString text = QString::fromLocal8Bit(getJob->data()); text = formatter->formatSource(text, url, mime); text = m_sourceFormatterController->addModelineForCurrentLang(text, url, mime); auto putJob = KIO::storedPut(text.toLocal8Bit(), url, -1, KIO::Overwrite); // see getJob if (!putJob->exec()) // TODO: integrate with job error reporting, use showErrorMessage? KMessageBox::error(nullptr, putJob->errorString()); } else KMessageBox::error(nullptr, getJob->errorString()); } diff --git a/plugins/astyle/astyle_formatter.cpp b/plugins/astyle/astyle_formatter.cpp index a75c1aea8d..4a8f2cfa91 100644 --- a/plugins/astyle/astyle_formatter.cpp +++ b/plugins/astyle/astyle_formatter.cpp @@ -1,540 +1,540 @@ /* This file is part of KDevelop * Copyright (C) 2008 Cédric Pasteur Copyright (C) 2001 Matthias Hölzer-Klüpfel This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "astyle_formatter.h" #include #include #include #include "astyle_stringiterator.h" #include "debug.h" using namespace KDevelop; AStyleFormatter::AStyleFormatter() : ASFormatter() { } QString AStyleFormatter::formatSource(const QString &text, const QString& leftContext, const QString& rightContext) { QString useText = leftContext + text + rightContext; AStyleStringIterator is(useText); QString output; QTextStream os(&output, QIODevice::WriteOnly); init(&is); while(hasMoreLines()) os << QString::fromUtf8(nextLine().c_str()) << endl; init(nullptr); return extractFormattedTextFromContext(output, text, leftContext, rightContext, m_options[QStringLiteral("FillCount")].toInt()); } void AStyleFormatter::setOption(const QString &key, const QVariant &value) { m_options[key] = value; } void AStyleFormatter::updateFormatter() { - qCDebug(KDEV_ASTYLE) << "Updating option with: " << ISourceFormatter::optionMapToString(m_options) << endl; + qCDebug(KDEV_ASTYLE) << "Updating option with: " << ISourceFormatter::optionMapToString(m_options); // fill int wsCount = m_options[QStringLiteral("FillCount")].toInt(); if(m_options[QStringLiteral("Fill")].toString() == QLatin1String("Tabs")) { ///TODO: rename FillForce somehow... bool force = m_options[QStringLiteral("FillForce")].toBool(); AStyleFormatter::setTabSpaceConversionMode(false); AStyleFormatter::setTabIndentation(wsCount, force ); m_indentString = QStringLiteral("\t"); } else { AStyleFormatter::setSpaceIndentation(wsCount); m_indentString.fill(QLatin1Char(' '), wsCount); AStyleFormatter::setTabSpaceConversionMode(m_options[QStringLiteral("FillForce")].toBool()); } AStyleFormatter::setEmptyLineFill(m_options[QStringLiteral("Fill_EmptyLines")].toBool()); // indent AStyleFormatter::setSwitchIndent(m_options[QStringLiteral("IndentSwitches")].toBool()); AStyleFormatter::setClassIndent(m_options[QStringLiteral("IndentClasses")].toBool()); AStyleFormatter::setCaseIndent(m_options[QStringLiteral("IndentCases")].toBool()); AStyleFormatter::setBracketIndent(m_options[QStringLiteral("IndentBrackets")].toBool()); AStyleFormatter::setNamespaceIndent(m_options[QStringLiteral("IndentNamespaces")].toBool()); AStyleFormatter::setLabelIndent(m_options[QStringLiteral("IndentLabels")].toBool()); AStyleFormatter::setBlockIndent(m_options[QStringLiteral("IndentBlocks")].toBool()); AStyleFormatter::setPreprocessorIndent(m_options[QStringLiteral("IndentPreprocessors")].toBool()); AStyleFormatter::setAfterParens(m_options[QStringLiteral("AfterParens")].toBool()); AStyleFormatter::setContinuation(m_options[QStringLiteral("Continuation")].toInt()); // continuation AStyleFormatter::setMaxInStatementIndentLength(m_options[QStringLiteral("MaxStatement")].toInt()); if(m_options[QStringLiteral("MinConditional")].toInt() != -1) AStyleFormatter::setMinConditionalIndentLength(m_options[QStringLiteral("MinConditional")].toInt()); // brackets QString s = m_options[QStringLiteral("Brackets")].toString(); if(s == QLatin1String("Break")) AStyleFormatter::setBracketFormatMode(astyle::BREAK_MODE); else if(s == QLatin1String("Attach")) AStyleFormatter::setBracketFormatMode(astyle::ATTACH_MODE); else if(s == QLatin1String("Linux")) AStyleFormatter::setBracketFormatMode(astyle::LINUX_MODE); else if(s == QLatin1String("Stroustrup")) // In astyle 2.06 BracketMode STROUSTRUP_MODE was removed and LINUX_MODE is the replacement AStyleFormatter::setBracketFormatMode(astyle::LINUX_MODE); else if(s == QLatin1String("Horstmann") || s == QLatin1String("RunInMode")) AStyleFormatter::setBracketFormatMode(astyle::RUN_IN_MODE); else AStyleFormatter::setBracketFormatMode(astyle::NONE_MODE); AStyleFormatter::setBreakClosingHeaderBracketsMode(m_options[QStringLiteral("BracketsCloseHeaders")].toBool()); // blocks AStyleFormatter::setBreakBlocksMode(m_options[QStringLiteral("BlockBreak")].toBool()); AStyleFormatter::setBreakClosingHeaderBlocksMode(m_options[QStringLiteral("BlockBreakAll")].toBool()); AStyleFormatter::setBreakElseIfsMode(m_options[QStringLiteral("BlockIfElse")].toBool()); // padding AStyleFormatter::setOperatorPaddingMode(m_options[QStringLiteral("PadOperators")].toBool()); AStyleFormatter::setParensInsidePaddingMode(m_options[QStringLiteral("PadParenthesesIn")].toBool()); AStyleFormatter::setParensOutsidePaddingMode(m_options[QStringLiteral("PadParenthesesOut")].toBool()); AStyleFormatter::setParensHeaderPaddingMode(m_options[QStringLiteral("PadParenthesesHeader")].toBool()); AStyleFormatter::setParensUnPaddingMode(m_options[QStringLiteral("PadParenthesesUn")].toBool()); // oneliner AStyleFormatter::setBreakOneLineBlocksMode(!m_options[QStringLiteral("KeepBlocks")].toBool()); AStyleFormatter::setBreakOneLineStatementsMode(!m_options[QStringLiteral("KeepStatements")].toBool()); // pointer s = m_options[QStringLiteral("PointerAlign")].toString(); if(s == QLatin1String("Name")) AStyleFormatter::setPointerAlignment(astyle::PTR_ALIGN_NAME); else if(s == QLatin1String("Middle")) AStyleFormatter::setPointerAlignment(astyle::PTR_ALIGN_MIDDLE); else if(s == QLatin1String("Type")) AStyleFormatter::setPointerAlignment(astyle::PTR_ALIGN_TYPE); else AStyleFormatter::setPointerAlignment(astyle::PTR_ALIGN_NONE); } void AStyleFormatter::resetStyle() { setSpaceIndentation(4); setBracketFormatMode(astyle::NONE_MODE); setBreakOneLineBlocksMode(true); setBreakOneLineStatementsMode(true); // blocks setBreakBlocksMode(false); setBreakClosingHeaderBlocksMode(false); setBreakElseIfsMode(false); setBreakClosingHeaderBracketsMode(false); //indent setTabIndentation(4, false); setEmptyLineFill(false); setMaxInStatementIndentLength(40); setMinConditionalIndentLength(-1); setSwitchIndent(true); setClassIndent(true); setCaseIndent(false); setBracketIndent(false); setNamespaceIndent(true); setLabelIndent(true); setBlockIndent(false); setPreprocessorIndent(false); setAfterParens(false); setContinuation(1); //padding setOperatorPaddingMode(false); setParensInsidePaddingMode(true); setParensOutsidePaddingMode(true); setParensHeaderPaddingMode(true); setParensUnPaddingMode(true); } bool AStyleFormatter::predefinedStyle( const QString & style ) { if(style == QLatin1String("ANSI")) { resetStyle(); setBracketIndent(false); setSpaceIndentation(4); setBracketFormatMode(astyle::BREAK_MODE); setClassIndent(false); setSwitchIndent(false); setNamespaceIndent(false); return true; } else if(style == QLatin1String("K&R")) { resetStyle(); setBracketIndent(false); setSpaceIndentation(4); setBracketFormatMode(astyle::ATTACH_MODE); setClassIndent(false); setSwitchIndent(false); setNamespaceIndent(false); return true; } else if(style == QLatin1String("Linux")) { resetStyle(); setBracketIndent(false); setSpaceIndentation(8); setBracketFormatMode(astyle::LINUX_MODE); setClassIndent(false); setSwitchIndent(false); setNamespaceIndent(false); return true; } else if(style == QLatin1String("GNU")) { resetStyle(); setBlockIndent(true); setSpaceIndentation(2); setBracketFormatMode(astyle::BREAK_MODE); setClassIndent(false); setSwitchIndent(false); setNamespaceIndent(false); return true; } else if(style == QLatin1String("Java")) { resetStyle(); setJavaStyle(); setBracketIndent(false); setSpaceIndentation(4); setBracketFormatMode(astyle::ATTACH_MODE); setSwitchIndent(false); return true; } else if (style == QLatin1String("Stroustrup")) { resetStyle(); setBracketFormatMode(astyle::LINUX_MODE); setBlockIndent(false); setBracketIndent(false); setSpaceIndentation(5); setClassIndent(false); setSwitchIndent(false); setNamespaceIndent(false); return true; } else if (style == QLatin1String("Horstmann")) { resetStyle(); setBracketFormatMode(astyle::RUN_IN_MODE); setBlockIndent(false); setBracketIndent(false); setSwitchIndent(true); setSpaceIndentation(3); setClassIndent(false); setNamespaceIndent(false); return true; } else if (style == QLatin1String("Whitesmith")) { resetStyle(); setSpaceIndentation(4); setBracketFormatMode(astyle::BREAK_MODE); setBlockIndent(false); setBracketIndent(true); setClassIndent(true); setSwitchIndent(true); setNamespaceIndent(false); return true; } else if (style == QLatin1String("Banner")) { resetStyle(); setSpaceIndentation(4); setBracketFormatMode(astyle::ATTACH_MODE); setBlockIndent(false); setBracketIndent(true); setClassIndent(true); setSwitchIndent(true); setNamespaceIndent(false); return true; } else if (style == QLatin1String("1TBS")) { resetStyle(); setSpaceIndentation(4); setBracketFormatMode(astyle::LINUX_MODE); setBlockIndent(false); setBracketIndent(false); setAddBracketsMode(true); setClassIndent(false); setSwitchIndent(false); setNamespaceIndent(false); return true; } else if (style == QLatin1String("KDELibs")) { // https://community.kde.org/Policies/Kdelibs_Coding_Style resetStyle(); setSpaceIndentation(4); setBracketFormatMode(astyle::LINUX_MODE); setPointerAlignment(astyle::PTR_ALIGN_TYPE); setLabelIndent(true); setOperatorPaddingMode(true); setParensInsidePaddingMode(false); setParensOutsidePaddingMode(false); setParensHeaderPaddingMode(true); setParensUnPaddingMode(true); setBreakOneLineStatementsMode(false); setTabSpaceConversionMode(true); setPreprocessorIndent(true); setSwitchIndent(false); setClassIndent(false); setNamespaceIndent(false); return true; } else if (style == QLatin1String("Qt")) { // https://wiki.qt.io/Qt_Coding_Style resetStyle(); setPointerAlignment(astyle::PTR_ALIGN_NAME); setOperatorPaddingMode(true); setBracketFormatMode(astyle::LINUX_MODE); setSwitchIndent(false); setParensInsidePaddingMode(false); setParensOutsidePaddingMode(false); setParensHeaderPaddingMode(true); setParensUnPaddingMode(true); setSpaceIndentation(4); setClassIndent(false); setNamespaceIndent(false); return true; } return false; } QVariant AStyleFormatter::option(const QString &key) { if(!m_options.contains(key)) - qCDebug(KDEV_ASTYLE) << "Missing option name " << key << endl; + qCDebug(KDEV_ASTYLE) << "Missing option name " << key; return m_options[key]; } QString AStyleFormatter::indentString() { return QString::fromUtf8(getIndentString().c_str()); } void AStyleFormatter::loadStyle(const QString &content) { m_options = ISourceFormatter::stringToOptionMap(content); updateFormatter(); } QString AStyleFormatter::saveStyle() { return ISourceFormatter::optionMapToString(m_options); } void AStyleFormatter::setTabIndentation(int length, bool forceTabs) { ASFormatter::setTabIndentation(length, forceTabs); m_options[QStringLiteral("Fill")] = QStringLiteral("Tabs"); m_options[QStringLiteral("FillForce")] = forceTabs; m_options[QStringLiteral("FillCount")] = length; } void AStyleFormatter::setSpaceIndentation(int length) { ASFormatter::setSpaceIndentation(length); m_options[QStringLiteral("Fill")] = QStringLiteral("Spaces"); m_options[QStringLiteral("FillCount")] = length; } void AStyleFormatter::setTabSpaceConversionMode(bool mode) { m_options[QStringLiteral("FillForce")] = mode; ASFormatter::setTabSpaceConversionMode(mode); } void AStyleFormatter::setFillEmptyLines(bool on) { m_options[QStringLiteral("FillEmptyLines")] = on; ASFormatter::setEmptyLineFill(on); } void AStyleFormatter::setBlockIndent(bool on) { m_options[QStringLiteral("IndentBlocks")] = on; ASFormatter::setBlockIndent(on); } void AStyleFormatter::setBracketIndent(bool on) { m_options[QStringLiteral("IndentBrackets")] = on; ASFormatter::setBraceIndent(on); } void AStyleFormatter::setCaseIndent(bool on) { m_options[QStringLiteral("IndentCases")] = on; ASFormatter::setCaseIndent(on); } void AStyleFormatter::setClassIndent(bool on) { m_options[QStringLiteral("IndentClasses")] = on; ASFormatter::setClassIndent(on); } void AStyleFormatter::setLabelIndent(bool on) { m_options[QStringLiteral("IndentLabels")] = on; ASFormatter::setLabelIndent(on); } void AStyleFormatter::setNamespaceIndent(bool on) { m_options[QStringLiteral("IndentNamespaces")] = on; ASFormatter::setNamespaceIndent(on); } void AStyleFormatter::setPreprocessorIndent(bool on) { m_options[QStringLiteral("IndentPreprocessors")] = on; ASFormatter::setPreprocDefineIndent(on); } void AStyleFormatter::setSwitchIndent(bool on) { m_options[QStringLiteral("IndentSwitches")] = on; ASFormatter::setSwitchIndent(on); } void AStyleFormatter::setMaxInStatementIndentLength(int max) { m_options[QStringLiteral("MaxStatement")] = max; ASFormatter::setMaxInStatementIndentLength(max); } void AStyleFormatter::setMinConditionalIndentLength(int min) { m_options[QStringLiteral("MinConditional")] = min; ASFormatter::setMinConditionalIndentOption(min); ASFormatter::setMinConditionalIndentLength(); } void AStyleFormatter::setAfterParens(bool on) { m_options[QStringLiteral("AfterParens")] = on; ASFormatter::setAfterParenIndent(on); } void AStyleFormatter::setContinuation(int n) { m_options[QStringLiteral("Continuation")] = n; ASFormatter::setContinuationIndentation(n); } void AStyleFormatter::setBracketFormatMode(astyle::BraceMode mode) { switch (mode) { case astyle::NONE_MODE: m_options[QStringLiteral("Brackets")] = QString(); break; case astyle::ATTACH_MODE: m_options[QStringLiteral("Brackets")] = QStringLiteral("Attach"); break; case astyle::BREAK_MODE: m_options[QStringLiteral("Brackets")] = QStringLiteral("Break"); break; case astyle::LINUX_MODE: m_options[QStringLiteral("Brackets")] = QStringLiteral("Linux"); break; case astyle::RUN_IN_MODE: m_options[QStringLiteral("Brackets")] = QStringLiteral("RunInMode"); break; } ASFormatter::setBraceFormatMode(mode); } void AStyleFormatter::setBreakClosingHeaderBracketsMode(bool state) { m_options[QStringLiteral("BracketsCloseHeaders")] = state; ASFormatter::setBreakClosingHeaderBracketsMode(state); } void AStyleFormatter::setBreakBlocksMode(bool state) { m_options[QStringLiteral("BlockBreak")] = state; ASFormatter::setBreakBlocksMode(state); } void AStyleFormatter::setBreakElseIfsMode(bool state) { m_options[QStringLiteral("BlockIfElse")] = state; ASFormatter::setBreakElseIfsMode(state); } void AStyleFormatter::setBreakClosingHeaderBlocksMode(bool state) { m_options[QStringLiteral("BlockBreakAll")] = state; ASFormatter::setBreakClosingHeaderBlocksMode(state); } void AStyleFormatter::setOperatorPaddingMode(bool mode) { m_options[QStringLiteral("PadOperators")] = mode; ASFormatter::setOperatorPaddingMode(mode); } void AStyleFormatter::setParensOutsidePaddingMode(bool mode) { m_options[QStringLiteral("PadParenthesesOut")] = mode; ASFormatter::setParensOutsidePaddingMode(mode); } void AStyleFormatter::setParensInsidePaddingMode(bool mode) { m_options[QStringLiteral("PadParenthesesIn")] = mode; ASFormatter::setParensInsidePaddingMode(mode); } void AStyleFormatter::setParensHeaderPaddingMode(bool mode) { m_options[QStringLiteral("PadParenthesesHeader")] = mode; ASFormatter::setParensHeaderPaddingMode(mode); } void AStyleFormatter::setParensUnPaddingMode(bool state) { m_options[QStringLiteral("PadParenthesesUn")] = state; ASFormatter::setParensUnPaddingMode(state); } void AStyleFormatter::setBreakOneLineBlocksMode(bool state) { m_options[QStringLiteral("KeepBlocks")] = !state; ASFormatter::setBreakOneLineBlocksMode(state); } void AStyleFormatter::setBreakOneLineStatementsMode(bool state) { m_options[QStringLiteral("KeepStatements")] = !state; ASFormatter::setBreakOneLineStatementsMode(state); } void AStyleFormatter::setPointerAlignment(astyle::PointerAlign alignment) { switch (alignment) { case astyle::PTR_ALIGN_NONE: m_options[QStringLiteral("PointerAlign")] = QStringLiteral("None"); break; case astyle::PTR_ALIGN_NAME: m_options[QStringLiteral("PointerAlign")] = QStringLiteral("Name"); break; case astyle::PTR_ALIGN_MIDDLE: m_options[QStringLiteral("PointerAlign")] = QStringLiteral("Middle"); break; case astyle::PTR_ALIGN_TYPE: m_options[QStringLiteral("PointerAlign")] = QStringLiteral("Type"); break; } ASFormatter::setPointerAlignment(alignment); } diff --git a/plugins/customscript/customscript_plugin.cpp b/plugins/customscript/customscript_plugin.cpp index 8c74e61e34..ab4fbe3a7f 100644 --- a/plugins/customscript/customscript_plugin.cpp +++ b/plugins/customscript/customscript_plugin.cpp @@ -1,581 +1,581 @@ /* This file is part of KDevelop Copyright (C) 2008 Cédric Pasteur Copyright (C) 2011 David Nolden This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "customscript_plugin.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace KDevelop; static QPointer indentPluginSingleton; K_PLUGIN_FACTORY_WITH_JSON(CustomScriptFactory, "kdevcustomscript.json", registerPlugin(); ) // Replaces ${KEY} in command with variables[KEY] static QString replaceVariables(QString command, const QMap& variables) { while (command.contains(QLatin1String("${"))) { int pos = command.indexOf(QLatin1String("${")); int end = command.indexOf(QLatin1Char('}'), pos + 2); if (end == -1) { break; } QString key = command.mid(pos + 2, end - pos - 2); const auto variableIt = variables.constFind(key); if (variableIt != variables.constEnd()) { command.replace(pos, 1 + end - pos, *variableIt ); } else { qCDebug(CUSTOMSCRIPT) << "found no variable while replacing in shell-command" << command << "key" << key << "available:" << variables; command.remove(pos, 1 + end - pos); } } return command; } CustomScriptPlugin::CustomScriptPlugin(QObject* parent, const QVariantList&) : IPlugin(QStringLiteral("kdevcustomscript"), parent) { m_currentStyle = predefinedStyles().at(0); indentPluginSingleton = this; } CustomScriptPlugin::~CustomScriptPlugin() { } QString CustomScriptPlugin::name() const { // This needs to match the X-KDE-PluginInfo-Name entry from the .desktop file! return QStringLiteral("kdevcustomscript"); } QString CustomScriptPlugin::caption() const { return QStringLiteral("Custom Script Formatter"); } QString CustomScriptPlugin::description() const { return i18n("Indent and Format Source Code.
" "This plugin allows using powerful external formatting tools " "that can be invoked through the command-line.
" "For example, the uncrustify, astyle or indent " "formatters can be used.
" "The advantage of command-line formatters is that formatting configurations " "can be easily shared by all team members, independent of their preferred IDE."); } QString CustomScriptPlugin::formatSourceWithStyle(SourceFormatterStyle style, const QString& text, const QUrl& url, const QMimeType& /*mime*/, const QString& leftContext, const QString& rightContext) const { KProcess proc; QTextStream ios(&proc); std::unique_ptr tmpFile; if (style.content().isEmpty()) { style = predefinedStyle(style.name()); if (style.content().isEmpty()) { qCWarning(CUSTOMSCRIPT) << "Empty contents for style" << style.name() << "for indent plugin"; return text; } } QString useText = text; useText = leftContext + useText + rightContext; QMap projectVariables; const auto projects = ICore::self()->projectController()->projects(); for (IProject* project : projects) { projectVariables[project->name()] = project->path().toUrl().toLocalFile(); } QString command = style.content(); // Replace ${Project} with the project path command = replaceVariables(command, projectVariables); command.replace(QLatin1String("$FILE"), url.toLocalFile()); if (command.contains(QLatin1String("$TMPFILE"))) { tmpFile.reset(new QTemporaryFile(QDir::tempPath() + QLatin1String("/code"))); tmpFile->setAutoRemove(false); if (tmpFile->open()) { qCDebug(CUSTOMSCRIPT) << "using temporary file" << tmpFile->fileName(); command.replace(QLatin1String("$TMPFILE"), tmpFile->fileName()); QByteArray useTextArray = useText.toLocal8Bit(); if (tmpFile->write(useTextArray) != useTextArray.size()) { qCWarning(CUSTOMSCRIPT) << "failed to write text to temporary file"; return text; } } else { qCWarning(CUSTOMSCRIPT) << "Failed to create a temporary file"; return text; } tmpFile->close(); } qCDebug(CUSTOMSCRIPT) << "using shell command for indentation: " << command; proc.setShellCommand(command); proc.setOutputChannelMode(KProcess::OnlyStdoutChannel); proc.start(); if (!proc.waitForStarted()) { - qCDebug(CUSTOMSCRIPT) << "Unable to start indent" << endl; + qCDebug(CUSTOMSCRIPT) << "Unable to start indent"; return text; } if (!tmpFile.get()) { proc.write(useText.toLocal8Bit()); } proc.closeWriteChannel(); if (!proc.waitForFinished()) { - qCDebug(CUSTOMSCRIPT) << "Process doesn't finish" << endl; + qCDebug(CUSTOMSCRIPT) << "Process doesn't finish"; return text; } QString output; if (tmpFile.get()) { QFile f(tmpFile->fileName()); if (f.open(QIODevice::ReadOnly)) { output = QString::fromLocal8Bit(f.readAll()); } else { qCWarning(CUSTOMSCRIPT) << "Failed opening the temporary file for reading"; return text; } } else { output = ios.readAll(); } if (output.isEmpty()) { qCWarning(CUSTOMSCRIPT) << "indent returned empty text for style" << style.name() << style.content(); return text; } int tabWidth = 4; if ((!leftContext.isEmpty() || !rightContext.isEmpty()) && (text.contains(QLatin1Char(' ')) || output.contains(QLatin1Char('\t')))) { // If we have to do contex-matching with tabs, determine the correct tab-width so that the context // can be matched correctly Indentation indent = indentation(url); if (indent.indentationTabWidth > 0) { tabWidth = indent.indentationTabWidth; } } return KDevelop::extractFormattedTextFromContext(output, text, leftContext, rightContext, tabWidth); } QString CustomScriptPlugin::formatSource(const QString& text, const QUrl& url, const QMimeType& mime, const QString& leftContext, const QString& rightContext) const { auto style = KDevelop::ICore::self()->sourceFormatterController()->styleForUrl(url, mime); return formatSourceWithStyle(style, text, url, mime, leftContext, rightContext); } static QVector stylesFromLanguagePlugins() { QVector styles; const auto loadedLanguages = ICore::self()->languageController()->loadedLanguages(); for (auto* lang : loadedLanguages) { const SourceFormatterItemList& languageStyles = lang->sourceFormatterItems(); for (const SourceFormatterStyleItem& item: languageStyles) { if (item.engine == QLatin1String("customscript")) { styles << item.style; } } } return styles; } KDevelop::SourceFormatterStyle CustomScriptPlugin::predefinedStyle(const QString& name) const { const auto& langStyles = stylesFromLanguagePlugins(); for (auto& langStyle: langStyles) { qCDebug(CUSTOMSCRIPT) << "looking at style from language with custom sample" << langStyle.description() << langStyle.overrideSample(); if (langStyle.name() == name) { return langStyle; } } SourceFormatterStyle result(name); if (name == QLatin1String("GNU_indent_GNU")) { result.setCaption(i18n("Gnu Indent: GNU")); result.setContent(QStringLiteral("indent")); result.setUsePreview(true); } else if (name == QLatin1String("GNU_indent_KR")) { result.setCaption(i18n("Gnu Indent: Kernighan & Ritchie")); result.setContent(QStringLiteral("indent -kr")); result.setUsePreview(true); } else if (name == QLatin1String("GNU_indent_orig")) { result.setCaption(i18n("Gnu Indent: Original Berkeley indent style")); result.setContent(QStringLiteral("indent -orig")); result.setUsePreview(true); } else if (name == QLatin1String("clang_format")) { result.setCaption(i18n("Clang Format")); result.setContent(QStringLiteral("clang-format -assume-filename=\"$FILE\"")); result.setUsePreview(false); result.setDescription(i18n("Description:

" "clang-format is an automatic source formater by the LLVM " "project. It supports a variety of formatting style options via " "a .clang-format configuration file, usually located in " "the project root directory.")); } else if (name == QLatin1String("kdev_format_source")) { result.setCaption(QStringLiteral("KDevelop: kdev_format_source")); result.setContent(QStringLiteral("kdev_format_source $FILE $TMPFILE")); result.setUsePreview(false); result.setDescription(i18n("Description:
" "kdev_format_source is a script bundled with KDevelop " "which allows using fine-grained formatting rules by placing " "meta-files called format_sources into the file-system.

" "Each line of the format_sources files defines a list of wildcards " "followed by a colon and the used formatting-command.

" "The formatting-command should use $TMPFILE to reference the " "temporary file to reformat.

" "Example:
" "*.cpp *.h : myformatter $TMPFILE
" "This will reformat all files ending with .cpp or .h using " "the custom formatting script myformatter.

" "Example:
" "subdir/* : uncrustify -l CPP -f $TMPFILE -c uncrustify.config -o $TMPFILE
" "This will reformat all files in subdirectory subdir using the uncrustify " "tool with the config-file uncrustify.config.")); } result.setMimeTypes({ {QStringLiteral("text/x-c++src"), QStringLiteral("C++")}, {QStringLiteral("text/x-chdr"), QStringLiteral("C")}, {QStringLiteral("text/x-c++hdr"), QStringLiteral("C++")}, {QStringLiteral("text/x-csrc"), QStringLiteral("C")}, {QStringLiteral("text/x-java"), QStringLiteral("Java")}, {QStringLiteral("text/x-csharp"), QStringLiteral("C#")}, {QStringLiteral("text/x-objcsrc"), QStringLiteral("Objective-C")}, {QStringLiteral("text/x-objc++src"), QStringLiteral("Objective-C++")}, {QStringLiteral("text/x-objchdr"), QStringLiteral("Objective-C")}, }); return result; } QVector CustomScriptPlugin::predefinedStyles() const { const QVector styles = stylesFromLanguagePlugins() + QVector{ predefinedStyle(QStringLiteral("kdev_format_source")), predefinedStyle(QStringLiteral("clang_format")), predefinedStyle(QStringLiteral("GNU_indent_GNU")), predefinedStyle(QStringLiteral("GNU_indent_KR")), predefinedStyle(QStringLiteral("GNU_indent_orig")), }; return styles; } KDevelop::SettingsWidget* CustomScriptPlugin::editStyleWidget(const QMimeType& mime) const { Q_UNUSED(mime); return new CustomScriptPreferences(); } static QString formattingSample() { return QStringLiteral( "// Formatting\n" "void func(){\n" "\tif(isFoo(a,b))\n" "\tbar(a,b);\n" "if(isFoo)\n" "\ta=bar((b-c)*a,*d--);\n" "if( isFoo( a,b ) )\n" "\tbar(a, b);\n" "if (isFoo) {isFoo=false;cat << isFoo <::const_iterator it = list.begin();\n" "}\n" "namespace A {\n" "namespace B {\n" "void foo() {\n" " if (true) {\n" " func();\n" " } else {\n" " // bla\n" " }\n" "}\n" "}\n" "}\n"); } static QString indentingSample() { return QStringLiteral( "// Indentation\n" "#define foobar(A)\\\n" "{Foo();Bar();}\n" "#define anotherFoo(B)\\\n" "return Bar()\n" "\n" "namespace Bar\n" "{\n" "class Foo\n" "{public:\n" "Foo();\n" "virtual ~Foo();\n" "};\n" "void bar(int foo)\n" "{\n" "switch (foo)\n" "{\n" "case 1:\n" "a+=1;\n" "break;\n" "case 2:\n" "{\n" "a += 2;\n" " break;\n" "}\n" "}\n" "if (isFoo)\n" "{\n" "bar();\n" "}\n" "else\n" "{\n" "anotherBar();\n" "}\n" "}\n" "int foo()\n" "\twhile(isFoo)\n" "\t\t{\n" "\t\t\t// ...\n" "\t\t\tgoto error;\n" "\t\t/* .... */\n" "\t\terror:\n" "\t\t\t//...\n" "\t\t}\n" "\t}\n" "fooArray[]={ red,\n" "\tgreen,\n" "\tdarkblue};\n" "fooFunction(barArg1,\n" "\tbarArg2,\n" "\tbarArg3);\n" ); } QString CustomScriptPlugin::previewText(const SourceFormatterStyle& style, const QMimeType& /*mime*/) const { if (!style.overrideSample().isEmpty()) { return style.overrideSample(); } return formattingSample() + QLatin1String("\n\n") + indentingSample(); } QStringList CustomScriptPlugin::computeIndentationFromSample(const QUrl& url) const { QStringList ret; auto languages = ICore::self()->languageController()->languagesForUrl(url); if (languages.isEmpty()) { return ret; } QString sample = languages[0]->indentationSample(); QString formattedSample = formatSource(sample, url, QMimeDatabase().mimeTypeForUrl(url), QString(), QString()); const QStringList lines = formattedSample.split(QLatin1Char('\n')); for (const QString& line : lines) { if (!line.isEmpty() && line[0].isSpace()) { QString indent; for (const QChar c : line) { if (c.isSpace()) { indent.push_back(c); } else { break; } } if (!indent.isEmpty() && !ret.contains(indent)) { ret.push_back(indent); } } } return ret; } CustomScriptPlugin::Indentation CustomScriptPlugin::indentation(const QUrl& url) const { Indentation ret; QStringList indent = computeIndentationFromSample(url); if (indent.isEmpty()) { qCDebug(CUSTOMSCRIPT) << "failed extracting a valid indentation from sample for url" << url; return ret; // No valid indentation could be extracted } if (indent[0].contains(QLatin1Char(' '))) { ret.indentWidth = indent[0].count(QLatin1Char(' ')); } if (!indent.join(QString()).contains(QLatin1Char(' '))) { ret.indentationTabWidth = -1; // Tabs are not used for indentation } if (indent[0] == QLatin1String(" ")) { // The script indents with tabs-only // The problem is that we don't know how // wide a tab is supposed to be. // // We need indentation-width=tab-width // to make the editor do tab-only formatting, // so choose a random with of 4. ret.indentWidth = 4; ret.indentationTabWidth = 4; } else if (ret.indentWidth) { // Tabs are used for indentation, alongside with spaces // Try finding out how many spaces one tab stands for. // Do it by assuming a uniform indentation-step with each level. for (int pos = 0; pos < indent.size(); ++pos) { if (indent[pos] == QLatin1String(" ")&& pos >= 1) { // This line consists of only a tab. int prevWidth = indent[pos - 1].length(); int prevPrevWidth = (pos >= 2) ? indent[pos - 2].length() : 0; int step = prevWidth - prevPrevWidth; qCDebug(CUSTOMSCRIPT) << "found in line " << pos << prevWidth << prevPrevWidth << step; if (step > 0 && step <= prevWidth) { qCDebug(CUSTOMSCRIPT) << "Done"; ret.indentationTabWidth = prevWidth + step; break; } } } } qCDebug(CUSTOMSCRIPT) << "indent-sample" << QLatin1Char('\"') + indent.join(QLatin1Char('\n')) + QLatin1Char('\"') << "extracted tab-width" << ret.indentationTabWidth << "extracted indentation width" << ret.indentWidth; return ret; } void CustomScriptPreferences::updateTimeout() { const QString& text = indentPluginSingleton.data()->previewText(m_style, QMimeType()); QString formatted = indentPluginSingleton.data()->formatSourceWithStyle(m_style, text, QUrl(), QMimeType()); emit previewTextChanged(formatted); } CustomScriptPreferences::CustomScriptPreferences() { m_updateTimer = new QTimer(this); m_updateTimer->setSingleShot(true); m_updateTimer->setInterval(1000); connect(m_updateTimer, &QTimer::timeout, this, &CustomScriptPreferences::updateTimeout); m_vLayout = new QVBoxLayout(this); m_vLayout->setMargin(0); m_captionLabel = new QLabel; m_vLayout->addWidget(m_captionLabel); m_vLayout->addSpacing(10); m_hLayout = new QHBoxLayout; m_vLayout->addLayout(m_hLayout); m_commandLabel = new QLabel; m_hLayout->addWidget(m_commandLabel); m_commandEdit = new QLineEdit; m_hLayout->addWidget(m_commandEdit); m_commandLabel->setText(i18n("Command:")); m_vLayout->addSpacing(10); m_bottomLabel = new QLabel; m_vLayout->addWidget(m_bottomLabel); m_bottomLabel->setTextFormat(Qt::RichText); m_bottomLabel->setText( i18n("You can enter an arbitrary shell command.
" "The unformatted source-code is reached to the command
" "through the standard input, and the
" "formatted result is read from the standard output.
" "
" "If you add $TMPFILE into the command, then
" "a temporary file is used for transferring the code.")); connect(m_commandEdit, &QLineEdit::textEdited, m_updateTimer, QOverload<>::of(&QTimer::start)); m_vLayout->addSpacing(10); m_moreVariablesButton = new QPushButton(i18n("More Variables")); connect(m_moreVariablesButton, &QPushButton::clicked, this, &CustomScriptPreferences::moreVariablesClicked); m_vLayout->addWidget(m_moreVariablesButton); m_vLayout->addStretch(); } void CustomScriptPreferences::load(const KDevelop::SourceFormatterStyle& style) { m_style = style; m_commandEdit->setText(style.content()); m_captionLabel->setText(i18n("Style: %1", style.caption())); updateTimeout(); } QString CustomScriptPreferences::save() { return m_commandEdit->text(); } void CustomScriptPreferences::moreVariablesClicked(bool) { KMessageBox::information(ICore::self()->uiController()->activeMainWindow(), i18n("$TMPFILE will be replaced with the path to a temporary file.
" "The code will be written into the file, the temporary
" "file will be substituted into that position, and the result
" "will be read out of that file.
" "
" "$FILE will be replaced with the path of the original file.
" "The contents of the file must not be modified, changes are allowed
" "only in $TMPFILE.
" "
" "${PROJECT_NAME} will be replaced by the path of
" "the currently open project with the matching name." ), i18n("Variable Replacements")); } #include "customscript_plugin.moc" diff --git a/plugins/subversion/kdevsvnplugin.cpp b/plugins/subversion/kdevsvnplugin.cpp index 1e8a4e5d7e..4c5c9e0f01 100644 --- a/plugins/subversion/kdevsvnplugin.cpp +++ b/plugins/subversion/kdevsvnplugin.cpp @@ -1,447 +1,447 @@ /*************************************************************************** * Copyright 2007 Dukju Ahn * * Copyright 2008 Andreas Pakulat * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "kdevsvnplugin.h" #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 "kdevsvncpp/apr.hpp" #include "svncommitjob.h" #include "svnstatusjob.h" #include "svnaddjob.h" #include "svnrevertjob.h" #include "svnremovejob.h" #include "svnupdatejob.h" #include "svninfojob.h" #include "svndiffjob.h" #include "svncopyjob.h" #include "svnmovejob.h" #include "svnlogjob.h" #include "svnblamejob.h" #include "svnimportjob.h" #include "svncheckoutjob.h" #include "svnimportmetadatawidget.h" #include #include #include "svnlocationwidget.h" #include "debug.h" K_PLUGIN_FACTORY_WITH_JSON(KDevSvnFactory, "kdevsubversion.json", registerPlugin();) KDevSvnPlugin::KDevSvnPlugin(QObject *parent, const QVariantList &) : KDevelop::IPlugin(QStringLiteral("kdevsubversion"), parent) , m_common(new KDevelop::VcsPluginHelper(this, this)) , copy_action( nullptr ) , move_action( nullptr ) , m_jobQueue(new ThreadWeaver::Queue(this)) { qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); } KDevSvnPlugin::~KDevSvnPlugin() { } bool KDevSvnPlugin::isValidRemoteRepositoryUrl(const QUrl& remoteLocation) { const QString scheme = remoteLocation.scheme(); if (scheme == QLatin1String("svn") || scheme == QLatin1String("svn+ssh")) { return true; } return false; } bool KDevSvnPlugin::isVersionControlled(const QUrl &localLocation) { ///TODO: also check this in the other functions? if (!localLocation.isValid()) { return false; } auto* job = new SvnInfoJob(this); job->setLocation(localLocation); if (job->exec()) { QVariant result = job->fetchResults(); if (result.isValid()) { SvnInfoHolder h = result.value(); return !h.name.isEmpty(); } } else { qCDebug(PLUGIN_SVN) << "Couldn't execute job"; } return false; } KDevelop::VcsJob* KDevSvnPlugin::repositoryLocation(const QUrl &localLocation) { auto* job = new SvnInfoJob(this); job->setLocation(localLocation); job->setProvideInformation(SvnInfoJob::RepoUrlOnly); return job; } KDevelop::VcsJob* KDevSvnPlugin::status(const QList& localLocations, KDevelop::IBasicVersionControl::RecursionMode mode) { auto* job = new SvnStatusJob(this); job->setLocations(localLocations); job->setRecursive((mode == KDevelop::IBasicVersionControl::Recursive)); return job; } KDevelop::VcsJob* KDevSvnPlugin::add(const QList& localLocations, KDevelop::IBasicVersionControl::RecursionMode recursion) { auto* job = new SvnAddJob(this); job->setLocations(localLocations); job->setRecursive((recursion == KDevelop::IBasicVersionControl::Recursive)); return job; } KDevelop::VcsJob* KDevSvnPlugin::remove(const QList& localLocations) { auto* job = new SvnRemoveJob(this); job->setLocations(localLocations); return job; } KDevelop::VcsJob* KDevSvnPlugin::edit(const QUrl& /*localLocation*/) { return nullptr; } KDevelop::VcsJob* KDevSvnPlugin::unedit(const QUrl& /*localLocation*/) { return nullptr; } KDevelop::VcsJob* KDevSvnPlugin::localRevision(const QUrl &localLocation, KDevelop::VcsRevision::RevisionType type) { auto* job = new SvnInfoJob(this); job->setLocation(localLocation); job->setProvideInformation(SvnInfoJob::RevisionOnly); job->setProvideRevisionType(type); return job; } KDevelop::VcsJob* KDevSvnPlugin::copy(const QUrl &localLocationSrc, const QUrl& localLocationDstn) { auto* job = new SvnCopyJob(this); job->setSourceLocation(localLocationSrc); job->setDestinationLocation(localLocationDstn); return job; } KDevelop::VcsJob* KDevSvnPlugin::move(const QUrl &localLocationSrc, const QUrl& localLocationDst) { auto* job = new SvnMoveJob(this); job->setSourceLocation(localLocationSrc); job->setDestinationLocation(localLocationDst); return job; } KDevelop::VcsJob* KDevSvnPlugin::revert(const QList& localLocations, KDevelop::IBasicVersionControl::RecursionMode recursion) { auto* job = new SvnRevertJob(this); job->setLocations(localLocations); job->setRecursive((recursion == KDevelop::IBasicVersionControl::Recursive)); return job; } KDevelop::VcsJob* KDevSvnPlugin::update(const QList& localLocations, const KDevelop::VcsRevision& rev, KDevelop::IBasicVersionControl::RecursionMode recursion) { auto* job = new SvnUpdateJob(this); job->setLocations(localLocations); job->setRevision(rev); job->setRecursive((recursion == KDevelop::IBasicVersionControl::Recursive)); return job; } KDevelop::VcsJob* KDevSvnPlugin::commit(const QString& message, const QList& localLocations, KDevelop::IBasicVersionControl::RecursionMode recursion) { auto* job = new SvnCommitJob(this); - qCDebug(PLUGIN_SVN) << "Committing locations:" << localLocations << endl; + qCDebug(PLUGIN_SVN) << "Committing locations:" << localLocations; job->setUrls(localLocations); job->setCommitMessage(message) ; job->setRecursive((recursion == KDevelop::IBasicVersionControl::Recursive)); return job; } KDevelop::VcsJob* KDevSvnPlugin::diff(const QUrl &fileOrDirectory, const KDevelop::VcsRevision& srcRevision, const KDevelop::VcsRevision& dstRevision, KDevelop::IBasicVersionControl::RecursionMode recurse) { KDevelop::VcsLocation loc(fileOrDirectory); return diff2(loc, loc, srcRevision, dstRevision, recurse); } KDevelop::VcsJob* KDevSvnPlugin::diff2(const KDevelop::VcsLocation& src, const KDevelop::VcsLocation& dst, const KDevelop::VcsRevision& srcRevision, const KDevelop::VcsRevision& dstRevision, KDevelop::IBasicVersionControl::RecursionMode recurse) { auto* job = new SvnDiffJob(this); job->setSource(src); job->setDestination(dst); job->setSrcRevision(srcRevision); job->setDstRevision(dstRevision); job->setRecursive((recurse == KDevelop::IBasicVersionControl::Recursive)); return job; } KDevelop::VcsJob* KDevSvnPlugin::log(const QUrl &localLocation, const KDevelop::VcsRevision& rev, unsigned long limit) { auto* job = new SvnLogJob(this); job->setLocation(localLocation); job->setStartRevision(rev); job->setLimit(limit); return job; } KDevelop::VcsJob* KDevSvnPlugin::log(const QUrl &localLocation, const KDevelop::VcsRevision& startRev, const KDevelop::VcsRevision& endRev) { auto* job = new SvnLogJob(this); job->setLocation(localLocation); job->setStartRevision(startRev); job->setEndRevision(endRev); return job; } KDevelop::VcsJob* KDevSvnPlugin::annotate(const QUrl &localLocation, const KDevelop::VcsRevision& rev) { auto* job = new SvnBlameJob(this); job->setLocation(localLocation); job->setEndRevision(rev); return job; } KDevelop::VcsJob* KDevSvnPlugin::merge(const KDevelop::VcsLocation& localOrRepoLocationSrc, const KDevelop::VcsLocation& localOrRepoLocationDst, const KDevelop::VcsRevision& srcRevision, const KDevelop::VcsRevision& dstRevision, const QUrl &localLocation) { // TODO implement merge Q_UNUSED(localOrRepoLocationSrc) Q_UNUSED(localOrRepoLocationDst) Q_UNUSED(srcRevision) Q_UNUSED(dstRevision) Q_UNUSED(localLocation) return nullptr; } KDevelop::VcsJob* KDevSvnPlugin::resolve(const QList& /*localLocations*/, KDevelop::IBasicVersionControl::RecursionMode /*recursion*/) { return nullptr; } KDevelop::VcsJob* KDevSvnPlugin::import(const QString & commitMessage, const QUrl &sourceDirectory, const KDevelop::VcsLocation & destinationRepository) { auto* job = new SvnImportJob(this); job->setMapping(sourceDirectory, destinationRepository); job->setMessage(commitMessage); return job; } KDevelop::VcsJob* KDevSvnPlugin::createWorkingCopy(const KDevelop::VcsLocation & sourceRepository, const QUrl &destinationDirectory, KDevelop::IBasicVersionControl::RecursionMode recursion) { auto* job = new SvnCheckoutJob(this); job->setMapping(sourceRepository, destinationDirectory, recursion); return job; } KDevelop::ContextMenuExtension KDevSvnPlugin::contextMenuExtension(KDevelop::Context* context, QWidget* parent) { m_common->setupFromContext(context); const QList & ctxUrlList = m_common->contextUrlList(); bool hasVersionControlledEntries = false; for (const QUrl& url : ctxUrlList) { if (isVersionControlled(url) || isVersionControlled(KIO::upUrl(url))) { hasVersionControlledEntries = true; break; } } qCDebug(PLUGIN_SVN) << "version controlled?" << hasVersionControlledEntries; if (!hasVersionControlledEntries) return IPlugin::contextMenuExtension(context, parent); QMenu* svnmenu = m_common->commonActions(parent); svnmenu->addSeparator(); if( !copy_action ) { copy_action = new QAction(i18n("Copy..."), this); connect(copy_action, &QAction::triggered, this, &KDevSvnPlugin::ctxCopy); } svnmenu->addAction(copy_action); if( !move_action ) { move_action = new QAction(i18n("Move..."), this); connect(move_action, &QAction::triggered, this, &KDevSvnPlugin::ctxMove); } svnmenu->addAction(move_action); KDevelop::ContextMenuExtension menuExt; menuExt.addAction(KDevelop::ContextMenuExtension::VcsGroup, svnmenu->menuAction()); return menuExt; } void KDevSvnPlugin::ctxCopy() { QList const & ctxUrlList = m_common->contextUrlList(); if (ctxUrlList.count() > 1) { KMessageBox::error(nullptr, i18n("Please select only one item for this operation")); return; } QUrl source = ctxUrlList.first(); if (source.isLocalFile()) { QUrl dir = source; bool isFile = QFileInfo(source.toLocalFile()).isFile(); if (isFile) { dir = dir.adjusted(QUrl::RemoveFilename|QUrl::StripTrailingSlash); } KUrlRequesterDialog dlg(dir, i18n("Destination file/directory"), nullptr); if (isFile) { dlg.urlRequester()->setMode(KFile::File | KFile::Directory | KFile::LocalOnly); } else { dlg.urlRequester()->setMode(KFile::Directory | KFile::LocalOnly); } if (dlg.exec() == QDialog::Accepted) { // krazy:exclude=crashy KDevelop::ICore::self()->runController()->registerJob(copy(source, dlg.selectedUrl())); } } else { KMessageBox::error(nullptr, i18n("Copying only works on local files")); return; } } void KDevSvnPlugin::ctxMove() { QList const & ctxUrlList = m_common->contextUrlList(); if (ctxUrlList.count() != 1) { KMessageBox::error(nullptr, i18n("Please select only one item for this operation")); return; } QUrl source = ctxUrlList.first(); if (source.isLocalFile()) { QUrl dir = source; bool isFile = QFileInfo(source.toLocalFile()).isFile(); if (isFile) { dir = source.adjusted(QUrl::RemoveFilename|QUrl::StripTrailingSlash); } KUrlRequesterDialog dlg(dir, i18n("Destination file/directory"), nullptr); if (isFile) { dlg.urlRequester()->setMode(KFile::File | KFile::Directory | KFile::LocalOnly); } else { dlg.urlRequester()->setMode(KFile::Directory | KFile::LocalOnly); } if (dlg.exec() == QDialog::Accepted) { // krazy:exclude=crashy KDevelop::ICore::self()->runController()->registerJob(move(source, dlg.selectedUrl())); } } else { KMessageBox::error(nullptr, i18n("Moving only works on local files/dirs")); return; } } QString KDevSvnPlugin::name() const { return i18n("Subversion"); } KDevelop::VcsImportMetadataWidget* KDevSvnPlugin::createImportMetadataWidget(QWidget* parent) { return new SvnImportMetadataWidget(parent); } KDevelop::VcsLocationWidget* KDevSvnPlugin::vcsLocation(QWidget* parent) const { return new SvnLocationWidget(parent); } ThreadWeaver::Queue* KDevSvnPlugin::jobQueue() const { return m_jobQueue; } #include "kdevsvnplugin.moc"