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->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();
}

View File

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

View File

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

View File

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

View File

@@ -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

View File

@@ -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

View File

@@ -44,6 +44,7 @@
#include <projectexplorer/session.h>
#include <texteditor/icodestylepreferencesfactory.h>
#include <texteditor/textdocumentlayout.h>
#include <texteditor/texteditorsettings.h>
#include <coreplugin/editormanager/editormanager.h>
@@ -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<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 CppEditor

View File

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

View File

@@ -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<QTextBlock> &blocks,
const TabSettings &tabSettings,