From 97d38587202e153a47f509cfa4974f8037a07d41 Mon Sep 17 00:00:00 2001 From: Ivan Donchevskii Date: Fri, 9 Nov 2018 11:07:54 +0100 Subject: [PATCH] ClangTools: Apply indention/formatting after fix-its Fix-its do not follow the current formatting style therefore apply indention/formatting by using indenter. This change will work best when ClangFormat plugin is enabled to not only reindent line but also format it. Task-number: QTCREATORBUG-21448 Change-Id: I8bcafcf49f3118aff7840326547e7a24052469b2 Reviewed-by: Marco Bubke --- .../clangformat/clangformatindenter.cpp | 6 +- src/plugins/clangformat/clangformatindenter.h | 1 + .../clangfixitsrefactoringchanges.cpp | 86 +++++++++++++++++++ .../clangfixitsrefactoringchanges.h | 19 ++++ src/plugins/clangtools/clangtidyclazytool.cpp | 1 - src/plugins/texteditor/indenter.cpp | 7 +- src/plugins/texteditor/indenter.h | 3 + src/plugins/texteditor/textdocument.cpp | 2 +- 8 files changed, 115 insertions(+), 10 deletions(-) diff --git a/src/plugins/clangformat/clangformatindenter.cpp b/src/plugins/clangformat/clangformatindenter.cpp index d3b181982bc..e94e5ea7a0b 100644 --- a/src/plugins/clangformat/clangformatindenter.cpp +++ b/src/plugins/clangformat/clangformatindenter.cpp @@ -384,14 +384,10 @@ void ClangFormatIndenter::indent(QTextDocument *doc, } QtReplacements ClangFormatIndenter::format(QTextDocument *doc, + const Utils::FileName &fileName, const QTextCursor &cursor, const TextEditor::TabSettings & /*tabSettings*/) { - TextEditorWidget *editor = TextEditorWidget::currentTextEditorWidget(); - if (!editor) - return QtReplacements(); - - const Utils::FileName fileName = editor->textDocument()->filePath(); int utf8Offset; int utf8Length; const QByteArray buffer = doc->toPlainText().toUtf8(); diff --git a/src/plugins/clangformat/clangformatindenter.h b/src/plugins/clangformat/clangformatindenter.h index c9d38ac670a..a12ffaee443 100644 --- a/src/plugins/clangformat/clangformatindenter.h +++ b/src/plugins/clangformat/clangformatindenter.h @@ -41,6 +41,7 @@ public: const QTextCursor &cursor, const TextEditor::TabSettings &tabSettings) override; TextEditor::Replacements format(QTextDocument *doc, + const Utils::FileName &fileName, const QTextCursor &cursor, const TextEditor::TabSettings &tabSettings) override; void indentBlock(QTextDocument *doc, diff --git a/src/plugins/clangtools/clangfixitsrefactoringchanges.cpp b/src/plugins/clangtools/clangfixitsrefactoringchanges.cpp index ca06bf684c9..6fd0e07fd06 100644 --- a/src/plugins/clangtools/clangfixitsrefactoringchanges.cpp +++ b/src/plugins/clangtools/clangfixitsrefactoringchanges.cpp @@ -26,6 +26,13 @@ #include "clangfixitsrefactoringchanges.h" #include +#include +#include +#include + +#include +#include +#include #include #include @@ -35,8 +42,11 @@ #include +#include + Q_LOGGING_CATEGORY(fixitsLog, "qtc.clangtools.fixits", QtWarningMsg); +using namespace TextEditor; using namespace Utils; namespace ClangTools { @@ -71,6 +81,13 @@ bool FixitsRefactoringFile::apply() QTC_ASSERT(!m_filePath.isEmpty(), return false); + ICodeStylePreferencesFactory *factory = TextEditorSettings::codeStyleFactory( + CppTools::Constants::CPP_SETTINGS_ID); + std::unique_ptr indenter(factory->createIndenter()); + + const TextEditor::TabSettings tabSettings + = CppTools::CppCodeStyleSettings::currentProjectTabSettings(); + // Apply changes for (int i=0; i < m_replacementOperations.size(); ++i) { ReplacementOperation &op = *m_replacementOperations[i]; @@ -90,6 +107,8 @@ bool FixitsRefactoringFile::apply() cursor.setPosition(op.pos); cursor.setPosition(op.pos + op.length, QTextCursor::KeepAnchor); cursor.insertText(op.text); + + tryToFormat(*indenter, tabSettings, doc, op, i); } } @@ -108,6 +127,33 @@ bool FixitsRefactoringFile::apply() return true; } +void FixitsRefactoringFile::tryToFormat(TextEditor::Indenter &indenter, + const TextEditor::TabSettings &tabSettings, + QTextDocument *doc, + const ReplacementOperation &op, + int currentIndex) +{ + QTextCursor cursor(doc); + cursor.beginEditBlock(); + cursor.setPosition(op.pos); + cursor.movePosition(QTextCursor::Right, + QTextCursor::KeepAnchor, + op.text.length()); + const Replacements replacements = indenter.format(doc, + Utils::FileName::fromString(op.fileName), + cursor, + tabSettings); + cursor.endEditBlock(); + + if (replacements.empty()) + return; + + if (hasIntersection(op.fileName, replacements, currentIndex + 1)) + doc->undo(&cursor); + else + shiftAffectedReplacements(op.fileName, replacements, currentIndex + 1); +} + QTextDocument *FixitsRefactoringFile::document(const QString &filePath) const { if (m_documents.find(filePath) == m_documents.end()) { @@ -148,5 +194,45 @@ void FixitsRefactoringFile::shiftAffectedReplacements(const ReplacementOperation } } +bool FixitsRefactoringFile::hasIntersection(const QString &fileName, + const Replacements &replacements, + int startIndex) const +{ + for (int i = startIndex; i < m_replacementOperations.size(); ++i) { + const ReplacementOperation ¤t = *m_replacementOperations[i]; + if (fileName != current.fileName) + continue; + + // Usually the number of replacements is from 1 to 3. + if (std::any_of(replacements.begin(), + replacements.end(), + [¤t](const const Replacement &replacement) { + return replacement.offset + replacement.length > current.pos + && replacement.offset < current.pos + current.length; + })) { + return true; + } + } + + return false; +} + +void FixitsRefactoringFile::shiftAffectedReplacements(const QString &fileName, + const Replacements &replacements, + int startIndex) +{ + for (int i = startIndex; i < m_replacementOperations.size(); ++i) { + ReplacementOperation ¤t = *m_replacementOperations[i]; + if (fileName != current.fileName) + continue; + + for (const auto &replacement : replacements) { + if (replacement.offset > current.pos) + break; + current.pos += replacement.text.size() - replacement.length; + } + } +} + } // namespace Internal } // namespace ClangTools diff --git a/src/plugins/clangtools/clangfixitsrefactoringchanges.h b/src/plugins/clangtools/clangfixitsrefactoringchanges.h index 1ca809f4e70..63aea839b1d 100644 --- a/src/plugins/clangtools/clangfixitsrefactoringchanges.h +++ b/src/plugins/clangtools/clangfixitsrefactoringchanges.h @@ -32,6 +32,13 @@ #include #include +namespace TextEditor { +class Indenter; +class Replacement; +using Replacements = std::vector; +class TabSettings; +} + namespace ClangTools { namespace Internal { @@ -65,6 +72,18 @@ private: QTextDocument *document(const QString &filePath) const; void shiftAffectedReplacements(const ReplacementOperation &op, int startIndex); + void tryToFormat(TextEditor::Indenter &indenter, + const TextEditor::TabSettings &tabSettings, + QTextDocument *doc, + const ReplacementOperation &op, + int currentIndex); + void shiftAffectedReplacements(const QString &fileName, + const TextEditor::Replacements &replacements, + int startIndex); + bool hasIntersection(const QString &fileName, + const TextEditor::Replacements &replacements, + int startIndex) const; + QString m_filePath; mutable Utils::TextFileFormat m_textFileFormat; mutable QHash m_documents; diff --git a/src/plugins/clangtools/clangtidyclazytool.cpp b/src/plugins/clangtools/clangtidyclazytool.cpp index bc169a0e9be..3e469534e9e 100644 --- a/src/plugins/clangtools/clangtidyclazytool.cpp +++ b/src/plugins/clangtools/clangtidyclazytool.cpp @@ -179,7 +179,6 @@ public: QVector itemsInvalidated; fileInfo.file.setReplacements(ops); - model->removeWatchedPath(ops.first()->fileName); if (fileInfo.file.apply()) { itemsApplied = itemsScheduled; diff --git a/src/plugins/texteditor/indenter.cpp b/src/plugins/texteditor/indenter.cpp index a7c26b6c59d..6f0dce63cf2 100644 --- a/src/plugins/texteditor/indenter.cpp +++ b/src/plugins/texteditor/indenter.cpp @@ -42,9 +42,9 @@ bool Indenter::isElectricCharacter(const QChar &) const } void Indenter::indentBlock(QTextDocument *doc, - const QTextBlock &block, - const QChar &typedChar, - const TabSettings &tabSettings) + const QTextBlock &block, + const QChar &typedChar, + const TabSettings &tabSettings) { Q_UNUSED(doc); Q_UNUSED(typedChar); @@ -103,6 +103,7 @@ void Indenter::reindent(QTextDocument *doc, const QTextCursor &cursor, const Tab } Replacements Indenter::format(QTextDocument *doc, + const Utils::FileName & /*fileName*/, const QTextCursor &cursor, const TabSettings &tabSettings) { diff --git a/src/plugins/texteditor/indenter.h b/src/plugins/texteditor/indenter.h index aef78658718..300e92a135e 100644 --- a/src/plugins/texteditor/indenter.h +++ b/src/plugins/texteditor/indenter.h @@ -39,6 +39,8 @@ class QTextBlock; class QChar; QT_END_NAMESPACE +namespace Utils { class FileName; } + namespace TextEditor { class ICodeStylePreferences; @@ -85,6 +87,7 @@ public: // By default just calls indent with default settings. virtual Replacements format(QTextDocument *doc, + const Utils::FileName &fileName, const QTextCursor &cursor, const TabSettings &tabSettings); diff --git a/src/plugins/texteditor/textdocument.cpp b/src/plugins/texteditor/textdocument.cpp index 7363988c60b..7edbdd1488b 100644 --- a/src/plugins/texteditor/textdocument.cpp +++ b/src/plugins/texteditor/textdocument.cpp @@ -428,7 +428,7 @@ void TextDocument::autoReindent(const QTextCursor &cursor) void TextDocument::autoFormat(const QTextCursor &cursor) { - d->m_indenter->format(&d->m_document, cursor, tabSettings()); + d->m_indenter->format(&d->m_document, filePath(), cursor, tabSettings()); } QTextCursor TextDocument::indent(const QTextCursor &cursor, bool blockSelection, int column,