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 <riitta-leena.miettinen@qt.io>
Reviewed-by: Marco Bubke <marco.bubke@qt.io>
This commit is contained in:
Ivan Donchevskii
2019-01-28 12:25:36 +01:00
parent 536b733f29
commit 5792291520
10 changed files with 85 additions and 1 deletions

View File

@@ -131,6 +131,7 @@ void ClangFormatConfigWidget::hideGlobalCheckboxes()
{ {
m_ui->formatAlways->hide(); m_ui->formatAlways->hide();
m_ui->formatWhileTyping->hide(); m_ui->formatWhileTyping->hide();
m_ui->formatOnSave->hide();
} }
void ClangFormatConfigWidget::showGlobalCheckboxes() void ClangFormatConfigWidget::showGlobalCheckboxes()
@@ -140,6 +141,9 @@ void ClangFormatConfigWidget::showGlobalCheckboxes()
m_ui->formatWhileTyping->setChecked(ClangFormatSettings::instance().formatWhileTyping()); m_ui->formatWhileTyping->setChecked(ClangFormatSettings::instance().formatWhileTyping());
m_ui->formatWhileTyping->show(); m_ui->formatWhileTyping->show();
m_ui->formatOnSave->setChecked(ClangFormatSettings::instance().formatOnSave());
m_ui->formatOnSave->show();
} }
void ClangFormatConfigWidget::initialize() void ClangFormatConfigWidget::initialize()
@@ -200,7 +204,6 @@ void ClangFormatConfigWidget::fillTable()
std::string configText = clang::format::configurationAsText(style); std::string configText = clang::format::configurationAsText(style);
std::istringstream stream(configText); std::istringstream stream(configText);
readTable(m_ui->clangFormatOptionsTable, stream); readTable(m_ui->clangFormatOptionsTable, stream);
} }
ClangFormatConfigWidget::~ClangFormatConfigWidget() = default; ClangFormatConfigWidget::~ClangFormatConfigWidget() = default;
@@ -211,6 +214,7 @@ void ClangFormatConfigWidget::apply()
ClangFormatSettings &settings = ClangFormatSettings::instance(); ClangFormatSettings &settings = ClangFormatSettings::instance();
settings.setFormatCodeInsteadOfIndent(m_ui->formatAlways->isChecked()); settings.setFormatCodeInsteadOfIndent(m_ui->formatAlways->isChecked());
settings.setFormatWhileTyping(m_ui->formatWhileTyping->isChecked()); settings.setFormatWhileTyping(m_ui->formatWhileTyping->isChecked());
settings.setFormatOnSave(m_ui->formatOnSave->isChecked());
settings.write(); settings.write();
} }

View File

@@ -40,6 +40,13 @@
</property> </property>
</widget> </widget>
</item> </item>
<item>
<widget class="QCheckBox" name="formatOnSave">
<property name="text">
<string>Format edited code on file save</string>
</property>
</widget>
</item>
<item> <item>
<widget class="QLabel" name="projectHasClangFormat"> <widget class="QLabel" name="projectHasClangFormat">
<property name="text"> <property name="text">

View File

@@ -33,5 +33,6 @@ static const char SAMPLE_FILE_NAME[] = "test.cpp";
static const char SETTINGS_ID[] = "ClangFormat"; static const char SETTINGS_ID[] = "ClangFormat";
static const char FORMAT_CODE_INSTEAD_OF_INDENT_ID[] = "ClangFormat.FormatCodeInsteadOfIndent"; static const char FORMAT_CODE_INSTEAD_OF_INDENT_ID[] = "ClangFormat.FormatCodeInsteadOfIndent";
static const char FORMAT_WHILE_TYPING_ID[] = "ClangFormat.FormatWhileTyping"; static const char FORMAT_WHILE_TYPING_ID[] = "ClangFormat.FormatWhileTyping";
static const char FORMAT_CODE_ON_SAVE_ID[] = "ClangFormat.FormatCodeOnSave";
} // namespace Constants } // namespace Constants
} // namespace ClangFormat } // namespace ClangFormat

View File

@@ -87,4 +87,9 @@ int ClangFormatIndenter::lastSaveRevision() const
return qobject_cast<TextEditor::TextDocumentLayout *>(m_doc->documentLayout())->lastSaveRevision; return qobject_cast<TextEditor::TextDocumentLayout *>(m_doc->documentLayout())->lastSaveRevision;
} }
bool ClangFormatIndenter::formatOnSave() const
{
return ClangFormatSettings::instance().formatOnSave();
}
} // namespace ClangFormat } // namespace ClangFormat

View File

@@ -36,6 +36,7 @@ class ClangFormatIndenter final : public ClangFormatBaseIndenter
public: public:
ClangFormatIndenter(QTextDocument *doc); ClangFormatIndenter(QTextDocument *doc);
Utils::optional<TextEditor::TabSettings> tabSettings() const override; Utils::optional<TextEditor::TabSettings> tabSettings() const override;
bool formatOnSave() const override;
private: private:
bool formatCodeInsteadOfIndent() const override; bool formatCodeInsteadOfIndent() const override;

