From 5792291520fb128764264140497233b5545c375d Mon Sep 17 00:00:00 2001 From: Ivan Donchevskii Date: Mon, 28 Jan 2019 12:25:36 +0100 Subject: [PATCH] ClangFormat: Format edited chunks of file on save The similar implementation to the one in Beautifier plugin with the difference that the clangformat indenter logic is used and only modified chunks are formatted. That means that all code which was not touched will stay in the initial condition. Change-Id: I47b11eb99852454ed0031ef6cfc6dbed1ecd390d Reviewed-by: Leena Miettinen Reviewed-by: Marco Bubke --- .../clangformat/clangformatconfigwidget.cpp | 6 ++- .../clangformat/clangformatconfigwidget.ui | 7 +++ .../clangformat/clangformatconstants.h | 1 + .../clangformat/clangformatindenter.cpp | 5 +++ src/plugins/clangformat/clangformatindenter.h | 1 + .../clangformat/clangformatsettings.cpp | 13 ++++++ src/plugins/clangformat/clangformatsettings.h | 4 ++ src/plugins/cppeditor/cppeditordocument.cpp | 43 +++++++++++++++++++ src/plugins/cppeditor/cppeditordocument.h | 4 ++ src/plugins/texteditor/indenter.h | 2 + 10 files changed, 85 insertions(+), 1 deletion(-) diff --git a/src/plugins/clangformat/clangformatconfigwidget.cpp b/src/plugins/clangformat/clangformatconfigwidget.cpp index bcede8c0101..f0d4808cf5c 100644 --- a/src/plugins/clangformat/clangformatconfigwidget.cpp +++ b/src/plugins/clangformat/clangformatconfigwidget.cpp @@ -131,6 +131,7 @@ void ClangFormatConfigWidget::hideGlobalCheckboxes() { m_ui->formatAlways->hide(); m_ui->formatWhileTyping->hide(); + m_ui->formatOnSave->hide(); } void ClangFormatConfigWidget::showGlobalCheckboxes() @@ -140,6 +141,9 @@ void ClangFormatConfigWidget::showGlobalCheckboxes() m_ui->formatWhileTyping->setChecked(ClangFormatSettings::instance().formatWhileTyping()); m_ui->formatWhileTyping->show(); + + m_ui->formatOnSave->setChecked(ClangFormatSettings::instance().formatOnSave()); + m_ui->formatOnSave->show(); } void ClangFormatConfigWidget::initialize() @@ -200,7 +204,6 @@ void ClangFormatConfigWidget::fillTable() std::string configText = clang::format::configurationAsText(style); std::istringstream stream(configText); readTable(m_ui->clangFormatOptionsTable, stream); - } ClangFormatConfigWidget::~ClangFormatConfigWidget() = default; @@ -211,6 +214,7 @@ void ClangFormatConfigWidget::apply() ClangFormatSettings &settings = ClangFormatSettings::instance(); settings.setFormatCodeInsteadOfIndent(m_ui->formatAlways->isChecked()); settings.setFormatWhileTyping(m_ui->formatWhileTyping->isChecked()); + settings.setFormatOnSave(m_ui->formatOnSave->isChecked()); settings.write(); } diff --git a/src/plugins/clangformat/clangformatconfigwidget.ui b/src/plugins/clangformat/clangformatconfigwidget.ui index 37f228e97cc..2094bca1090 100644 --- a/src/plugins/clangformat/clangformatconfigwidget.ui +++ b/src/plugins/clangformat/clangformatconfigwidget.ui @@ -40,6 +40,13 @@ + + + + Format edited code on file save + + + diff --git a/src/plugins/clangformat/clangformatconstants.h b/src/plugins/clangformat/clangformatconstants.h index 40b43e56407..8b7cbcf9bd2 100644 --- a/src/plugins/clangformat/clangformatconstants.h +++ b/src/plugins/clangformat/clangformatconstants.h @@ -33,5 +33,6 @@ static const char SAMPLE_FILE_NAME[] = "test.cpp"; static const char SETTINGS_ID[] = "ClangFormat"; static const char FORMAT_CODE_INSTEAD_OF_INDENT_ID[] = "ClangFormat.FormatCodeInsteadOfIndent"; static const char FORMAT_WHILE_TYPING_ID[] = "ClangFormat.FormatWhileTyping"; +static const char FORMAT_CODE_ON_SAVE_ID[] = "ClangFormat.FormatCodeOnSave"; } // namespace Constants } // namespace ClangFormat diff --git a/src/plugins/clangformat/clangformatindenter.cpp b/src/plugins/clangformat/clangformatindenter.cpp index f1ece90164a..ef9dbf5d1ca 100644 --- a/src/plugins/clangformat/clangformatindenter.cpp +++ b/src/plugins/clangformat/clangformatindenter.cpp @@ -87,4 +87,9 @@ int ClangFormatIndenter::lastSaveRevision() const return qobject_cast(m_doc->documentLayout())->lastSaveRevision; } +bool ClangFormatIndenter::formatOnSave() const +{ + return ClangFormatSettings::instance().formatOnSave(); +} + } // namespace ClangFormat diff --git a/src/plugins/clangformat/clangformatindenter.h b/src/plugins/clangformat/clangformatindenter.h index dca5f583a64..6d605eb5c91 100644 --- a/src/plugins/clangformat/clangformatindenter.h +++ b/src/plugins/clangformat/clangformatindenter.h @@ -36,6 +36,7 @@ class ClangFormatIndenter final : public ClangFormatBaseIndenter public: ClangFormatIndenter(QTextDocument *doc); Utils::optional tabSettings() const override; + bool formatOnSave() const override; private: bool formatCodeInsteadOfIndent() const override; diff --git a/src/plugins/clangformat/clangformatsettings.cpp b/src/plugins/clangformat/clangformatsettings.cpp index d8369bf5efd..8be2b3cefee 100644 --- a/src/plugins/clangformat/clangformatsettings.cpp +++ b/src/plugins/clangformat/clangformatsettings.cpp @@ -44,6 +44,8 @@ ClangFormatSettings::ClangFormatSettings() = settings->value(QLatin1String(Constants::FORMAT_CODE_INSTEAD_OF_INDENT_ID), false).toBool(); m_formatWhileTyping = settings->value(QLatin1String(Constants::FORMAT_WHILE_TYPING_ID), false) .toBool(); + m_formatOnSave = settings->value(QLatin1String(Constants::FORMAT_CODE_ON_SAVE_ID), false) + .toBool(); settings->endGroup(); } @@ -54,6 +56,7 @@ void ClangFormatSettings::write() const settings->setValue(QLatin1String(Constants::FORMAT_CODE_INSTEAD_OF_INDENT_ID), m_formatCodeInsteadOfIndent); settings->setValue(QLatin1String(Constants::FORMAT_WHILE_TYPING_ID), m_formatWhileTyping); + settings->setValue(QLatin1String(Constants::FORMAT_CODE_ON_SAVE_ID), m_formatOnSave); settings->endGroup(); } @@ -77,4 +80,14 @@ bool ClangFormatSettings::formatWhileTyping() const return m_formatWhileTyping; } +void ClangFormatSettings::setFormatOnSave(bool enable) +{ + m_formatOnSave = enable; +} + +bool ClangFormatSettings::formatOnSave() const +{ + return m_formatOnSave; +} + } // namespace ClangFormat diff --git a/src/plugins/clangformat/clangformatsettings.h b/src/plugins/clangformat/clangformatsettings.h index 4a62eb83035..0ea9ed9747e 100644 --- a/src/plugins/clangformat/clangformatsettings.h +++ b/src/plugins/clangformat/clangformatsettings.h @@ -40,9 +40,13 @@ public: void setFormatWhileTyping(bool enable); bool formatWhileTyping() const; + + void setFormatOnSave(bool enable); + bool formatOnSave() const; private: bool m_formatCodeInsteadOfIndent = false; bool m_formatWhileTyping = false; + bool m_formatOnSave = false; }; } // namespace ClangFormat diff --git a/src/plugins/cppeditor/cppeditordocument.cpp b/src/plugins/cppeditor/cppeditordocument.cpp index c8951c12ebc..d18735c7501 100644 --- a/src/plugins/cppeditor/cppeditordocument.cpp +++ b/src/plugins/cppeditor/cppeditordocument.cpp @@ -44,6 +44,7 @@ #include #include +#include #include #include @@ -447,5 +448,47 @@ TextEditor::TabSettings CppEditorDocument::tabSettings() const return indenter()->tabSettings().value_or(TextEditor::TextDocument::tabSettings()); } +static int formatRange(QTextDocument *doc, + TextEditor::Indenter *indenter, + std::pair editedRange, + const TextEditor::TabSettings &tabSettings) +{ + QTextCursor cursor(doc); + cursor.setPosition(editedRange.first); + cursor.setPosition(editedRange.second, QTextCursor::KeepAnchor); + const int oldBlockCount = doc->blockCount(); + indenter->format(cursor, tabSettings); + return doc->blockCount() - oldBlockCount; +} + +bool CppEditorDocument::save(QString *errorString, const QString &fileName, bool autoSave) +{ + if (indenter()->formatOnSave()) { + auto *layout = qobject_cast(document()->documentLayout()); + const int documentRevision = layout->lastSaveRevision; + + std::pair editedRange; + for (int i = 0; i < document()->blockCount(); ++i) { + const QTextBlock block = document()->findBlockByNumber(i); + if (block.revision() == documentRevision) { + if (editedRange.first != -1) + i += formatRange(document(), indenter(), editedRange, tabSettings()); + + editedRange = std::make_pair(-1, -1); + continue; + } + + // block.revision() != documentRevision + if (editedRange.first == -1) + editedRange.first = block.position(); + editedRange.second = block.position() + block.length(); + } + if (editedRange.first != -1) + formatRange(document(), indenter(), editedRange, tabSettings()); + } + + return TextEditor::TextDocument::save(errorString, fileName, autoSave); +} + } // namespace Internal } // namespace CppEditor diff --git a/src/plugins/cppeditor/cppeditordocument.h b/src/plugins/cppeditor/cppeditordocument.h index 44fe04becb7..26cff2043aa 100644 --- a/src/plugins/cppeditor/cppeditordocument.h +++ b/src/plugins/cppeditor/cppeditordocument.h @@ -69,6 +69,10 @@ public: QFuture cursorInfo(const CppTools::CursorInfoParams ¶ms); TextEditor::TabSettings tabSettings() const override; + bool save(QString *errorString, + const QString &fileName = QString(), + bool autoSave = false) override; + signals: void codeWarningsUpdated(unsigned contentsRevision, const QList selections, diff --git a/src/plugins/texteditor/indenter.h b/src/plugins/texteditor/indenter.h index bb23c9d7ac2..2907ea783ce 100644 --- a/src/plugins/texteditor/indenter.h +++ b/src/plugins/texteditor/indenter.h @@ -99,6 +99,8 @@ public: return Replacements(); } + virtual bool formatOnSave() const { return false; } + // Expects a list of blocks in order of occurrence in the document. virtual IndentationForBlock indentationForBlocks(const QVector &blocks, const TabSettings &tabSettings,