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"