forked from qt-creator/qt-creator
Clang: Significantly improve ClangFormat plugin usability
What's new: 1. New LibFormat option is used to prevent lines shrink, which allows to drop most of tricks used before for that purpose. 2. Cached UTF-8 source code is used to improve performance 3. Improved error handling. 4. Slightly improved UI. Change-Id: I4605200fa103167369a40650b2e1ad2c61e8133b Reviewed-by: Marco Bubke <marco.bubke@qt.io>
This commit is contained in:
@@ -140,5 +140,20 @@ QTextCursor wordStartCursor(const QTextCursor &textCursor)
|
|||||||
return cursor;
|
return cursor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int utf8NthLineOffset(const QTextDocument *textDocument, const QByteArray &buffer, int line)
|
||||||
|
{
|
||||||
|
if (textDocument->characterCount() == buffer.size() + 1)
|
||||||
|
return textDocument->findBlockByNumber(line - 1).position();
|
||||||
|
|
||||||
|
int pos = 0;
|
||||||
|
for (int count = 0; count < line - 1; ++count) {
|
||||||
|
pos = buffer.indexOf('\n', pos);
|
||||||
|
if (pos == -1)
|
||||||
|
return -1;
|
||||||
|
++pos;
|
||||||
|
}
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
} // Text
|
} // Text
|
||||||
} // Utils
|
} // Utils
|
||||||
|
|||||||
@@ -55,6 +55,10 @@ QTCREATOR_UTILS_EXPORT QTextCursor flippedCursor(const QTextCursor &cursor);
|
|||||||
|
|
||||||
QTCREATOR_UTILS_EXPORT QTextCursor wordStartCursor(const QTextCursor &cursor);
|
QTCREATOR_UTILS_EXPORT QTextCursor wordStartCursor(const QTextCursor &cursor);
|
||||||
|
|
||||||
|
QTCREATOR_UTILS_EXPORT int utf8NthLineOffset(const QTextDocument *textDocument,
|
||||||
|
const QByteArray &buffer,
|
||||||
|
int line);
|
||||||
|
|
||||||
template <class CharacterProvider>
|
template <class CharacterProvider>
|
||||||
void moveToPrevChar(CharacterProvider &provider, QTextCursor &cursor)
|
void moveToPrevChar(CharacterProvider &provider, QTextCursor &cursor)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -24,7 +24,8 @@ HEADERS = \
|
|||||||
clangformatconfigwidget.h \
|
clangformatconfigwidget.h \
|
||||||
clangformatindenter.h \
|
clangformatindenter.h \
|
||||||
clangformatplugin.h \
|
clangformatplugin.h \
|
||||||
clangformatconstants.h
|
clangformatconstants.h \
|
||||||
|
clangformatutils.h
|
||||||
|
|
||||||
FORMS += \
|
FORMS += \
|
||||||
clangformatconfigwidget.ui
|
clangformatconfigwidget.ui
|
||||||
|
|||||||
@@ -27,10 +27,11 @@ QtcPlugin {
|
|||||||
"clangformatconfigwidget.cpp",
|
"clangformatconfigwidget.cpp",
|
||||||
"clangformatconfigwidget.h",
|
"clangformatconfigwidget.h",
|
||||||
"clangformatconfigwidget.ui",
|
"clangformatconfigwidget.ui",
|
||||||
|
"clangformatconstants.h",
|
||||||
"clangformatindenter.cpp",
|
"clangformatindenter.cpp",
|
||||||
"clangformatindenter.h",
|
"clangformatindenter.h",
|
||||||
"clangformatplugin.cpp",
|
"clangformatplugin.cpp",
|
||||||
"clangformatplugin.h",
|
"clangformatplugin.h",
|
||||||
"clangformatconstants.h",
|
"clangformatutils.h",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,8 @@
|
|||||||
|
|
||||||
|
|
||||||
#include "clangformatconfigwidget.h"
|
#include "clangformatconfigwidget.h"
|
||||||
|
|
||||||
|
#include "clangformatutils.h"
|
||||||
#include "ui_clangformatconfigwidget.h"
|
#include "ui_clangformatconfigwidget.h"
|
||||||
|
|
||||||
#include <clang/Format/Format.h>
|
#include <clang/Format/Format.h>
|
||||||
@@ -40,23 +42,6 @@
|
|||||||
using namespace ProjectExplorer;
|
using namespace ProjectExplorer;
|
||||||
|
|
||||||
namespace ClangFormat {
|
namespace ClangFormat {
|
||||||
namespace Internal {
|
|
||||||
|
|
||||||
static void createGlobalClangFormatFileIfNeeded(const QString &settingsDir)
|
|
||||||
{
|
|
||||||
const QString fileName = settingsDir + "/.clang-format";
|
|
||||||
if (QFile::exists(fileName))
|
|
||||||
return;
|
|
||||||
|
|
||||||
QFile file(fileName);
|
|
||||||
if (!file.open(QFile::WriteOnly))
|
|
||||||
return;
|
|
||||||
|
|
||||||
const clang::format::FormatStyle defaultStyle = clang::format::getLLVMStyle();
|
|
||||||
const std::string configuration = clang::format::configurationAsText(defaultStyle);
|
|
||||||
file.write(configuration.c_str());
|
|
||||||
file.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void readTable(QTableWidget *table, std::istringstream &stream)
|
static void readTable(QTableWidget *table, std::istringstream &stream)
|
||||||
{
|
{
|
||||||
@@ -137,50 +122,75 @@ ClangFormatConfigWidget::ClangFormatConfigWidget(ProjectExplorer::Project *proje
|
|||||||
{
|
{
|
||||||
m_ui->setupUi(this);
|
m_ui->setupUi(this);
|
||||||
|
|
||||||
std::string testFilePath;
|
initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClangFormatConfigWidget::initialize()
|
||||||
|
{
|
||||||
|
m_ui->projectHasClangFormat->show();
|
||||||
|
m_ui->clangFormatOptionsTable->show();
|
||||||
|
m_ui->applyButton->show();
|
||||||
|
|
||||||
if (m_project && !m_project->projectDirectory().appendPath(".clang-format").exists()) {
|
if (m_project && !m_project->projectDirectory().appendPath(".clang-format").exists()) {
|
||||||
m_ui->projectHasClangFormat->setText("No .clang-format file for the project");
|
m_ui->projectHasClangFormat->setText(tr("No .clang-format file for the project."));
|
||||||
m_ui->clangFormatOptionsTable->hide();
|
m_ui->clangFormatOptionsTable->hide();
|
||||||
m_ui->applyButton->hide();
|
m_ui->applyButton->hide();
|
||||||
|
|
||||||
|
connect(m_ui->createFileButton, &QPushButton::clicked,
|
||||||
|
this, [this]() {
|
||||||
|
createStyleFileIfNeeded(m_project->projectDirectory());
|
||||||
|
initialize();
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_ui->createFileButton->hide();
|
||||||
|
|
||||||
|
std::string testFilePath;
|
||||||
if (m_project) {
|
if (m_project) {
|
||||||
|
m_ui->projectHasClangFormat->hide();
|
||||||
testFilePath = m_project->projectDirectory().appendPath("t.cpp").toString().toStdString();
|
testFilePath = m_project->projectDirectory().appendPath("t.cpp").toString().toStdString();
|
||||||
connect(m_ui->applyButton, &QPushButton::clicked, this, &ClangFormatConfigWidget::apply);
|
connect(m_ui->applyButton, &QPushButton::clicked, this, &ClangFormatConfigWidget::apply);
|
||||||
} else {
|
} else {
|
||||||
|
const Project *currentProject = SessionManager::startupProject();
|
||||||
|
if (!currentProject
|
||||||
|
|| !currentProject->projectDirectory().appendPath(".clang-format").exists()) {
|
||||||
|
m_ui->projectHasClangFormat->hide();
|
||||||
|
} else {
|
||||||
|
m_ui->projectHasClangFormat->setText(
|
||||||
|
tr(" Current project has its own .clang-format file "
|
||||||
|
"and can be configured in Projects > Clang Format."));
|
||||||
|
}
|
||||||
const QString settingsDir = Core::ICore::userResourcePath();
|
const QString settingsDir = Core::ICore::userResourcePath();
|
||||||
createGlobalClangFormatFileIfNeeded(settingsDir);
|
createStyleFileIfNeeded(Utils::FileName::fromString(settingsDir));
|
||||||
testFilePath = settingsDir.toStdString() + "/t.cpp";
|
testFilePath = settingsDir.toStdString() + "/t.cpp";
|
||||||
m_ui->applyButton->hide();
|
m_ui->applyButton->hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
llvm::Expected<clang::format::FormatStyle> formatStyle =
|
fillTable(testFilePath);
|
||||||
clang::format::getStyle("file", testFilePath, "LLVM", "");
|
|
||||||
if (!formatStyle)
|
|
||||||
return;
|
|
||||||
|
|
||||||
const std::string configText = clang::format::configurationAsText(*formatStyle);
|
|
||||||
std::istringstream stream(configText);
|
|
||||||
|
|
||||||
readTable(m_ui->clangFormatOptionsTable, stream);
|
|
||||||
|
|
||||||
if (m_project) {
|
|
||||||
m_ui->projectHasClangFormat->hide();
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const Project *currentProject = SessionManager::startupProject();
|
void ClangFormatConfigWidget::fillTable(const std::string &testFilePath)
|
||||||
if (!currentProject || !currentProject->projectDirectory().appendPath(".clang-format").exists())
|
{
|
||||||
m_ui->projectHasClangFormat->hide();
|
llvm::Expected<clang::format::FormatStyle> formatStyle =
|
||||||
|
clang::format::getStyle("file", testFilePath, "LLVM");
|
||||||
connect(SessionManager::instance(), &SessionManager::startupProjectChanged,
|
std::string configText;
|
||||||
this, [this](ProjectExplorer::Project *project) {
|
bool brokenConfig = false;
|
||||||
if (project && project->projectDirectory().appendPath(".clang-format").exists())
|
if (!formatStyle) {
|
||||||
m_ui->projectHasClangFormat->show();
|
handleAllErrors(formatStyle.takeError(), [](const llvm::ErrorInfoBase &) {
|
||||||
else
|
// do nothing
|
||||||
m_ui->projectHasClangFormat->hide();
|
|
||||||
});
|
});
|
||||||
|
configText = clang::format::configurationAsText(clang::format::getLLVMStyle());
|
||||||
|
brokenConfig = true;
|
||||||
|
} else {
|
||||||
|
configText = clang::format::configurationAsText(*formatStyle);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::istringstream stream(configText);
|
||||||
|
readTable(m_ui->clangFormatOptionsTable, stream);
|
||||||
|
if (brokenConfig)
|
||||||
|
apply();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ClangFormatConfigWidget::~ClangFormatConfigWidget() = default;
|
ClangFormatConfigWidget::~ClangFormatConfigWidget() = default;
|
||||||
@@ -201,5 +211,4 @@ void ClangFormatConfigWidget::apply()
|
|||||||
file.close();
|
file.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Internal
|
|
||||||
} // namespace ClangFormat
|
} // namespace ClangFormat
|
||||||
|
|||||||
@@ -32,7 +32,6 @@
|
|||||||
namespace ProjectExplorer { class Project; }
|
namespace ProjectExplorer { class Project; }
|
||||||
|
|
||||||
namespace ClangFormat {
|
namespace ClangFormat {
|
||||||
namespace Internal {
|
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
class ClangFormatConfigWidget;
|
class ClangFormatConfigWidget;
|
||||||
@@ -49,9 +48,11 @@ public:
|
|||||||
void apply();
|
void apply();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void initialize();
|
||||||
|
void fillTable(const std::string &testFilePath);
|
||||||
|
|
||||||
ProjectExplorer::Project *m_project;
|
ProjectExplorer::Project *m_project;
|
||||||
std::unique_ptr<Ui::ClangFormatConfigWidget> m_ui;
|
std::unique_ptr<Ui::ClangFormatConfigWidget> m_ui;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Internal
|
|
||||||
} // namespace ClangFormat
|
} // namespace ClangFormat
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<ui version="4.0">
|
<ui version="4.0">
|
||||||
<class>ClangFormat::Internal::ClangFormatConfigWidget</class>
|
<class>ClangFormat::ClangFormatConfigWidget</class>
|
||||||
<widget class="QWidget" name="ClangFormat::Internal::ClangFormatConfigWidget">
|
<widget class="QWidget" name="ClangFormat::ClangFormatConfigWidget">
|
||||||
<property name="geometry">
|
<property name="geometry">
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
@@ -29,7 +29,7 @@
|
|||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="projectHasClangFormat">
|
<widget class="QLabel" name="projectHasClangFormat">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string> Current project has its own .clang-format file and can be configured in Projects -> ClangFormat.</string>
|
<string/>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@@ -38,6 +38,13 @@
|
|||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="createFileButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>Create Clang Format Configuration File</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QPushButton" name="applyButton">
|
<widget class="QPushButton" name="applyButton">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
|
|||||||
@@ -25,16 +25,21 @@
|
|||||||
|
|
||||||
#include "clangformatindenter.h"
|
#include "clangformatindenter.h"
|
||||||
|
|
||||||
|
#include "clangformatutils.h"
|
||||||
|
|
||||||
#include <clang/Format/Format.h>
|
#include <clang/Format/Format.h>
|
||||||
#include <clang/Tooling/Core/Replacement.h>
|
#include <clang/Tooling/Core/Replacement.h>
|
||||||
|
|
||||||
#include <coreplugin/icore.h>
|
#include <coreplugin/icore.h>
|
||||||
|
#include <cpptools/cppmodelmanager.h>
|
||||||
#include <projectexplorer/project.h>
|
#include <projectexplorer/project.h>
|
||||||
#include <projectexplorer/session.h>
|
#include <projectexplorer/session.h>
|
||||||
#include <texteditor/textdocument.h>
|
#include <texteditor/textdocument.h>
|
||||||
#include <texteditor/texteditor.h>
|
#include <texteditor/texteditor.h>
|
||||||
|
|
||||||
#include <utils/hostosinfo.h>
|
#include <utils/hostosinfo.h>
|
||||||
|
#include <utils/textutils.h>
|
||||||
|
#include <utils/qtcassert.h>
|
||||||
|
|
||||||
#include <llvm/Config/llvm-config.h>
|
#include <llvm/Config/llvm-config.h>
|
||||||
|
|
||||||
@@ -42,6 +47,8 @@
|
|||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
#include <QTextBlock>
|
#include <QTextBlock>
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
using namespace clang;
|
using namespace clang;
|
||||||
using namespace format;
|
using namespace format;
|
||||||
using namespace llvm;
|
using namespace llvm;
|
||||||
@@ -50,67 +57,35 @@ using namespace ProjectExplorer;
|
|||||||
using namespace TextEditor;
|
using namespace TextEditor;
|
||||||
|
|
||||||
namespace ClangFormat {
|
namespace ClangFormat {
|
||||||
namespace Internal {
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
void adjustFormatStyleForLineBreak(format::FormatStyle &style,
|
void adjustFormatStyleForLineBreak(format::FormatStyle &style)
|
||||||
int length,
|
|
||||||
int prevBlockSize,
|
|
||||||
bool prevBlockEndsWithPunctuation)
|
|
||||||
{
|
{
|
||||||
if (length > 0)
|
style.DisableFormat = false;
|
||||||
style.ColumnLimit = prevBlockSize;
|
style.ColumnLimit = 0;
|
||||||
style.AlwaysBreakBeforeMultilineStrings = true;
|
#ifdef KEEP_LINE_BREAKS_FOR_NON_EMPTY_LINES_BACKPORTED
|
||||||
#if LLVM_VERSION_MAJOR >= 7
|
style.KeepLineBreaksForNonEmptyLines = true;
|
||||||
style.AlwaysBreakTemplateDeclarations = FormatStyle::BTDS_Yes;
|
|
||||||
#else
|
|
||||||
style.AlwaysBreakTemplateDeclarations = true;
|
|
||||||
#endif
|
#endif
|
||||||
|
style.MaxEmptyLinesToKeep = 2;
|
||||||
style.AllowAllParametersOfDeclarationOnNextLine = true;
|
|
||||||
style.AllowShortBlocksOnASingleLine = true;
|
|
||||||
style.AllowShortCaseLabelsOnASingleLine = true;
|
|
||||||
style.AllowShortFunctionsOnASingleLine = FormatStyle::SFS_Empty;
|
|
||||||
style.AllowShortIfStatementsOnASingleLine = true;
|
|
||||||
style.AllowShortLoopsOnASingleLine = true;
|
|
||||||
if (prevBlockEndsWithPunctuation) {
|
|
||||||
style.BreakBeforeBinaryOperators = FormatStyle::BOS_None;
|
|
||||||
style.BreakBeforeTernaryOperators = false;
|
|
||||||
style.BreakConstructorInitializers = FormatStyle::BCIS_AfterColon;
|
|
||||||
} else {
|
|
||||||
style.BreakBeforeBinaryOperators = FormatStyle::BOS_All;
|
|
||||||
style.BreakBeforeTernaryOperators = true;
|
|
||||||
style.BreakConstructorInitializers = FormatStyle::BCIS_BeforeComma;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Replacements filteredReplacements(const Replacements &replacements,
|
Replacements filteredReplacements(const Replacements &replacements,
|
||||||
unsigned int offset,
|
int offset,
|
||||||
unsigned int lengthForFilter,
|
int lengthForFilter,
|
||||||
int extraOffsetToAdd,
|
int extraOffsetToAdd)
|
||||||
int prevBlockLength)
|
|
||||||
{
|
{
|
||||||
Replacements filtered;
|
Replacements filtered;
|
||||||
for (const Replacement &replacement : replacements) {
|
for (const Replacement &replacement : replacements) {
|
||||||
unsigned int replacementOffset = replacement.getOffset();
|
int replacementOffset = static_cast<int>(replacement.getOffset());
|
||||||
if (replacementOffset > offset + lengthForFilter)
|
if (replacementOffset > offset + lengthForFilter)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (offset > static_cast<unsigned int>(prevBlockLength)
|
|
||||||
&& replacementOffset < offset - static_cast<unsigned int>(prevBlockLength))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (lengthForFilter == 0 && replacement.getReplacementText().find('\n') == std::string::npos
|
|
||||||
&& filtered.empty()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (replacementOffset + 1 >= offset)
|
if (replacementOffset + 1 >= offset)
|
||||||
replacementOffset += static_cast<unsigned int>(extraOffsetToAdd);
|
replacementOffset += extraOffsetToAdd;
|
||||||
|
|
||||||
Error error = filtered.add(Replacement(replacement.getFilePath(),
|
Error error = filtered.add(Replacement(replacement.getFilePath(),
|
||||||
replacementOffset,
|
static_cast<unsigned int>(replacementOffset),
|
||||||
replacement.getLength(),
|
replacement.getLength(),
|
||||||
replacement.getReplacementText()));
|
replacement.getReplacementText()));
|
||||||
// Throws if error is not checked.
|
// Throws if error is not checked.
|
||||||
@@ -120,147 +95,58 @@ Replacements filteredReplacements(const Replacements &replacements,
|
|||||||
return filtered;
|
return filtered;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string assumedFilePath()
|
Utils::FileName styleConfigPath()
|
||||||
{
|
{
|
||||||
const Project *project = SessionManager::startupProject();
|
const Project *project = SessionManager::startupProject();
|
||||||
if (project && project->projectDirectory().appendPath(".clang-format").exists())
|
if (project && project->projectDirectory().appendPath(".clang-format").exists())
|
||||||
return project->projectDirectory().appendPath("test.cpp").toString().toStdString();
|
return project->projectDirectory();
|
||||||
|
|
||||||
return QString(Core::ICore::userResourcePath() + "/test.cpp").toStdString();
|
return Utils::FileName::fromString(Core::ICore::userResourcePath());
|
||||||
}
|
}
|
||||||
|
|
||||||
FormatStyle formatStyle()
|
FormatStyle formatStyle(Utils::FileName styleConfigPath)
|
||||||
{
|
{
|
||||||
Expected<FormatStyle> style = format::getStyle("file", assumedFilePath(), "none", "");
|
createStyleFileIfNeeded(styleConfigPath);
|
||||||
|
|
||||||
|
Expected<FormatStyle> style = format::getStyle(
|
||||||
|
"file", styleConfigPath.appendPath("test.cpp").toString().toStdString(), "LLVM");
|
||||||
if (style)
|
if (style)
|
||||||
return *style;
|
return *style;
|
||||||
return FormatStyle();
|
|
||||||
|
handleAllErrors(style.takeError(), [](const ErrorInfoBase &) {
|
||||||
|
// do nothing
|
||||||
|
});
|
||||||
|
|
||||||
|
return format::getLLVMStyle();
|
||||||
}
|
}
|
||||||
|
|
||||||
Replacements replacements(const std::string &buffer,
|
void trimFirstNonEmptyBlock(const QTextBlock ¤tBlock)
|
||||||
unsigned int offset,
|
|
||||||
unsigned int length,
|
|
||||||
bool blockFormatting = false,
|
|
||||||
const QChar &typedChar = QChar::Null,
|
|
||||||
int extraOffsetToAdd = 0,
|
|
||||||
int prevBlockLength = 1,
|
|
||||||
bool prevBlockEndsWithPunctuation = false)
|
|
||||||
{
|
|
||||||
FormatStyle style = formatStyle();
|
|
||||||
|
|
||||||
if (blockFormatting && typedChar == QChar::Null)
|
|
||||||
adjustFormatStyleForLineBreak(style, length, prevBlockLength, prevBlockEndsWithPunctuation);
|
|
||||||
|
|
||||||
std::vector<Range> ranges{{offset, length}};
|
|
||||||
FormattingAttemptStatus status;
|
|
||||||
|
|
||||||
Replacements replacements = reformat(style, buffer, ranges, assumedFilePath(), &status);
|
|
||||||
|
|
||||||
if (!status.FormatComplete)
|
|
||||||
Replacements();
|
|
||||||
|
|
||||||
unsigned int lengthForFilter = 0;
|
|
||||||
if (!blockFormatting)
|
|
||||||
lengthForFilter = length;
|
|
||||||
|
|
||||||
return filteredReplacements(replacements,
|
|
||||||
offset,
|
|
||||||
lengthForFilter,
|
|
||||||
extraOffsetToAdd,
|
|
||||||
prevBlockLength);
|
|
||||||
}
|
|
||||||
|
|
||||||
void applyReplacements(QTextDocument *doc,
|
|
||||||
const std::string &stdStrBuffer,
|
|
||||||
const tooling::Replacements &replacements,
|
|
||||||
int totalShift)
|
|
||||||
{
|
|
||||||
if (replacements.empty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
QTextCursor editCursor(doc);
|
|
||||||
int fullOffsetDiff = 0;
|
|
||||||
for (const Replacement &replacement : replacements) {
|
|
||||||
const int utf16Offset
|
|
||||||
= QString::fromStdString(stdStrBuffer.substr(0, replacement.getOffset())).length()
|
|
||||||
+ totalShift + fullOffsetDiff;
|
|
||||||
const int utf16Length = QString::fromStdString(stdStrBuffer.substr(replacement.getOffset(),
|
|
||||||
replacement.getLength()))
|
|
||||||
.length();
|
|
||||||
const QString replacementText = QString::fromStdString(replacement.getReplacementText());
|
|
||||||
|
|
||||||
editCursor.beginEditBlock();
|
|
||||||
editCursor.setPosition(utf16Offset);
|
|
||||||
editCursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, utf16Length);
|
|
||||||
editCursor.removeSelectedText();
|
|
||||||
editCursor.insertText(replacementText);
|
|
||||||
editCursor.endEditBlock();
|
|
||||||
fullOffsetDiff += replacementText.length() - utf16Length;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns offset shift.
|
|
||||||
int modifyToIndentEmptyLines(QString &buffer, int &offset, int &length, const QTextBlock &block)
|
|
||||||
{
|
|
||||||
//This extra text works for the most cases.
|
|
||||||
QString extraText("a;");
|
|
||||||
|
|
||||||
const QString blockText = block.text().trimmed();
|
|
||||||
// Search for previous character
|
|
||||||
QTextBlock prevBlock = block.previous();
|
|
||||||
while (prevBlock.position() > 0 && prevBlock.text().trimmed().isEmpty())
|
|
||||||
prevBlock = prevBlock.previous();
|
|
||||||
if (prevBlock.text().endsWith(','))
|
|
||||||
extraText = "int a,";
|
|
||||||
|
|
||||||
const bool closingParenBlock = blockText.startsWith(')');
|
|
||||||
if (closingParenBlock) {
|
|
||||||
if (prevBlock.text().endsWith(','))
|
|
||||||
extraText = "int a";
|
|
||||||
else
|
|
||||||
extraText = "&& a";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (length == 0 || closingParenBlock) {
|
|
||||||
length += extraText.length();
|
|
||||||
buffer.insert(offset, extraText);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (blockText.startsWith('}')) {
|
|
||||||
buffer.insert(offset - 1, extraText);
|
|
||||||
offset += extraText.size();
|
|
||||||
return extraText.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns first non-empty block (searches from current block backwards).
|
|
||||||
QTextBlock clearFirstNonEmptyBlockFromExtraSpaces(const QTextBlock ¤tBlock)
|
|
||||||
{
|
{
|
||||||
QTextBlock prevBlock = currentBlock.previous();
|
QTextBlock prevBlock = currentBlock.previous();
|
||||||
while (prevBlock.position() > 0 && prevBlock.text().trimmed().isEmpty())
|
while (prevBlock.position() > 0 && prevBlock.text().trimmed().isEmpty())
|
||||||
prevBlock = prevBlock.previous();
|
prevBlock = prevBlock.previous();
|
||||||
|
|
||||||
if (prevBlock.text().trimmed().isEmpty())
|
if (prevBlock.text().trimmed().isEmpty())
|
||||||
return prevBlock;
|
return;
|
||||||
|
|
||||||
const QString initialText = prevBlock.text();
|
const QString initialText = prevBlock.text();
|
||||||
if (!initialText.at(initialText.length() - 1).isSpace())
|
if (!initialText.at(initialText.size() - 1).isSpace())
|
||||||
return prevBlock;
|
return;
|
||||||
|
|
||||||
|
int extraSpaceCount = 1;
|
||||||
|
for (int i = initialText.size() - 2; i >= 0; --i) {
|
||||||
|
if (!initialText.at(i).isSpace())
|
||||||
|
break;
|
||||||
|
++extraSpaceCount;
|
||||||
|
}
|
||||||
|
|
||||||
QTextCursor cursor(prevBlock);
|
QTextCursor cursor(prevBlock);
|
||||||
cursor.beginEditBlock();
|
cursor.beginEditBlock();
|
||||||
cursor.movePosition(QTextCursor::EndOfBlock);
|
cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor,
|
||||||
cursor.movePosition(QTextCursor::Left);
|
initialText.size() - extraSpaceCount);
|
||||||
|
cursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, extraSpaceCount);
|
||||||
while (cursor.positionInBlock() >= 0 && initialText.at(cursor.positionInBlock()).isSpace())
|
|
||||||
cursor.movePosition(QTextCursor::Left, QTextCursor::KeepAnchor);
|
|
||||||
if (cursor.hasSelection())
|
|
||||||
cursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor);
|
|
||||||
cursor.removeSelectedText();
|
cursor.removeSelectedText();
|
||||||
cursor.endEditBlock();
|
cursor.endEditBlock();
|
||||||
return prevBlock;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the total langth of previous lines with pure whitespace.
|
// Returns the total langth of previous lines with pure whitespace.
|
||||||
@@ -276,42 +162,175 @@ int previousEmptyLinesLength(const QTextBlock ¤tBlock)
|
|||||||
return length;
|
return length;
|
||||||
}
|
}
|
||||||
|
|
||||||
static constexpr const int MinCharactersBeforeCurrentInBuffer = 200;
|
void modifyToIndentEmptyLines(QByteArray &buffer, int &offset, int &length, const QTextBlock &block)
|
||||||
static constexpr const int MaxCharactersBeforeCurrentInBuffer = 500;
|
|
||||||
|
|
||||||
int startOfIndentationBuffer(const QString &buffer, int start)
|
|
||||||
{
|
{
|
||||||
if (start < MaxCharactersBeforeCurrentInBuffer)
|
const QString blockText = block.text().trimmed();
|
||||||
return 0;
|
const bool closingParenBlock = blockText.startsWith(')');
|
||||||
|
if (length != 0 && !closingParenBlock)
|
||||||
|
return;
|
||||||
|
|
||||||
auto it = buffer.cbegin() + (start - MinCharactersBeforeCurrentInBuffer);
|
//This extra text works for the most cases.
|
||||||
for (; it != buffer.cbegin() + (start - MaxCharactersBeforeCurrentInBuffer); --it) {
|
QByteArray extraText("a;");
|
||||||
if (*it == '{') {
|
|
||||||
// Find the start of it's line.
|
// Search for previous character
|
||||||
for (auto inner = it;
|
QTextBlock prevBlock = block.previous();
|
||||||
inner != buffer.cbegin() + (start - MaxCharactersBeforeCurrentInBuffer);
|
while (prevBlock.position() > 0 && prevBlock.text().trimmed().isEmpty())
|
||||||
--inner) {
|
prevBlock = prevBlock.previous();
|
||||||
if (*inner == '\n')
|
if (prevBlock.text().endsWith(','))
|
||||||
return inner + 1 - buffer.cbegin();
|
extraText = "int a,";
|
||||||
|
|
||||||
|
if (closingParenBlock) {
|
||||||
|
if (prevBlock.text().endsWith(','))
|
||||||
|
extraText = "int a";
|
||||||
|
else
|
||||||
|
extraText = "&& a";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
length += extraText.length();
|
||||||
|
buffer.insert(offset, extraText);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const int kMaxLinesFromCurrentBlock = 200;
|
||||||
|
|
||||||
|
Replacements replacements(QByteArray buffer,
|
||||||
|
int utf8Offset,
|
||||||
|
int utf8Length,
|
||||||
|
const QTextBlock *block = nullptr,
|
||||||
|
const QChar &typedChar = QChar::Null)
|
||||||
|
{
|
||||||
|
Utils::FileName stylePath = styleConfigPath();
|
||||||
|
FormatStyle style = formatStyle(stylePath);
|
||||||
|
|
||||||
|
int extraOffset = 0;
|
||||||
|
if (block) {
|
||||||
|
if (block->blockNumber() > kMaxLinesFromCurrentBlock) {
|
||||||
|
extraOffset = Utils::Text::utf8NthLineOffset(
|
||||||
|
block->document(), buffer, block->blockNumber() - kMaxLinesFromCurrentBlock);
|
||||||
|
}
|
||||||
|
buffer = buffer.mid(extraOffset,
|
||||||
|
std::min(buffer.size(), utf8Offset + kMaxLinesFromCurrentBlock)
|
||||||
|
- extraOffset);
|
||||||
|
utf8Offset -= extraOffset;
|
||||||
|
|
||||||
|
const int emptySpaceLength = previousEmptyLinesLength(*block);
|
||||||
|
utf8Offset -= emptySpaceLength;
|
||||||
|
buffer.remove(utf8Offset, emptySpaceLength);
|
||||||
|
|
||||||
|
extraOffset += emptySpaceLength;
|
||||||
|
|
||||||
|
adjustFormatStyleForLineBreak(style);
|
||||||
|
if (typedChar == QChar::Null)
|
||||||
|
modifyToIndentEmptyLines(buffer, utf8Offset, utf8Length, *block);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Range> ranges{{static_cast<unsigned int>(utf8Offset),
|
||||||
|
static_cast<unsigned int>(utf8Length)}};
|
||||||
|
FormattingAttemptStatus status;
|
||||||
|
|
||||||
|
const std::string assumedFilePath
|
||||||
|
= stylePath.appendPath("test.cpp").toString().toStdString();
|
||||||
|
Replacements replacements = reformat(style, buffer.data(), ranges, assumedFilePath, &status);
|
||||||
|
|
||||||
|
if (!status.FormatComplete)
|
||||||
|
Replacements();
|
||||||
|
|
||||||
|
int lengthForFilter = 0;
|
||||||
|
if (block == nullptr)
|
||||||
|
lengthForFilter = utf8Length;
|
||||||
|
|
||||||
|
return filteredReplacements(replacements,
|
||||||
|
utf8Offset,
|
||||||
|
lengthForFilter,
|
||||||
|
extraOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
Utils::LineColumn utf16LineColumn(const QTextBlock &block,
|
||||||
|
int blockOffsetUtf8,
|
||||||
|
const QByteArray &utf8Buffer,
|
||||||
|
int utf8Offset)
|
||||||
|
{
|
||||||
|
if (utf8Offset < blockOffsetUtf8 - 1)
|
||||||
|
return Utils::LineColumn();
|
||||||
|
|
||||||
|
if (utf8Offset == blockOffsetUtf8 - 1) {
|
||||||
|
const int lineStart = utf8Buffer.lastIndexOf('\n', utf8Offset - 1) + 1;
|
||||||
|
const QByteArray lineText = utf8Buffer.mid(lineStart, utf8Offset - lineStart);
|
||||||
|
return Utils::LineColumn(block.blockNumber(), QString::fromUtf8(lineText).size() + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int pos = blockOffsetUtf8;
|
||||||
|
int prevPos = pos;
|
||||||
|
int line = block.blockNumber(); // Start with previous line.
|
||||||
|
while (pos != -1 && pos <= utf8Offset) {
|
||||||
|
// Find the first pos which comes after offset and take the previous line.
|
||||||
|
++line;
|
||||||
|
prevPos = pos;
|
||||||
|
pos = utf8Buffer.indexOf('\n', pos);
|
||||||
|
if (pos != -1)
|
||||||
|
++pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QByteArray lineText = utf8Buffer.mid(prevPos, utf8Offset - prevPos);
|
||||||
|
return Utils::LineColumn(line, QString::fromUtf8(lineText).size() + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
tooling::Replacements utf16Replacements(const QTextBlock &block,
|
||||||
|
int blockOffsetUtf8,
|
||||||
|
const QByteArray &utf8Buffer,
|
||||||
|
const tooling::Replacements &replacements)
|
||||||
|
{
|
||||||
|
tooling::Replacements convertedReplacements;
|
||||||
|
for (const Replacement &replacement : replacements) {
|
||||||
|
const Utils::LineColumn lineColUtf16 = utf16LineColumn(
|
||||||
|
block, blockOffsetUtf8, utf8Buffer, static_cast<int>(replacement.getOffset()));
|
||||||
|
if (!lineColUtf16.isValid())
|
||||||
|
continue;
|
||||||
|
const int utf16Offset = Utils::Text::positionInText(block.document(),
|
||||||
|
lineColUtf16.line,
|
||||||
|
lineColUtf16.column);
|
||||||
|
const int utf16Length = QString::fromUtf8(
|
||||||
|
utf8Buffer.mid(static_cast<int>(replacement.getOffset()),
|
||||||
|
static_cast<int>(replacement.getLength()))).size();
|
||||||
|
Error error = convertedReplacements.add(
|
||||||
|
Replacement(replacement.getFilePath(),
|
||||||
|
static_cast<unsigned int>(utf16Offset),
|
||||||
|
static_cast<unsigned int>(utf16Length),
|
||||||
|
replacement.getReplacementText()));
|
||||||
|
// Throws if error is not checked.
|
||||||
|
if (error)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return convertedReplacements;
|
||||||
}
|
}
|
||||||
|
|
||||||
return it - buffer.cbegin();
|
void applyReplacements(const QTextBlock &block,
|
||||||
}
|
int blockOffsetUtf8,
|
||||||
|
const QByteArray &utf8Buffer,
|
||||||
int nextEndingScopePosition(const QString &buffer, int start)
|
const tooling::Replacements &replacements)
|
||||||
{
|
{
|
||||||
if (start >= buffer.size() - 1)
|
if (replacements.empty())
|
||||||
return buffer.size() - 1;
|
return;
|
||||||
|
|
||||||
for (auto it = buffer.cbegin() + (start + 1); it != buffer.cend(); ++it) {
|
tooling::Replacements convertedReplacements = utf16Replacements(block,
|
||||||
if (*it == '}')
|
blockOffsetUtf8,
|
||||||
return it - buffer.cbegin();
|
utf8Buffer,
|
||||||
|
replacements);
|
||||||
|
|
||||||
|
int fullOffsetShift = 0;
|
||||||
|
QTextCursor editCursor(block);
|
||||||
|
for (const Replacement &replacement : convertedReplacements) {
|
||||||
|
const QString replacementString = QString::fromStdString(replacement.getReplacementText());
|
||||||
|
const int replacementLength = static_cast<int>(replacement.getLength());
|
||||||
|
editCursor.beginEditBlock();
|
||||||
|
editCursor.setPosition(static_cast<int>(replacement.getOffset()) + fullOffsetShift);
|
||||||
|
editCursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor,
|
||||||
|
replacementLength);
|
||||||
|
editCursor.removeSelectedText();
|
||||||
|
editCursor.insertText(replacementString);
|
||||||
|
editCursor.endEditBlock();
|
||||||
|
fullOffsetShift += replacementString.length() - replacementLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
return buffer.size() - 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // anonymous namespace
|
} // anonymous namespace
|
||||||
@@ -342,30 +361,34 @@ void ClangFormatIndenter::indent(QTextDocument *doc,
|
|||||||
bool autoTriggered)
|
bool autoTriggered)
|
||||||
{
|
{
|
||||||
if (typedChar == QChar::Null && (cursor.hasSelection() || !autoTriggered)) {
|
if (typedChar == QChar::Null && (cursor.hasSelection() || !autoTriggered)) {
|
||||||
TextEditorWidget *editor = TextEditorWidget::currentTextEditorWidget();
|
int utf8Offset;
|
||||||
int offset;
|
int utf8Length;
|
||||||
int length;
|
const QByteArray buffer = doc->toPlainText().toUtf8();
|
||||||
if (cursor.hasSelection()) {
|
if (cursor.hasSelection()) {
|
||||||
const QTextBlock start = doc->findBlock(cursor.selectionStart());
|
const QTextBlock start = doc->findBlock(cursor.selectionStart());
|
||||||
const QTextBlock end = doc->findBlock(cursor.selectionEnd());
|
const QTextBlock end = doc->findBlock(cursor.selectionEnd());
|
||||||
offset = start.position();
|
utf8Offset = Utils::Text::utf8NthLineOffset(doc, buffer, start.blockNumber() + 1);
|
||||||
length = std::max(0, end.position() + end.length() - start.position() - 1);
|
QTC_ASSERT(utf8Offset >= 0, return;);
|
||||||
|
utf8Length =
|
||||||
|
Utils::Text::textAt(
|
||||||
|
QTextCursor(doc),
|
||||||
|
start.position(),
|
||||||
|
std::max(0, end.position() + end.length() - start.position() - 1))
|
||||||
|
.toUtf8().size();
|
||||||
|
applyReplacements(start,
|
||||||
|
utf8Offset,
|
||||||
|
buffer,
|
||||||
|
replacements(buffer, utf8Offset, utf8Length));
|
||||||
} else {
|
} else {
|
||||||
const QTextBlock block = cursor.block();
|
const QTextBlock block = cursor.block();
|
||||||
offset = block.position();
|
utf8Offset = Utils::Text::utf8NthLineOffset(doc, buffer, block.blockNumber() + 1);
|
||||||
length = std::max(0, block.length() - 1);
|
QTC_ASSERT(utf8Offset >= 0, return;);
|
||||||
|
utf8Length = block.text().toUtf8().size();
|
||||||
|
applyReplacements(block,
|
||||||
|
utf8Offset,
|
||||||
|
buffer,
|
||||||
|
replacements(buffer, utf8Offset, utf8Length));
|
||||||
}
|
}
|
||||||
QString buffer = editor->toPlainText();
|
|
||||||
const int totalShift = startOfIndentationBuffer(buffer, offset);
|
|
||||||
const int cutAtPos = nextEndingScopePosition(buffer, offset + length) + 1;
|
|
||||||
buffer = buffer.mid(totalShift, cutAtPos - totalShift);
|
|
||||||
offset -= totalShift;
|
|
||||||
const std::string stdStrBefore = buffer.left(offset).toStdString();
|
|
||||||
const std::string stdStrBuffer = stdStrBefore + buffer.mid(offset).toStdString();
|
|
||||||
applyReplacements(doc,
|
|
||||||
stdStrBuffer,
|
|
||||||
replacements(stdStrBuffer, stdStrBefore.length(), length),
|
|
||||||
totalShift);
|
|
||||||
} else {
|
} else {
|
||||||
indentBlock(doc, cursor.block(), typedChar, tabSettings);
|
indentBlock(doc, cursor.block(), typedChar, tabSettings);
|
||||||
}
|
}
|
||||||
@@ -389,46 +412,16 @@ void ClangFormatIndenter::indentBlock(QTextDocument *doc,
|
|||||||
if (!editor)
|
if (!editor)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const QTextBlock prevBlock = clearFirstNonEmptyBlockFromExtraSpaces(block);
|
trimFirstNonEmptyBlock(block);
|
||||||
|
const QByteArray buffer = doc->toPlainText().toUtf8();
|
||||||
|
const int utf8Offset = Utils::Text::utf8NthLineOffset(doc, buffer, block.blockNumber() + 1);
|
||||||
|
QTC_ASSERT(utf8Offset >= 0, return;);
|
||||||
|
const int utf8Length = block.text().toUtf8().size();
|
||||||
|
|
||||||
int offset = block.position();
|
applyReplacements(block,
|
||||||
int length = std::max(0, block.length() - 1);
|
utf8Offset,
|
||||||
QString buffer = editor->toPlainText();
|
buffer,
|
||||||
|
replacements(buffer, utf8Offset, utf8Length, &block, typedChar));
|
||||||
int emptySpaceLength = previousEmptyLinesLength(block);
|
|
||||||
offset -= emptySpaceLength;
|
|
||||||
buffer.remove(offset, emptySpaceLength);
|
|
||||||
|
|
||||||
int extraPrevBlockLength{0};
|
|
||||||
int prevBlockTextLength{1};
|
|
||||||
bool prevBlockEndsWithPunctuation = false;
|
|
||||||
if (typedChar == QChar::Null) {
|
|
||||||
extraPrevBlockLength = modifyToIndentEmptyLines(buffer, offset, length, block);
|
|
||||||
|
|
||||||
const QString prevBlockText = prevBlock.text();
|
|
||||||
prevBlockEndsWithPunctuation = prevBlockText.size() > 0
|
|
||||||
? prevBlockText.at(prevBlockText.size() - 1).isPunct()
|
|
||||||
: false;
|
|
||||||
prevBlockTextLength = prevBlockText.size() + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
const int totalShift = startOfIndentationBuffer(buffer, offset);
|
|
||||||
const int cutAtPos = nextEndingScopePosition(buffer, offset + length) + 1;
|
|
||||||
buffer = buffer.mid(totalShift, cutAtPos - totalShift);
|
|
||||||
offset -= totalShift;
|
|
||||||
const std::string stdStrBefore = buffer.left(offset).toStdString();
|
|
||||||
const std::string stdStrBuffer = stdStrBefore + buffer.mid(offset).toStdString();
|
|
||||||
applyReplacements(doc,
|
|
||||||
stdStrBuffer,
|
|
||||||
replacements(stdStrBuffer,
|
|
||||||
stdStrBefore.length(),
|
|
||||||
length,
|
|
||||||
true,
|
|
||||||
typedChar,
|
|
||||||
emptySpaceLength - extraPrevBlockLength,
|
|
||||||
prevBlockTextLength + extraPrevBlockLength,
|
|
||||||
prevBlockEndsWithPunctuation),
|
|
||||||
totalShift);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int ClangFormatIndenter::indentFor(const QTextBlock &block, const TextEditor::TabSettings &)
|
int ClangFormatIndenter::indentFor(const QTextBlock &block, const TextEditor::TabSettings &)
|
||||||
@@ -437,36 +430,14 @@ int ClangFormatIndenter::indentFor(const QTextBlock &block, const TextEditor::Ta
|
|||||||
if (!editor)
|
if (!editor)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
const QTextBlock prevBlock = clearFirstNonEmptyBlockFromExtraSpaces(block);
|
trimFirstNonEmptyBlock(block);
|
||||||
|
const QTextDocument *doc = block.document();
|
||||||
|
const QByteArray buffer = doc->toPlainText().toUtf8();
|
||||||
|
const int utf8Offset = Utils::Text::utf8NthLineOffset(doc, buffer, block.blockNumber() + 1);
|
||||||
|
QTC_ASSERT(utf8Offset >= 0, return 0;);
|
||||||
|
const int utf8Length = block.text().toUtf8().size();
|
||||||
|
|
||||||
int offset = block.position();
|
Replacements toReplace = replacements(buffer, utf8Offset, utf8Length, &block);
|
||||||
int length = std::max(0, block.length() - 1);
|
|
||||||
QString buffer = editor->toPlainText();
|
|
||||||
|
|
||||||
int emptySpaceLength = previousEmptyLinesLength(block);
|
|
||||||
offset -= emptySpaceLength;
|
|
||||||
buffer.replace(offset, emptySpaceLength, "");
|
|
||||||
int extraPrevBlockLength = modifyToIndentEmptyLines(buffer, offset, length, block);
|
|
||||||
|
|
||||||
const QString prevBlockText = prevBlock.text();
|
|
||||||
bool prevBlockEndsWithPunctuation = prevBlockText.size() > 0
|
|
||||||
? prevBlockText.at(prevBlockText.size() - 1).isPunct()
|
|
||||||
: false;
|
|
||||||
|
|
||||||
const int totalShift = startOfIndentationBuffer(buffer, offset);
|
|
||||||
const int cutAtPos = nextEndingScopePosition(buffer, offset + length) + 1;
|
|
||||||
buffer = buffer.mid(totalShift, cutAtPos - totalShift);
|
|
||||||
offset -= totalShift;
|
|
||||||
const std::string stdStrBefore = buffer.left(offset).toStdString();
|
|
||||||
const std::string stdStrBuffer = stdStrBefore + buffer.mid(offset).toStdString();
|
|
||||||
Replacements toReplace = replacements(stdStrBuffer,
|
|
||||||
stdStrBefore.length(),
|
|
||||||
length,
|
|
||||||
true,
|
|
||||||
QChar::Null,
|
|
||||||
emptySpaceLength - extraPrevBlockLength,
|
|
||||||
prevBlockText.size() + extraPrevBlockLength + 1,
|
|
||||||
prevBlockEndsWithPunctuation);
|
|
||||||
|
|
||||||
if (toReplace.empty())
|
if (toReplace.empty())
|
||||||
return -1;
|
return -1;
|
||||||
@@ -481,7 +452,7 @@ int ClangFormatIndenter::indentFor(const QTextBlock &block, const TextEditor::Ta
|
|||||||
|
|
||||||
TabSettings ClangFormatIndenter::tabSettings() const
|
TabSettings ClangFormatIndenter::tabSettings() const
|
||||||
{
|
{
|
||||||
FormatStyle style = formatStyle();
|
FormatStyle style = formatStyle(styleConfigPath());
|
||||||
TabSettings tabSettings;
|
TabSettings tabSettings;
|
||||||
|
|
||||||
switch (style.UseTab) {
|
switch (style.UseTab) {
|
||||||
@@ -495,8 +466,8 @@ TabSettings ClangFormatIndenter::tabSettings() const
|
|||||||
tabSettings.m_tabPolicy = TabSettings::MixedTabPolicy;
|
tabSettings.m_tabPolicy = TabSettings::MixedTabPolicy;
|
||||||
}
|
}
|
||||||
|
|
||||||
tabSettings.m_tabSize = style.TabWidth;
|
tabSettings.m_tabSize = static_cast<int>(style.TabWidth);
|
||||||
tabSettings.m_indentSize = style.IndentWidth;
|
tabSettings.m_indentSize = static_cast<int>(style.IndentWidth);
|
||||||
|
|
||||||
if (style.AlignAfterOpenBracket)
|
if (style.AlignAfterOpenBracket)
|
||||||
tabSettings.m_continuationAlignBehavior = TabSettings::ContinuationAlignWithSpaces;
|
tabSettings.m_continuationAlignBehavior = TabSettings::ContinuationAlignWithSpaces;
|
||||||
@@ -506,5 +477,4 @@ TabSettings ClangFormatIndenter::tabSettings() const
|
|||||||
return tabSettings;
|
return tabSettings;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Internal
|
|
||||||
} // namespace ClangFormat
|
} // namespace ClangFormat
|
||||||
|
|||||||
@@ -28,7 +28,6 @@
|
|||||||
#include <texteditor/indenter.h>
|
#include <texteditor/indenter.h>
|
||||||
|
|
||||||
namespace ClangFormat {
|
namespace ClangFormat {
|
||||||
namespace Internal {
|
|
||||||
|
|
||||||
class ClangFormatIndenter final : public TextEditor::Indenter
|
class ClangFormatIndenter final : public TextEditor::Indenter
|
||||||
{
|
{
|
||||||
@@ -53,5 +52,4 @@ public:
|
|||||||
TextEditor::TabSettings tabSettings() const override;
|
TextEditor::TabSettings tabSettings() const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Internal
|
|
||||||
} // namespace ClangFormat
|
} // namespace ClangFormat
|
||||||
|
|||||||
@@ -45,6 +45,8 @@
|
|||||||
#include <projectexplorer/projectpanelfactory.h>
|
#include <projectexplorer/projectpanelfactory.h>
|
||||||
#include <projectexplorer/target.h>
|
#include <projectexplorer/target.h>
|
||||||
|
|
||||||
|
#include <clang/Format/Format.h>
|
||||||
|
|
||||||
#include <QAction>
|
#include <QAction>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QMainWindow>
|
#include <QMainWindow>
|
||||||
@@ -56,7 +58,6 @@
|
|||||||
using namespace ProjectExplorer;
|
using namespace ProjectExplorer;
|
||||||
|
|
||||||
namespace ClangFormat {
|
namespace ClangFormat {
|
||||||
namespace Internal {
|
|
||||||
|
|
||||||
class ClangFormatOptionsPage : public Core::IOptionsPage
|
class ClangFormatOptionsPage : public Core::IOptionsPage
|
||||||
{
|
{
|
||||||
@@ -98,7 +99,7 @@ bool ClangFormatPlugin::initialize(const QStringList &arguments, QString *errorS
|
|||||||
{
|
{
|
||||||
Q_UNUSED(arguments);
|
Q_UNUSED(arguments);
|
||||||
Q_UNUSED(errorString);
|
Q_UNUSED(errorString);
|
||||||
|
#ifdef KEEP_LINE_BREAKS_FOR_NON_EMPTY_LINES_BACKPORTED
|
||||||
m_optionsPage = std::make_unique<ClangFormatOptionsPage>();
|
m_optionsPage = std::make_unique<ClangFormatOptionsPage>();
|
||||||
|
|
||||||
auto panelFactory = new ProjectPanelFactory();
|
auto panelFactory = new ProjectPanelFactory();
|
||||||
@@ -112,9 +113,8 @@ bool ClangFormatPlugin::initialize(const QStringList &arguments, QString *errorS
|
|||||||
CppTools::CppModelManager::instance()->setCppIndenterCreator([]() {
|
CppTools::CppModelManager::instance()->setCppIndenterCreator([]() {
|
||||||
return new ClangFormatIndenter();
|
return new ClangFormatIndenter();
|
||||||
});
|
});
|
||||||
|
#endif
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Internal
|
|
||||||
} // namespace ClangFormat
|
} // namespace ClangFormat
|
||||||
|
|||||||
@@ -30,7 +30,6 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
namespace ClangFormat {
|
namespace ClangFormat {
|
||||||
namespace Internal {
|
|
||||||
|
|
||||||
class ClangFormatOptionsPage;
|
class ClangFormatOptionsPage;
|
||||||
|
|
||||||
@@ -50,5 +49,4 @@ private:
|
|||||||
std::unique_ptr<ClangFormatOptionsPage> m_optionsPage;
|
std::unique_ptr<ClangFormatOptionsPage> m_optionsPage;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Internal
|
|
||||||
} // namespace ClangTools
|
} // namespace ClangTools
|
||||||
|
|||||||
51
src/plugins/clangformat/clangformatutils.h
Normal file
51
src/plugins/clangformat/clangformatutils.h
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2018 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <utils/fileutils.h>
|
||||||
|
#include <clang/Format/Format.h>
|
||||||
|
|
||||||
|
#include <QFile>
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
namespace ClangFormat {
|
||||||
|
|
||||||
|
inline void createStyleFileIfNeeded(Utils::FileName styleConfigPath)
|
||||||
|
{
|
||||||
|
const QString configFile = styleConfigPath.appendPath(".clang-format").toString();
|
||||||
|
if (QFile::exists(configFile))
|
||||||
|
return;
|
||||||
|
|
||||||
|
clang::format::FormatStyle newStyle = clang::format::getLLVMStyle();
|
||||||
|
std::fstream newStyleFile(configFile.toStdString(), std::fstream::out);
|
||||||
|
if (newStyleFile.is_open()) {
|
||||||
|
newStyleFile << clang::format::configurationAsText(newStyle);
|
||||||
|
newStyleFile.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -2474,6 +2474,7 @@ void TextEditorWidget::keyPressEvent(QKeyEvent *e)
|
|||||||
e->accept();
|
e->accept();
|
||||||
|
|
||||||
if (extraBlocks > 0) {
|
if (extraBlocks > 0) {
|
||||||
|
cursor.joinPreviousEditBlock();
|
||||||
const int cursorPosition = cursor.position();
|
const int cursorPosition = cursor.position();
|
||||||
QTextCursor ensureVisible = cursor;
|
QTextCursor ensureVisible = cursor;
|
||||||
while (extraBlocks > 0) {
|
while (extraBlocks > 0) {
|
||||||
@@ -2493,6 +2494,7 @@ void TextEditorWidget::keyPressEvent(QKeyEvent *e)
|
|||||||
}
|
}
|
||||||
setTextCursor(ensureVisible);
|
setTextCursor(ensureVisible);
|
||||||
cursor.setPosition(cursorPosition);
|
cursor.setPosition(cursorPosition);
|
||||||
|
cursor.endEditBlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
setTextCursor(cursor);
|
setTextCursor(cursor);
|
||||||
|
|||||||
Reference in New Issue
Block a user