View File

@@ -44,6 +44,8 @@ ClangFormatSettings::ClangFormatSettings()
= settings->value(QLatin1String(Constants::FORMAT_CODE_INSTEAD_OF_INDENT_ID), false).toBool(); = settings->value(QLatin1String(Constants::FORMAT_CODE_INSTEAD_OF_INDENT_ID), false).toBool();
m_formatWhileTyping = settings->value(QLatin1String(Constants::FORMAT_WHILE_TYPING_ID), false) m_formatWhileTyping = settings->value(QLatin1String(Constants::FORMAT_WHILE_TYPING_ID), false)
.toBool(); .toBool();
m_formatOnSave = settings->value(QLatin1String(Constants::FORMAT_CODE_ON_SAVE_ID), false)
.toBool();
settings->endGroup(); settings->endGroup();
} }
@@ -54,6 +56,7 @@ void ClangFormatSettings::write() const
settings->setValue(QLatin1String(Constants::FORMAT_CODE_INSTEAD_OF_INDENT_ID), settings->setValue(QLatin1String(Constants::FORMAT_CODE_INSTEAD_OF_INDENT_ID),
m_formatCodeInsteadOfIndent); m_formatCodeInsteadOfIndent);
settings->setValue(QLatin1String(Constants::FORMAT_WHILE_TYPING_ID), m_formatWhileTyping); settings->setValue(QLatin1String(Constants::FORMAT_WHILE_TYPING_ID), m_formatWhileTyping);
settings->setValue(QLatin1String(Constants::FORMAT_CODE_ON_SAVE_ID), m_formatOnSave);
settings->endGroup(); settings->endGroup();
} }
@@ -77,4 +80,14 @@ bool ClangFormatSettings::formatWhileTyping() const
return m_formatWhileTyping; return m_formatWhileTyping;
} }
void ClangFormatSettings::setFormatOnSave(bool enable)
{
m_formatOnSave = enable;
}
bool ClangFormatSettings::formatOnSave() const
{
return m_formatOnSave;
}
} // namespace ClangFormat } // namespace ClangFormat

View File

@@ -40,9 +40,13 @@ public:
void setFormatWhileTyping(bool enable); void setFormatWhileTyping(bool enable);
bool formatWhileTyping() const; bool formatWhileTyping() const;
void setFormatOnSave(bool enable);
bool formatOnSave() const;
private: private:
bool m_formatCodeInsteadOfIndent = false; bool m_formatCodeInsteadOfIndent = false;
bool m_formatWhileTyping = false; bool m_formatWhileTyping = false;
bool m_formatOnSave = false;
}; };
} // namespace ClangFormat } // namespace ClangFormat

View File

@@ -44,6 +44,7 @@
#include <projectexplorer/session.h> #include <projectexplorer/session.h>
#include <texteditor/icodestylepreferencesfactory.h> #include <texteditor/icodestylepreferencesfactory.h>
#include <texteditor/textdocumentlayout.h>
#include <texteditor/texteditorsettings.h> #include <texteditor/texteditorsettings.h>
#include <coreplugin/editormanager/editormanager.h> #include <coreplugin/editormanager/editormanager.h>
@@ -447,5 +448,47 @@ TextEditor::TabSettings CppEditorDocument::tabSettings() const
return indenter()->tabSettings().value_or(TextEditor::TextDocument::tabSettings()); return indenter()->tabSettings().value_or(TextEditor::TextDocument::tabSettings());
} }
static int formatRange(QTextDocument *doc,
TextEditor::Indenter *indenter,
std::pair<int, int> 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<TextEditor::TextDocumentLayout *>(document()->documentLayout());
const int documentRevision = layout->lastSaveRevision;
std::pair<int, int> 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 Internal
} // namespace CppEditor } // namespace CppEditor

View File

@@ -69,6 +69,10 @@ public:
QFuture<CppTools::CursorInfo> cursorInfo(const CppTools::CursorInfoParams &params); QFuture<CppTools::CursorInfo> cursorInfo(const CppTools::CursorInfoParams &params);
TextEditor::TabSettings tabSettings() const override; TextEditor::TabSettings tabSettings() const override;
bool save(QString *errorString,
const QString &fileName = QString(),
bool autoSave = false) override;
signals: signals:
void codeWarningsUpdated(unsigned contentsRevision, void codeWarningsUpdated(unsigned contentsRevision,
const QList<QTextEdit::ExtraSelection> selections, const QList<QTextEdit::ExtraSelection> selections,

View File

@@ -99,6 +99,8 @@ public:
return Replacements(); return Replacements();
} }
virtual bool formatOnSave() const { return false; }
// Expects a list of blocks in order of occurrence in the document. // Expects a list of blocks in order of occurrence in the document.
virtual IndentationForBlock indentationForBlocks(const QVector<QTextBlock> &blocks, virtual IndentationForBlock indentationForBlocks(const QVector<QTextBlock> &blocks,
const TabSettings &tabSettings, const TabSettings &tabSettings,