forked from qt-creator/qt-creator
Clang: Use clang-format for indentation
This is the new experimental plugin based on LibFormat. It replaces the default indenter for CppEditorDocument and applies clang-format after the CR or the set of 'electric' characters. Uses the global .clang-format kept in QtC settings or the one for current project. Both can be configured. For indentation some style modifications and code manipulations are done to prevent line shrinking when it's not expected. Manual indentation uses unmodified style from .clang-format file. Change-Id: I6279b805e418e1804b553efa615f5c843f395a58 Reviewed-by: Marco Bubke <marco.bubke@qt.io>
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -29,7 +29,6 @@
|
||||
.DS_Store
|
||||
/.qmake.cache
|
||||
/.qmake.stash
|
||||
/.clang-format
|
||||
Makefile*
|
||||
Thumbs.db
|
||||
core
|
||||
|
||||
24
dist/clangformat/README.md
vendored
24
dist/clangformat/README.md
vendored
@@ -1,6 +1,6 @@
|
||||
# .clang-format for Qt Creator
|
||||
|
||||
Alongside this file you find an EXPERIMENTAL .clang-format configuration file
|
||||
In you Qt Creator root there is an EXPERIMENTAL .clang-format configuration file
|
||||
for the Qt Creator code base.
|
||||
|
||||
The current configuration is useful, but not fully in accordance with the
|
||||
@@ -26,26 +26,12 @@ For more information about clang-format, see
|
||||
|
||||
## Set up Qt Creator for use with clang-format
|
||||
|
||||
### Install the configuration file
|
||||
|
||||
For a given source file to format, clang-format it will read the configuration
|
||||
from .clang-format in the closest parent directory for the file to format.
|
||||
|
||||
Hence symlink/copy .clang-format from this directory to e.g. Qt Creator's top
|
||||
level directory:
|
||||
|
||||
For Linux/macOS:
|
||||
|
||||
$ cd $QTC_SOURCE
|
||||
$ ln -s dist/clangformat/.clang-format
|
||||
|
||||
For Windows:
|
||||
|
||||
$ cd $QTC_SOURCE
|
||||
$ copy dist\clangformat\.clang-format # Do not forget to keep this updated
|
||||
|
||||
### Configure Qt Creator
|
||||
|
||||
0. Enable experimental ClangFormat plugin to get C++ indentation based on it.
|
||||
|
||||
Or use an alternative way:
|
||||
|
||||
1. Enable the Beautifier plugin and restart to load it.
|
||||
|
||||
2. Configure the plugin:
|
||||
|
||||
20
src/plugins/clangformat/ClangFormat.json.in
Normal file
20
src/plugins/clangformat/ClangFormat.json.in
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
\"Name\" : \"ClangFormat\",
|
||||
\"Version\" : \"$$QTCREATOR_VERSION\",
|
||||
\"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\",
|
||||
\"Experimental\" : true,
|
||||
\"Vendor\" : \"The Qt Company Ltd\",
|
||||
\"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\",
|
||||
\"License\" : [ \"Commercial Usage\",
|
||||
\"\",
|
||||
\"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\",
|
||||
\"\",
|
||||
\"GNU General Public License Usage\",
|
||||
\"\",
|
||||
\"Alternatively, this plugin 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 plugin. 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.\"
|
||||
],
|
||||
\"Category\" : \"C++\",
|
||||
\"Description\" : \"clang-format indentation plugin.\",
|
||||
\"Url\" : \"http://www.qt.io\",
|
||||
$$dependencyList
|
||||
}
|
||||
28
src/plugins/clangformat/clangformat.pro
Normal file
28
src/plugins/clangformat/clangformat.pro
Normal file
@@ -0,0 +1,28 @@
|
||||
include(../../qtcreatorplugin.pri)
|
||||
include(../../shared/clang/clang_installation.pri)
|
||||
|
||||
include(../../shared/clang/clang_defines.pri)
|
||||
|
||||
requires(!isEmpty(LLVM_VERSION))
|
||||
|
||||
win32 {
|
||||
LLVM_BUILDMODE = $$system($$llvm_config --build-mode, lines)
|
||||
CONFIG(debug, debug|release):requires(equals(LLVM_BUILDMODE, "Debug"))
|
||||
}
|
||||
|
||||
LIBS += $$CLANGFORMAT_LIBS
|
||||
INCLUDEPATH += $$LLVM_INCLUDEPATH
|
||||
|
||||
SOURCES = \
|
||||
clangformatconfigwidget.cpp \
|
||||
clangformatindenter.cpp \
|
||||
clangformatplugin.cpp
|
||||
|
||||
HEADERS = \
|
||||
clangformatconfigwidget.h \
|
||||
clangformatindenter.h \
|
||||
clangformatplugin.h \
|
||||
clangformatconstants.h
|
||||
|
||||
FORMS += \
|
||||
clangformatconfigwidget.ui
|
||||
36
src/plugins/clangformat/clangformat.qbs
Normal file
36
src/plugins/clangformat/clangformat.qbs
Normal file
@@ -0,0 +1,36 @@
|
||||
import qbs
|
||||
import qbs.FileInfo
|
||||
|
||||
QtcPlugin {
|
||||
name: "ClangFormat"
|
||||
|
||||
Depends { name: "Core" }
|
||||
Depends { name: "TextEditor" }
|
||||
Depends { name: "CppTools" }
|
||||
Depends { name: "ExtensionSystem" }
|
||||
Depends { name: "Utils" }
|
||||
|
||||
Depends { name: "libclang"; required: false }
|
||||
Depends { name: "clang_defines" }
|
||||
|
||||
Depends { name: "Qt.widgets" }
|
||||
|
||||
condition: libclang.present
|
||||
&& (!qbs.targetOS.contains("windows") || libclang.llvmBuildModeMatches)
|
||||
|
||||
cpp.defines: base.concat("CLANGPCHMANAGER_LIB")
|
||||
cpp.includePaths: base.concat(libclang.llvmIncludeDir)
|
||||
cpp.libraryPaths: base.concat(libclang.llvmLibDir)
|
||||
cpp.dynamicLibraries: base.concat(libclang.llvmLibs)
|
||||
cpp.rpaths: base.concat(libclang.llvmLibDir)
|
||||
|
||||
files: [
|
||||
"clangformatconfigwidget.cpp",
|
||||
"clangformatconfigwidget.h",
|
||||
"clangformatindenter.cpp",
|
||||
"clangformatindenter.h",
|
||||
"clangformatplugin.cpp",
|
||||
"clangformatplugin.h",
|
||||
"clangformatconstants.h",
|
||||
]
|
||||
}
|
||||
7
src/plugins/clangformat/clangformat_dependencies.pri
Normal file
7
src/plugins/clangformat/clangformat_dependencies.pri
Normal file
@@ -0,0 +1,7 @@
|
||||
QTC_PLUGIN_NAME = ClangFormat
|
||||
QTC_LIB_DEPENDS += \
|
||||
extensionsystem \
|
||||
utils
|
||||
QTC_PLUGIN_DEPENDS += \
|
||||
cpptools \
|
||||
texteditor
|
||||
205
src/plugins/clangformat/clangformatconfigwidget.cpp
Normal file
205
src/plugins/clangformat/clangformatconfigwidget.cpp
Normal file
@@ -0,0 +1,205 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
|
||||
#include "clangformatconfigwidget.h"
|
||||
#include "ui_clangformatconfigwidget.h"
|
||||
|
||||
#include <clang/Format/Format.h>
|
||||
|
||||
#include <coreplugin/icore.h>
|
||||
#include <projectexplorer/project.h>
|
||||
#include <projectexplorer/session.h>
|
||||
|
||||
#include <QFile>
|
||||
|
||||
#include <sstream>
|
||||
|
||||
using namespace ProjectExplorer;
|
||||
|
||||
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)
|
||||
{
|
||||
table->horizontalHeader()->hide();
|
||||
table->verticalHeader()->hide();
|
||||
|
||||
table->setColumnCount(2);
|
||||
table->setRowCount(0);
|
||||
|
||||
std::string line;
|
||||
while (std::getline(stream, line)) {
|
||||
if (line == "---" || line == "...")
|
||||
continue;
|
||||
|
||||
const size_t firstLetter = line.find_first_not_of(' ');
|
||||
if (firstLetter == std::string::npos || line.at(firstLetter) == '#')
|
||||
continue;
|
||||
|
||||
// Increase indent where it already exists.
|
||||
if (firstLetter > 0 && firstLetter < 5)
|
||||
line = " " + line;
|
||||
|
||||
table->insertRow(table->rowCount());
|
||||
const size_t colonPos = line.find_first_of(':');
|
||||
auto *keyItem = new QTableWidgetItem;
|
||||
auto *valueItem = new QTableWidgetItem;
|
||||
|
||||
keyItem->setFlags(keyItem->flags() & ~Qt::ItemFlags(Qt::ItemIsEditable));
|
||||
table->setItem(table->rowCount() - 1, 0, keyItem);
|
||||
table->setItem(table->rowCount() - 1, 1, valueItem);
|
||||
|
||||
if (colonPos == std::string::npos) {
|
||||
keyItem->setText(QString::fromStdString(line));
|
||||
valueItem->setFlags(valueItem->flags() & ~Qt::ItemFlags(Qt::ItemIsEditable));
|
||||
continue;
|
||||
}
|
||||
|
||||
keyItem->setText(QString::fromStdString(line.substr(0, colonPos)));
|
||||
|
||||
const size_t optionValueStart = line.find_first_not_of(' ', colonPos + 1);
|
||||
if (optionValueStart == std::string::npos)
|
||||
valueItem->setFlags(valueItem->flags() & ~Qt::ItemFlags(Qt::ItemIsEditable));
|
||||
else
|
||||
valueItem->setText(QString::fromStdString(line.substr(optionValueStart)));
|
||||
}
|
||||
|
||||
table->resizeColumnToContents(0);
|
||||
table->resizeColumnToContents(1);
|
||||
}
|
||||
|
||||
static QByteArray tableToYAML(QTableWidget *table)
|
||||
{
|
||||
QByteArray text;
|
||||
text += "---\n";
|
||||
for (int i = 0; i < table->rowCount(); ++i) {
|
||||
auto *keyItem = table->item(i, 0);
|
||||
auto *valueItem = table->item(i, 1);
|
||||
|
||||
QByteArray itemText = keyItem->text().toUtf8();
|
||||
// Change the indent back to 2 spaces
|
||||
itemText.replace(" ", " ");
|
||||
if (!valueItem->text().isEmpty() || !itemText.trimmed().startsWith('-'))
|
||||
itemText += ": ";
|
||||
itemText += valueItem->text().toUtf8() + '\n';
|
||||
|
||||
text += itemText;
|
||||
}
|
||||
text += "...\n";
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
ClangFormatConfigWidget::ClangFormatConfigWidget(ProjectExplorer::Project *project,
|
||||
QWidget *parent)
|
||||
: QWidget(parent)
|
||||
, m_ui(new Ui::ClangFormatConfigWidget)
|
||||
, m_project(project)
|
||||
{
|
||||
m_ui->setupUi(this);
|
||||
|
||||
std::string testFilePath;
|
||||
if (m_project && !m_project->projectDirectory().appendPath(".clang-format").exists()) {
|
||||
m_ui->projectHasClangFormat->setText("No .clang-format file for the project");
|
||||
m_ui->clangFormatOptionsTable->hide();
|
||||
m_ui->applyButton->hide();
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_project) {
|
||||
testFilePath = m_project->projectDirectory().appendPath("t.cpp").toString().toStdString();
|
||||
connect(m_ui->applyButton, &QPushButton::clicked, this, &ClangFormatConfigWidget::apply);
|
||||
} else {
|
||||
const QString settingsDir = Core::ICore::userResourcePath();
|
||||
createGlobalClangFormatFileIfNeeded(settingsDir);
|
||||
testFilePath = settingsDir.toStdString() + "/t.cpp";
|
||||
m_ui->applyButton->hide();
|
||||
}
|
||||
|
||||
llvm::Expected<clang::format::FormatStyle> formatStyle =
|
||||
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();
|
||||
if (!currentProject || !currentProject->projectDirectory().appendPath(".clang-format").exists())
|
||||
m_ui->projectHasClangFormat->hide();
|
||||
|
||||
connect(SessionManager::instance(), &SessionManager::startupProjectChanged,
|
||||
this, [this](ProjectExplorer::Project *project) {
|
||||
if (project && project->projectDirectory().appendPath(".clang-format").exists())
|
||||
m_ui->projectHasClangFormat->show();
|
||||
else
|
||||
m_ui->projectHasClangFormat->hide();
|
||||
});
|
||||
}
|
||||
|
||||
ClangFormatConfigWidget::~ClangFormatConfigWidget() = default;
|
||||
|
||||
void ClangFormatConfigWidget::apply()
|
||||
{
|
||||
const QByteArray text = tableToYAML(m_ui->clangFormatOptionsTable);
|
||||
QString filePath;
|
||||
if (m_project)
|
||||
filePath = m_project->projectDirectory().appendPath(".clang-format").toString();
|
||||
else
|
||||
filePath = Core::ICore::userResourcePath() + "/.clang-format";
|
||||
QFile file(filePath);
|
||||
if (!file.open(QFile::WriteOnly))
|
||||
return;
|
||||
|
||||
file.write(text);
|
||||
file.close();
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace ClangFormat
|
||||
57
src/plugins/clangformat/clangformatconfigwidget.h
Normal file
57
src/plugins/clangformat/clangformatconfigwidget.h
Normal file
@@ -0,0 +1,57 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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 <QWidget>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace ProjectExplorer { class Project; }
|
||||
|
||||
namespace ClangFormat {
|
||||
namespace Internal {
|
||||
|
||||
namespace Ui {
|
||||
class ClangFormatConfigWidget;
|
||||
}
|
||||
|
||||
class ClangFormatConfigWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ClangFormatConfigWidget(ProjectExplorer::Project *project = nullptr,
|
||||
QWidget *parent = nullptr);
|
||||
~ClangFormatConfigWidget();
|
||||
void apply();
|
||||
|
||||
private:
|
||||
ProjectExplorer::Project *m_project;
|
||||
std::unique_ptr<Ui::ClangFormatConfigWidget> m_ui;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace ClangFormat
|
||||
67
src/plugins/clangformat/clangformatconfigwidget.ui
Normal file
67
src/plugins/clangformat/clangformatconfigwidget.ui
Normal file
@@ -0,0 +1,67 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ClangFormat::Internal::ClangFormatConfigWidget</class>
|
||||
<widget class="QWidget" name="ClangFormat::Internal::ClangFormatConfigWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>489</width>
|
||||
<height>305</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="leftMargin">
|
||||
<number>8</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>8</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>8</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>8</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="projectHasClangFormat">
|
||||
<property name="text">
|
||||
<string> Current project has its own .clang-format file and can be configured in Projects -> ClangFormat.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTableWidget" name="clangFormatOptionsTable"/>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QPushButton" name="applyButton">
|
||||
<property name="text">
|
||||
<string>Apply</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
26
src/plugins/clangformat/clangformatconstants.h
Normal file
26
src/plugins/clangformat/clangformatconstants.h
Normal file
@@ -0,0 +1,26 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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
|
||||
498
src/plugins/clangformat/clangformatindenter.cpp
Normal file
498
src/plugins/clangformat/clangformatindenter.cpp
Normal file
@@ -0,0 +1,498 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "clangformatindenter.h"
|
||||
|
||||
#include <clang/Format/Format.h>
|
||||
#include <clang/Tooling/Core/Replacement.h>
|
||||
|
||||
#include <coreplugin/icore.h>
|
||||
#include <projectexplorer/project.h>
|
||||
#include <projectexplorer/session.h>
|
||||
#include <texteditor/textdocument.h>
|
||||
#include <texteditor/texteditor.h>
|
||||
|
||||
#include <utils/hostosinfo.h>
|
||||
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
#include <QTextBlock>
|
||||
|
||||
using namespace clang;
|
||||
using namespace format;
|
||||
using namespace llvm;
|
||||
using namespace tooling;
|
||||
using namespace ProjectExplorer;
|
||||
using namespace TextEditor;
|
||||
|
||||
namespace ClangFormat {
|
||||
namespace Internal {
|
||||
|
||||
namespace {
|
||||
|
||||
void adjustFormatStyleForLineBreak(format::FormatStyle &style,
|
||||
int length,
|
||||
int prevBlockSize,
|
||||
bool prevBlockEndsWithPunctuation)
|
||||
{
|
||||
if (length > 0)
|
||||
style.ColumnLimit = prevBlockSize;
|
||||
style.AlwaysBreakBeforeMultilineStrings = true;
|
||||
style.AlwaysBreakTemplateDeclarations = true;
|
||||
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,
|
||||
unsigned int offset,
|
||||
unsigned int lengthForFilter,
|
||||
int extraOffsetToAdd,
|
||||
int prevBlockLength)
|
||||
{
|
||||
Replacements filtered;
|
||||
for (const Replacement &replacement : replacements) {
|
||||
unsigned int replacementOffset = replacement.getOffset();
|
||||
if (replacementOffset > offset + lengthForFilter)
|
||||
break;
|
||||
|
||||
if (offset > prevBlockLength && replacementOffset < offset - prevBlockLength)
|
||||
continue;
|
||||
|
||||
if (lengthForFilter == 0 && replacement.getReplacementText().find('\n') == std::string::npos
|
||||
&& filtered.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (replacementOffset + 1 >= offset)
|
||||
replacementOffset += extraOffsetToAdd;
|
||||
|
||||
Error error = filtered.add(Replacement(replacement.getFilePath(),
|
||||
replacementOffset,
|
||||
replacement.getLength(),
|
||||
replacement.getReplacementText()));
|
||||
// Throws if error is not checked.
|
||||
if (error)
|
||||
break;
|
||||
}
|
||||
return filtered;
|
||||
}
|
||||
|
||||
std::string assumedFilePath()
|
||||
{
|
||||
const Project *project = SessionManager::startupProject();
|
||||
if (project && project->projectDirectory().appendPath(".clang-format").exists())
|
||||
return project->projectDirectory().appendPath("test.cpp").toString().toStdString();
|
||||
|
||||
return QString(Core::ICore::userResourcePath() + "/test.cpp").toStdString();
|
||||
}
|
||||
|
||||
FormatStyle formatStyle()
|
||||
{
|
||||
Expected<FormatStyle> style = format::getStyle("file", assumedFilePath(), "none", "");
|
||||
if (style)
|
||||
return *style;
|
||||
return FormatStyle();
|
||||
}
|
||||
|
||||
Replacements replacements(const std::string &buffer,
|
||||
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();
|
||||
while (prevBlock.position() > 0 && prevBlock.text().trimmed().isEmpty())
|
||||
prevBlock = prevBlock.previous();
|
||||
|
||||
if (prevBlock.text().trimmed().isEmpty())
|
||||
return prevBlock;
|
||||
|
||||
const QString initialText = prevBlock.text();
|
||||
if (!initialText.at(initialText.length() - 1).isSpace())
|
||||
return prevBlock;
|
||||
|
||||
QTextCursor cursor(prevBlock);
|
||||
cursor.beginEditBlock();
|
||||
cursor.movePosition(QTextCursor::EndOfBlock);
|
||||
cursor.movePosition(QTextCursor::Left);
|
||||
|
||||
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.endEditBlock();
|
||||
return prevBlock;
|
||||
}
|
||||
|
||||
// Returns the total langth of previous lines with pure whitespace.
|
||||
int previousEmptyLinesLength(const QTextBlock ¤tBlock)
|
||||
{
|
||||
int length{0};
|
||||
QTextBlock prevBlock = currentBlock.previous();
|
||||
while (prevBlock.position() > 0 && prevBlock.text().trimmed().isEmpty()) {
|
||||
length += prevBlock.text().length() + 1;
|
||||
prevBlock = prevBlock.previous();
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
static constexpr const int MinCharactersBeforeCurrentInBuffer = 200;
|
||||
static constexpr const int MaxCharactersBeforeCurrentInBuffer = 500;
|
||||
|
||||
int startOfIndentationBuffer(const QString &buffer, int start)
|
||||
{
|
||||
if (start < MaxCharactersBeforeCurrentInBuffer)
|
||||
return 0;
|
||||
|
||||
auto it = buffer.cbegin() + (start - MinCharactersBeforeCurrentInBuffer);
|
||||
for (; it != buffer.cbegin() + (start - MaxCharactersBeforeCurrentInBuffer); --it) {
|
||||
if (*it == '{') {
|
||||
// Find the start of it's line.
|
||||
for (auto inner = it;
|
||||
inner != buffer.cbegin() + (start - MaxCharactersBeforeCurrentInBuffer);
|
||||
--inner) {
|
||||
if (*inner == '\n')
|
||||
return inner + 1 - buffer.cbegin();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return it - buffer.cbegin();
|
||||
}
|
||||
|
||||
int nextEndingScopePosition(const QString &buffer, int start)
|
||||
{
|
||||
if (start >= buffer.size() - 1)
|
||||
return buffer.size() - 1;
|
||||
|
||||
for (auto it = buffer.cbegin() + (start + 1); it != buffer.cend(); ++it) {
|
||||
if (*it == '}')
|
||||
return it - buffer.cbegin();
|
||||
}
|
||||
|
||||
return buffer.size() - 1;
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
bool ClangFormatIndenter::isElectricCharacter(const QChar &ch) const
|
||||
{
|
||||
switch (ch.toLatin1()) {
|
||||
case '{':
|
||||
case '}':
|
||||
case ':':
|
||||
case '#':
|
||||
case '<':
|
||||
case '>':
|
||||
case ';':
|
||||
case '(':
|
||||
case ')':
|
||||
case ',':
|
||||
case '.':
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ClangFormatIndenter::indent(QTextDocument *doc,
|
||||
const QTextCursor &cursor,
|
||||
const QChar &typedChar,
|
||||
const TabSettings &tabSettings,
|
||||
bool autoTriggered)
|
||||
{
|
||||
if (typedChar == QChar::Null && (cursor.hasSelection() || !autoTriggered)) {
|
||||
TextEditorWidget *editor = TextEditorWidget::currentTextEditorWidget();
|
||||
int offset;
|
||||
int length;
|
||||
if (cursor.hasSelection()) {
|
||||
const QTextBlock start = doc->findBlock(cursor.selectionStart());
|
||||
const QTextBlock end = doc->findBlock(cursor.selectionEnd());
|
||||
offset = start.position();
|
||||
length = std::max(0, end.position() + end.length() - start.position() - 1);
|
||||
} else {
|
||||
const QTextBlock block = cursor.block();
|
||||
offset = block.position();
|
||||
length = std::max(0, block.length() - 1);
|
||||
}
|
||||
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 {
|
||||
indentBlock(doc, cursor.block(), typedChar, tabSettings);
|
||||
}
|
||||
}
|
||||
|
||||
void ClangFormatIndenter::reindent(QTextDocument *doc,
|
||||
const QTextCursor &cursor,
|
||||
const TabSettings &tabSettings)
|
||||
{
|
||||
indent(doc, cursor, QChar::Null, tabSettings);
|
||||
}
|
||||
|
||||
void ClangFormatIndenter::indentBlock(QTextDocument *doc,
|
||||
const QTextBlock &block,
|
||||
const QChar &typedChar,
|
||||
const TabSettings &tabSettings)
|
||||
{
|
||||
TextEditorWidget *editor = TextEditorWidget::currentTextEditorWidget();
|
||||
if (!editor)
|
||||
return;
|
||||
|
||||
const QTextBlock prevBlock = clearFirstNonEmptyBlockFromExtraSpaces(block);
|
||||
|
||||
int offset = block.position();
|
||||
int length = std::max(0, block.length() - 1);
|
||||
QString buffer = editor->toPlainText();
|
||||
|
||||
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 &)
|
||||
{
|
||||
TextEditorWidget *editor = TextEditorWidget::currentTextEditorWidget();
|
||||
if (!editor)
|
||||
return -1;
|
||||
|
||||
const QTextBlock prevBlock = clearFirstNonEmptyBlockFromExtraSpaces(block);
|
||||
|
||||
int offset = block.position();
|
||||
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())
|
||||
return -1;
|
||||
|
||||
const Replacement replacement = *toReplace.begin();
|
||||
|
||||
const StringRef text = replacement.getReplacementText();
|
||||
size_t afterLineBreak = text.find_last_of('\n');
|
||||
afterLineBreak = (afterLineBreak == std::string::npos) ? 0 : afterLineBreak + 1;
|
||||
return static_cast<int>(text.size() - afterLineBreak);
|
||||
}
|
||||
|
||||
TabSettings ClangFormatIndenter::tabSettings() const
|
||||
{
|
||||
FormatStyle style = formatStyle();
|
||||
TabSettings tabSettings;
|
||||
|
||||
switch (style.UseTab) {
|
||||
case FormatStyle::UT_Never:
|
||||
tabSettings.m_tabPolicy = TabSettings::SpacesOnlyTabPolicy;
|
||||
case FormatStyle::UT_Always:
|
||||
tabSettings.m_tabPolicy = TabSettings::TabsOnlyTabPolicy;
|
||||
default:
|
||||
tabSettings.m_tabPolicy = TabSettings::MixedTabPolicy;
|
||||
}
|
||||
|
||||
tabSettings.m_tabSize = style.TabWidth;
|
||||
tabSettings.m_indentSize = style.IndentWidth;
|
||||
|
||||
if (style.AlignAfterOpenBracket)
|
||||
tabSettings.m_continuationAlignBehavior = TabSettings::ContinuationAlignWithSpaces;
|
||||
else
|
||||
tabSettings.m_continuationAlignBehavior = TabSettings::ContinuationAlignWithIndent;
|
||||
|
||||
return tabSettings;
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace ClangFormat
|
||||
57
src/plugins/clangformat/clangformatindenter.h
Normal file
57
src/plugins/clangformat/clangformatindenter.h
Normal file
@@ -0,0 +1,57 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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 <texteditor/indenter.h>
|
||||
|
||||
namespace ClangFormat {
|
||||
namespace Internal {
|
||||
|
||||
class ClangFormatIndenter final : public TextEditor::Indenter
|
||||
{
|
||||
public:
|
||||
void indent(QTextDocument *doc,
|
||||
const QTextCursor &cursor,
|
||||
const QChar &typedChar,
|
||||
const TextEditor::TabSettings &tabSettings,
|
||||
bool autoTriggered = true) override;
|
||||
void reindent(QTextDocument *doc,
|
||||
const QTextCursor &cursor,
|
||||
const TextEditor::TabSettings &tabSettings) override;
|
||||
void indentBlock(QTextDocument *doc,
|
||||
const QTextBlock &block,
|
||||
const QChar &typedChar,
|
||||
const TextEditor::TabSettings &tabSettings) override;
|
||||
int indentFor(const QTextBlock &block, const TextEditor::TabSettings &tabSettings) override;
|
||||
|
||||
bool isElectricCharacter(const QChar &ch) const override;
|
||||
|
||||
bool hasTabSettings() const override { return true; }
|
||||
TextEditor::TabSettings tabSettings() const override;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace ClangFormat
|
||||
120
src/plugins/clangformat/clangformatplugin.cpp
Normal file
120
src/plugins/clangformat/clangformatplugin.cpp
Normal file
@@ -0,0 +1,120 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 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.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "clangformatplugin.h"
|
||||
|
||||
#include "clangformatconfigwidget.h"
|
||||
#include "clangformatindenter.h"
|
||||
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
#include <coreplugin/icore.h>
|
||||
#include <coreplugin/icontext.h>
|
||||
#include <coreplugin/actionmanager/actionmanager.h>
|
||||
#include <coreplugin/actionmanager/command.h>
|
||||
#include <coreplugin/actionmanager/actioncontainer.h>
|
||||
#include <coreplugin/coreconstants.h>
|
||||
#include <coreplugin/dialogs/ioptionspage.h>
|
||||
|
||||
#include <cpptools/cpptoolsconstants.h>
|
||||
#include <cpptools/cppmodelmanager.h>
|
||||
|
||||
#include <projectexplorer/kitinformation.h>
|
||||
#include <projectexplorer/projectpanelfactory.h>
|
||||
#include <projectexplorer/target.h>
|
||||
|
||||
#include <QAction>
|
||||
#include <QDebug>
|
||||
#include <QMainWindow>
|
||||
#include <QMessageBox>
|
||||
#include <QMenu>
|
||||
|
||||
#include <QtPlugin>
|
||||
|
||||
using namespace ProjectExplorer;
|
||||
|
||||
namespace ClangFormat {
|
||||
namespace Internal {
|
||||
|
||||
class ClangFormatOptionsPage : public Core::IOptionsPage
|
||||
{
|
||||
public:
|
||||
explicit ClangFormatOptionsPage()
|
||||
{
|
||||
setId("Cpp.CodeStyle.ClangFormat");
|
||||
setDisplayName(QCoreApplication::translate(
|
||||
"ClangFormat::Internal::ClangFormatOptionsPage",
|
||||
"Clang Format"));
|
||||
setCategory(CppTools::Constants::CPP_SETTINGS_CATEGORY);
|
||||
}
|
||||
|
||||
QWidget *widget()
|
||||
{
|
||||
if (!m_widget)
|
||||
m_widget = new ClangFormatConfigWidget;
|
||||
return m_widget;
|
||||
}
|
||||
|
||||
void apply()
|
||||
{
|
||||
m_widget->apply();
|
||||
}
|
||||
|
||||
void finish()
|
||||
{
|
||||
delete m_widget;
|
||||
}
|
||||
|
||||
private:
|
||||
QPointer<ClangFormatConfigWidget> m_widget;
|
||||
};
|
||||
|
||||
ClangFormatPlugin::ClangFormatPlugin() = default;
|
||||
ClangFormatPlugin::~ClangFormatPlugin() = default;
|
||||
|
||||
bool ClangFormatPlugin::initialize(const QStringList &arguments, QString *errorString)
|
||||
{
|
||||
Q_UNUSED(arguments);
|
||||
Q_UNUSED(errorString);
|
||||
|
||||
m_optionsPage = std::make_unique<ClangFormatOptionsPage>();
|
||||
|
||||
auto panelFactory = new ProjectPanelFactory();
|
||||
panelFactory->setPriority(120);
|
||||
panelFactory->setDisplayName(tr("Clang Format"));
|
||||
panelFactory->setCreateWidgetFunction([](Project *project) {
|
||||
return new ClangFormatConfigWidget(project);
|
||||
});
|
||||
ProjectPanelFactory::registerFactory(panelFactory);
|
||||
|
||||
CppTools::CppModelManager::instance()->setCppIndenterCreator([]() {
|
||||
return new ClangFormatIndenter();
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace ClangFormat
|
||||
54
src/plugins/clangformat/clangformatplugin.h
Normal file
54
src/plugins/clangformat/clangformatplugin.h
Normal file
@@ -0,0 +1,54 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 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 <extensionsystem/iplugin.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace ClangFormat {
|
||||
namespace Internal {
|
||||
|
||||
class ClangFormatOptionsPage;
|
||||
|
||||
class ClangFormatPlugin : public ExtensionSystem::IPlugin
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "ClangFormat.json")
|
||||
|
||||
public:
|
||||
ClangFormatPlugin();
|
||||
~ClangFormatPlugin();
|
||||
|
||||
private:
|
||||
bool initialize(const QStringList &arguments, QString *errorString) final;
|
||||
void extensionsInitialized() final {}
|
||||
|
||||
std::unique_ptr<ClangFormatOptionsPage> m_optionsPage;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace ClangTools
|
||||
@@ -80,10 +80,7 @@ ClangToolsConfigWidget::ClangToolsConfigWidget(
|
||||
});
|
||||
}
|
||||
|
||||
ClangToolsConfigWidget::~ClangToolsConfigWidget()
|
||||
{
|
||||
delete m_ui;
|
||||
}
|
||||
ClangToolsConfigWidget::~ClangToolsConfigWidget() = default;
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace ClangTools
|
||||
|
||||
@@ -29,6 +29,8 @@
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace ClangTools {
|
||||
namespace Internal {
|
||||
|
||||
@@ -41,12 +43,10 @@ class ClangToolsConfigWidget : public QWidget
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ClangToolsConfigWidget(ClangToolsSettings *settings,
|
||||
QWidget *parent = 0);
|
||||
ClangToolsConfigWidget(ClangToolsSettings *settings, QWidget *parent = nullptr);
|
||||
~ClangToolsConfigWidget();
|
||||
|
||||
private:
|
||||
Ui::ClangToolsConfigWidget *m_ui;
|
||||
std::unique_ptr<Ui::ClangToolsConfigWidget> m_ui;
|
||||
ClangToolsSettings *m_settings;
|
||||
};
|
||||
|
||||
|
||||
@@ -48,6 +48,9 @@
|
||||
#include <coreplugin/coreconstants.h>
|
||||
#include <coreplugin/dialogs/ioptionspage.h>
|
||||
|
||||
#include <cpptools/cpptoolsconstants.h>
|
||||
#include <cpptools/cppmodelmanager.h>
|
||||
|
||||
#include <projectexplorer/kitinformation.h>
|
||||
#include <projectexplorer/projectpanelfactory.h>
|
||||
#include <projectexplorer/target.h>
|
||||
|
||||
@@ -103,7 +103,7 @@ CppEditorDocument::CppEditorDocument()
|
||||
{
|
||||
setId(CppEditor::Constants::CPPEDITOR_ID);
|
||||
setSyntaxHighlighter(new CppHighlighter);
|
||||
setIndenter(new CppTools::CppQtStyleIndenter);
|
||||
setIndenter(CppTools::CppModelManager::instance()->createCppIndenter());
|
||||
|
||||
connect(this, &TextEditor::TextDocument::tabSettingsChanged,
|
||||
this, &CppEditorDocument::invalidateFormatterCache);
|
||||
@@ -433,5 +433,12 @@ CppTools::BaseEditorDocumentProcessor *CppEditorDocument::processor()
|
||||
return m_processor.data();
|
||||
}
|
||||
|
||||
TextEditor::TabSettings CppEditorDocument::tabSettings() const
|
||||
{
|
||||
return indenter()->hasTabSettings()
|
||||
? indenter()->tabSettings()
|
||||
: TextEditor::TextDocument::tabSettings();
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace CppEditor
|
||||
|
||||
@@ -67,6 +67,7 @@ public:
|
||||
ParseContextModel &parseContextModel();
|
||||
|
||||
QFuture<CppTools::CursorInfo> cursorInfo(const CppTools::CursorInfoParams ¶ms);
|
||||
TextEditor::TabSettings tabSettings() const override;
|
||||
|
||||
signals:
|
||||
void codeWarningsUpdated(unsigned contentsRevision,
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
#include "cpplocatordata.h"
|
||||
#include "cpplocatorfilter.h"
|
||||
#include "cppmodelmanagersupportinternal.h"
|
||||
#include "cppqtstyleindenter.h"
|
||||
#include "cpprefactoringchanges.h"
|
||||
#include "cpprefactoringengine.h"
|
||||
#include "cppsourceprocessor.h"
|
||||
@@ -61,6 +62,7 @@
|
||||
#include <projectexplorer/projectexplorer.h>
|
||||
#include <projectexplorer/projectmacro.h>
|
||||
#include <projectexplorer/session.h>
|
||||
#include <texteditor/indenter.h>
|
||||
#include <utils/fileutils.h>
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
@@ -507,7 +509,9 @@ void CppModelManager::initializeBuiltinModelManagerSupport()
|
||||
}
|
||||
|
||||
CppModelManager::CppModelManager()
|
||||
: CppModelManagerBase(nullptr), d(new CppModelManagerPrivate)
|
||||
: CppModelManagerBase(nullptr)
|
||||
, createCppIndenter([]() { return new CppQtStyleIndenter; })
|
||||
, d(new CppModelManagerPrivate)
|
||||
{
|
||||
d->m_indexingSupporter = 0;
|
||||
d->m_enableGC = true;
|
||||
|
||||
@@ -48,6 +48,7 @@ namespace CPlusPlus { class LookupContext; }
|
||||
namespace ProjectExplorer { class Project; }
|
||||
namespace TextEditor {
|
||||
class BaseHoverHandler;
|
||||
class Indenter;
|
||||
class TextDocument;
|
||||
} // namespace TextEditor
|
||||
|
||||
@@ -212,6 +213,13 @@ public:
|
||||
RefactoringEngineInterface *refactoringEngine);
|
||||
static void removeRefactoringEngine(RefactoringEngineType type);
|
||||
|
||||
using CppIndenterCreator = std::function<TextEditor::Indenter *()>;
|
||||
void setCppIndenterCreator(CppIndenterCreator indenterCreator)
|
||||
{
|
||||
createCppIndenter = std::move(indenterCreator);
|
||||
}
|
||||
CppIndenterCreator createCppIndenter;
|
||||
|
||||
void setLocatorFilter(std::unique_ptr<Core::ILocatorFilter> &&filter);
|
||||
void setClassesFilter(std::unique_ptr<Core::ILocatorFilter> &&filter);
|
||||
void setIncludesFilter(std::unique_ptr<Core::ILocatorFilter> &&filter);
|
||||
|
||||
@@ -129,7 +129,8 @@ void CppQtStyleIndenter::indentBlock(QTextDocument *doc,
|
||||
void CppQtStyleIndenter::indent(QTextDocument *doc,
|
||||
const QTextCursor &cursor,
|
||||
const QChar &typedChar,
|
||||
const TextEditor::TabSettings &tabSettings)
|
||||
const TextEditor::TabSettings &tabSettings,
|
||||
bool /*autoTriggered*/)
|
||||
{
|
||||
if (cursor.hasSelection()) {
|
||||
QTextBlock block = doc->findBlock(cursor.selectionStart());
|
||||
|
||||
@@ -53,7 +53,8 @@ public:
|
||||
void indent(QTextDocument *doc,
|
||||
const QTextCursor &cursor,
|
||||
const QChar &typedChar,
|
||||
const TextEditor::TabSettings &tabSettings) override;
|
||||
const TextEditor::TabSettings &tabSettings,
|
||||
bool autoTriggered = true) override;
|
||||
|
||||
void setCodeStylePreferences(TextEditor::ICodeStylePreferences *preferences) override;
|
||||
void invalidateCache(QTextDocument *doc) override;
|
||||
|
||||
@@ -84,7 +84,8 @@ void GlslIndenter::indentBlock(QTextDocument *doc,
|
||||
void GlslIndenter::indent(QTextDocument *doc,
|
||||
const QTextCursor &cursor,
|
||||
const QChar &typedChar,
|
||||
const TextEditor::TabSettings &tabSettings)
|
||||
const TextEditor::TabSettings &tabSettings,
|
||||
bool /*autoTriggered*/)
|
||||
{
|
||||
if (cursor.hasSelection()) {
|
||||
QTextBlock block = doc->findBlock(cursor.selectionStart());
|
||||
|
||||
@@ -45,7 +45,8 @@ public:
|
||||
virtual void indent(QTextDocument *doc,
|
||||
const QTextCursor &cursor,
|
||||
const QChar &typedChar,
|
||||
const TextEditor::TabSettings &tabSettings) override;
|
||||
const TextEditor::TabSettings &tabSettings,
|
||||
bool autoTriggered = true) override;
|
||||
|
||||
int indentFor(const QTextBlock &block, const TextEditor::TabSettings &tabSettings) override;
|
||||
TextEditor::IndentationForBlock indentationForBlocks(const QVector<QTextBlock> &blocks,
|
||||
|
||||
@@ -4,6 +4,7 @@ TEMPLATE = subdirs
|
||||
|
||||
SUBDIRS = \
|
||||
autotest \
|
||||
clangformat \
|
||||
clangtools \
|
||||
coreplugin \
|
||||
texteditor \
|
||||
|
||||
@@ -13,6 +13,7 @@ Project {
|
||||
"bineditor/bineditor.qbs",
|
||||
"bookmarks/bookmarks.qbs",
|
||||
"clangcodemodel/clangcodemodel.qbs",
|
||||
"clangformat/clangformat.qbs",
|
||||
"clangpchmanager/clangpchmanager.qbs",
|
||||
"clangrefactoring/clangrefactoring.qbs",
|
||||
"clangtools/clangtools.qbs",
|
||||
|
||||
@@ -290,7 +290,7 @@ static void showError(const QString &error)
|
||||
* Checks the state of @a task and if the formatting was successful calls updateEditorText() with
|
||||
* the respective members of @a task.
|
||||
*/
|
||||
void checkAndApplyTask(const FormatTask &task)
|
||||
static void checkAndApplyTask(const FormatTask &task)
|
||||
{
|
||||
if (!task.error.isEmpty()) {
|
||||
showError(task.error);
|
||||
|
||||
@@ -59,7 +59,8 @@ void Indenter::indentBlock(QTextDocument *doc,
|
||||
void Indenter::indent(QTextDocument *doc,
|
||||
const QTextCursor &cursor,
|
||||
const QChar &typedChar,
|
||||
const TabSettings &tabSettings)
|
||||
const TabSettings &tabSettings,
|
||||
bool /*autoTriggered*/)
|
||||
{
|
||||
if (cursor.hasSelection()) {
|
||||
QTextBlock block = doc->findBlock(cursor.selectionStart());
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
|
||||
#include "texteditor_global.h"
|
||||
|
||||
#include "tabsettings.h"
|
||||
|
||||
#include <QMap>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
@@ -62,7 +64,8 @@ public:
|
||||
virtual void indent(QTextDocument *doc,
|
||||
const QTextCursor &cursor,
|
||||
const QChar &typedChar,
|
||||
const TabSettings &tabSettings);
|
||||
const TabSettings &tabSettings,
|
||||
bool autoTriggered = true);
|
||||
|
||||
// Reindent at cursor. Selection will be adjusted according to the indentation
|
||||
// change of the first block.
|
||||
@@ -77,6 +80,9 @@ public:
|
||||
// Expects a list of blocks in order of occurrence in the document.
|
||||
virtual IndentationForBlock indentationForBlocks(const QVector<QTextBlock> &blocks,
|
||||
const TextEditor::TabSettings &tabSettings);
|
||||
|
||||
virtual bool hasTabSettings() const { return false; }
|
||||
virtual TabSettings tabSettings() const { return TabSettings(); }
|
||||
};
|
||||
|
||||
} // namespace TextEditor
|
||||
|
||||
@@ -83,6 +83,7 @@ public:
|
||||
}
|
||||
|
||||
QTextCursor indentOrUnindent(const QTextCursor &textCursor, bool doIndent,
|
||||
const TabSettings &tabSettings,
|
||||
bool blockSelection = false, int column = 0,
|
||||
int *offset = nullptr);
|
||||
void resetRevisions();
|
||||
@@ -110,17 +111,16 @@ public:
|
||||
};
|
||||
|
||||
QTextCursor TextDocumentPrivate::indentOrUnindent(const QTextCursor &textCursor, bool doIndent,
|
||||
const TabSettings &tabSettings,
|
||||
bool blockSelection, int columnIn, int *offset)
|
||||
{
|
||||
QTextCursor cursor = textCursor;
|
||||
cursor.beginEditBlock();
|
||||
|
||||
TabSettings &ts = m_tabSettings;
|
||||
|
||||
// Indent or unindent the selected lines
|
||||
int pos = cursor.position();
|
||||
int column = blockSelection ? columnIn
|
||||
: ts.columnAt(cursor.block().text(), cursor.positionInBlock());
|
||||
: tabSettings.columnAt(cursor.block().text(), cursor.positionInBlock());
|
||||
int anchor = cursor.anchor();
|
||||
int start = qMin(anchor, pos);
|
||||
int end = qMax(anchor, pos);
|
||||
@@ -140,12 +140,13 @@ QTextCursor TextDocumentPrivate::indentOrUnindent(const QTextCursor &textCursor,
|
||||
if (cursor.hasSelection() && !blockSelection && !oneLinePartial) {
|
||||
for (QTextBlock block = startBlock; block != endBlock; block = block.next()) {
|
||||
const QString text = block.text();
|
||||
int indentPosition = ts.lineIndentPosition(text);
|
||||
int indentPosition = tabSettings.lineIndentPosition(text);
|
||||
if (!doIndent && !indentPosition)
|
||||
indentPosition = ts.firstNonSpace(text);
|
||||
int targetColumn = ts.indentedColumn(ts.columnAt(text, indentPosition), doIndent);
|
||||
indentPosition = tabSettings.firstNonSpace(text);
|
||||
int targetColumn = tabSettings.indentedColumn(
|
||||
tabSettings.columnAt(text, indentPosition), doIndent);
|
||||
cursor.setPosition(block.position() + indentPosition);
|
||||
cursor.insertText(ts.indentationString(0, targetColumn, 0, block));
|
||||
cursor.insertText(tabSettings.indentationString(0, targetColumn, 0, block));
|
||||
cursor.setPosition(block.position());
|
||||
cursor.setPosition(block.position() + indentPosition, QTextCursor::KeepAnchor);
|
||||
cursor.removeSelectedText();
|
||||
@@ -170,27 +171,30 @@ QTextCursor TextDocumentPrivate::indentOrUnindent(const QTextCursor &textCursor,
|
||||
for (QTextBlock block = startBlock; block != endBlock; block = block.next()) {
|
||||
QString text = block.text();
|
||||
|
||||
int blockColumn = ts.columnAt(text, text.size());
|
||||
int blockColumn = tabSettings.columnAt(text, text.size());
|
||||
if (blockColumn < column) {
|
||||
cursor.setPosition(block.position() + text.size());
|
||||
cursor.insertText(ts.indentationString(blockColumn, column, 0, block));
|
||||
cursor.insertText(tabSettings.indentationString(blockColumn, column, 0, block));
|
||||
text = block.text();
|
||||
}
|
||||
|
||||
int indentPosition = ts.positionAtColumn(text, column, nullptr, true);
|
||||
int spaces = ts.spacesLeftFromPosition(text, indentPosition);
|
||||
int startColumn = ts.columnAt(text, indentPosition - spaces);
|
||||
int targetColumn = ts.indentedColumn(ts.columnAt(text, indentPosition), doIndent);
|
||||
int indentPosition = tabSettings.positionAtColumn(text, column, nullptr, true);
|
||||
int spaces = tabSettings.spacesLeftFromPosition(text, indentPosition);
|
||||
int startColumn = tabSettings.columnAt(text, indentPosition - spaces);
|
||||
int targetColumn = tabSettings.indentedColumn(
|
||||
tabSettings.columnAt(text, indentPosition), doIndent);
|
||||
cursor.setPosition(block.position() + indentPosition);
|
||||
cursor.setPosition(block.position() + indentPosition - spaces, QTextCursor::KeepAnchor);
|
||||
cursor.removeSelectedText();
|
||||
cursor.insertText(ts.indentationString(startColumn, targetColumn, 0, block));
|
||||
cursor.insertText(tabSettings.indentationString(startColumn, targetColumn, 0, block));
|
||||
}
|
||||
// Preserve initial anchor of block selection
|
||||
if (blockSelection) {
|
||||
end = cursor.position();
|
||||
if (offset)
|
||||
*offset = ts.columnAt(cursor.block().text(), cursor.positionInBlock()) - column;
|
||||
if (offset) {
|
||||
*offset = tabSettings.columnAt(cursor.block().text(), cursor.positionInBlock())
|
||||
- column;
|
||||
}
|
||||
cursor.setPosition(start);
|
||||
cursor.setPosition(end, QTextCursor::KeepAnchor);
|
||||
}
|
||||
@@ -330,19 +334,19 @@ const StorageSettings &TextDocument::storageSettings() const
|
||||
return d->m_storageSettings;
|
||||
}
|
||||
|
||||
void TextDocument::setTabSettings(const TabSettings &tabSettings)
|
||||
void TextDocument::setTabSettings(const TabSettings &newTabSettings)
|
||||
{
|
||||
if (tabSettings == d->m_tabSettings)
|
||||
if (newTabSettings == d->m_tabSettings)
|
||||
return;
|
||||
d->m_tabSettings = tabSettings;
|
||||
d->m_tabSettings = newTabSettings;
|
||||
|
||||
if (Highlighter *highlighter = qobject_cast<Highlighter *>(d->m_highlighter))
|
||||
highlighter->setTabSettings(tabSettings);
|
||||
highlighter->setTabSettings(tabSettings());
|
||||
|
||||
emit tabSettingsChanged();
|
||||
}
|
||||
|
||||
const TabSettings &TextDocument::tabSettings() const
|
||||
TabSettings TextDocument::tabSettings() const
|
||||
{
|
||||
return d->m_tabSettings;
|
||||
}
|
||||
@@ -412,26 +416,26 @@ void TextDocument::setExtraEncodingSettings(const ExtraEncodingSettings &extraEn
|
||||
d->m_extraEncodingSettings = extraEncodingSettings;
|
||||
}
|
||||
|
||||
void TextDocument::autoIndent(const QTextCursor &cursor, QChar typedChar)
|
||||
void TextDocument::autoIndent(const QTextCursor &cursor, QChar typedChar, bool autoTriggered)
|
||||
{
|
||||
d->m_indenter->indent(&d->m_document, cursor, typedChar, d->m_tabSettings);
|
||||
d->m_indenter->indent(&d->m_document, cursor, typedChar, tabSettings(), autoTriggered);
|
||||
}
|
||||
|
||||
void TextDocument::autoReindent(const QTextCursor &cursor)
|
||||
{
|
||||
d->m_indenter->reindent(&d->m_document, cursor, d->m_tabSettings);
|
||||
d->m_indenter->reindent(&d->m_document, cursor, tabSettings());
|
||||
}
|
||||
|
||||
QTextCursor TextDocument::indent(const QTextCursor &cursor, bool blockSelection, int column,
|
||||
int *offset)
|
||||
{
|
||||
return d->indentOrUnindent(cursor, true, blockSelection, column, offset);
|
||||
return d->indentOrUnindent(cursor, true, tabSettings(), blockSelection, column, offset);
|
||||
}
|
||||
|
||||
QTextCursor TextDocument::unindent(const QTextCursor &cursor, bool blockSelection, int column,
|
||||
int *offset)
|
||||
{
|
||||
return d->indentOrUnindent(cursor, false, blockSelection, column, offset);
|
||||
return d->indentOrUnindent(cursor, false, tabSettings(), blockSelection, column, offset);
|
||||
}
|
||||
|
||||
const ExtraEncodingSettings &TextDocument::extraEncodingSettings() const
|
||||
@@ -814,23 +818,24 @@ void TextDocument::cleanWhitespace(QTextCursor &cursor, bool cleanIndentation, b
|
||||
if (blocks.isEmpty())
|
||||
return;
|
||||
|
||||
const TabSettings currentTabSettings = tabSettings();
|
||||
const IndentationForBlock &indentations =
|
||||
d->m_indenter->indentationForBlocks(blocks, d->m_tabSettings);
|
||||
d->m_indenter->indentationForBlocks(blocks, currentTabSettings);
|
||||
|
||||
foreach (block, blocks) {
|
||||
QString blockText = block.text();
|
||||
d->m_tabSettings.removeTrailingWhitespace(cursor, block);
|
||||
currentTabSettings.removeTrailingWhitespace(cursor, block);
|
||||
const int indent = indentations[block.blockNumber()];
|
||||
if (cleanIndentation && !d->m_tabSettings.isIndentationClean(block, indent)) {
|
||||
if (cleanIndentation && !currentTabSettings.isIndentationClean(block, indent)) {
|
||||
cursor.setPosition(block.position());
|
||||
int firstNonSpace = d->m_tabSettings.firstNonSpace(blockText);
|
||||
int firstNonSpace = currentTabSettings.firstNonSpace(blockText);
|
||||
if (firstNonSpace == blockText.length()) {
|
||||
cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
|
||||
cursor.removeSelectedText();
|
||||
} else {
|
||||
int column = d->m_tabSettings.columnAt(blockText, firstNonSpace);
|
||||
int column = currentTabSettings.columnAt(blockText, firstNonSpace);
|
||||
cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, firstNonSpace);
|
||||
QString indentationString = d->m_tabSettings.indentationString(0, column, column - indent, block);
|
||||
QString indentationString = currentTabSettings.indentationString(0, column, column - indent, block);
|
||||
cursor.insertText(indentationString);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,13 +81,14 @@ public:
|
||||
|
||||
const TypingSettings &typingSettings() const;
|
||||
const StorageSettings &storageSettings() const;
|
||||
const TabSettings &tabSettings() const;
|
||||
virtual TabSettings tabSettings() const;
|
||||
const ExtraEncodingSettings &extraEncodingSettings() const;
|
||||
const FontSettings &fontSettings() const;
|
||||
|
||||
void setIndenter(Indenter *indenter);
|
||||
Indenter *indenter() const;
|
||||
void autoIndent(const QTextCursor &cursor, QChar typedChar = QChar::Null);
|
||||
void autoIndent(const QTextCursor &cursor, QChar typedChar = QChar::Null,
|
||||
bool autoTriggered = true);
|
||||
void autoReindent(const QTextCursor &cursor);
|
||||
QTextCursor indent(const QTextCursor &cursor, bool blockSelection = false, int column = 0,
|
||||
int *offset = nullptr);
|
||||
|
||||
@@ -2451,7 +2451,7 @@ void TextEditorWidget::keyPressEvent(QKeyEvent *e)
|
||||
}
|
||||
|
||||
QTextCursor cursor = textCursor();
|
||||
const TabSettings &ts = d->m_document->tabSettings();
|
||||
const TabSettings ts = d->m_document->tabSettings();
|
||||
const TypingSettings &tps = d->m_document->typingSettings();
|
||||
cursor.beginEditBlock();
|
||||
|
||||
@@ -2474,6 +2474,7 @@ void TextEditorWidget::keyPressEvent(QKeyEvent *e)
|
||||
e->accept();
|
||||
|
||||
if (extraBlocks > 0) {
|
||||
const int cursorPosition = cursor.position();
|
||||
QTextCursor ensureVisible = cursor;
|
||||
while (extraBlocks > 0) {
|
||||
--extraBlocks;
|
||||
@@ -2491,6 +2492,7 @@ void TextEditorWidget::keyPressEvent(QKeyEvent *e)
|
||||
}
|
||||
}
|
||||
setTextCursor(ensureVisible);
|
||||
cursor.setPosition(cursorPosition);
|
||||
}
|
||||
|
||||
setTextCursor(cursor);
|
||||
@@ -3738,7 +3740,7 @@ QString TextEditorWidgetPrivate::copyBlockSelection()
|
||||
if (!m_inBlockSelectionMode)
|
||||
return QString();
|
||||
QString selection;
|
||||
const TabSettings &ts = m_document->tabSettings();
|
||||
const TabSettings ts = m_document->tabSettings();
|
||||
QTextBlock block =
|
||||
m_document->document()->findBlockByNumber(m_blockSelection.firstBlockNumber());
|
||||
const QTextBlock &lastBlock =
|
||||
@@ -3780,7 +3782,7 @@ QString TextEditorWidgetPrivate::copyBlockSelection()
|
||||
|
||||
void TextEditorWidgetPrivate::setCursorToColumn(QTextCursor &cursor, int column, QTextCursor::MoveMode moveMode)
|
||||
{
|
||||
const TabSettings &ts = m_document->tabSettings();
|
||||
const TabSettings ts = m_document->tabSettings();
|
||||
int offset = 0;
|
||||
const int cursorPosition = cursor.position();
|
||||
const int pos = ts.positionAtColumn(cursor.block().text(), column, &offset);
|
||||
@@ -3841,7 +3843,7 @@ void TextEditorWidgetPrivate::insertIntoBlockSelection(const QString &text)
|
||||
- m_blockSelection.firstBlockNumber();
|
||||
const int textNewLineCount = text.count(QLatin1Char('\n')) ;
|
||||
QStringList textLines = text.split(QLatin1Char('\n'));
|
||||
const TabSettings &ts = m_document->tabSettings();
|
||||
const TabSettings ts = m_document->tabSettings();
|
||||
int textLength = 0;
|
||||
const QStringList::const_iterator endLine = textLines.constEnd();
|
||||
for (QStringList::const_iterator textLine = textLines.constBegin(); textLine != endLine; ++textLine)
|
||||
@@ -3903,7 +3905,7 @@ void TextEditorWidgetPrivate::removeBlockSelection()
|
||||
cursor.clearSelection();
|
||||
cursor.beginEditBlock();
|
||||
|
||||
const TabSettings &ts = m_document->tabSettings();
|
||||
const TabSettings ts = m_document->tabSettings();
|
||||
QTextBlock block = m_document->document()->findBlockByNumber(m_blockSelection.firstBlockNumber());
|
||||
const QTextBlock &lastBlock = m_document->document()->findBlockByNumber(m_blockSelection.lastBlockNumber());
|
||||
for (;;) {
|
||||
@@ -3930,7 +3932,7 @@ void TextEditorWidgetPrivate::removeBlockSelection()
|
||||
|
||||
void TextEditorWidgetPrivate::enableBlockSelection(const QTextCursor &cursor)
|
||||
{
|
||||
const TabSettings &ts = m_document->tabSettings();
|
||||
const TabSettings ts = m_document->tabSettings();
|
||||
const QTextBlock &positionTextBlock = cursor.block();
|
||||
int positionBlock = positionTextBlock.blockNumber();
|
||||
int positionColumn = ts.columnAt(positionTextBlock.text(),
|
||||
@@ -4350,7 +4352,7 @@ void TextEditorWidgetPrivate::paintFindScope(const PaintEventData &data, QPainte
|
||||
&& block.position() <= m_findScopeEnd.block().position()) {
|
||||
QTextLayout *layout = block.layout();
|
||||
QString text = block.text();
|
||||
const TabSettings &ts = m_document->tabSettings();
|
||||
const TabSettings ts = m_document->tabSettings();
|
||||
qreal spacew = QFontMetricsF(q->font()).width(QLatin1Char(' '));
|
||||
|
||||
int offset = 0;
|
||||
@@ -4440,7 +4442,7 @@ void TextEditorWidgetPrivate::paintBlockSelection(const PaintEventData &data, QP
|
||||
QTextLayout *layout = data.block.layout();
|
||||
QRectF blockBoundingRect = q->blockBoundingRect(data.block).translated(data.offset);
|
||||
QString text = data.block.text();
|
||||
const TabSettings &tabSettings = m_document->tabSettings();
|
||||
const TabSettings tabSettings = m_document->tabSettings();
|
||||
const qreal spacew = QFontMetricsF(q->font()).width(QLatin1Char(' '));
|
||||
const int cursorw = q->overwriteMode() ? QFontMetrics(q->font()).width(QLatin1Char(' '))
|
||||
: q->cursorWidth();
|
||||
@@ -4726,7 +4728,7 @@ void TextEditorWidgetPrivate::setupSelections(const PaintEventData &data,
|
||||
o.format = range.format;
|
||||
if (i == data.blockSelectionIndex) {
|
||||
QString text = data.block.text();
|
||||
const TabSettings &ts = m_document->tabSettings();
|
||||
const TabSettings ts = m_document->tabSettings();
|
||||
o.start = ts.positionAtColumn(text, m_blockSelection.firstVisualColumn());
|
||||
o.length = ts.positionAtColumn(text, m_blockSelection.lastVisualColumn()) - o.start;
|
||||
}
|
||||
@@ -5511,12 +5513,13 @@ void TextEditorWidget::mouseMoveEvent(QMouseEvent *e)
|
||||
QPlainTextEdit::mouseMoveEvent(e);
|
||||
|
||||
if (e->modifiers() & Qt::AltModifier) {
|
||||
const TabSettings tabSettings = d->m_document->tabSettings();
|
||||
if (!d->m_inBlockSelectionMode) {
|
||||
if (textCursor().hasSelection()) {
|
||||
d->enableBlockSelection(textCursor());
|
||||
} else {
|
||||
const QTextCursor &cursor = cursorForPosition(e->pos());
|
||||
int column = d->m_document->tabSettings().columnAt(
|
||||
int column = tabSettings.columnAt(
|
||||
cursor.block().text(), cursor.positionInBlock());
|
||||
if (cursor.positionInBlock() == cursor.block().length()-1)
|
||||
column += (e->pos().x() - cursorRect().center().x()) / QFontMetricsF(font()).width(QLatin1Char(' '));
|
||||
@@ -5529,7 +5532,7 @@ void TextEditorWidget::mouseMoveEvent(QMouseEvent *e)
|
||||
const QTextCursor &cursor = textCursor();
|
||||
|
||||
// get visual column
|
||||
int column = d->m_document->tabSettings().columnAt(
|
||||
int column = tabSettings.columnAt(
|
||||
cursor.block().text(), cursor.positionInBlock());
|
||||
if (cursor.positionInBlock() == cursor.block().length()-1)
|
||||
column += (e->pos().x() - cursorRect().center().x()) / QFontMetricsF(font()).width(QLatin1Char(' '));
|
||||
@@ -6056,7 +6059,7 @@ void TextEditorWidgetPrivate::handleBackspaceKey()
|
||||
cursorWithinSnippet = snippetCheckCursor(snippetCursor);
|
||||
}
|
||||
|
||||
const TabSettings &tabSettings = m_document->tabSettings();
|
||||
const TabSettings tabSettings = m_document->tabSettings();
|
||||
const TypingSettings &typingSettings = m_document->typingSettings();
|
||||
|
||||
if (typingSettings.m_autoIndent
|
||||
@@ -7131,7 +7134,7 @@ void TextEditorWidget::format()
|
||||
{
|
||||
QTextCursor cursor = textCursor();
|
||||
cursor.beginEditBlock();
|
||||
d->m_document->autoIndent(cursor);
|
||||
d->m_document->autoIndent(cursor, QChar::Null, false);
|
||||
cursor.endEditBlock();
|
||||
}
|
||||
|
||||
@@ -8059,7 +8062,7 @@ QTextCursor TextBlockSelection::cursor(const TextDocument *baseTextDocument,
|
||||
if (!baseTextDocument)
|
||||
return QTextCursor();
|
||||
QTextDocument *document = baseTextDocument->document();
|
||||
const TabSettings &ts = baseTextDocument->tabSettings();
|
||||
const TabSettings ts = baseTextDocument->tabSettings();
|
||||
|
||||
int selectionAnchorColumn;
|
||||
int selectionPositionColumn;
|
||||
@@ -8119,7 +8122,7 @@ bool TextEditorWidget::inFindScope(int selectionStart, int selectionEnd)
|
||||
if (block != document()->findBlock(selectionEnd))
|
||||
return false;
|
||||
QString text = block.text();
|
||||
const TabSettings &ts = d->m_document->tabSettings();
|
||||
const TabSettings ts = d->m_document->tabSettings();
|
||||
int startPosition = ts.positionAtColumn(text, d->m_findScopeVerticalBlockSelectionFirstColumn);
|
||||
int endPosition = ts.positionAtColumn(text, d->m_findScopeVerticalBlockSelectionLastColumn);
|
||||
if (selectionStart - block.position() < startPosition)
|
||||
@@ -8246,7 +8249,7 @@ void TextEditorWidgetPrivate::transformSelection(TransformationMethod method)
|
||||
void TextEditorWidgetPrivate::transformBlockSelection(TransformationMethod method)
|
||||
{
|
||||
QTextCursor cursor = q->textCursor();
|
||||
const TabSettings &ts = m_document->tabSettings();
|
||||
const TabSettings ts = m_document->tabSettings();
|
||||
|
||||
// saved to restore the blockselection
|
||||
const int positionColumn = m_blockSelection.positionColumn;
|
||||
|
||||
@@ -116,6 +116,9 @@ CLANGTOOLING_LIBS=-lclangTooling -lclangIndex -lclangFrontend -lclangParse -lcla
|
||||
-lclangASTMatchers -lclangToolingCore -lclangAST -lclangLex -lclangBasic
|
||||
win32:CLANGTOOLING_LIBS += -lversion
|
||||
|
||||
CLANGFORMAT_LIBS=-lclangFormat -lclangToolingCore -lclangRewrite -lclangLex -lclangBasic
|
||||
win32:CLANGFORMAT_LIBS += -lversion
|
||||
|
||||
BIN_EXTENSION =
|
||||
win32: BIN_EXTENSION = .exe
|
||||
|
||||
@@ -199,6 +202,8 @@ isEmpty(LLVM_VERSION) {
|
||||
warning("Clang LibTooling is disabled. Set QTC_ENABLE_CLANG_LIBTOOLING to enable it.")
|
||||
}
|
||||
|
||||
CLANGFORMAT_LIBS = -L$${LLVM_LIBDIR} $$CLANGFORMAT_LIBS $$LLVM_STATIC_LIBS
|
||||
|
||||
contains(QMAKE_DEFAULT_INCDIRS, $$LLVM_INCLUDEPATH): LLVM_INCLUDEPATH =
|
||||
|
||||
# Remove unwanted flags. It is a workaround for linking.
|
||||
|
||||
Reference in New Issue
Block a user