diff --git a/interfaces/isourceformatter.cpp b/interfaces/isourceformatter.cpp index 0cf79361ab..53946110a7 100644 --- a/interfaces/isourceformatter.cpp +++ b/interfaces/isourceformatter.cpp @@ -1,128 +1,195 @@ /* This file is part of KDevelop 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 "isourceformatter.h" #include namespace KDevelop { SettingsWidget::SettingsWidget(QWidget *parent) : QWidget(parent) { } SettingsWidget::~SettingsWidget() { } ISourceFormatter::~ISourceFormatter() { } SourceFormatterStyle::SourceFormatterStyle() : m_usePreview(false) { }; SourceFormatterStyle::SourceFormatterStyle(const QString &name) : m_name(name) { } void SourceFormatterStyle::setContent(const QString &content) { m_content = content; } void SourceFormatterStyle::setCaption(const QString &caption) { m_caption = caption; } QString SourceFormatterStyle::content() const { return m_content; } QString SourceFormatterStyle::caption() const { return m_caption; } QString SourceFormatterStyle::name() const { return m_name; } QString SourceFormatterStyle::description() const { return m_description; } void SourceFormatterStyle::setDescription(const QString &desc) { m_description = desc; } bool SourceFormatterStyle::usePreview() const { return m_usePreview; } void SourceFormatterStyle::setUsePreview(bool use) { m_usePreview = use; } +void SourceFormatterStyle::setMimeTypes(const SourceFormatterStyle::MimeList& types) +{ + m_mimeTypes = types; +} + +void SourceFormatterStyle::setMimeTypes(const QStringList& types) +{ + for ( auto t: types ) { + auto items = t.split('|'); + if ( items.size() != 2 ) { + continue; + } + m_mimeTypes << MimeHighlightPair{items.at(0), items.at(1)}; + } +} + +void SourceFormatterStyle::setOverrideSample(const QString &sample) +{ + m_overrideSample = sample; +} + +QString SourceFormatterStyle::overrideSample() const +{ + return m_overrideSample; +} + +SourceFormatterStyle::MimeList SourceFormatterStyle::mimeTypes() const +{ + return m_mimeTypes; +} + +QVariant SourceFormatterStyle::mimeTypesVariant() const +{ + QStringList result; + for ( const auto& item: m_mimeTypes ) { + result << item.mimeType + "|" + item.highlightMode; + } + return QVariant::fromValue(result); +} + +bool SourceFormatterStyle::supportsLanguage(const QString &language) const +{ + for ( const auto& item: m_mimeTypes ) { + if ( item.highlightMode == language ) { + return true; + } + } + return false; +} + +QString SourceFormatterStyle::modeForMimetype(const KMimeType::Ptr& mime) const +{ + for ( const auto& item: mimeTypes() ) { + if ( mime->is(item.mimeType) ) { + return item.highlightMode; + } + } + return QString(); +} + +void SourceFormatterStyle::copyDataFrom(SourceFormatterStyle *other) +{ + m_content = other->content(); + m_mimeTypes = other->mimeTypes(); + m_overrideSample = other->overrideSample(); +} + QString ISourceFormatter::optionMapToString(const QMap &map) { QString options; QMap::const_iterator it = map.constBegin(); for (; it != map.constEnd(); ++it) { options += it.key(); options += '='; options += it.value().toString(); options += ','; } return options; } QMap ISourceFormatter::stringToOptionMap(const QString &options) { QMap map; QStringList pairs = options.split(',', QString::SkipEmptyParts); QStringList::const_iterator it; for (it = pairs.constBegin(); it != pairs.constEnd(); ++it) { QStringList bits = (*it).split('='); map[bits[0]] = bits[1]; } return map; } QString ISourceFormatter::missingExecutableMessage(const QString &name) { return i18n("The executable %1 cannot be found. Please make sure" " it is installed and can be executed.
" "The plugin will not work until you fix this problem.", "" + name + ""); } } // kate: indent-mode cstyle; space-indent off; tab-width 4; diff --git a/interfaces/isourceformatter.h b/interfaces/isourceformatter.h index c7e600db80..fcac0a4356 100644 --- a/interfaces/isourceformatter.h +++ b/interfaces/isourceformatter.h @@ -1,193 +1,229 @@ /* This file is part of KDevelop 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. */ #ifndef KDEVPLATFORM_ISOURCEFORMATTER_H #define KDEVPLATFORM_ISOURCEFORMATTER_H #include #include #include #include "interfacesexport.h" namespace KDevelop { class KDEVPLATFORMINTERFACES_EXPORT SourceFormatterStyle { public: + struct MimeHighlightPair { + QString mimeType; + QString highlightMode; + }; + typedef QVector MimeList; + SourceFormatterStyle(); SourceFormatterStyle( const QString& name ); void setContent( const QString& content ); void setCaption( const QString& caption ); QString content() const; QString caption() const; QString name() const; QString description() const; void setDescription( const QString& desc ); bool usePreview() const; void setUsePreview(bool use); + + void setMimeTypes( const MimeList& types ); + void setMimeTypes( const QStringList& types ); + + /// Provides the possibility to the item to return a better-suited + /// code sample. If empty, the default is used. + QString overrideSample() const; + void setOverrideSample( const QString& sample ); + + MimeList mimeTypes() const; + /// mime types as a QVariantList, type and mode separated by | in strings + QVariant mimeTypesVariant() const; + bool supportsLanguage(const QString& language) const; + + /// get the language / highlight mode for a given @p mime + QString modeForMimetype(const KMimeType::Ptr& mime) const; + + /// Copy content, mime types and code sample from @p other. + void copyDataFrom(SourceFormatterStyle *other); + private: bool m_usePreview; QString m_name; QString m_caption; QString m_content; QString m_description; + QString m_overrideSample; + MimeList m_mimeTypes; }; +/** + * @brief An object describing a style associated with a plugin + * which can deal with this style. + */ +struct SourceFormatterStyleItem { + QString engine; + SourceFormatterStyle style; +}; + +typedef QVector SourceFormatterItemList; + /** * @short A widget to edit a style * A plugin should inherit this widget to create a widget to * edit a style. * @author Cédric Pasteur */ class KDEVPLATFORMINTERFACES_EXPORT SettingsWidget : public QWidget { Q_OBJECT public: SettingsWidget(QWidget *parent = 0); virtual ~SettingsWidget(); /** This function is called after the creation of the dialog. * it should initialise the widgets with the values corresponding to * the predefined style \arg name if it's not empty, or * to the string \arg content. */ virtual void load(const SourceFormatterStyle&) = 0; /** \return A string representing the state of the config. */ virtual QString save() = 0; Q_SIGNALS: /** Emits this signal when a setting was changed and the preview * needs to be updated. \arg text is the text that will be shown in the * editor. One might want to show different text * according to the different pages shown in the widget. * Text should already be formatted. */ void previewTextChanged(const QString &text); }; /** * @short An interface for a source beautifier * An example of a plugin using an external executable to do * the formatting can be found in kdevelop/plugins/formatters/indent_plugin.cpp. * @author Cédric Pasteur */ class KDEVPLATFORMINTERFACES_EXPORT ISourceFormatter { public: virtual ~ISourceFormatter(); /** \return The name of the plugin. This should contain only * ASCII chars and no spaces. This will be used internally to identify * the plugin. */ virtual QString name() = 0; /** \return A caption describing the plugin. */ virtual QString caption() = 0; /** \return A more complete description of the plugin. * The string should be written in Rich text. It can eg contain * a link to the project homepage. */ virtual QString description() = 0; - /** \return The highlighting mode for Kate corresponding to this mime. - */ - virtual QString highlightModeForMime(const KMimeType::Ptr &mime) = 0; - /** Formats using the current style. * @param text The text to format * @param url The URL to which the text belongs (its contents must not be changed). * @param leftContext The context at the left side of the text. If it is in another line, it must end with a newline. * @param rightContext The context at the right side of the text. If it is in the next line, it must start with a newline. * * If the source-formatter cannot work correctly with the context, it will just return the input text. */ virtual QString formatSource(const QString &text, const KUrl& url, const KMimeType::Ptr &mime, const QString& leftContext = QString(), const QString& rightContext = QString()) = 0; /** * Format with the given style, this is mostly for the kcm to format the preview text * Its a bit of a hassle that this needs to be public API, but I can't find a better way * to do this. */ virtual QString formatSourceWithStyle( SourceFormatterStyle, const QString& text, const KUrl& url, const KMimeType::Ptr &mime, const QString& leftContext = QString(), const QString& rightContext = QString() ) = 0; /** \return A map of predefined styles (a key and a caption for each type) */ virtual QList predefinedStyles() = 0; /** \return The widget to edit a style. */ virtual SettingsWidget* editStyleWidget(const KMimeType::Ptr &mime) = 0; /** \return The text used in the config dialog to preview the current style. */ - virtual QString previewText(const KMimeType::Ptr &mime) = 0; + virtual QString previewText(const SourceFormatterStyle* style, const KMimeType::Ptr &mime) = 0; struct Indentation { Indentation() : indentationTabWidth(0), indentWidth(0) { } // If this indentation is really valid bool isValid() const { return indentationTabWidth != 0 || indentWidth != 0; } // The length of one tab used for indentation. // Zero if unknown, -1 if tabs should not be used for indentation int indentationTabWidth; // The number of columns that equal one indentation level. // If this is zero, the default should be used. int indentWidth; }; /** \return The indentation of the style applicable for the given url. */ virtual Indentation indentation(const KUrl& url) = 0; /** \return A string representing the map. Values are written in the form * key=value and separated with ','. */ static QString optionMapToString(const QMap &map); /** \return A map corresponding to the string, that was created with * \ref optionMapToString. */ static QMap stringToOptionMap(const QString &option); /** \return A message to display when an executable needed by a * plugin is missing. This should be returned as description * if a needed executable is not found. */ static QString missingExecutableMessage(const QString &name); }; } Q_DECLARE_INTERFACE(KDevelop::ISourceFormatter, "org.kdevelop.ISourceFormatter") +Q_DECLARE_TYPEINFO(KDevelop::SourceFormatterStyle::MimeHighlightPair, Q_MOVABLE_TYPE); #endif // KDEVPLATFORM_ISOURCEFORMATTER_H // kate: indent-mode cstyle; space-indent off; tab-width 4; diff --git a/language/interfaces/ilanguagesupport.cpp b/language/interfaces/ilanguagesupport.cpp index 1d50d9495d..5797511f19 100644 --- a/language/interfaces/ilanguagesupport.cpp +++ b/language/interfaces/ilanguagesupport.cpp @@ -1,81 +1,86 @@ /*************************************************************************** * Copyright 2008 David Nolden * * * * 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 Library 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 "ilanguagesupport.h" #include "../duchain/duchain.h" #include "../editor/simplerange.h" #include #include #include namespace KDevelop { TopDUContext* ILanguageSupport::standardContext(const KUrl& url, bool proxyContext) { Q_UNUSED(proxyContext) return DUChain::self()->chainForDocument(url); } SimpleRange ILanguageSupport::specialLanguageObjectRange(const KUrl& url, const SimpleCursor& position) { Q_UNUSED(url) Q_UNUSED(position) return SimpleRange::invalid(); } QPair ILanguageSupport::specialLanguageObjectJumpCursor(const KUrl& url, const SimpleCursor& position) { Q_UNUSED(url) Q_UNUSED(position) return QPair(KUrl(), SimpleCursor::invalid()); } QWidget* ILanguageSupport::specialLanguageObjectNavigationWidget(const KUrl& url, const SimpleCursor& position) { Q_UNUSED(url) Q_UNUSED(position) return 0; } ICodeHighlighting* ILanguageSupport::codeHighlighting() const { return 0; } ICreateClassHelper* ILanguageSupport::createClassHelper() const { return 0; } ILanguage* ILanguageSupport::language() { return ICore::self()->languageController()->language(name()); } DocumentChangeTracker* ILanguageSupport::createChangeTrackerForDocument ( KTextEditor::Document* document ) const { return new DocumentChangeTracker(document); } ILanguageSupport::WhitespaceSensitivity ILanguageSupport::whitespaceSensititivy() const { return ILanguageSupport::Insensitive; } +SourceFormatterItemList ILanguageSupport::sourceFormatterItems() const +{ + return SourceFormatterItemList(); +} + QString ILanguageSupport::indentationSample() const { return ""; } } diff --git a/language/interfaces/ilanguagesupport.h b/language/interfaces/ilanguagesupport.h index 74892d5aa9..5c8747b0cf 100644 --- a/language/interfaces/ilanguagesupport.h +++ b/language/interfaces/ilanguagesupport.h @@ -1,134 +1,147 @@ /*************************************************************************** * Copyright 2007 Alexander Dymo * * * * 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 Library 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. * ***************************************************************************/ #ifndef KDEVPLATFORM_ILANGUAGESUPPORT_H #define KDEVPLATFORM_ILANGUAGESUPPORT_H #include #include "../editor/simplerange.h" #include "../languageexport.h" +#include "interfaces/isourceformatter.h" + namespace KDevelop { class IndexedString; class ParseJob; class ILanguage; class TopDUContext; class DocumentRange; class SimpleCursor; class SimpleRange; class ICodeHighlighting; class DocumentChangeTracker; class ICreateClassHelper; class KDEVPLATFORMLANGUAGE_EXPORT ILanguageSupport { public: virtual ~ILanguageSupport() {} /** @return the name of the language.*/ virtual QString name() const = 0; /** @return the parse job that is used by background parser to parse given @p url.*/ virtual ParseJob *createParseJob(const IndexedString &url) = 0; /** @return the language for this support.*/ virtual ILanguage *language(); /** * Only important for languages that can parse multiple different versions of a file, like C++ due to the preprocessor. * The default-implementation for other languages is "return DUChain::chainForDocument(url);" * * @param proxyContext Whether the returned context should be a proxy-contexts. In C++, a proxy-contexts has no direct content. * It mainly just imports an actual content-context, and it holds all the imports. It can also represent * multiple different versions of the same content in the eyes of the preprocessor. Also, a proxy-context may contain the problem- * descriptions of preprocessor problems. * The proxy-context should be preferred whenever the problem-list is required, or for deciding whether a document needs to be updated * (only the proxy-context knows about all the dependencies, since it contains most of the imports) * * @warning The DUChain must be locked before calling this, @see KDevelop::DUChainReadLocker * * @return the standard context used by this language for the given @param url. * */ virtual TopDUContext *standardContext(const KUrl& url, bool proxyContext = false); /** * Should return a code-highlighting instance for this language, or zero. */ virtual ICodeHighlighting* codeHighlighting() const; /** * Should return a document change-tracker for this language that tracks the changes in the given document * */ virtual DocumentChangeTracker* createChangeTrackerForDocument(KTextEditor::Document* document) const; /** * Should return a class creating helper for this language, or zero. * * If zero is returned, a default class helper will be created. * Reimplementing this method is therefore not necessary to have classes created in this language. * */ virtual ICreateClassHelper* createClassHelper() const; /** * The following functions are used to allow navigation-features, tooltips, etc. for non-duchain language objects. * In C++, they are used to allow highlighting and navigation of macro-uses. * */ /**Should return the local range within the given url that belongs to the *special language-object that contains @param position, or (KUrl(), SimpleRange:invalid()) */ virtual SimpleRange specialLanguageObjectRange(const KUrl& url, const SimpleCursor& position); /**Should return the source-range and source-document that the *special language-object that contains @param position refers to, or SimpleRange:invalid(). */ virtual QPair specialLanguageObjectJumpCursor(const KUrl& url, const SimpleCursor& position); /**Should return a navigation-widget for the *special language-object that contains @param position refers, or 0. *If you setProperty("DoNotCloseOnCursorMove", true) on the widget returned, *then the widget will not close when the cursor moves in the document, which *enables you to change the document contents from the widget without immediately closing the widget.*/ virtual QWidget* specialLanguageObjectNavigationWidget(const KUrl& url, const SimpleCursor& position); /**Should return a tiny piece of code which makes it possible for KDevelop to derive the indentation *settings from an automatic source formatter. Example for C++: "class C{\n class D {\n void c() {\n int m;\n }\n }\n};\n" *The sample must be completely unindented (no line must start with leading whitespace), *and it must contain at least 4 indentation levels! *The default implementation returns an empty string.*/ virtual QString indentationSample() const; + + /** + * Can return a list of source formatting items for this language. + * For example, if your language wants to use the CustomScript engine with + * a specific executable, return an item with "customscript" as the engine + * and a style describing your options as the style (in this case, especially + * the command to execute in the "content" member). + * Multiple items can be returned. Make sure to set the mime type(s) of your language + * on the returned items. + */ + virtual SourceFormatterItemList sourceFormatterItems() const; enum WhitespaceSensitivity { Insensitive = 0, IndentOnly = 1, Sensitive = 2 }; /**Specifies whether this language is sensitive to whitespace changes. * - The default "Insensitive" will only schedule a document for reparsing when * a change in a non-whitespace area happens (non-whitespace chars added or whitespace * added where it was surrounded by characters) * - "IndentOnly" will additionally schedule the document for reparsing if a whitespace * change occurs at the beginning of the line (more exactly, if all characters before the * changed ones are whitespace) * - "Sensitive" will always schedule the document for reparsing, no matter what was changed. */ virtual WhitespaceSensitivity whitespaceSensititivy() const; }; } Q_DECLARE_INTERFACE( KDevelop::ILanguageSupport, "org.kdevelop.ILanguageSupport") #endif diff --git a/shell/settings/editstyledialog.cpp b/shell/settings/editstyledialog.cpp index 3b019bb4db..cf1e1a9d20 100644 --- a/shell/settings/editstyledialog.cpp +++ b/shell/settings/editstyledialog.cpp @@ -1,117 +1,118 @@ /* This file is part of KDevelop * Copyright (C) 2008 Cédric Pasteur 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 "editstyledialog.h" #include #include #include #include #include #include #include #include using KDevelop::ISourceFormatter; using KDevelop::SettingsWidget; using KDevelop::SourceFormatterStyle; EditStyleDialog::EditStyleDialog(ISourceFormatter *formatter, const KMimeType::Ptr &mime, const SourceFormatterStyle &style, QWidget *parent) : KDialog(parent), m_sourceFormatter(formatter), m_mimeType(mime), m_style( style ) { m_content = new QWidget(); m_ui.setupUi(m_content); setMainWidget(m_content); m_settingsWidget = m_sourceFormatter->editStyleWidget(mime); init(); if (m_settingsWidget) m_settingsWidget->load(style); } EditStyleDialog::~EditStyleDialog() { } void EditStyleDialog::init() { // add plugin settings widget if(m_settingsWidget) { QVBoxLayout *layout = new QVBoxLayout(m_ui.settingsWidgetParent); layout->addWidget(m_settingsWidget); m_ui.settingsWidgetParent->setLayout(layout); connect(m_settingsWidget, SIGNAL(previewTextChanged(QString)), this, SLOT(updatePreviewText(QString))); } // add texteditor preview KTextEditor::Editor *editor = KTextEditor::EditorChooser::editor(); if (!editor) KMessageBox::error(this, i18n("A KDE text-editor component could not be found.\n" "Please check your KDE installation.")); m_document = editor->createDocument(this); m_document->setReadWrite(false); - QString mode = m_sourceFormatter->highlightModeForMime(m_mimeType); - m_document->setHighlightingMode(mode); + m_document->setHighlightingMode(m_style.modeForMimetype(m_mimeType)); m_view = qobject_cast( m_document->createView(m_ui.textEditor)); QVBoxLayout *layout2 = new QVBoxLayout(m_ui.textEditor); layout2->addWidget(m_view); m_ui.textEditor->setLayout(layout2); m_view->show(); KTextEditor::ConfigInterface *iface = qobject_cast(m_view); if (iface) { iface->setConfigValue("dynamic-word-wrap", false); iface->setConfigValue("icon-bar", false); } - if (m_sourceFormatter) - updatePreviewText(m_sourceFormatter->previewText(m_mimeType)); + if (m_sourceFormatter) { + QString text = m_sourceFormatter->previewText(&m_style, m_mimeType); + updatePreviewText(text); + } } void EditStyleDialog::updatePreviewText(const QString &text) { m_document->setReadWrite(true); m_style.setContent( content() ); if (m_sourceFormatter) { m_document->setText(m_sourceFormatter->formatSourceWithStyle( m_style, text, KUrl(), m_mimeType )); } else { m_document->setText( i18n( "No Source Formatter available" ) ); } m_document->activeView()->setCursorPosition( KTextEditor::Cursor( 0, 0 ) ); m_document->setReadWrite(false); } QString EditStyleDialog::content() { if(m_settingsWidget) return m_settingsWidget->save(); return QString(); } #include "editstyledialog.moc" // kate: indent-mode cstyle; space-indent off; tab-width 4; diff --git a/shell/settings/sourceformattersettings.cpp b/shell/settings/sourceformattersettings.cpp index fda6cde29f..4bbb895650 100644 --- a/shell/settings/sourceformattersettings.cpp +++ b/shell/settings/sourceformattersettings.cpp @@ -1,532 +1,518 @@ /* This file is part of KDevelop * Copyright (C) 2008 Cédric Pasteur 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 "sourceformattersettings.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include #include #include #include "editstyledialog.h" #define STYLE_ROLE (Qt::UserRole+1) K_PLUGIN_FACTORY(SourceFormatterSettingsFactory, registerPlugin();) K_EXPORT_PLUGIN(SourceFormatterSettingsFactory("kcm_kdevsourceformattersettings")) using KDevelop::Core; using KDevelop::ISourceFormatter; using KDevelop::SourceFormatterStyle; using KDevelop::SourceFormatterController; - +using KDevelop::SourceFormatter; const QString SourceFormatterSettings::userStylePrefix( "User" ); -SourceFormatter::~SourceFormatter() -{ - qDeleteAll(styles); -} - LanguageSettings::LanguageSettings() : selectedFormatter(0), selectedStyle(0) { } SourceFormatterSettings::SourceFormatterSettings(QWidget *parent, const QVariantList &args) : KCModule(SourceFormatterSettingsFactory::componentData(), parent, args) { setupUi(this); connect( cbLanguages, SIGNAL(currentIndexChanged(int)), SLOT(selectLanguage(int)) ); connect( cbFormatters, SIGNAL(currentIndexChanged(int)), SLOT(selectFormatter(int)) ); connect( chkKateModelines, SIGNAL(toggled(bool)), SLOT(somethingChanged()) ); connect( chkKateOverrideIndentation, SIGNAL(toggled(bool)), SLOT(somethingChanged()) ); connect( styleList, SIGNAL(currentRowChanged(int)), SLOT(selectStyle(int)) ); connect( btnDelStyle, SIGNAL(clicked()), SLOT(deleteStyle()) ); connect( btnNewStyle, SIGNAL(clicked()), SLOT(newStyle()) ); connect( btnEditStyle, SIGNAL(clicked()), SLOT(editStyle()) ); connect( styleList, SIGNAL(itemChanged(QListWidgetItem*)), SLOT(styleNameChanged(QListWidgetItem*)) ); KTextEditor::Editor *editor = KTextEditor::EditorChooser::editor(); if (!editor) KMessageBox::error(this, i18n("A KDE text-editor component could not be found.\n" "Please check your KDE installation.")); m_document = editor->createDocument(this); m_document->setReadWrite(false); KTextEditor::View* view = qobject_cast(m_document->createView(textEditor)); QVBoxLayout *layout2 = new QVBoxLayout(textEditor); layout2->addWidget(view); textEditor->setLayout(layout2); view->show(); KTextEditor::ConfigInterface *iface = qobject_cast(view); if (iface) { iface->setConfigValue("dynamic-word-wrap", false); iface->setConfigValue("icon-bar", false); } } SourceFormatterSettings::~SourceFormatterSettings() { qDeleteAll(formatters); } void selectAvailableStyle(LanguageSettings& lang) { Q_ASSERT(!lang.selectedFormatter->styles.empty()); lang.selectedStyle = *lang.selectedFormatter->styles.begin(); } void SourceFormatterSettings::load() { SourceFormatterController* fmtctrl = Core::self()->sourceFormatterControllerInternal(); foreach( KDevelop::IPlugin* plugin, KDevelop::ICore::self()->pluginController()->allPluginsForExtension( "org.kdevelop.ISourceFormatter" ) ) { KDevelop::ISourceFormatter* ifmt = plugin->extension(); KPluginInfo info = KDevelop::Core::self()->pluginControllerInternal()->pluginInfo( plugin ); - SourceFormatter* formatter; + KDevelop::SourceFormatter* formatter; FormatterMap::const_iterator iter = formatters.constFind(ifmt->name()); if (iter == formatters.constEnd()) { - formatter = new SourceFormatter(); - formatter->formatter = ifmt; + formatter = fmtctrl->createFormatterForPlugin(ifmt); formatters[ifmt->name()] = formatter; - // 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 = fmtctrl->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 ); - s->setCaption( stylegrp.readEntry( SourceFormatterController::styleCaptionKey, "" ) ); - s->setContent( stylegrp.readEntry( SourceFormatterController::styleContentKey, "" ) ); - formatter->styles[ s->name() ] = s; - } - } } else { formatter = iter.value(); } - foreach( const QString& mime, info.property( SourceFormatterController::supportedMimeTypesKey ).toStringList() ) - { - KMimeType::Ptr mimePtr = KMimeType::mimeType(mime); - if (!mimePtr) { - kWarning() << "plugin" << info.name() << "supports unknown mimetype entry" << mime; - continue; + for( const SourceFormatterStyle* style: formatter->styles ) { + for ( const SourceFormatterStyle::MimeHighlightPair& item: style->mimeTypes() ) { + KMimeType::Ptr mimePtr = KMimeType::mimeType(item.mimeType); + if (!mimePtr) { + kWarning() << "plugin" << info.name() << "supports unknown mimetype entry" << item.mimeType; + continue; + } + QString languageName = item.highlightMode; + LanguageSettings& l = languages[languageName]; + l.mimetypes.append( mimePtr ); + l.formatters.insert( formatter ); } - QString languageName = formatter->formatter->highlightModeForMime(mimePtr); - LanguageSettings& l = languages[languageName]; - l.mimetypes.append( mimePtr ); - l.formatters.insert( formatter ); } } // Sort the languages, preferring firstly active, then loaded languages QList sortedLanguages; foreach( KDevelop::ILanguage* language, KDevelop::ICore::self()->languageController()->activeLanguages() + KDevelop::ICore::self()->languageController()->loadedLanguages() ) - if( languages.contains( language->name() ) && !sortedLanguages.contains(language->name()) ) + { + if( languages.contains( language->name() ) && !sortedLanguages.contains(language->name()) ) { sortedLanguages.push_back( language->name() ); + } + } foreach( const QString& name, languages.keys() ) if( !sortedLanguages.contains( name ) ) sortedLanguages.push_back( name ); foreach( const QString& name, sortedLanguages ) { // Pick the first appropriate mimetype for this language KConfigGroup grp = fmtctrl->configuration(); LanguageSettings& l = languages[name]; foreach (const KMimeType::Ptr& mimetype, l.mimetypes) { QStringList formatterAndStyleName = grp.readEntry( mimetype->name(), "" ).split( "||", QString::KeepEmptyParts ); FormatterMap::const_iterator formatterIter = formatters.constFind(formatterAndStyleName.first()); if (formatterIter == formatters.constEnd()) { kDebug() << "Reference to unknown formatter" << formatterAndStyleName.first(); Q_ASSERT(!l.formatters.empty()); // otherwise there should be no entry for 'name' l.selectedFormatter = *l.formatters.begin(); selectAvailableStyle(l); } else { l.selectedFormatter = formatterIter.value(); SourceFormatter::StyleMap::const_iterator styleIter = l.selectedFormatter->styles.constFind(formatterAndStyleName.at( 1 )); if (styleIter == l.selectedFormatter->styles.constEnd()) { kDebug() << "No style" << formatterAndStyleName.at( 1 ) << "found for formatter" << formatterAndStyleName.first(); selectAvailableStyle(l); } else { l.selectedStyle = styleIter.value(); } } break; } if (!l.selectedFormatter) { Q_ASSERT(!l.formatters.empty()); l.selectedFormatter = *l.formatters.begin(); } if (!l.selectedStyle) { selectAvailableStyle(l); } } bool b = blockSignals( true ); cbLanguages->blockSignals( !b ); cbFormatters->blockSignals( !b ); styleList->blockSignals( !b ); chkKateModelines->blockSignals( !b ); chkKateOverrideIndentation->blockSignals( !b ); cbLanguages->clear(); cbFormatters->clear(); styleList->clear(); chkKateModelines->setChecked( fmtctrl->configuration().readEntry( SourceFormatterController::kateModeLineConfigKey, false ) ); chkKateOverrideIndentation->setChecked( fmtctrl->configuration().readEntry( SourceFormatterController::kateOverrideIndentationConfigKey, false ) ); foreach( const QString& name, sortedLanguages ) { cbLanguages->addItem( name ); } if( cbLanguages->count() == 0 ) { cbLanguages->setEnabled( false ); selectLanguage( -1 ); } else { cbLanguages->setCurrentIndex( 0 ); selectLanguage( 0 ); } updatePreview(); blockSignals( b ); cbLanguages->blockSignals( b ); cbFormatters->blockSignals( b ); styleList->blockSignals( b ); chkKateModelines->blockSignals( b ); chkKateOverrideIndentation->blockSignals( b ); } void SourceFormatterSettings::save() { KConfigGroup grp = Core::self()->sourceFormatterControllerInternal()->configuration(); for ( LanguageMap::const_iterator iter = languages.constBegin(); iter != languages.constEnd(); ++iter ) { foreach( const KMimeType::Ptr& mime, iter.value().mimetypes ) { grp.writeEntry( mime->name(), QString("%1||%2").arg(iter.value().selectedFormatter->formatter->name()).arg( iter.value().selectedStyle->name() ) ); } } foreach( SourceFormatter* fmt, formatters ) { KConfigGroup fmtgrp = grp.group( fmt->formatter->name() ); // delete all styles so we don't leave any behind when all user styles are deleted foreach( const QString& subgrp, fmtgrp.groupList() ) { if( subgrp.startsWith( userStylePrefix ) ) { fmtgrp.deleteGroup( subgrp ); } } foreach( const SourceFormatterStyle* style, fmt->styles ) { if( style->name().startsWith( userStylePrefix ) ) { KConfigGroup stylegrp = fmtgrp.group( style->name() ); stylegrp.writeEntry( SourceFormatterController::styleCaptionKey, style->caption() ); stylegrp.writeEntry( SourceFormatterController::styleContentKey, style->content() ); + stylegrp.writeEntry( SourceFormatterController::styleMimeTypesKey, style->mimeTypesVariant() ); + stylegrp.writeEntry( SourceFormatterController::styleSampleKey, style->overrideSample() ); } } } grp.writeEntry( SourceFormatterController::kateModeLineConfigKey, chkKateModelines->isChecked() ); grp.writeEntry( SourceFormatterController::kateOverrideIndentationConfigKey, chkKateOverrideIndentation->isChecked() ); grp.sync(); Core::self()->sourceFormatterControllerInternal()->settingsChanged(); } void SourceFormatterSettings::enableStyleButtons() { bool userEntry = styleList->currentItem() && styleList->currentItem()->data( STYLE_ROLE ).toString().startsWith( userStylePrefix ); QString languageName = cbLanguages->currentText(); QMap< QString, LanguageSettings >::const_iterator it = languages.constFind(languageName); bool hasEditWidget = false; if (it != languages.constEnd()) { const LanguageSettings& l = it.value(); Q_ASSERT(l.selectedFormatter); ISourceFormatter* fmt = l.selectedFormatter->formatter; hasEditWidget = ( fmt && fmt->editStyleWidget( l.mimetypes.first() ) ); } btnDelStyle->setEnabled( userEntry ); btnEditStyle->setEnabled( userEntry && hasEditWidget ); btnNewStyle->setEnabled( cbFormatters->currentIndex() >= 0 && hasEditWidget ); } void SourceFormatterSettings::selectLanguage( int idx ) { cbFormatters->clear(); if( idx < 0 ) { cbFormatters->setEnabled( false ); selectFormatter( -1 ); return; } cbFormatters->setEnabled( true ); bool b = cbFormatters->blockSignals( true ); LanguageSettings& l = languages[cbLanguages->itemText( idx )]; foreach( const SourceFormatter* fmt, l.formatters ) { cbFormatters->addItem( fmt->formatter->caption(), fmt->formatter->name() ); } cbFormatters->setCurrentIndex(cbFormatters->findData(l.selectedFormatter->formatter->name())); cbFormatters->blockSignals(b); selectFormatter( cbFormatters->currentIndex() ); emit changed( true ); } void SourceFormatterSettings::selectFormatter( int idx ) { styleList->clear(); if( idx < 0 ) { styleList->setEnabled( false ); enableStyleButtons(); return; } styleList->setEnabled( true ); LanguageSettings& l = languages[ cbLanguages->currentText() ]; Q_ASSERT( idx < l.formatters.size() ); FormatterMap::const_iterator formatterIter = formatters.constFind(cbFormatters->itemData( idx ).toString()); Q_ASSERT( formatterIter != formatters.constEnd() ); Q_ASSERT( l.formatters.contains(formatterIter.value()) ); if (l.selectedFormatter != formatterIter.value()) { l.selectedFormatter = formatterIter.value(); l.selectedStyle = 0; // will hold 0 until a style is picked } foreach( const SourceFormatterStyle* style, formatterIter.value()->styles ) { + if ( ! style->supportsLanguage(cbLanguages->currentText())) { + // do not list items which do not support the selected language + continue; + } QListWidgetItem* item = addStyle( *style ); if (style == l.selectedStyle) { styleList->setCurrentItem(item); } } if (l.selectedStyle == 0) { styleList->setCurrentRow(0); } enableStyleButtons(); emit changed( true ); } void SourceFormatterSettings::selectStyle( int row ) { if( row < 0 ) { enableStyleButtons(); return; } styleList->setCurrentRow( row ); LanguageSettings& l = languages[ cbLanguages->currentText() ]; l.selectedStyle = l.selectedFormatter->styles[styleList->item( row )->data( STYLE_ROLE ).toString()]; enableStyleButtons(); updatePreview(); emit changed( true ); } void SourceFormatterSettings::deleteStyle() { Q_ASSERT( styleList->currentRow() >= 0 ); QListWidgetItem* item = styleList->currentItem(); LanguageSettings& l = languages[ cbLanguages->currentText() ]; SourceFormatter* fmt = l.selectedFormatter; SourceFormatter::StyleMap::iterator styleIter = fmt->styles.find(item->data( STYLE_ROLE ).toString()); QStringList otherLanguageNames; QList otherlanguages; for ( LanguageMap::iterator languageIter = languages.begin(); languageIter != languages.end(); ++languageIter ) { if ( &languageIter.value() != &l && languageIter.value().selectedStyle == styleIter.value() ) { otherLanguageNames.append(languageIter.key()); otherlanguages.append(&languageIter.value()); } } if (!otherLanguageNames.empty() && KMessageBox::warningContinueCancel(this, i18n("The style %1 is also used for the following languages:\n%2.\nAre you sure you want to delete it?", styleIter.value()->caption(), otherLanguageNames.join("\n")), i18n("Style being deleted")) != KMessageBox::Continue) { return; } styleList->takeItem( styleList->currentRow() ); fmt->styles.erase(styleIter); delete item; selectStyle( styleList->count() > 0 ? 0 : -1 ); foreach (LanguageSettings* lang, otherlanguages) { selectAvailableStyle(*lang); } updatePreview(); emit changed( true ); } void SourceFormatterSettings::editStyle() { QString language = cbLanguages->currentText(); Q_ASSERT( languages.contains( language ) ); LanguageSettings& l = languages[ language ]; SourceFormatter* fmt = l.selectedFormatter; KMimeType::Ptr mimetype = l.mimetypes.first(); if( fmt->formatter->editStyleWidget( mimetype ) != 0 ) { EditStyleDialog dlg( fmt->formatter, mimetype, *l.selectedStyle, this ); if( dlg.exec() == QDialog::Accepted ) { l.selectedStyle->setContent(dlg.content()); } updatePreview(); emit changed( true ); } } void SourceFormatterSettings::newStyle() { QListWidgetItem* item = styleList->currentItem(); LanguageSettings& l = languages[ cbLanguages->currentText() ]; SourceFormatter* fmt = l.selectedFormatter; int idx = 0; for( int i = 0; i < styleList->count(); i++ ) { QString name = styleList->item( i )->data( STYLE_ROLE ).toString(); if( name.startsWith( userStylePrefix ) && name.mid( userStylePrefix.length() ).toInt() >= idx ) { idx = name.mid( userStylePrefix.length() ).toInt(); } } // Increase number for next style idx++; SourceFormatterStyle* s = new SourceFormatterStyle( QString( "%1%2" ).arg( userStylePrefix ).arg( idx ) ); if( item ) { SourceFormatterStyle* existstyle = fmt->styles[ item->data( STYLE_ROLE ).toString() ]; s->setCaption( i18n( "New %1", existstyle->caption() ) ); - s->setContent( existstyle->content() ); + s->copyDataFrom( existstyle ); } else { s->setCaption( i18n( "New Style" ) ); } fmt->styles[ s->name() ] = s; QListWidgetItem* newitem = addStyle( *s ); selectStyle( styleList->row( newitem ) ); styleList->editItem( newitem ); emit changed( true ); } void SourceFormatterSettings::styleNameChanged( QListWidgetItem* item ) { if ( !item->isSelected() ) { return; } LanguageSettings& l = languages[ cbLanguages->currentText() ]; l.selectedStyle->setCaption( item->text() ); emit changed( true ); } QListWidgetItem* SourceFormatterSettings::addStyle( const SourceFormatterStyle& s ) { QListWidgetItem* item = new QListWidgetItem( styleList ); item->setText( s.caption() ); item->setData( STYLE_ROLE, s.name() ); if( s.name().startsWith( userStylePrefix ) ) { item->setFlags( item->flags() | Qt::ItemIsEditable ); } styleList->addItem( item ); return item; } void SourceFormatterSettings::updatePreview() { m_document->setReadWrite( true ); QString langName = cbLanguages->itemText( cbLanguages->currentIndex() ); if( !langName.isEmpty() ) { LanguageSettings& l = languages[ langName ]; SourceFormatter* fmt = l.selectedFormatter; SourceFormatterStyle* style = l.selectedStyle; descriptionLabel->setText( style->description() ); if( style->description().isEmpty() ) descriptionLabel->hide(); else descriptionLabel->show(); if( style->usePreview() ) { ISourceFormatter* ifmt = fmt->formatter; KMimeType::Ptr mime = l.mimetypes.first(); - m_document->setHighlightingMode( ifmt->highlightModeForMime( mime ) ); + m_document->setHighlightingMode( style->modeForMimetype( mime ) ); //NOTE: this is ugly, but otherwise kate might remove tabs again :-/ // see also: https://bugs.kde.org/show_bug.cgi?id=291074 KTextEditor::ConfigInterface* iface = qobject_cast(m_document); QVariant oldReplaceTabs; if (iface) { oldReplaceTabs = iface->configValue("replace-tabs"); iface->setConfigValue("replace-tabs", false); } - m_document->setText( ifmt->formatSourceWithStyle( *style, ifmt->previewText( mime ), KUrl(), mime ) ); + m_document->setText( ifmt->formatSourceWithStyle( *style, ifmt->previewText( style, mime ), KUrl(), mime ) ); if (iface) { iface->setConfigValue("replace-tabs", oldReplaceTabs); } previewLabel->show(); textEditor->show(); }else{ previewLabel->hide(); textEditor->hide(); } } else { m_document->setText( i18n( "No Language selected" ) ); } m_document->activeView()->setCursorPosition( KTextEditor::Cursor( 0, 0 ) ); m_document->setReadWrite( false ); } void SourceFormatterSettings::somethingChanged() { // Widgets are managed manually, so we have to explicitly tell KCModule // that we have some changes, otherwise it won't call "save" and/or will not activate // "Appy" unmanagedWidgetChangeState(true); } #include "sourceformattersettings.moc" diff --git a/shell/settings/sourceformattersettings.h b/shell/settings/sourceformattersettings.h index 4cb8c48703..a9e7c77472 100644 --- a/shell/settings/sourceformattersettings.h +++ b/shell/settings/sourceformattersettings.h @@ -1,98 +1,91 @@ /* This file is part of KDevelop * Copyright (C) 2008 Cédric Pasteur 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. */ #ifndef KDEVPLATFORM_SOURCEFORMATTERSETTINGS_H #define KDEVPLATFORM_SOURCEFORMATTERSETTINGS_H #include #include #include +#include + #include "ui_sourceformattersettings.h" class QListWidgetItem; namespace KTextEditor { class Document; } namespace KDevelop { class ISourceFormatter; class SourceFormatterStyle; } -struct SourceFormatter -{ - KDevelop::ISourceFormatter* formatter; - // style name -> style. style objects owned by this - typedef QMap StyleMap; - StyleMap styles; - ~SourceFormatter(); -}; - struct LanguageSettings { LanguageSettings(); QList mimetypes; - QSet formatters; + QSet formatters; // weak pointers to selected formatter and style, no ownership - SourceFormatter* selectedFormatter; // Should never be zero + KDevelop::SourceFormatter* selectedFormatter; // Should never be zero KDevelop::SourceFormatterStyle* selectedStyle; // TODO: can this be zero? Assume that not }; /** \short The settings modulefor the Source formatter plugin. * It supports predefined and custom styles. A live preview of the style * is shown on the right side of the page.s */ class SourceFormatterSettings : public KCModule, public Ui::SourceFormatterSettingsUI { Q_OBJECT public: SourceFormatterSettings( QWidget *parent, const QVariantList &args ); virtual ~SourceFormatterSettings(); public slots: virtual void load(); virtual void save(); private slots: void deleteStyle(); void editStyle(); void newStyle(); void selectLanguage( int ); void selectFormatter( int ); void selectStyle( int ); void styleNameChanged( QListWidgetItem* ); void somethingChanged(); private: void updatePreview(); QListWidgetItem* addStyle( const KDevelop::SourceFormatterStyle& s ); static const QString userStylePrefix; void enableStyleButtons(); // Language name -> language settings typedef QMap LanguageMap; LanguageMap languages; // formatter name -> formatter. Formatters owned by this - typedef QMap FormatterMap; + typedef QMap FormatterMap; FormatterMap formatters; KTextEditor::Document* m_document; }; #endif // KDEVPLATFORM_SOURCEFORMATTERSETTINGS_H diff --git a/shell/sourceformattercontroller.cpp b/shell/sourceformattercontroller.cpp index 7b063055b0..d9d9d6e04f 100644 --- a/shell/sourceformattercontroller.cpp +++ b/shell/sourceformattercontroller.cpp @@ -1,628 +1,659 @@ /* 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 "core.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "plugincontroller.h" #include 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::supportedMimeTypesKey = "X-KDevelop-SupportedMimeTypes"; +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 astyle 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 astyle 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 astyle 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() +KConfigGroup SourceFormatterController::configuration() const { return Core::self()->activeSession()->config()->group( "SourceFormatter" ); } -static ISourceFormatter* findFirstFormatterForMimeType( const KMimeType::Ptr& mime ) +ISourceFormatter* SourceFormatterController::findFirstFormatterForMimeType( const KMimeType::Ptr& mime ) const { static QHash 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 ); - if( info.property( SourceFormatterController::supportedMimeTypesKey ).toStringList().contains( mime->name() ) ) { - ISourceFormatter *formatter = p->extension(); - knownFormatters[mime->name()] = formatter; - return formatter; + ISourceFormatter *iformatter = p->extension(); + QSharedPointer 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( 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( "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(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 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(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(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) ); - s.setCaption( stylegrp.readEntry( styleCaptionKey, "" ) ); - s.setContent( stylegrp.readEntry( styleContentKey, "" ) ); + 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 "sourceformattercontroller.moc" // kate: indent-mode cstyle; space-indent off; tab-width 4; diff --git a/shell/sourceformattercontroller.h b/shell/sourceformattercontroller.h index ea6ef555e5..17e13c4a4a 100644 --- a/shell/sourceformattercontroller.h +++ b/shell/sourceformattercontroller.h @@ -1,123 +1,162 @@ /* 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. */ #ifndef KDEVPLATFORM_SOURCEFORMATTERCONTROLLER_H #define KDEVPLATFORM_SOURCEFORMATTERCONTROLLER_H #include #include #include #include +#include + #include #include #include #include #include "shellexport.h" class KAction; namespace KDevelop { class Context; class ContextMenuExtension; class ProjectBaseItem; class IDocument; class ISourceFormatter; class IPlugin; +struct SourceFormatter +{ + KDevelop::ISourceFormatter* formatter; + // style name -> style. style objects owned by this + typedef QMap StyleMap; + StyleMap styles; + // Get a list of supported mime types from the style map. + QSet supportedMimeTypes() const + { + QSet supported; + for ( auto style: styles ) { + for ( auto item: style->mimeTypes() ) { + supported.insert(item.mimeType); + } + } + return supported; + } + ~SourceFormatter() + { + qDeleteAll(styles); + }; +}; + /** \short A singleton class managing all source formatter plugins */ class KDEVPLATFORMSHELL_EXPORT SourceFormatterController : public ISourceFormatterController, public KXMLGUIClient { Q_OBJECT public: static const QString kateModeLineConfigKey; static const QString kateOverrideIndentationConfigKey; static const QString styleCaptionKey; static const QString styleContentKey; - static const QString supportedMimeTypesKey; + static const QString styleMimeTypesKey; + static const QString styleSampleKey; SourceFormatterController(QObject *parent = 0); virtual ~SourceFormatterController(); void initialize(); void cleanup(); //----------------- Public API defined in interfaces ------------------- /** \return The formatter corresponding to the language * of the document corresponding to the \arg url. */ ISourceFormatter* formatterForUrl(const KUrl &url); /** Loads and returns a source formatter for this mime type. * The language is then activated and the style is loaded. * The source formatter is then ready to use on a file. */ ISourceFormatter* formatterForMimeType(const KMimeType::Ptr &mime); /** \return Whether this mime type is supported by any plugin. */ bool isMimeTypeSupported(const KMimeType::Ptr &mime); + /** + * @brief Instantiate a Formatter for the given plugin and load its configuration. + * + * @param ifmt The ISourceFormatter interface of the plugin + * @return KDevelop::SourceFormatter* the SourceFormatter instance for the plugin, including config items + */ + SourceFormatter* createFormatterForPlugin(KDevelop::ISourceFormatter* ifmt) const; + + /** + * @brief Find the first formatter which supports a given mime type. + */ + ISourceFormatter* findFirstFormatterForMimeType( const KMimeType::Ptr& mime ) const; + KDevelop::ContextMenuExtension contextMenuExtension(KDevelop::Context* context); virtual KDevelop::SourceFormatterStyle styleForMimeType( const KMimeType::Ptr& mime ); - KConfigGroup configuration(); + KConfigGroup configuration() const; void settingsChanged(); virtual void disableSourceFormatting(bool disable); virtual bool sourceFormattingEnabled(); private Q_SLOTS: void activeDocumentChanged(KDevelop::IDocument *doc); void beautifySource(); void beautifyLine(); void formatFiles(); void documentLoaded( KDevelop::IDocument* ); private: /** \return A modeline string (to add at the end or the beginning of a file) * corresponding to the settings of the active language. */ QString addModelineForCurrentLang(QString input, const KUrl& url, const KMimeType::Ptr&); /** \return The name of kate indentation mode for the mime type. * examples are cstyle, python, etc. */ QString indentationMode(const KMimeType::Ptr &mime); void formatDocument(KDevelop::IDocument *doc, ISourceFormatter *formatter, const KMimeType::Ptr &mime); // Adapts the mode of the editor regarding indentation-style void adaptEditorIndentationMode(KDevelop::IDocument* doc, KDevelop::ISourceFormatter* formatter, bool ignoreModeline = false); void formatFiles(KUrl::List &list); // GUI actions KAction* m_formatTextAction; KAction* m_formatFilesAction; KAction* m_formatLine; QList m_prjItems; KUrl::List m_urls; bool m_enabled; }; } #endif // KDEVPLATFORM_SOURCEFORMATTERMANAGER_H // kate: indent-mode cstyle; space-indent off; tab-width 4;