kde-extraapps/kdevplatform/shell/sourceformattercontroller.cpp
2015-07-26 14:23:17 +03:00

659 lines
23 KiB
C++

/* This file is part of KDevelop
Copyright 2009 Andreas Pakulat <apaku@gmx.de>
Copyright (C) 2008 Cédric Pasteur <cedric.pasteur@free.fr>
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 <QVariant>
#include <QStringList>
#include <QRegExp>
#include <KPluginInfo>
#include <KDebug>
#include <interfaces/icore.h>
#include <interfaces/iplugincontroller.h>
#include <interfaces/ilanguagecontroller.h>
#include <language/interfaces/ilanguagesupport.h>
#include <language/codegen/coderepresentation.h>
#include <interfaces/isourceformatter.h>
#include "core.h"
#include <ktexteditor/view.h>
#include <project/projectmodel.h>
#include <util/path.h>
#include <kio/netaccess.h>
#include <kmessagebox.h>
#include <qfile.h>
#include <interfaces/context.h>
#include <interfaces/contextmenuextension.h>
#include <ktexteditor/commandinterface.h>
#include <kactioncollection.h>
#include <kaction.h>
#include <interfaces/idocument.h>
#include <interfaces/idocumentcontroller.h>
#include <ktexteditor/document.h>
#include <ktexteditor/editor.h>
#include <ktexteditor/configinterface.h>
#include "plugincontroller.h"
#include <interfaces/isession.h>
namespace KDevelop
{
const QString SourceFormatterController::kateModeLineConfigKey = "ModelinesEnabled";
const QString SourceFormatterController::kateOverrideIndentationConfigKey = "OverrideKateIndentation";
const QString SourceFormatterController::styleCaptionKey = "Caption";
const QString SourceFormatterController::styleContentKey = "Content";
const QString SourceFormatterController::styleMimeTypesKey = "MimeTypes";
const QString SourceFormatterController::styleSampleKey = "StyleSample";
SourceFormatterController::SourceFormatterController(QObject *parent)
: ISourceFormatterController(parent), m_enabled(true)
{
setObjectName("SourceFormatterController");
setComponentData(KComponentData("kdevsourceformatter"));
setXMLFile("kdevsourceformatter.rc");
if (Core::self()->setupFlags() & Core::NoUi) return;
m_formatTextAction = actionCollection()->addAction("edit_reformat_source");
m_formatTextAction->setText(i18n("&Reformat Source"));
m_formatTextAction->setToolTip(i18n("Reformat source using AStyle"));
m_formatTextAction->setWhatsThis(i18n("Source reformatting functionality using <b>astyle</b> library."));
connect(m_formatTextAction, SIGNAL(triggered()), this, SLOT(beautifySource()));
m_formatLine = actionCollection()->addAction("edit_reformat_line");
m_formatLine->setText(i18n("Reformat Line"));
m_formatLine->setToolTip(i18n("Reformat current line using AStyle"));
m_formatLine->setWhatsThis(i18n("Source reformatting of line under cursor using <b>astyle</b> library."));
connect(m_formatLine, SIGNAL(triggered()), this, SLOT(beautifyLine()));
m_formatFilesAction = actionCollection()->addAction("tools_astyle");
m_formatFilesAction->setText(i18n("Format Files"));
m_formatFilesAction->setToolTip(i18n("Format file(s) using the current theme"));
m_formatFilesAction->setWhatsThis(i18n("Formatting functionality using <b>astyle</b> library."));
connect(m_formatFilesAction, SIGNAL(triggered()), this, SLOT(formatFiles()));
m_formatTextAction->setEnabled(false);
m_formatFilesAction->setEnabled(true);
connect(Core::self()->documentController(), SIGNAL(documentActivated(KDevelop::IDocument*)),
this, SLOT(activeDocumentChanged(KDevelop::IDocument*)));
// Use a queued connection, because otherwise the view is not yet fully set up
connect(Core::self()->documentController(), SIGNAL(documentLoaded(KDevelop::IDocument*)),
this, SLOT(documentLoaded(KDevelop::IDocument*)), Qt::QueuedConnection);
activeDocumentChanged(Core::self()->documentController()->activeDocument());
}
void SourceFormatterController::documentLoaded( IDocument* 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->textDocument()) {
return;
}
KMimeType::Ptr mime = KMimeType::findByUrl(doc->url());
adaptEditorIndentationMode( doc, formatterForMimeType(mime) );
}
void SourceFormatterController::initialize()
{
}
SourceFormatterController::~SourceFormatterController()
{
}
ISourceFormatter* SourceFormatterController::formatterForUrl(const KUrl &url)
{
KMimeType::Ptr mime = KMimeType::findByUrl(url);
return formatterForMimeType(mime);
}
KConfigGroup SourceFormatterController::configuration() const
{
return Core::self()->activeSession()->config()->group( "SourceFormatter" );
}
ISourceFormatter* SourceFormatterController::findFirstFormatterForMimeType( const KMimeType::Ptr& mime ) const
{
static QHash<QString, ISourceFormatter*> knownFormatters;
if (knownFormatters.contains(mime->name()))
return knownFormatters[mime->name()];
foreach( IPlugin* p, Core::self()->pluginController()->allPluginsForExtension( "org.kdevelop.ISourceFormatter" ) ) {
KPluginInfo info = Core::self()->pluginController()->pluginInfo( p );
ISourceFormatter *iformatter = p->extension<ISourceFormatter>();
QSharedPointer<SourceFormatter> formatter(createFormatterForPlugin(iformatter));
if( formatter->supportedMimeTypes().contains(mime->name()) ) {
knownFormatters[mime->name()] = iformatter;
return iformatter;
}
}
knownFormatters[mime->name()] = 0;
return 0;
}
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<QStringList>( SourceFormatterController::styleMimeTypesKey, QStringList() ) );
s->setOverrideSample( stylegrp.readEntry( SourceFormatterController::styleSampleKey, QString() ) );
}
SourceFormatter* SourceFormatterController::createFormatterForPlugin(ISourceFormatter *ifmt) const
{
SourceFormatter* formatter = new SourceFormatter();
formatter->formatter = ifmt;
// Inserted a new formatter. Now fill it with styles
foreach( const KDevelop::SourceFormatterStyle& style, ifmt->predefinedStyles() ) {
formatter->styles[ style.name() ] = new SourceFormatterStyle(style);
}
KConfigGroup grp = configuration();
if( grp.hasGroup( ifmt->name() ) ) {
KConfigGroup fmtgrp = grp.group( ifmt->name() );
foreach( const QString& subgroup, fmtgrp.groupList() ) {
SourceFormatterStyle* s = new SourceFormatterStyle( subgroup );
KConfigGroup stylegrp = fmtgrp.group( subgroup );
populateStyleFromConfigGroup(s, stylegrp);
formatter->styles[ s->name() ] = s;
}
}
return formatter;
}
ISourceFormatter* SourceFormatterController::formatterForMimeType(const KMimeType::Ptr &mime)
{
if( !m_enabled || !isMimeTypeSupported( mime ) ) {
return 0;
}
QString formatter = configuration().readEntry( mime->name(), "" );
if( formatter.isEmpty() )
{
return findFirstFormatterForMimeType( mime );
}
QStringList formatterinfo = formatter.split( "||", QString::SkipEmptyParts );
if( formatterinfo.size() != 2 ) {
kDebug() << "Broken formatting entry for mime:" << mime << "current value:" << formatter;
return 0;
}
return Core::self()->pluginControllerInternal()->extensionForPlugin<ISourceFormatter>( "org.kdevelop.ISourceFormatter", formatterinfo.at(0) );
}
bool SourceFormatterController::isMimeTypeSupported(const KMimeType::Ptr &mime)
{
if( findFirstFormatterForMimeType( mime ) ) {
return true;
}
return false;
}
QString SourceFormatterController::indentationMode(const KMimeType::Ptr &mime)
{
if (mime->is("text/x-c++src") || mime->is("text/x-chdr") ||
mime->is("text/x-c++hdr") || mime->is("text/x-csrc") ||
mime->is("text/x-java") || mime->is("text/x-csharp"))
return "cstyle";
return "none";
}
QString SourceFormatterController::addModelineForCurrentLang(QString input, const KUrl& url, const KMimeType::Ptr& mime)
{
if( !isMimeTypeSupported(mime) )
return input;
QRegExp kateModelineWithNewline("\\s*\\n//\\s*kate:(.*)$");
// If there already is a modeline in the document, adapt it while formatting, even
// if "add modeline" is disabled.
if( !configuration().readEntry( SourceFormatterController::kateModeLineConfigKey, false ) &&
kateModelineWithNewline.indexIn( input ) == -1 )
return input;
ISourceFormatter* fmt = formatterForMimeType( 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("// kate: ");
QString indentLength = QString::number(indentation.indentWidth);
QString tabLength = QString::number(indentation.indentationTabWidth);
// add indentation style
modeline.append("indent-mode ").append(indentationMode(mime).append("; "));
if(indentation.indentWidth) // We know something about indentation-width
modeline.append(QString("indent-width %1; ").arg(indentation.indentWidth));
if(indentation.indentationTabWidth != 0) // We know something about tab-usage
{
modeline.append(QString("replace-tabs %1; ").arg((indentation.indentationTabWidth == -1) ? "on" : "off"));
if(indentation.indentationTabWidth > 0)
modeline.append(QString("tab-width %1; ").arg(indentation.indentationTabWidth));
}
kDebug() << "created modeline: " << modeline << endl;
QRegExp kateModeline("^\\s*//\\s*kate:(.*)$");
bool modelinefound = false;
QRegExp knownOptions("\\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
kDebug() << "Found a kate modeline: " << line << endl;
modelinefound = true;
QString options = kateModeline.cap(1);
QStringList optionList = options.split(';', QString::SkipEmptyParts);
os << modeline;
foreach(QString s, optionList) {
if (knownOptions.indexIn(s) < 0) { // unknown option, add it
if(s.startsWith(' '))
s=s.mid(1);
os << s << ";";
kDebug() << "Found unknown option: " << s << endl;
}
}
os << endl;
} else
os << line << endl;
}
if (!modelinefound)
os << modeline << endl;
return output;
}
void SourceFormatterController::cleanup()
{
}
void SourceFormatterController::activeDocumentChanged(IDocument* doc)
{
bool enabled = false;
if (doc) {
KMimeType::Ptr mime = KMimeType::findByUrl(doc->url());
if (isMimeTypeSupported(mime))
enabled = true;
}
m_formatTextAction->setEnabled(enabled);
}
void SourceFormatterController::beautifySource()
{
KDevelop::IDocumentController *docController = KDevelop::ICore::self()->documentController();
KDevelop::IDocument *doc = docController->activeDocument();
if (!doc)
return;
// load the appropriate formatter
KMimeType::Ptr mime = KMimeType::findByUrl(doc->url());
ISourceFormatter *formatter = formatterForMimeType(mime);
if( !formatter ) {
kDebug() << "no formatter available for" << mime;
return;
}
// Ignore the modeline, as the modeline will be changed anyway
adaptEditorIndentationMode( doc, formatter, true );
bool has_selection = false;
KTextEditor::View *view = doc->textDocument()->activeView();
if (view && view->selection())
has_selection = true;
if (has_selection) {
QString original = view->selectionText();
QString output = formatter->formatSource(view->selectionText(), doc->url(), mime,
view->document()->text(KTextEditor::Range(KTextEditor::Cursor(0,0),view->selectionRange().start())),
view->document()->text(KTextEditor::Range(view->selectionRange().end(), view->document()->documentRange().end())));
//remove the final newline character, unless it should be there
if (!original.endsWith('\n') && output.endsWith('\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 = DynamicCodeRepresentation::Ptr::dynamicCast( KDevelop::createCodeRepresentation( IndexedString( doc->url() ) ) );
Q_ASSERT( code );
code->replace( view->selectionRange(), original, output );
} else {
formatDocument(doc, 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();
if (!tDoc->activeView())
return;
// load the appropriate formatter
KMimeType::Ptr mime = KMimeType::findByUrl(doc->url());
ISourceFormatter *formatter = formatterForMimeType(mime);
if( !formatter ) {
kDebug() << "no formatter available for" << mime;
return;
}
const KTextEditor::Cursor cursor = tDoc->activeView()->cursorPosition();
const QString line = tDoc->line(cursor.line());
const QString prev = tDoc->text(KTextEditor::Range(0, 0, cursor.line(), 0));
const QString post = '\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 = DynamicCodeRepresentation::Ptr::dynamicCast( KDevelop::createCodeRepresentation( IndexedString( doc->url() ) ) );
Q_ASSERT( code );
code->replace( KTextEditor::Range(cursor.line(), 0, cursor.line(), line.length()), line, formatted );
// advance cursor one line
tDoc->activeView()->setCursorPosition(KTextEditor::Cursor(cursor.line() + 1, 0));
}
void SourceFormatterController::formatDocument(KDevelop::IDocument *doc, ISourceFormatter *formatter, const KMimeType::Ptr &mime)
{
// 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()
{
if( configuration().readEntry( SourceFormatterController::kateOverrideIndentationConfigKey, false ) )
foreach( KDevelop::IDocument* doc, ICore::self()->documentController()->openDocuments() )
adaptEditorIndentationMode( doc, formatterForUrl(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(KDevelop::IDocument *doc, ISourceFormatter *formatter, bool ignoreModeline )
{
if( !formatter || !configuration().readEntry( SourceFormatterController::kateOverrideIndentationConfigKey, false ) || !doc->isTextDocument() )
return;
KTextEditor::Document *textDoc = doc->textDocument();
kDebug() << "adapting mode for" << doc->url();
Q_ASSERT(textDoc);
QRegExp kateModelineWithNewline("\\s*\\n//\\s*kate:(.*)$");
// modelines should always take precedence
if( !ignoreModeline && kateModelineWithNewline.indexIn( textDoc->text() ) != -1 )
{
kDebug() << "ignoring because a kate modeline was found";
return;
}
ISourceFormatter::Indentation indentation = formatter->indentation(doc->url());
if(indentation.isValid())
{
struct CommandCaller {
CommandCaller(KTextEditor::Document* _doc) : doc(_doc), ci(qobject_cast<KTextEditor::CommandInterface*>(doc->editor())) {
Q_ASSERT(ci);
}
void operator()(QString cmd) {
KTextEditor::Command* command = ci->queryCommand( cmd );
Q_ASSERT(command);
QString msg;
kDebug() << "calling" << cmd;
if( !command->exec( doc->activeView(), cmd, msg ) )
kWarning() << "setting indentation width failed: " << msg;
}
KTextEditor::Document* doc;
KTextEditor::CommandInterface* ci;
} call(textDoc);
if( indentation.indentWidth ) // We know something about indentation-width
call( QString("set-indent-width %1").arg(indentation.indentWidth ) );
if( indentation.indentationTabWidth != 0 ) // We know something about tab-usage
{
call( QString("set-replace-tabs %1").arg( (indentation.indentationTabWidth == -1) ? 1 : 0 ) );
if( indentation.indentationTabWidth > 0 )
call( QString("set-tab-width %1").arg(indentation.indentationTabWidth ) );
}
}else{
kDebug() << "found no valid indentation";
}
}
void SourceFormatterController::formatFiles()
{
if (m_prjItems.isEmpty())
return;
//get a list of all files in this folder recursively
QList<KDevelop::ProjectFolderItem*> folders;
foreach(KDevelop::ProjectBaseItem *item, m_prjItems) {
if (!item)
continue;
if (item->folder())
folders.append(item->folder());
else if (item->file())
m_urls.append(item->file()->path().toUrl());
else if (item->target()) {
foreach(KDevelop::ProjectFileItem *f, item->fileList())
m_urls.append(f->path().toUrl());
}
}
while (!folders.isEmpty()) {
KDevelop::ProjectFolderItem *item = folders.takeFirst();
foreach(KDevelop::ProjectFolderItem *f, item->folderList())
folders.append(f);
foreach(KDevelop::ProjectTargetItem *f, item->targetList()) {
foreach(KDevelop::ProjectFileItem *child, f->fileList())
m_urls.append(child->path().toUrl());
}
foreach(KDevelop::ProjectFileItem *f, item->fileList())
m_urls.append(f->path().toUrl());
}
formatFiles(m_urls);
}
void SourceFormatterController::formatFiles(KUrl::List &list)
{
//! \todo IStatus
for (int fileCount = 0; fileCount < list.size(); fileCount++) {
// check mimetype
KMimeType::Ptr mime = KMimeType::findByUrl(list[fileCount]);
kDebug() << "Checking file " << list[fileCount].pathOrUrl() << " of mime type " << mime->name() << endl;
ISourceFormatter *formatter = formatterForMimeType(mime);
if (!formatter) // unsupported mime type
continue;
// if the file is opened in the editor, format the text in the editor without saving it
KDevelop::IDocumentController *docController = KDevelop::ICore::self()->documentController();
KDevelop::IDocument *doc = docController->documentForUrl(list[fileCount]);
if (doc) {
kDebug() << "Processing file " << list[fileCount].pathOrUrl() << "opened in editor" << endl;
formatDocument(doc, formatter, mime);
continue;
}
kDebug() << "Processing file " << list[fileCount].pathOrUrl() << endl;
QString tmpFile, output;
if (KIO::NetAccess::download(list[fileCount], tmpFile, 0)) {
QFile file(tmpFile);
// read file
if (file.open(QFile::ReadOnly)) {
QTextStream is(&file);
output = formatter->formatSource(is.readAll(), list[fileCount], mime);
file.close();
} else
KMessageBox::error(0, i18n("Unable to read %1", list[fileCount].prettyUrl()));
//write new content
if (file.open(QFile::WriteOnly | QIODevice::Truncate)) {
QTextStream os(&file);
os << addModelineForCurrentLang(output, list[fileCount], mime);
file.close();
} else
KMessageBox::error(0, i18n("Unable to write to %1", list[fileCount].prettyUrl()));
if (!KIO::NetAccess::upload(tmpFile, list[fileCount], 0))
KMessageBox::error(0, KIO::NetAccess::lastErrorString());
KIO::NetAccess::removeTempFile(tmpFile);
} else
KMessageBox::error(0, KIO::NetAccess::lastErrorString());
}
}
KDevelop::ContextMenuExtension SourceFormatterController::contextMenuExtension(KDevelop::Context* context)
{
KDevelop::ContextMenuExtension ext;
m_urls.clear();
m_prjItems.clear();
if (context->hasType(KDevelop::Context::EditorContext))
{
if(m_formatTextAction->isEnabled())
ext.addAction(KDevelop::ContextMenuExtension::EditGroup, m_formatTextAction);
} else if (context->hasType(KDevelop::Context::FileContext)) {
KDevelop::FileContext* filectx = dynamic_cast<KDevelop::FileContext*>(context);
m_urls = filectx->urls();
ext.addAction(KDevelop::ContextMenuExtension::EditGroup, m_formatFilesAction);
} else if (context->hasType(KDevelop::Context::CodeContext)) {
} else if (context->hasType(KDevelop::Context::ProjectItemContext)) {
KDevelop::ProjectItemContext* prjctx = dynamic_cast<KDevelop::ProjectItemContext*>(context);
m_prjItems = prjctx->items();
if ( !m_prjItems.isEmpty() ) {
ext.addAction(KDevelop::ContextMenuExtension::ExtensionGroup, m_formatFilesAction);
}
}
return ext;
}
SourceFormatterStyle SourceFormatterController::styleForMimeType( const KMimeType::Ptr& mime )
{
QStringList formatter = configuration().readEntry( mime->name(), "" ).split( "||", QString::SkipEmptyParts );
if( formatter.count() == 2 )
{
SourceFormatterStyle s( formatter.at( 1 ) );
KConfigGroup fmtgrp = configuration().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)
{
m_enabled = !disable;
}
bool SourceFormatterController::sourceFormattingEnabled()
{
return m_enabled;
}
/*
Code copied from source formatter plugin, unused currently but shouldn't be just thrown away
QString SourceFormatterPlugin::replaceSpacesWithTab(const QString &input, ISourceFormatter *formatter)
{
QString output(input);
int wsCount = formatter->indentationLength();
ISourceFormatter::IndentationType type = formatter->indentationType();
if (type == ISourceFormatter::IndentWithTabs) {
// tabs and wsCount spaces to be a tab
QString replace;
for (int i = 0; i < wsCount;i++)
replace += ' ';
output = output.replace(replace, QChar('\t'));
// input = input.remove(' ');
} else if (type == ISourceFormatter::IndentWithSpacesAndConvertTabs) {
//convert tabs to spaces
QString replace;
for (int i = 0;i < wsCount;i++)
replace += ' ';
output = output.replace(QChar('\t'), replace);
}
return output;
}
QString SourceFormatterPlugin::addIndentation(QString input, const QString indentWith)
{
QString output;
QTextStream os(&output, QIODevice::WriteOnly);
QTextStream is(&input, QIODevice::ReadOnly);
while (!is.atEnd())
os << indentWith << is.readLine() << endl;
return output;
}
*/
}
#include "moc_sourceformattercontroller.cpp"
// kate: indent-mode cstyle; space-indent off; tab-width 4;