diff --git a/formatters/astyle/astyle_plugin.cpp b/formatters/astyle/astyle_plugin.cpp index a227f65fb9..b39ffa31d3 100644 --- a/formatters/astyle/astyle_plugin.cpp +++ b/formatters/astyle/astyle_plugin.cpp @@ -1,265 +1,266 @@ /* This file is part of KDevelop * Copyright (C) 2008 Cédric Pasteur Copyright (C) 2001 Matthias Hölzer-Klüpfel This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "astyle_plugin.h" #include #include #include #include #include "astyle_formatter.h" #include "astyle_stringiterator.h" #include "astyle_preferences.h" #include #include using namespace KDevelop; K_PLUGIN_FACTORY_WITH_JSON(AStyleFactory, "kdevastyle.json", registerPlugin();) AStylePlugin::AStylePlugin(QObject *parent, const QVariantList&) : IPlugin(QStringLiteral("kdevastyle"), parent) , m_formatter(new AStyleFormatter()) { currentStyle = predefinedStyles().at(0); } AStylePlugin::~AStylePlugin() { } QString AStylePlugin::name() { // This needs to match the X-KDE-PluginInfo-Name entry from the .desktop file! return QStringLiteral("kdevastyle"); } QString AStylePlugin::caption() { return QStringLiteral("Artistic Style"); } QString AStylePlugin::description() { return i18n("Artistic Style is a source code indenter, formatter," " and beautifier for the C, C++, C# and Java programming languages.
" "Home Page: http://astyle.sourceforge.net"); } QString AStylePlugin::formatSourceWithStyle( SourceFormatterStyle s, const QString& text, const QUrl& /*url*/, const QMimeType& mime, const QString& leftContext, const QString& rightContext ) { if(mime.inherits(QStringLiteral("text/x-java"))) m_formatter->setJavaStyle(); else if(mime.inherits(QStringLiteral("text/x-csharp"))) m_formatter->setSharpStyle(); else m_formatter->setCStyle(); if( s.content().isEmpty() ) { m_formatter->predefinedStyle( s.name() ); } else { m_formatter->loadStyle( s.content() ); } return m_formatter->formatSource(text, leftContext, rightContext); } QString AStylePlugin::formatSource(const QString& text, const QUrl &url, const QMimeType& mime, const QString& leftContext, const QString& rightContext) { - return formatSourceWithStyle( ICore::self()->sourceFormatterController()->styleForMimeType( mime ), text, url, mime, leftContext, rightContext ); + auto style = ICore::self()->sourceFormatterController()->styleForUrl(url, mime); + return formatSourceWithStyle(style, text, url, mime, leftContext, rightContext); } static SourceFormatterStyle::MimeList supportedMimeTypes() { return { {QStringLiteral("text/x-c++src"), QStringLiteral("C++")}, {QStringLiteral("text/x-chdr"), QStringLiteral("C")}, {QStringLiteral("text/x-c++hdr"), QStringLiteral("C++")}, {QStringLiteral("text/x-csrc"), QStringLiteral("C")}, {QStringLiteral("text/x-java"), QStringLiteral("Java")}, {QStringLiteral("text/x-csharp"), QStringLiteral("C#")}, }; } SourceFormatterStyle predefinedStyle(const QString& name, const QString& caption = QString()) { SourceFormatterStyle st = SourceFormatterStyle( name ); st.setCaption( caption.isEmpty() ? name : caption ); AStyleFormatter fmt; fmt.predefinedStyle( name ); st.setContent( fmt.saveStyle() ); st.setMimeTypes(supportedMimeTypes()); st.setUsePreview(true); return st; } QList AStylePlugin::predefinedStyles() { return { predefinedStyle(QStringLiteral("ANSI")), predefinedStyle(QStringLiteral("GNU")), predefinedStyle(QStringLiteral("Java")), predefinedStyle(QStringLiteral("K&R"), QStringLiteral("Kernighan & Ritchie")), predefinedStyle(QStringLiteral("Linux")), predefinedStyle(QStringLiteral("Stroustrup")), predefinedStyle(QStringLiteral("Horstmann")), predefinedStyle(QStringLiteral("Whitesmith")), predefinedStyle(QStringLiteral("Banner")), predefinedStyle(QStringLiteral("1TBS")), predefinedStyle(QStringLiteral("KDELibs")), predefinedStyle(QStringLiteral("Qt")), }; } SettingsWidget* AStylePlugin::editStyleWidget(const QMimeType& mime) { AStylePreferences::Language lang = AStylePreferences::CPP; if(mime.inherits(QStringLiteral("text/x-java"))) lang = AStylePreferences::Java; else if(mime.inherits(QStringLiteral("text/x-csharp"))) lang = AStylePreferences::CSharp; return new AStylePreferences(lang); } QString AStylePlugin::previewText(const SourceFormatterStyle& /*style*/, const QMimeType& /*mime*/) { return "// Indentation\n" + indentingSample() + "\t// Formatting\n" + formattingSample(); } AStylePlugin::Indentation AStylePlugin::indentation( const QUrl &url ) { // Call formatSource first, to initialize the m_formatter data structures according to the URL formatSource( QLatin1String(""), url, QMimeDatabase().mimeTypeForUrl(url), QString(), QString() ); Indentation ret; ret.indentWidth = m_formatter->option(QStringLiteral("FillCount")).toInt(); QString s = m_formatter->option(QStringLiteral("Fill")).toString(); if(s == QLatin1String("Tabs")) { // Do tabs-only indentation ret.indentationTabWidth = ret.indentWidth; }else{ // Don't use tabs at all ret.indentationTabWidth = -1; } return ret; } QString AStylePlugin::formattingSample() { return "void func(){\n" "\tif(isFoo(a,b))\n" "\tbar(a,b);\n" "if(isFoo)\n" "\ta=bar((b-c)*a,*d--);\n" "if( isFoo( a,b ) )\n" "\tbar(a, b);\n" "if (isFoo) {isFoo=false;cat << isFoo <::const_iterator it = list.begin();\n" "}\n" "namespace A {\n" "namespace B {\n" "class someClass {\n" "void foo() {\n" " if (true) {\n" " func();\n" " } else {\n" " // bla\n" " }\n" "}\n" "};\n" "}\n" "}\n"; } QString AStylePlugin::indentingSample() { return "#define foobar(A)\\\n" "{Foo();Bar();}\n" "#define anotherFoo(B)\\\n" "return Bar()\n" "\n" "namespace Bar\n" "{\n" "class Foo\n" "{public:\n" "Foo();\n" "virtual ~Foo();\n" "};\n" "switch (foo)\n" "{\n" "case 1:\n" "a+=1;\n" "break;\n" "case 2:\n" "{\n" "a += 2;\n" " break;\n" "}\n" "}\n" "if (isFoo)\n" "{\n" "bar();\n" "}\n" "else\n" "{\n" "anotherBar();\n" "}\n" "int foo()\n" "\twhile(isFoo)\n" "\t\t{\n" "\t\t\t...\n" "\t\t\tgoto error;\n" "\t\t....\n" "\t\terror:\n" "\t\t\t...\n" "\t\t}\n" "\t}\n" "fooArray[]={ red,\n" "\tgreen,\n" "\tdarkblue};\n" "fooFunction(barArg1,\n" "\tbarArg2,\n" "\tbarArg3);\n" "struct foo{ int bar() {} };\n"; } #include "astyle_plugin.moc" diff --git a/formatters/customscript/customscript_plugin.cpp b/formatters/customscript/customscript_plugin.cpp index 926ca9577b..bd15aa4968 100644 --- a/formatters/customscript/customscript_plugin.cpp +++ b/formatters/customscript/customscript_plugin.cpp @@ -1,554 +1,555 @@ /* This file is part of KDevelop Copyright (C) 2008 Cédric Pasteur Copyright (C) 2011 David Nolden This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "customscript_plugin.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace KDevelop; static QPointer indentPluginSingleton; K_PLUGIN_FACTORY_WITH_JSON(CustomScriptFactory, "kdevcustomscript.json", registerPlugin(); ) // Replaces ${KEY} in command with variables[KEY] static QString replaceVariables(QString command, QMap variables) { while (command.contains(QLatin1String("${"))) { int pos = command.indexOf(QLatin1String("${")); int end = command.indexOf(QLatin1String("}"), pos + 2); if (end == -1) { break; } QString key = command.mid(pos + 2, end - pos - 2); if (variables.contains(key)) { command.replace(pos, 1 + end - pos, variables[key]); } else { qCDebug(CUSTOMSCRIPT) << "found no variable while replacing in shell-command" << command << "key" << key << "available:" << variables; command.replace(pos, 1 + end - pos, QLatin1String("")); } } return command; } CustomScriptPlugin::CustomScriptPlugin(QObject* parent, const QVariantList&) : IPlugin(QStringLiteral("kdevcustomscript"), parent) { m_currentStyle = predefinedStyles().at(0); indentPluginSingleton = this; } CustomScriptPlugin::~CustomScriptPlugin() { } QString CustomScriptPlugin::name() { // This needs to match the X-KDE-PluginInfo-Name entry from the .desktop file! return QStringLiteral("kdevcustomscript"); } QString CustomScriptPlugin::caption() { return QStringLiteral("Custom Script Formatter"); } QString CustomScriptPlugin::description() { return i18n("Indent and Format Source Code.
" "This plugin allows using powerful external formatting tools " "that can be invoked through the command-line.
" "For example, the uncrustify, astyle or indent " "formatters can be used.
" "The advantage of command-line formatters is that formatting configurations " "can be easily shared by all team members, independent of their preferred IDE."); } QString CustomScriptPlugin::formatSourceWithStyle(SourceFormatterStyle style, const QString& text, const QUrl& url, const QMimeType& /*mime*/, const QString& leftContext, const QString& rightContext) { KProcess proc; QTextStream ios(&proc); std::unique_ptr tmpFile; if (style.content().isEmpty()) { style = predefinedStyle(style.name()); if (style.content().isEmpty()) { qCWarning(CUSTOMSCRIPT) << "Empty contents for style" << style.name() << "for indent plugin"; return text; } } QString useText = text; useText = leftContext + useText + rightContext; QMap projectVariables; foreach (IProject* project, ICore::self()->projectController()->projects()) { projectVariables[project->name()] = project->path().toUrl().toLocalFile(); } QString command = style.content(); // Replace ${Project} with the project path command = replaceVariables(command, projectVariables); command.replace(QLatin1String("$FILE"), url.toLocalFile()); if (command.contains(QLatin1String("$TMPFILE"))) { tmpFile.reset(new QTemporaryFile(QDir::tempPath() + "/code")); tmpFile->setAutoRemove(false); if (tmpFile->open()) { qCDebug(CUSTOMSCRIPT) << "using temporary file" << tmpFile->fileName(); command.replace(QLatin1String("$TMPFILE"), tmpFile->fileName()); QByteArray useTextArray = useText.toLocal8Bit(); if (tmpFile->write(useTextArray) != useTextArray.size()) { qCWarning(CUSTOMSCRIPT) << "failed to write text to temporary file"; return text; } } else { qCWarning(CUSTOMSCRIPT) << "Failed to create a temporary file"; return text; } tmpFile->close(); } qCDebug(CUSTOMSCRIPT) << "using shell command for indentation: " << command; proc.setShellCommand(command); proc.setOutputChannelMode(KProcess::OnlyStdoutChannel); proc.start(); if (!proc.waitForStarted()) { qCDebug(CUSTOMSCRIPT) << "Unable to start indent" << endl; return text; } if (!tmpFile.get()) { proc.write(useText.toLocal8Bit()); } proc.closeWriteChannel(); if (!proc.waitForFinished()) { qCDebug(CUSTOMSCRIPT) << "Process doesn't finish" << endl; return text; } QString output; if (tmpFile.get()) { QFile f(tmpFile->fileName()); if (f.open(QIODevice::ReadOnly)) { output = QString::fromLocal8Bit(f.readAll()); } else { qCWarning(CUSTOMSCRIPT) << "Failed opening the temporary file for reading"; return text; } } else { output = ios.readAll(); } if (output.isEmpty()) { qCWarning(CUSTOMSCRIPT) << "indent returned empty text for style" << style.name() << style.content(); return text; } int tabWidth = 4; if ((!leftContext.isEmpty() || !rightContext.isEmpty()) && (text.contains(' ') || output.contains(' '))) { // If we have to do contex-matching with tabs, determine the correct tab-width so that the context // can be matched correctly Indentation indent = indentation(url); if (indent.indentationTabWidth > 0) { tabWidth = indent.indentationTabWidth; } } return KDevelop::extractFormattedTextFromContext(output, text, leftContext, rightContext, tabWidth); } QString CustomScriptPlugin::formatSource(const QString& text, const QUrl& url, const QMimeType& mime, const QString& leftContext, const QString& rightContext) { - return formatSourceWithStyle(KDevelop::ICore::self()->sourceFormatterController()->styleForMimeType(mime), text, url, mime, leftContext, rightContext); + auto style = KDevelop::ICore::self()->sourceFormatterController()->styleForUrl(url, mime); + return formatSourceWithStyle(style, text, url, mime, leftContext, rightContext); } static QList stylesFromLanguagePlugins() { QList styles; foreach (auto lang, ICore::self()->languageController()->loadedLanguages()) { const SourceFormatterItemList& languageStyles = lang->sourceFormatterItems(); for (const SourceFormatterStyleItem& item: languageStyles) { if (item.engine == QLatin1String("customscript")) { styles << item.style; } } } return styles; }; KDevelop::SourceFormatterStyle CustomScriptPlugin::predefinedStyle(const QString& name) { for (auto langStyle: stylesFromLanguagePlugins()) { qCDebug(CUSTOMSCRIPT) << "looking at style from language with custom sample" << langStyle.description() << langStyle.overrideSample(); if (langStyle.name() == name) { return langStyle; } } SourceFormatterStyle result(name); if (name == QLatin1String("GNU_indent_GNU")) { result.setCaption(i18n("Gnu Indent: GNU")); result.setContent(QStringLiteral("indent")); result.setUsePreview(true); } else if (name == QLatin1String("GNU_indent_KR")) { result.setCaption(i18n("Gnu Indent: Kernighan & Ritchie")); result.setContent(QStringLiteral("indent -kr")); result.setUsePreview(true); } else if (name == QLatin1String("GNU_indent_orig")) { result.setCaption(i18n("Gnu Indent: Original Berkeley indent style")); result.setContent(QStringLiteral("indent -orig")); result.setUsePreview(true); } else if (name == QLatin1String("kdev_format_source")) { result.setCaption(QStringLiteral("KDevelop: kdev_format_source")); result.setContent(QStringLiteral("kdev_format_source $FILE $TMPFILE")); result.setUsePreview(false); result.setDescription(i18n("Description:
" "kdev_format_source is a script bundled with KDevelop " "which allows using fine-grained formatting rules by placing " "meta-files called format_sources into the file-system.

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

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

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

" "Example:
" "subdir/* : uncrustify -l CPP -f $TMPFILE -c uncrustify.config -o $TMPFILE
" "This will reformat all files in subdirectory subdir using the uncrustify " "tool with the config-file uncrustify.config.")); } result.setMimeTypes({ {"text/x-c++src", "C++"}, {"text/x-chdr", "C"}, {"text/x-c++hdr", "C++"}, {"text/x-csrc", "C"}, {"text/x-java", "Java"}, {"text/x-csharp", "C#"} }); return result; } QList CustomScriptPlugin::predefinedStyles() { QList styles = stylesFromLanguagePlugins(); styles << predefinedStyle(QStringLiteral("kdev_format_source")); styles << predefinedStyle(QStringLiteral("GNU_indent_GNU")); styles << predefinedStyle(QStringLiteral("GNU_indent_KR")); styles << predefinedStyle(QStringLiteral("GNU_indent_orig")); return styles; } KDevelop::SettingsWidget* CustomScriptPlugin::editStyleWidget(const QMimeType& mime) { Q_UNUSED(mime); return new CustomScriptPreferences(); } static QString formattingSample() { return "// Formatting\n" "void func(){\n" "\tif(isFoo(a,b))\n" "\tbar(a,b);\n" "if(isFoo)\n" "\ta=bar((b-c)*a,*d--);\n" "if( isFoo( a,b ) )\n" "\tbar(a, b);\n" "if (isFoo) {isFoo=false;cat << isFoo <::const_iterator it = list.begin();\n" "}\n" "namespace A {\n" "namespace B {\n" "void foo() {\n" " if (true) {\n" " func();\n" " } else {\n" " // bla\n" " }\n" "}\n" "}\n" "}\n"; } static QString indentingSample() { return "// Indentation\n" "#define foobar(A)\\\n" "{Foo();Bar();}\n" "#define anotherFoo(B)\\\n" "return Bar()\n" "\n" "namespace Bar\n" "{\n" "class Foo\n" "{public:\n" "Foo();\n" "virtual ~Foo();\n" "};\n" "switch (foo)\n" "{\n" "case 1:\n" "a+=1;\n" "break;\n" "case 2:\n" "{\n" "a += 2;\n" " break;\n" "}\n" "}\n" "if (isFoo)\n" "{\n" "bar();\n" "}\n" "else\n" "{\n" "anotherBar();\n" "}\n" "int foo()\n" "\twhile(isFoo)\n" "\t\t{\n" "\t\t\t...\n" "\t\t\tgoto error;\n" "\t\t....\n" "\t\terror:\n" "\t\t\t...\n" "\t\t}\n" "\t}\n" "fooArray[]={ red,\n" "\tgreen,\n" "\tdarkblue};\n" "fooFunction(barArg1,\n" "\tbarArg2,\n" "\tbarArg3);\n"; } QString CustomScriptPlugin::previewText(const SourceFormatterStyle& style, const QMimeType& /*mime*/) { if (!style.overrideSample().isEmpty()) { return style.overrideSample(); } return formattingSample() + "\n\n" + indentingSample(); } QStringList CustomScriptPlugin::computeIndentationFromSample(const QUrl& url) { QStringList ret; auto languages = ICore::self()->languageController()->languagesForUrl(url); if (languages.isEmpty()) { return ret; } QString sample = languages[0]->indentationSample(); QString formattedSample = formatSource(sample, url, QMimeDatabase().mimeTypeForUrl(url), QString(), QString()); QStringList lines = formattedSample.split(QStringLiteral("\n")); foreach (QString line, lines) { if (!line.isEmpty() && line[0].isSpace()) { QString indent; foreach (QChar c, line) { if (c.isSpace()) { indent.push_back(c); } else { break; } } if (!indent.isEmpty() && !ret.contains(indent)) { ret.push_back(indent); } } } return ret; } CustomScriptPlugin::Indentation CustomScriptPlugin::indentation(const QUrl& url) { Indentation ret; QStringList indent = computeIndentationFromSample(url); if (indent.isEmpty()) { qCDebug(CUSTOMSCRIPT) << "failed extracting a valid indentation from sample for url" << url; return ret; // No valid indentation could be extracted } if (indent[0].contains(' ')) { ret.indentWidth = indent[0].count(' '); } if (!indent.join(QLatin1String("")).contains(' ')) { ret.indentationTabWidth = -1; // Tabs are not used for indentation } if (indent[0] == QLatin1String(" ")) { // The script indents with tabs-only // The problem is that we don't know how // wide a tab is supposed to be. // // We need indentation-width=tab-width // to make the editor do tab-only formatting, // so choose a random with of 4. ret.indentWidth = 4; ret.indentationTabWidth = 4; } else if (ret.indentWidth) { // Tabs are used for indentation, alongside with spaces // Try finding out how many spaces one tab stands for. // Do it by assuming a uniform indentation-step with each level. for (int pos = 0; pos < indent.size(); ++pos) { if (indent[pos] == QLatin1String(" ")&& pos >= 1) { // This line consists of only a tab. int prevWidth = indent[pos - 1].length(); int prevPrevWidth = (pos >= 2) ? indent[pos - 2].length() : 0; int step = prevWidth - prevPrevWidth; qCDebug(CUSTOMSCRIPT) << "found in line " << pos << prevWidth << prevPrevWidth << step; if (step > 0 && step <= prevWidth) { qCDebug(CUSTOMSCRIPT) << "Done"; ret.indentationTabWidth = prevWidth + step; break; } } } } qCDebug(CUSTOMSCRIPT) << "indent-sample" << "\"" + indent.join(QStringLiteral("\n")) + "\"" << "extracted tab-width" << ret.indentationTabWidth << "extracted indentation width" << ret.indentWidth; return ret; } void CustomScriptPreferences::updateTimeout() { const QString& text = indentPluginSingleton.data()->previewText(m_style, QMimeType()); QString formatted = indentPluginSingleton.data()->formatSourceWithStyle(m_style, text, QUrl(), QMimeType()); emit previewTextChanged(formatted); } CustomScriptPreferences::CustomScriptPreferences() { m_updateTimer = new QTimer(this); m_updateTimer->setSingleShot(true); connect(m_updateTimer, &QTimer::timeout, this, &CustomScriptPreferences::updateTimeout); m_vLayout = new QVBoxLayout(this); m_vLayout->setMargin(0); m_captionLabel = new QLabel; m_vLayout->addWidget(m_captionLabel); m_vLayout->addSpacing(10); m_hLayout = new QHBoxLayout; m_vLayout->addLayout(m_hLayout); m_commandLabel = new QLabel; m_hLayout->addWidget(m_commandLabel); m_commandEdit = new QLineEdit; m_hLayout->addWidget(m_commandEdit); m_commandLabel->setText(i18n("Command:")); m_vLayout->addSpacing(10); m_bottomLabel = new QLabel; m_vLayout->addWidget(m_bottomLabel); m_bottomLabel->setTextFormat(Qt::RichText); m_bottomLabel->setText( i18n("You can enter an arbitrary shell command.
" "The unformatted source-code is reached to the command
" "through the standard input, and the
" "formatted result is read from the standard output.
" "
" "If you add $TMPFILE into the command, then
" "a temporary file is used for transferring the code.")); connect(m_commandEdit, &QLineEdit::textEdited, this, &CustomScriptPreferences::textEdited); m_vLayout->addSpacing(10); m_moreVariablesButton = new QPushButton(i18n("More Variables")); connect(m_moreVariablesButton, &QPushButton::clicked, this, &CustomScriptPreferences::moreVariablesClicked); m_vLayout->addWidget(m_moreVariablesButton); m_vLayout->addStretch(); } void CustomScriptPreferences::load(const KDevelop::SourceFormatterStyle& style) { m_style = style; m_commandEdit->setText(style.content()); m_captionLabel->setText(i18n("Style: %1", style.caption())); updateTimeout(); } QString CustomScriptPreferences::save() { return m_commandEdit->text(); } void CustomScriptPreferences::moreVariablesClicked(bool) { KMessageBox::information(ICore::self()->uiController()->activeMainWindow(), i18n("$TMPFILE will be replaced with the path to a temporary file.
" "The code will be written into the file, the temporary
" "file will be substituted into that position, and the result
" "will be read out of that file.
" "
" "$FILE will be replaced with the path of the original file.
" "The contents of the file must not be modified, changes are allowed
" "only in $TMPFILE.
" "
" "${PROJECT_NAME} will be replaced by the path of
" "the currently open project with the matching name." ), i18n("Variable Replacements")); } #include "customscript_plugin.moc" // kate: indent-mode cstyle; space-indent off; tab-width 4; diff --git a/languages/clang/codegen/sourcemanipulation.cpp b/languages/clang/codegen/sourcemanipulation.cpp index ee39c999ac..9ad0c425c3 100644 --- a/languages/clang/codegen/sourcemanipulation.cpp +++ b/languages/clang/codegen/sourcemanipulation.cpp @@ -1,287 +1,288 @@ /* * This file is part of KDevelop * * Copyright 2009 David Nolden * Copyright 2015 Sergey Kalinichev * * 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) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "sourcemanipulation.h" #include #include #include #include #include #include #include #include #include #include #include #include "codegenhelper.h" #include "adaptsignatureaction.h" #include "util/clangdebug.h" using namespace KDevelop; namespace { QualifiedIdentifier stripPrefixes(const DUContextPointer& ctx, const QualifiedIdentifier& id) { if (!ctx) { return id; } auto imports = ctx->fullyApplyAliases({}, ctx->topContext()); if (imports.contains(id)) { return {}; /// The id is a namespace that is imported into the current context } auto basicDecls = ctx->findDeclarations(id, CursorInRevision::invalid(), {}, nullptr, (DUContext::SearchFlags)(DUContext::NoSelfLookUp | DUContext::NoFiltering)); if (basicDecls.isEmpty()) { return id; } auto newId = id.mid(1); auto result = id; while (!newId.isEmpty()) { auto foundDecls = ctx->findDeclarations(newId, CursorInRevision::invalid(), {}, nullptr, (DUContext::SearchFlags)(DUContext::NoSelfLookUp | DUContext::NoFiltering)); if (foundDecls == basicDecls) { result = newId; // must continue to find the shortest possible identifier // esp. for cases where nested namespaces are used (e.g. using namespace a::b::c;) newId = newId.mid(1); } } return result; } // Re-indents the code so the leftmost line starts at zero QString zeroIndentation(const QString& str, int fromLine = 0) { QStringList lines = str.split(QLatin1Char('\n')); QStringList ret; if (fromLine < lines.size()) { ret = lines.mid(0, fromLine); lines = lines.mid(fromLine); } QRegExp nonWhiteSpace(QStringLiteral("\\S")); int minLineStart = 10000; foreach (const auto& line, lines) { int lineStart = line.indexOf(nonWhiteSpace); if (lineStart < minLineStart) { minLineStart = lineStart; } } foreach (const auto& line, lines) { ret << line.mid(minLineStart); } return ret.join(QStringLiteral("\n")); } } DocumentChangeSet SourceCodeInsertion::changes() { return m_changeSet; } void SourceCodeInsertion::setSubScope(const QualifiedIdentifier& scope) { m_scope = scope; if (!m_context) { return; } QStringList needNamespace = m_scope.toStringList(); bool foundChild = true; while (!needNamespace.isEmpty() && foundChild) { foundChild = false; foreach (DUContext* child, m_context->childContexts()) { clangDebug() << "checking child" << child->localScopeIdentifier().toString() << "against" << needNamespace.first(); if (child->localScopeIdentifier().toString() == needNamespace.first() && child->type() == DUContext::Namespace) { clangDebug() << "taking"; m_context = child; foundChild = true; needNamespace.pop_front(); break; } } } m_scope = stripPrefixes(m_context, QualifiedIdentifier(needNamespace.join(QStringLiteral("::")))); } QString SourceCodeInsertion::applySubScope(const QString& decl) const { if (m_scope.isEmpty()) { return decl; } QString scopeType = QStringLiteral("namespace"); QString scopeClose; if (m_context && m_context->type() == DUContext::Class) { scopeType = QStringLiteral("struct"); scopeClose = QStringLiteral(";"); } QString ret; foreach (const QString& scope, m_scope.toStringList()) { ret += scopeType + QStringLiteral(" ") + scope + QStringLiteral(" {\n"); } ret += decl; ret += QStringLiteral("}") + scopeClose + QStringLiteral("\n").repeated(m_scope.count()); return ret; } SourceCodeInsertion::SourceCodeInsertion(TopDUContext* topContext) : m_context(topContext) , m_topContext(topContext) , m_codeRepresentation(createCodeRepresentation(m_topContext->url())) { } SourceCodeInsertion::~SourceCodeInsertion() { } KTextEditor::Cursor SourceCodeInsertion::end() const { auto ret = m_context->rangeInCurrentRevision().end(); if (m_codeRepresentation && m_codeRepresentation->lines() && dynamic_cast(m_context.data())) { ret.setLine(m_codeRepresentation->lines() - 1); ret.setColumn(m_codeRepresentation->line(ret.line()).size()); } return ret; } KTextEditor::Range SourceCodeInsertion::insertionRange(int line) { if (line == 0 || !m_codeRepresentation) { return KTextEditor::Range(line, 0, line, 0); } KTextEditor::Range range(line - 1, m_codeRepresentation->line(line - 1).size(), line - 1, m_codeRepresentation->line(line - 1).size()); // If the context finishes on that line, then this will need adjusting if (!m_context->rangeInCurrentRevision().contains(range)) { range.start() = m_context->rangeInCurrentRevision().end(); if (range.start().column() > 0) { range.start() = range.start() - KTextEditor::Cursor(0, 1); } range.end() = range.start(); } return range; } bool SourceCodeInsertion::insertFunctionDeclaration(KDevelop::Declaration* declaration, const Identifier& id, const QString& body) { if (!m_context) { return false; } Signature signature; const auto localDeclarations = declaration->internalContext()->localDeclarations(); signature.parameters.reserve(localDeclarations.count()); std::transform(localDeclarations.begin(), localDeclarations.end(), std::back_inserter(signature.parameters), [] (Declaration* argument) -> ParameterItem { return {IndexedType(argument->indexedType()), argument->identifier().toString()}; }); auto funcType = declaration->type(); auto returnType = funcType->returnType(); if (auto classFunDecl = dynamic_cast(declaration)) { if (classFunDecl->isConstructor() || classFunDecl->isDestructor()) { returnType = nullptr; } } signature.returnType = IndexedType(returnType); signature.isConst = funcType->modifiers() & AbstractType::ConstModifier; QString decl = CodegenHelper::makeSignatureString(declaration, signature, true); decl.replace(declaration->qualifiedIdentifier().toString(), id.toString()); if (body.isEmpty()) { decl += QStringLiteral(";"); } else { if (!body.startsWith(QLatin1Char(' ')) && !body.startsWith(QLatin1Char('\n'))) { decl += QStringLiteral(" "); } decl += zeroIndentation(body); } int line = findInsertionPoint(); decl = QStringLiteral("\n\n") + applySubScope(decl); + const auto url = declaration->url().toUrl(); QMimeDatabase db; - QMimeType mime = db.mimeTypeForFile(declaration->url().str()); - auto i = ICore::self()->sourceFormatterController()->formatterForMimeType(mime); + QMimeType mime = db.mimeTypeForUrl(url); + auto i = ICore::self()->sourceFormatterController()->formatterForUrl(url, mime); if (i) { - decl = i->formatSource(decl, declaration->url().toUrl(), mime); + decl = i->formatSource(decl, url, mime); } return m_changeSet.addChange(DocumentChange(m_context->url(), insertionRange(line), QString(), decl)); } int SourceCodeInsertion::findInsertionPoint() const { int line = end().line(); foreach (auto decl, m_context->localDeclarations()) { if (m_context->type() == DUContext::Class) { continue; } if (!dynamic_cast(decl)) { continue; } line = decl->range().end.line + 1; if (decl->internalContext()) { line = decl->internalContext()->range().end.line + 1; } } clangDebug() << line << m_context->scopeIdentifier(true) << m_context->rangeInCurrentRevision() << m_context->url().toUrl() << m_context->parentContext(); clangDebug() << "count of declarations:" << m_context->topContext()->localDeclarations().size(); return line; }