forked from qt-creator/qt-creator
ClangFormat: Refactor indenter to allow ClangFormat unit-tests
We do not build texteditor files in unit-tests so some tricks were required to make ClangFormatIndenter available. First simple unit-test proofs it builds and runs. Change-Id: I81d5ea099bd27fd1c1ed8b5b7877299dcc62a67f Reviewed-by: Nikolai Kosjar <nikolai.kosjar@qt.io>
This commit is contained in:
@@ -49,7 +49,7 @@ static TextEditor::TextDocument *createJavaDocument()
|
||||
auto doc = new TextEditor::TextDocument;
|
||||
doc->setId(Constants::JAVA_EDITOR_ID);
|
||||
doc->setMimeType(QLatin1String(Constants::JAVA_MIMETYPE));
|
||||
doc->setIndenter(new JavaIndenter);
|
||||
doc->setIndenter(new JavaIndenter(doc->document()));
|
||||
return doc;
|
||||
}
|
||||
|
||||
|
@@ -31,7 +31,9 @@
|
||||
using namespace Android;
|
||||
using namespace Android::Internal;
|
||||
|
||||
JavaIndenter::JavaIndenter() = default;
|
||||
JavaIndenter::JavaIndenter(QTextDocument *doc)
|
||||
: TextEditor::TextIndenter(doc)
|
||||
{}
|
||||
|
||||
JavaIndenter::~JavaIndenter() = default;
|
||||
|
||||
@@ -44,20 +46,17 @@ bool JavaIndenter::isElectricCharacter(const QChar &ch) const
|
||||
return false;
|
||||
}
|
||||
|
||||
void JavaIndenter::indentBlock(QTextDocument *doc,
|
||||
const QTextBlock &block,
|
||||
void JavaIndenter::indentBlock(const QTextBlock &block,
|
||||
const QChar &typedChar,
|
||||
const TextEditor::TabSettings &tabSettings)
|
||||
{
|
||||
Q_UNUSED(doc);
|
||||
int indent = indentFor(block, tabSettings);
|
||||
if (typedChar == QLatin1Char('}'))
|
||||
indent -= tabSettings.m_indentSize;
|
||||
tabSettings.indentLine(block, qMax(0, indent));
|
||||
}
|
||||
|
||||
int JavaIndenter::indentFor(const QTextBlock &block,
|
||||
const TextEditor::TabSettings &tabSettings)
|
||||
int JavaIndenter::indentFor(const QTextBlock &block, const TextEditor::TabSettings &tabSettings)
|
||||
{
|
||||
QTextBlock previous = block.previous();
|
||||
if (!previous.isValid())
|
||||
|
@@ -25,20 +25,19 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <texteditor/indenter.h>
|
||||
#include <texteditor/textindenter.h>
|
||||
|
||||
namespace Android {
|
||||
namespace Internal {
|
||||
class JavaIndenter : public TextEditor::Indenter
|
||||
class JavaIndenter : public TextEditor::TextIndenter
|
||||
{
|
||||
public:
|
||||
JavaIndenter();
|
||||
explicit JavaIndenter(QTextDocument *doc);
|
||||
~JavaIndenter() override;
|
||||
|
||||
bool isElectricCharacter(const QChar &ch) const override;
|
||||
|
||||
void indentBlock(QTextDocument *doc,
|
||||
const QTextBlock &block,
|
||||
void indentBlock(const QTextBlock &block,
|
||||
const QChar &typedChar,
|
||||
const TextEditor::TabSettings &tabSettings) override;
|
||||
|
||||
|
8
src/plugins/clangformat/clangformat-source.pri
Normal file
8
src/plugins/clangformat/clangformat-source.pri
Normal file
@@ -0,0 +1,8 @@
|
||||
INCLUDEPATH += $$PWD
|
||||
|
||||
HEADERS += \
|
||||
$$PWD/clangformatconstants.h \
|
||||
$$PWD/clangformatbaseindenter.h
|
||||
|
||||
SOURCES += \
|
||||
$$PWD/clangformatbaseindenter.cpp
|
@@ -1,4 +1,5 @@
|
||||
include(../../qtcreatorplugin.pri)
|
||||
include(clangformat-source.pri)
|
||||
include(../../shared/clang/clang_installation.pri)
|
||||
|
||||
include(../../shared/clang/clang_defines.pri)
|
||||
@@ -19,17 +20,16 @@ QMAKE_CXXFLAGS *= $$LLVM_CXXFLAGS
|
||||
gcc:QMAKE_CXXFLAGS *= -Wno-comment
|
||||
unix:!macos:QMAKE_LFLAGS += -Wl,--exclude-libs,ALL
|
||||
|
||||
SOURCES = \
|
||||
SOURCES += \
|
||||
clangformatconfigwidget.cpp \
|
||||
clangformatindenter.cpp \
|
||||
clangformatplugin.cpp \
|
||||
clangformatutils.cpp
|
||||
|
||||
HEADERS = \
|
||||
HEADERS += \
|
||||
clangformatconfigwidget.h \
|
||||
clangformatindenter.h \
|
||||
clangformatplugin.h \
|
||||
clangformatconstants.h \
|
||||
clangformatutils.h
|
||||
|
||||
FORMS += \
|
||||
|
@@ -30,6 +30,8 @@ QtcPlugin {
|
||||
cpp.rpaths: base.concat(libclang.llvmLibDir)
|
||||
|
||||
files: [
|
||||
"clangformatbaseindenter.h",
|
||||
"clangformatbaseindenter.cpp",
|
||||
"clangformatconfigwidget.cpp",
|
||||
"clangformatconfigwidget.h",
|
||||
"clangformatconfigwidget.ui",
|
||||
|
500
src/plugins/clangformat/clangformatbaseindenter.cpp
Normal file
500
src/plugins/clangformat/clangformatbaseindenter.cpp
Normal file
@@ -0,0 +1,500 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 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 "clangformatbaseindenter.h"
|
||||
|
||||
#include <clang/Tooling/Core/Replacement.h>
|
||||
|
||||
#include <utils/fileutils.h>
|
||||
#include <utils/textutils.h>
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
#include <QTextDocument>
|
||||
|
||||
namespace ClangFormat {
|
||||
|
||||
static void adjustFormatStyleForLineBreak(clang::format::FormatStyle &style)
|
||||
{
|
||||
style.DisableFormat = false;
|
||||
style.ColumnLimit = 0;
|
||||
#ifdef KEEP_LINE_BREAKS_FOR_NON_EMPTY_LINES_BACKPORTED
|
||||
style.KeepLineBreaksForNonEmptyLines = true;
|
||||
#endif
|
||||
style.MaxEmptyLinesToKeep = 2;
|
||||
style.SortIncludes = false;
|
||||
style.SortUsingDeclarations = false;
|
||||
}
|
||||
|
||||
static llvm::StringRef clearExtraNewline(llvm::StringRef text)
|
||||
{
|
||||
while (text.startswith("\n\n"))
|
||||
text = text.drop_front();
|
||||
return text;
|
||||
}
|
||||
|
||||
static clang::tooling::Replacements filteredReplacements(
|
||||
const clang::tooling::Replacements &replacements,
|
||||
int offset,
|
||||
int extraOffsetToAdd,
|
||||
bool onlyIndention)
|
||||
{
|
||||
clang::tooling::Replacements filtered;
|
||||
for (const clang::tooling::Replacement &replacement : replacements) {
|
||||
int replacementOffset = static_cast<int>(replacement.getOffset());
|
||||
if (onlyIndention && replacementOffset != offset - 1)
|
||||
continue;
|
||||
|
||||
if (replacementOffset + 1 >= offset)
|
||||
replacementOffset += extraOffsetToAdd;
|
||||
|
||||
llvm::StringRef text = onlyIndention ? clearExtraNewline(replacement.getReplacementText())
|
||||
: replacement.getReplacementText();
|
||||
|
||||
llvm::Error error = filtered.add(
|
||||
clang::tooling::Replacement(replacement.getFilePath(),
|
||||
static_cast<unsigned int>(replacementOffset),
|
||||
replacement.getLength(),
|
||||
text));
|
||||
// Throws if error is not checked.
|
||||
if (error)
|
||||
break;
|
||||
}
|
||||
return filtered;
|
||||
}
|
||||
|
||||
static void trimFirstNonEmptyBlock(const QTextBlock ¤tBlock)
|
||||
{
|
||||
QTextBlock prevBlock = currentBlock.previous();
|
||||
while (prevBlock.position() > 0 && prevBlock.text().trimmed().isEmpty())
|
||||
prevBlock = prevBlock.previous();
|
||||
|
||||
if (prevBlock.text().trimmed().isEmpty())
|
||||
return;
|
||||
|
||||
const QString initialText = prevBlock.text();
|
||||
if (!initialText.at(initialText.size() - 1).isSpace())
|
||||
return;
|
||||
|
||||
auto lastNonSpace = std::find_if_not(initialText.rbegin(),
|
||||
initialText.rend(),
|
||||
[](const QChar &letter) { return letter.isSpace(); });
|
||||
const int extraSpaceCount = static_cast<int>(std::distance(initialText.rbegin(), lastNonSpace));
|
||||
|
||||
QTextCursor cursor(prevBlock);
|
||||
cursor.beginEditBlock();
|
||||
cursor.movePosition(QTextCursor::Right,
|
||||
QTextCursor::MoveAnchor,
|
||||
initialText.size() - extraSpaceCount);
|
||||
cursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, extraSpaceCount);
|
||||
cursor.removeSelectedText();
|
||||
cursor.endEditBlock();
|
||||
}
|
||||
|
||||
static void trimCurrentBlock(const QTextBlock ¤tBlock)
|
||||
{
|
||||
if (currentBlock.text().trimmed().isEmpty()) {
|
||||
// Clear the block containing only spaces
|
||||
QTextCursor cursor(currentBlock);
|
||||
cursor.beginEditBlock();
|
||||
cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
|
||||
cursor.removeSelectedText();
|
||||
cursor.endEditBlock();
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the total langth of previous lines with pure whitespace.
|
||||
static 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 void modifyToIndentEmptyLines(
|
||||
QByteArray &buffer, int offset, int &length, const QTextBlock &block, bool secondTry)
|
||||
{
|
||||
const QString blockText = block.text().trimmed();
|
||||
const bool closingParenBlock = blockText.startsWith(')');
|
||||
if (blockText.isEmpty() || closingParenBlock) {
|
||||
//This extra text works for the most cases.
|
||||
QByteArray dummyText("a;");
|
||||
|
||||
// Search for previous character
|
||||
QTextBlock prevBlock = block.previous();
|
||||
bool prevBlockIsEmpty = prevBlock.position() > 0 && prevBlock.text().trimmed().isEmpty();
|
||||
while (prevBlockIsEmpty) {
|
||||
prevBlock = prevBlock.previous();
|
||||
prevBlockIsEmpty = prevBlock.position() > 0 && prevBlock.text().trimmed().isEmpty();
|
||||
}
|
||||
if (prevBlock.text().endsWith(','))
|
||||
dummyText = "int a";
|
||||
|
||||
if (closingParenBlock) {
|
||||
if (prevBlock.text().endsWith(','))
|
||||
dummyText = "int a";
|
||||
else
|
||||
dummyText = "&& a";
|
||||
}
|
||||
|
||||
length += dummyText.length();
|
||||
buffer.insert(offset, dummyText);
|
||||
}
|
||||
|
||||
if (secondTry) {
|
||||
int nextLinePos = buffer.indexOf('\n', offset);
|
||||
if (nextLinePos > 0) {
|
||||
// If first try was not successful try to put ')' in the end of the line to close possibly
|
||||
// unclosed parentheses.
|
||||
// TODO: Does it help to add different endings depending on the context?
|
||||
buffer.insert(nextLinePos, ')');
|
||||
length += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static const int kMaxLinesFromCurrentBlock = 200;
|
||||
|
||||
static Utils::LineColumn utf16LineColumn(const QTextBlock &block,
|
||||
int blockOffsetUtf8,
|
||||
const QByteArray &utf8Buffer,
|
||||
int utf8Offset)
|
||||
{
|
||||
// If lastIndexOf('\n') returns -1 then we are fine to add 1 and get 0 offset.
|
||||
const int lineStartUtf8Offset = utf8Buffer.lastIndexOf('\n', utf8Offset - 1) + 1;
|
||||
int line = block.blockNumber() + 1; // Init with the line corresponding the block.
|
||||
|
||||
if (utf8Offset < blockOffsetUtf8) {
|
||||
line -= static_cast<int>(std::count(utf8Buffer.begin() + lineStartUtf8Offset,
|
||||
utf8Buffer.begin() + blockOffsetUtf8,
|
||||
'\n'));
|
||||
} else {
|
||||
line += static_cast<int>(std::count(utf8Buffer.begin() + blockOffsetUtf8,
|
||||
utf8Buffer.begin() + lineStartUtf8Offset,
|
||||
'\n'));
|
||||
}
|
||||
|
||||
const QByteArray lineText = utf8Buffer.mid(lineStartUtf8Offset,
|
||||
utf8Offset - lineStartUtf8Offset);
|
||||
return Utils::LineColumn(line, QString::fromUtf8(lineText).size() + 1);
|
||||
}
|
||||
|
||||
static TextEditor::Replacements utf16Replacements(const QTextBlock &block,
|
||||
int blockOffsetUtf8,
|
||||
const QByteArray &utf8Buffer,
|
||||
const clang::tooling::Replacements &replacements)
|
||||
{
|
||||
TextEditor::Replacements convertedReplacements;
|
||||
convertedReplacements.reserve(replacements.size());
|
||||
for (const clang::tooling::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();
|
||||
convertedReplacements.emplace_back(utf16Offset,
|
||||
utf16Length,
|
||||
QString::fromStdString(replacement.getReplacementText()));
|
||||
}
|
||||
|
||||
return convertedReplacements;
|
||||
}
|
||||
|
||||
static void applyReplacements(const QTextBlock &block, const TextEditor::Replacements &replacements)
|
||||
{
|
||||
if (replacements.empty())
|
||||
return;
|
||||
|
||||
int fullOffsetShift = 0;
|
||||
QTextCursor editCursor(block);
|
||||
for (const TextEditor::Replacement &replacement : replacements) {
|
||||
editCursor.beginEditBlock();
|
||||
editCursor.setPosition(replacement.offset + fullOffsetShift);
|
||||
editCursor.movePosition(QTextCursor::NextCharacter,
|
||||
QTextCursor::KeepAnchor,
|
||||
replacement.length);
|
||||
editCursor.removeSelectedText();
|
||||
editCursor.insertText(replacement.text);
|
||||
editCursor.endEditBlock();
|
||||
fullOffsetShift += replacement.text.length() - replacement.length;
|
||||
}
|
||||
}
|
||||
|
||||
static QString selectedLines(QTextDocument *doc,
|
||||
const QTextBlock &startBlock,
|
||||
const QTextBlock &endBlock)
|
||||
{
|
||||
QString text = Utils::Text::textAt(QTextCursor(doc),
|
||||
startBlock.position(),
|
||||
std::max(0,
|
||||
endBlock.position() + endBlock.length()
|
||||
- startBlock.position() - 1));
|
||||
while (!text.isEmpty() && text.rbegin()->isSpace())
|
||||
text.chop(1);
|
||||
return text;
|
||||
}
|
||||
|
||||
ClangFormatBaseIndenter::ClangFormatBaseIndenter(QTextDocument *doc)
|
||||
: TextEditor::Indenter(doc)
|
||||
{}
|
||||
|
||||
TextEditor::IndentationForBlock ClangFormatBaseIndenter::indentationForBlocks(
|
||||
const QVector<QTextBlock> &blocks, const TextEditor::TabSettings & /*tabSettings*/)
|
||||
{
|
||||
TextEditor::IndentationForBlock ret;
|
||||
for (QTextBlock block : blocks)
|
||||
ret.insert(block.blockNumber(), indentFor(block));
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ClangFormatBaseIndenter::indent(const QTextCursor &cursor, const QChar &typedChar)
|
||||
{
|
||||
if (cursor.hasSelection()) {
|
||||
// Calling currentBlock.next() might be unsafe because we change the document.
|
||||
// Let's operate with block numbers instead.
|
||||
const int startNumber = m_doc->findBlock(cursor.selectionStart()).blockNumber();
|
||||
const int endNumber = m_doc->findBlock(cursor.selectionEnd()).blockNumber();
|
||||
for (int currentBlockNumber = startNumber; currentBlockNumber <= endNumber;
|
||||
++currentBlockNumber) {
|
||||
const QTextBlock currentBlock = m_doc->findBlockByNumber(currentBlockNumber);
|
||||
if (currentBlock.isValid()) {
|
||||
const int blocksAmount = m_doc->blockCount();
|
||||
indentBlock(currentBlock, typedChar);
|
||||
QTC_CHECK(blocksAmount == m_doc->blockCount()
|
||||
&& "ClangFormat plugin indentation changed the amount of blocks.");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
indentBlock(cursor.block(), typedChar);
|
||||
}
|
||||
}
|
||||
|
||||
void ClangFormatBaseIndenter::indent(const QTextCursor &cursor,
|
||||
const QChar &typedChar,
|
||||
const TextEditor::TabSettings & /*tabSettings*/)
|
||||
{
|
||||
indent(cursor, typedChar);
|
||||
}
|
||||
|
||||
void ClangFormatBaseIndenter::reindent(const QTextCursor &cursor,
|
||||
const TextEditor::TabSettings & /*tabSettings*/)
|
||||
{
|
||||
indent(cursor, QChar::Null);
|
||||
}
|
||||
|
||||
TextEditor::Replacements ClangFormatBaseIndenter::format(
|
||||
const QTextCursor &cursor, const TextEditor::TabSettings & /*tabSettings*/)
|
||||
{
|
||||
int utf8Offset;
|
||||
int utf8Length;
|
||||
const QByteArray buffer = m_doc->toPlainText().toUtf8();
|
||||
QTextBlock block = cursor.block();
|
||||
if (cursor.hasSelection()) {
|
||||
block = m_doc->findBlock(cursor.selectionStart());
|
||||
const QTextBlock end = m_doc->findBlock(cursor.selectionEnd());
|
||||
utf8Offset = Utils::Text::utf8NthLineOffset(m_doc, buffer, block.blockNumber() + 1);
|
||||
QTC_ASSERT(utf8Offset >= 0, return TextEditor::Replacements(););
|
||||
utf8Length = selectedLines(m_doc, block, end).toUtf8().size();
|
||||
|
||||
} else {
|
||||
const QTextBlock block = cursor.block();
|
||||
utf8Offset = Utils::Text::utf8NthLineOffset(m_doc, buffer, block.blockNumber() + 1);
|
||||
QTC_ASSERT(utf8Offset >= 0, return TextEditor::Replacements(););
|
||||
utf8Length = block.text().toUtf8().size();
|
||||
}
|
||||
|
||||
const TextEditor::Replacements toReplace
|
||||
= replacements(buffer, utf8Offset, utf8Length, block, QChar::Null, false);
|
||||
applyReplacements(block, toReplace);
|
||||
|
||||
return toReplace;
|
||||
}
|
||||
|
||||
void ClangFormatBaseIndenter::indentBlock(const QTextBlock &block, const QChar &typedChar)
|
||||
{
|
||||
trimFirstNonEmptyBlock(block);
|
||||
trimCurrentBlock(block);
|
||||
const QByteArray buffer = m_doc->toPlainText().toUtf8();
|
||||
const int utf8Offset = Utils::Text::utf8NthLineOffset(m_doc, buffer, block.blockNumber() + 1);
|
||||
QTC_ASSERT(utf8Offset >= 0, return;);
|
||||
|
||||
applyReplacements(block, replacements(buffer, utf8Offset, 0, block, typedChar));
|
||||
}
|
||||
|
||||
void ClangFormatBaseIndenter::indentBlock(const QTextBlock &block,
|
||||
const QChar &typedChar,
|
||||
const TextEditor::TabSettings & /*tabSettings*/)
|
||||
{
|
||||
indentBlock(block, typedChar);
|
||||
}
|
||||
|
||||
int ClangFormatBaseIndenter::indentFor(const QTextBlock &block)
|
||||
{
|
||||
trimFirstNonEmptyBlock(block);
|
||||
trimCurrentBlock(block);
|
||||
const QByteArray buffer = m_doc->toPlainText().toUtf8();
|
||||
const int utf8Offset = Utils::Text::utf8NthLineOffset(m_doc, buffer, block.blockNumber() + 1);
|
||||
QTC_ASSERT(utf8Offset >= 0, return 0;);
|
||||
|
||||
const TextEditor::Replacements toReplace = replacements(buffer, utf8Offset, 0, block);
|
||||
|
||||
if (toReplace.empty())
|
||||
return -1;
|
||||
|
||||
const TextEditor::Replacement &replacement = toReplace.front();
|
||||
int afterLineBreak = replacement.text.lastIndexOf('\n');
|
||||
afterLineBreak = (afterLineBreak < 0) ? 0 : afterLineBreak + 1;
|
||||
return static_cast<int>(replacement.text.size() - afterLineBreak);
|
||||
}
|
||||
|
||||
int ClangFormatBaseIndenter::indentFor(const QTextBlock &block,
|
||||
const TextEditor::TabSettings & /*tabSettings*/)
|
||||
{
|
||||
return indentFor(block);
|
||||
}
|
||||
|
||||
bool ClangFormatBaseIndenter::isElectricCharacter(const QChar &ch) const
|
||||
{
|
||||
switch (ch.toLatin1()) {
|
||||
case '{':
|
||||
case '}':
|
||||
case ':':
|
||||
case '#':
|
||||
case '<':
|
||||
case '>':
|
||||
case ';':
|
||||
case '(':
|
||||
case ')':
|
||||
case ',':
|
||||
case '.':
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
clang::format::FormatStyle ClangFormatBaseIndenter::styleForFile() const
|
||||
{
|
||||
llvm::Expected<clang::format::FormatStyle> style
|
||||
= clang::format::getStyle("file", m_fileName.toString().toStdString(), "none");
|
||||
if (style)
|
||||
return *style;
|
||||
|
||||
handleAllErrors(style.takeError(), [](const llvm::ErrorInfoBase &) {
|
||||
// do nothing
|
||||
});
|
||||
|
||||
return clang::format::getLLVMStyle();
|
||||
}
|
||||
|
||||
TextEditor::Replacements ClangFormatBaseIndenter::replacements(QByteArray buffer,
|
||||
int utf8Offset,
|
||||
int utf8Length,
|
||||
const QTextBlock &block,
|
||||
const QChar &typedChar,
|
||||
bool onlyIndention,
|
||||
bool secondTry) const
|
||||
{
|
||||
clang::format::FormatStyle style = styleForFile();
|
||||
|
||||
int originalOffsetUtf8 = utf8Offset;
|
||||
int originalLengthUtf8 = utf8Length;
|
||||
QByteArray originalBuffer = buffer;
|
||||
|
||||
int extraOffset = 0;
|
||||
if (onlyIndention) {
|
||||
if (block.blockNumber() > kMaxLinesFromCurrentBlock) {
|
||||
extraOffset = Utils::Text::utf8NthLineOffset(block.document(),
|
||||
buffer,
|
||||
block.blockNumber()
|
||||
- kMaxLinesFromCurrentBlock);
|
||||
}
|
||||
int endOffset = Utils::Text::utf8NthLineOffset(block.document(),
|
||||
buffer,
|
||||
block.blockNumber()
|
||||
+ kMaxLinesFromCurrentBlock);
|
||||
if (endOffset == -1)
|
||||
endOffset = buffer.size();
|
||||
|
||||
buffer = buffer.mid(extraOffset, endOffset - 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, secondTry);
|
||||
}
|
||||
|
||||
std::vector<clang::tooling::Range> ranges{
|
||||
{static_cast<unsigned int>(utf8Offset), static_cast<unsigned int>(utf8Length)}};
|
||||
clang::format::FormattingAttemptStatus status;
|
||||
|
||||
clang::tooling::Replacements clangReplacements = reformat(style,
|
||||
buffer.data(),
|
||||
ranges,
|
||||
m_fileName.toString().toStdString(),
|
||||
&status);
|
||||
|
||||
if (!status.FormatComplete)
|
||||
return TextEditor::Replacements();
|
||||
|
||||
const clang::tooling::Replacements filtered = filteredReplacements(clangReplacements,
|
||||
utf8Offset,
|
||||
extraOffset,
|
||||
onlyIndention);
|
||||
const bool canTryAgain = onlyIndention && typedChar == QChar::Null && !secondTry;
|
||||
if (canTryAgain && filtered.empty()) {
|
||||
return replacements(originalBuffer,
|
||||
originalOffsetUtf8,
|
||||
originalLengthUtf8,
|
||||
block,
|
||||
typedChar,
|
||||
onlyIndention,
|
||||
true);
|
||||
}
|
||||
|
||||
return utf16Replacements(block, originalOffsetUtf8, originalBuffer, filtered);
|
||||
}
|
||||
|
||||
} // namespace ClangFormat
|
75
src/plugins/clangformat/clangformatbaseindenter.h
Normal file
75
src/plugins/clangformat/clangformatbaseindenter.h
Normal file
@@ -0,0 +1,75 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 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>
|
||||
|
||||
#include <clang/Format/Format.h>
|
||||
|
||||
namespace ClangFormat {
|
||||
|
||||
class ClangFormatBaseIndenter : public TextEditor::Indenter
|
||||
{
|
||||
public:
|
||||
ClangFormatBaseIndenter(QTextDocument *doc);
|
||||
|
||||
TextEditor::IndentationForBlock indentationForBlocks(
|
||||
const QVector<QTextBlock> &blocks, const TextEditor::TabSettings & /*tabSettings*/) override;
|
||||
void indent(const QTextCursor &cursor,
|
||||
const QChar &typedChar,
|
||||
const TextEditor::TabSettings & /*tabSettings*/) override;
|
||||
|
||||
void reindent(const QTextCursor &cursor,
|
||||
const TextEditor::TabSettings & /*tabSettings*/) override;
|
||||
|
||||
TextEditor::Replacements format(const QTextCursor &cursor,
|
||||
const TextEditor::TabSettings & /*tabSettings*/) override;
|
||||
|
||||
void indentBlock(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;
|
||||
|
||||
protected:
|
||||
virtual clang::format::FormatStyle styleForFile() const;
|
||||
|
||||
private:
|
||||
void indent(const QTextCursor &cursor, const QChar &typedChar);
|
||||
void indentBlock(const QTextBlock &block, const QChar &typedChar);
|
||||
int indentFor(const QTextBlock &block);
|
||||
TextEditor::Replacements replacements(QByteArray buffer,
|
||||
int utf8Offset,
|
||||
int utf8Length,
|
||||
const QTextBlock &block,
|
||||
const QChar &typedChar = QChar::Null,
|
||||
bool onlyIndention = true,
|
||||
bool secondTry = false) const;
|
||||
};
|
||||
|
||||
} // namespace ClangFormat
|
@@ -1,6 +1,6 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2018 The Qt Company Ltd.
|
||||
** Copyright (C) 2019 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
@@ -24,479 +24,26 @@
|
||||
****************************************************************************/
|
||||
|
||||
#include "clangformatindenter.h"
|
||||
|
||||
#include "clangformatutils.h"
|
||||
|
||||
#include <clang/Format/Format.h>
|
||||
#include <clang/Tooling/Core/Replacement.h>
|
||||
|
||||
#include <cpptools/cppmodelmanager.h>
|
||||
#include <texteditor/textdocument.h>
|
||||
#include <texteditor/texteditor.h>
|
||||
|
||||
#include <utils/hostosinfo.h>
|
||||
#include <utils/textutils.h>
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
#include <llvm/Config/llvm-config.h>
|
||||
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
#include <QTextBlock>
|
||||
|
||||
#include <fstream>
|
||||
|
||||
using ClangReplacement = clang::tooling::Replacement;
|
||||
using ClangReplacements = clang::tooling::Replacements;
|
||||
using QtReplacement = TextEditor::Replacement;
|
||||
using QtReplacements = TextEditor::Replacements;
|
||||
#include <texteditor/tabsettings.h>
|
||||
|
||||
using namespace clang;
|
||||
using namespace format;
|
||||
using namespace llvm;
|
||||
using namespace tooling;
|
||||
using namespace ProjectExplorer;
|
||||
using namespace TextEditor;
|
||||
|
||||
namespace ClangFormat {
|
||||
|
||||
namespace {
|
||||
ClangFormatIndenter::ClangFormatIndenter(QTextDocument *doc)
|
||||
: ClangFormatBaseIndenter(doc)
|
||||
{}
|
||||
|
||||
void adjustFormatStyleForLineBreak(format::FormatStyle &style)
|
||||
FormatStyle ClangFormatIndenter::styleForFile() const
|
||||
{
|
||||
style.DisableFormat = false;
|
||||
style.ColumnLimit = 0;
|
||||
#ifdef KEEP_LINE_BREAKS_FOR_NON_EMPTY_LINES_BACKPORTED
|
||||
style.KeepLineBreaksForNonEmptyLines = true;
|
||||
#endif
|
||||
style.MaxEmptyLinesToKeep = 2;
|
||||
style.SortIncludes = false;
|
||||
style.SortUsingDeclarations = false;
|
||||
return ClangFormat::styleForFile(m_fileName);
|
||||
}
|
||||
|
||||
StringRef clearExtraNewline(StringRef text)
|
||||
{
|
||||
while (text.startswith("\n\n"))
|
||||
text = text.drop_front();
|
||||
return text;
|
||||
}
|
||||
|
||||
ClangReplacements filteredReplacements(const ClangReplacements &replacements,
|
||||
int offset,
|
||||
int extraOffsetToAdd,
|
||||
bool onlyIndention)
|
||||
{
|
||||
ClangReplacements filtered;
|
||||
for (const ClangReplacement &replacement : replacements) {
|
||||
int replacementOffset = static_cast<int>(replacement.getOffset());
|
||||
if (onlyIndention && replacementOffset != offset - 1)
|
||||
continue;
|
||||
|
||||
if (replacementOffset + 1 >= offset)
|
||||
replacementOffset += extraOffsetToAdd;
|
||||
|
||||
StringRef text = onlyIndention ? clearExtraNewline(replacement.getReplacementText())
|
||||
: replacement.getReplacementText();
|
||||
|
||||
Error error = filtered.add(ClangReplacement(replacement.getFilePath(),
|
||||
static_cast<unsigned int>(replacementOffset),
|
||||
replacement.getLength(),
|
||||
text));
|
||||
// Throws if error is not checked.
|
||||
if (error)
|
||||
break;
|
||||
}
|
||||
return filtered;
|
||||
}
|
||||
|
||||
void trimFirstNonEmptyBlock(const QTextBlock ¤tBlock)
|
||||
{
|
||||
QTextBlock prevBlock = currentBlock.previous();
|
||||
while (prevBlock.position() > 0 && prevBlock.text().trimmed().isEmpty())
|
||||
prevBlock = prevBlock.previous();
|
||||
|
||||
if (prevBlock.text().trimmed().isEmpty())
|
||||
return;
|
||||
|
||||
const QString initialText = prevBlock.text();
|
||||
if (!initialText.at(initialText.size() - 1).isSpace())
|
||||
return;
|
||||
|
||||
auto lastNonSpace = std::find_if_not(initialText.rbegin(),
|
||||
initialText.rend(),
|
||||
[](const QChar &letter) {
|
||||
return letter.isSpace();
|
||||
});
|
||||
const int extraSpaceCount = static_cast<int>(std::distance(initialText.rbegin(), lastNonSpace));
|
||||
|
||||
QTextCursor cursor(prevBlock);
|
||||
cursor.beginEditBlock();
|
||||
cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor,
|
||||
initialText.size() - extraSpaceCount);
|
||||
cursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, extraSpaceCount);
|
||||
cursor.removeSelectedText();
|
||||
cursor.endEditBlock();
|
||||
}
|
||||
|
||||
void trimCurrentBlock(const QTextBlock ¤tBlock)
|
||||
{
|
||||
if (currentBlock.text().trimmed().isEmpty()) {
|
||||
// Clear the block containing only spaces
|
||||
QTextCursor cursor(currentBlock);
|
||||
cursor.beginEditBlock();
|
||||
cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
|
||||
cursor.removeSelectedText();
|
||||
cursor.endEditBlock();
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
void modifyToIndentEmptyLines(
|
||||
QByteArray &buffer, int offset, int &length, const QTextBlock &block, bool secondTry)
|
||||
{
|
||||
const QString blockText = block.text().trimmed();
|
||||
const bool closingParenBlock = blockText.startsWith(')');
|
||||
if (blockText.isEmpty() || closingParenBlock) {
|
||||
//This extra text works for the most cases.
|
||||
QByteArray dummyText("a;");
|
||||
|
||||
// Search for previous character
|
||||
QTextBlock prevBlock = block.previous();
|
||||
bool prevBlockIsEmpty = prevBlock.position() > 0 && prevBlock.text().trimmed().isEmpty();
|
||||
while (prevBlockIsEmpty) {
|
||||
prevBlock = prevBlock.previous();
|
||||
prevBlockIsEmpty = prevBlock.position() > 0 && prevBlock.text().trimmed().isEmpty();
|
||||
}
|
||||
if (prevBlock.text().endsWith(','))
|
||||
dummyText = "int a";
|
||||
|
||||
if (closingParenBlock) {
|
||||
if (prevBlock.text().endsWith(','))
|
||||
dummyText = "int a";
|
||||
else
|
||||
dummyText = "&& a";
|
||||
}
|
||||
|
||||
length += dummyText.length();
|
||||
buffer.insert(offset, dummyText);
|
||||
}
|
||||
|
||||
if (secondTry) {
|
||||
int nextLinePos = buffer.indexOf('\n', offset);
|
||||
if (nextLinePos > 0) {
|
||||
// If first try was not successful try to put ')' in the end of the line to close possibly
|
||||
// unclosed parentheses.
|
||||
// TODO: Does it help to add different endings depending on the context?
|
||||
buffer.insert(nextLinePos, ')');
|
||||
length += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static const int kMaxLinesFromCurrentBlock = 200;
|
||||
|
||||
Utils::LineColumn utf16LineColumn(const QTextBlock &block,
|
||||
int blockOffsetUtf8,
|
||||
const QByteArray &utf8Buffer,
|
||||
int utf8Offset)
|
||||
{
|
||||
// If lastIndexOf('\n') returns -1 then we are fine to add 1 and get 0 offset.
|
||||
const int lineStartUtf8Offset = utf8Buffer.lastIndexOf('\n', utf8Offset - 1) + 1;
|
||||
int line = block.blockNumber() + 1; // Init with the line corresponding the block.
|
||||
|
||||
if (utf8Offset < blockOffsetUtf8) {
|
||||
line -= static_cast<int>(std::count(utf8Buffer.begin() + lineStartUtf8Offset,
|
||||
utf8Buffer.begin() + blockOffsetUtf8,
|
||||
'\n'));
|
||||
} else {
|
||||
line += static_cast<int>(std::count(utf8Buffer.begin() + blockOffsetUtf8,
|
||||
utf8Buffer.begin() + lineStartUtf8Offset,
|
||||
'\n'));
|
||||
}
|
||||
|
||||
const QByteArray lineText = utf8Buffer.mid(lineStartUtf8Offset,
|
||||
utf8Offset - lineStartUtf8Offset);
|
||||
return Utils::LineColumn(line, QString::fromUtf8(lineText).size() + 1);
|
||||
}
|
||||
|
||||
QtReplacements utf16Replacements(const QTextBlock &block,
|
||||
int blockOffsetUtf8,
|
||||
const QByteArray &utf8Buffer,
|
||||
const ClangReplacements &replacements)
|
||||
{
|
||||
QtReplacements convertedReplacements;
|
||||
convertedReplacements.reserve(replacements.size());
|
||||
for (const ClangReplacement &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();
|
||||
convertedReplacements.emplace_back(utf16Offset,
|
||||
utf16Length,
|
||||
QString::fromStdString(replacement.getReplacementText()));
|
||||
}
|
||||
|
||||
return convertedReplacements;
|
||||
}
|
||||
|
||||
QtReplacements replacements(const Utils::FileName &fileName,
|
||||
QByteArray buffer,
|
||||
int utf8Offset,
|
||||
int utf8Length,
|
||||
const QTextBlock &block,
|
||||
const QChar &typedChar = QChar::Null,
|
||||
bool onlyIndention = true,
|
||||
bool secondTry = false)
|
||||
{
|
||||
FormatStyle style = styleForFile(fileName);
|
||||
|
||||
int originalOffsetUtf8 = utf8Offset;
|
||||
int originalLengthUtf8 = utf8Length;
|
||||
QByteArray originalBuffer = buffer;
|
||||
|
||||
int extraOffset = 0;
|
||||
if (onlyIndention) {
|
||||
if (block.blockNumber() > kMaxLinesFromCurrentBlock) {
|
||||
extraOffset = Utils::Text::utf8NthLineOffset(
|
||||
block.document(), buffer, block.blockNumber() - kMaxLinesFromCurrentBlock);
|
||||
}
|
||||
int endOffset = Utils::Text::utf8NthLineOffset(
|
||||
block.document(), buffer, block.blockNumber() + kMaxLinesFromCurrentBlock);
|
||||
if (endOffset == -1)
|
||||
endOffset = buffer.size();
|
||||
|
||||
buffer = buffer.mid(extraOffset, endOffset - 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, secondTry);
|
||||
}
|
||||
|
||||
std::vector<Range> ranges{{static_cast<unsigned int>(utf8Offset),
|
||||
static_cast<unsigned int>(utf8Length)}};
|
||||
FormattingAttemptStatus status;
|
||||
|
||||
ClangReplacements clangReplacements = reformat(style,
|
||||
buffer.data(),
|
||||
ranges,
|
||||
fileName.toString().toStdString(),
|
||||
&status);
|
||||
|
||||
if (!status.FormatComplete)
|
||||
QtReplacements();
|
||||
|
||||
const ClangReplacements filtered = filteredReplacements(clangReplacements,
|
||||
utf8Offset,
|
||||
extraOffset,
|
||||
onlyIndention);
|
||||
|
||||
const bool canTryAgain = onlyIndention && typedChar == QChar::Null && !secondTry;
|
||||
if (canTryAgain && filtered.empty()) {
|
||||
return replacements(fileName,
|
||||
originalBuffer,
|
||||
originalOffsetUtf8,
|
||||
originalLengthUtf8,
|
||||
block,
|
||||
typedChar,
|
||||
onlyIndention,
|
||||
true);
|
||||
}
|
||||
|
||||
return utf16Replacements(block, originalOffsetUtf8, originalBuffer, filtered);
|
||||
}
|
||||
|
||||
void applyReplacements(const QTextBlock &block, const QtReplacements &replacements)
|
||||
{
|
||||
if (replacements.empty())
|
||||
return;
|
||||
|
||||
int fullOffsetShift = 0;
|
||||
QTextCursor editCursor(block);
|
||||
for (const QtReplacement &replacement : replacements) {
|
||||
editCursor.beginEditBlock();
|
||||
editCursor.setPosition(replacement.offset + fullOffsetShift);
|
||||
editCursor.movePosition(QTextCursor::NextCharacter,
|
||||
QTextCursor::KeepAnchor,
|
||||
replacement.length);
|
||||
editCursor.removeSelectedText();
|
||||
editCursor.insertText(replacement.text);
|
||||
editCursor.endEditBlock();
|
||||
fullOffsetShift += replacement.text.length() - replacement.length;
|
||||
}
|
||||
}
|
||||
|
||||
QString selectedLines(QTextDocument *doc, const QTextBlock &startBlock, const QTextBlock &endBlock)
|
||||
{
|
||||
QString text = Utils::Text::textAt(
|
||||
QTextCursor(doc),
|
||||
startBlock.position(),
|
||||
std::max(0, endBlock.position() + endBlock.length() - startBlock.position() - 1));
|
||||
while (!text.isEmpty() && text.rbegin()->isSpace())
|
||||
text.chop(1);
|
||||
return text;
|
||||
}
|
||||
|
||||
} // 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 (cursor.hasSelection()) {
|
||||
// Calling currentBlock.next() might be unsafe because we change the document.
|
||||
// Let's operate with block numbers instead.
|
||||
const int startNumber = doc->findBlock(cursor.selectionStart()).blockNumber();
|
||||
const int endNumber = doc->findBlock(cursor.selectionEnd()).blockNumber();
|
||||
for (int currentBlockNumber = startNumber; currentBlockNumber <= endNumber;
|
||||
++currentBlockNumber) {
|
||||
const QTextBlock currentBlock = doc->findBlockByNumber(currentBlockNumber);
|
||||
if (currentBlock.isValid()) {
|
||||
const int blocksAmount = doc->blockCount();
|
||||
indentBlock(doc, currentBlock, typedChar, tabSettings);
|
||||
QTC_CHECK(blocksAmount == doc->blockCount()
|
||||
&& "ClangFormat plugin indentation changed the amount of blocks.");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
indentBlock(doc, cursor.block(), typedChar, tabSettings);
|
||||
}
|
||||
}
|
||||
|
||||
QtReplacements ClangFormatIndenter::format(QTextDocument *doc,
|
||||
const Utils::FileName &fileName,
|
||||
const QTextCursor &cursor,
|
||||
const TextEditor::TabSettings & /*tabSettings*/)
|
||||
{
|
||||
int utf8Offset;
|
||||
int utf8Length;
|
||||
const QByteArray buffer = doc->toPlainText().toUtf8();
|
||||
QTextBlock block = cursor.block();
|
||||
if (cursor.hasSelection()) {
|
||||
block = doc->findBlock(cursor.selectionStart());
|
||||
const QTextBlock end = doc->findBlock(cursor.selectionEnd());
|
||||
utf8Offset = Utils::Text::utf8NthLineOffset(doc, buffer, block.blockNumber() + 1);
|
||||
QTC_ASSERT(utf8Offset >= 0, return QtReplacements(););
|
||||
utf8Length = selectedLines(doc, block, end).toUtf8().size();
|
||||
|
||||
} else {
|
||||
const QTextBlock block = cursor.block();
|
||||
utf8Offset = Utils::Text::utf8NthLineOffset(doc, buffer, block.blockNumber() + 1);
|
||||
QTC_ASSERT(utf8Offset >= 0, return QtReplacements(););
|
||||
utf8Length = block.text().toUtf8().size();
|
||||
}
|
||||
|
||||
const QtReplacements toReplace
|
||||
= replacements(fileName, buffer, utf8Offset, utf8Length, block, QChar::Null, false);
|
||||
applyReplacements(block, toReplace);
|
||||
|
||||
return toReplace;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
Q_UNUSED(tabSettings);
|
||||
|
||||
TextEditorWidget *editor = TextEditorWidget::currentTextEditorWidget();
|
||||
if (!editor)
|
||||
return;
|
||||
|
||||
const Utils::FileName fileName = editor->textDocument()->filePath();
|
||||
trimFirstNonEmptyBlock(block);
|
||||
trimCurrentBlock(block);
|
||||
const QByteArray buffer = doc->toPlainText().toUtf8();
|
||||
const int utf8Offset = Utils::Text::utf8NthLineOffset(doc, buffer, block.blockNumber() + 1);
|
||||
QTC_ASSERT(utf8Offset >= 0, return;);
|
||||
|
||||
applyReplacements(block,
|
||||
replacements(fileName, buffer, utf8Offset, 0, block, typedChar));
|
||||
}
|
||||
|
||||
int ClangFormatIndenter::indentFor(const QTextBlock &block, const TextEditor::TabSettings &)
|
||||
{
|
||||
TextEditorWidget *editor = TextEditorWidget::currentTextEditorWidget();
|
||||
if (!editor)
|
||||
return -1;
|
||||
|
||||
const Utils::FileName fileName = editor->textDocument()->filePath();
|
||||
trimFirstNonEmptyBlock(block);
|
||||
trimCurrentBlock(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 QtReplacements toReplace = replacements(fileName, buffer, utf8Offset, 0, block);
|
||||
|
||||
if (toReplace.empty())
|
||||
return -1;
|
||||
|
||||
const QtReplacement &replacement = toReplace.front();
|
||||
int afterLineBreak = replacement.text.lastIndexOf('\n');
|
||||
afterLineBreak = (afterLineBreak < 0) ? 0 : afterLineBreak + 1;
|
||||
return static_cast<int>(replacement.text.size() - afterLineBreak);
|
||||
}
|
||||
|
||||
TabSettings ClangFormatIndenter::tabSettings() const
|
||||
Utils::optional<TabSettings> ClangFormatIndenter::tabSettings() const
|
||||
{
|
||||
FormatStyle style = currentProjectStyle();
|
||||
TabSettings tabSettings;
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2018 The Qt Company Ltd.
|
||||
** Copyright (C) 2019 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
@@ -25,35 +25,20 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <texteditor/indenter.h>
|
||||
#include "clangformatbaseindenter.h"
|
||||
|
||||
#include <texteditor/tabsettings.h>
|
||||
|
||||
namespace ClangFormat {
|
||||
|
||||
class ClangFormatIndenter final : public TextEditor::Indenter
|
||||
class ClangFormatIndenter final : public ClangFormatBaseIndenter
|
||||
{
|
||||
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;
|
||||
TextEditor::Replacements format(QTextDocument *doc,
|
||||
const Utils::FileName &fileName,
|
||||
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;
|
||||
ClangFormatIndenter(QTextDocument *doc);
|
||||
Utils::optional<TextEditor::TabSettings> tabSettings() const override;
|
||||
|
||||
bool isElectricCharacter(const QChar &ch) const override;
|
||||
|
||||
bool hasTabSettings() const override { return true; }
|
||||
TextEditor::TabSettings tabSettings() const override;
|
||||
private:
|
||||
clang::format::FormatStyle styleForFile() const override;
|
||||
};
|
||||
|
||||
} // namespace ClangFormat
|
||||
|
@@ -84,9 +84,9 @@ public:
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
TextEditor::Indenter *createIndenter() const override
|
||||
TextEditor::Indenter *createIndenter(QTextDocument *doc) const override
|
||||
{
|
||||
return new ClangFormatIndenter();
|
||||
return new ClangFormatIndenter(doc);
|
||||
}
|
||||
};
|
||||
|
||||
|
@@ -32,6 +32,7 @@
|
||||
|
||||
#include <texteditor/icodestylepreferencesfactory.h>
|
||||
#include <texteditor/indenter.h>
|
||||
#include <texteditor/tabsettings.h>
|
||||
#include <texteditor/texteditorsettings.h>
|
||||
|
||||
#include <QDebug>
|
||||
@@ -83,7 +84,6 @@ bool FixitsRefactoringFile::apply()
|
||||
|
||||
ICodeStylePreferencesFactory *factory = TextEditorSettings::codeStyleFactory(
|
||||
CppTools::Constants::CPP_SETTINGS_ID);
|
||||
std::unique_ptr<TextEditor::Indenter> indenter(factory->createIndenter());
|
||||
|
||||
const TextEditor::TabSettings tabSettings
|
||||
= CppTools::CppCodeStyleSettings::currentProjectTabSettings();
|
||||
@@ -103,6 +103,9 @@ bool FixitsRefactoringFile::apply()
|
||||
|
||||
// Apply
|
||||
QTextDocument *doc = document(op.fileName);
|
||||
std::unique_ptr<TextEditor::Indenter> indenter(factory->createIndenter(doc));
|
||||
indenter->setFileName(Utils::FileName::fromString(op.fileName));
|
||||
|
||||
QTextCursor cursor(doc);
|
||||
cursor.setPosition(op.pos);
|
||||
cursor.setPosition(op.pos + op.length, QTextCursor::KeepAnchor);
|
||||
@@ -139,10 +142,7 @@ void FixitsRefactoringFile::tryToFormat(TextEditor::Indenter &indenter,
|
||||
cursor.movePosition(QTextCursor::Right,
|
||||
QTextCursor::KeepAnchor,
|
||||
op.text.length());
|
||||
const Replacements replacements = indenter.format(doc,
|
||||
Utils::FileName::fromString(op.fileName),
|
||||
cursor,
|
||||
tabSettings);
|
||||
const Replacements replacements = indenter.format(cursor, tabSettings);
|
||||
cursor.endEditBlock();
|
||||
|
||||
if (replacements.empty())
|
||||
|
@@ -25,6 +25,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <texteditor/indenter.h>
|
||||
|
||||
#include <utils/changeset.h>
|
||||
#include <utils/textfileformat.h>
|
||||
|
||||
@@ -32,13 +34,6 @@
|
||||
#include <QTextDocument>
|
||||
#include <QVector>
|
||||
|
||||
namespace TextEditor {
|
||||
class Indenter;
|
||||
class Replacement;
|
||||
using Replacements = std::vector<Replacement>;
|
||||
class TabSettings;
|
||||
}
|
||||
|
||||
namespace ClangTools {
|
||||
namespace Internal {
|
||||
|
||||
|
@@ -229,7 +229,7 @@ CMakeEditorFactory::CMakeEditorFactory()
|
||||
setEditorCreator([]() { return new CMakeEditor; });
|
||||
setEditorWidgetCreator([]() { return new CMakeEditorWidget; });
|
||||
setDocumentCreator(createCMakeDocument);
|
||||
setIndenterCreator([]() { return new CMakeIndenter; });
|
||||
setIndenterCreator([](QTextDocument *doc) { return new CMakeIndenter(doc); });
|
||||
setUseGenericHighlighter(true);
|
||||
setCommentDefinition(Utils::CommentDefinition::HashStyle);
|
||||
setCodeFoldingSupported(true);
|
||||
|
@@ -34,6 +34,10 @@
|
||||
namespace CMakeProjectManager {
|
||||
namespace Internal {
|
||||
|
||||
CMakeIndenter::CMakeIndenter(QTextDocument *doc)
|
||||
: TextEditor::TextIndenter(doc)
|
||||
{}
|
||||
|
||||
bool CMakeIndenter::isElectricCharacter(const QChar &ch) const
|
||||
{
|
||||
return ch == QLatin1Char('(') || ch == QLatin1Char(')');
|
||||
@@ -98,7 +102,8 @@ static int paranthesesLevel(const QString &line)
|
||||
return -1;
|
||||
}
|
||||
|
||||
int CMakeIndenter::indentFor(const QTextBlock &block, const TextEditor::TabSettings &tabSettings)
|
||||
int CMakeIndenter::indentFor(const QTextBlock &block,
|
||||
const TextEditor::TabSettings &tabSettings)
|
||||
{
|
||||
QTextBlock previousBlock = block.previous();
|
||||
// find the next previous block that is non-empty (contains non-whitespace characters)
|
||||
|
@@ -27,17 +27,19 @@
|
||||
|
||||
#include "cmake_global.h"
|
||||
|
||||
#include <texteditor/indenter.h>
|
||||
#include <texteditor/textindenter.h>
|
||||
|
||||
namespace CMakeProjectManager {
|
||||
namespace Internal {
|
||||
|
||||
class CMAKE_EXPORT CMakeIndenter : public TextEditor::Indenter
|
||||
class CMAKE_EXPORT CMakeIndenter : public TextEditor::TextIndenter
|
||||
{
|
||||
public:
|
||||
explicit CMakeIndenter(QTextDocument *doc);
|
||||
bool isElectricCharacter(const QChar &ch) const override;
|
||||
|
||||
int indentFor(const QTextBlock &block, const TextEditor::TabSettings &tabSettings) override;
|
||||
int indentFor(const QTextBlock &block,
|
||||
const TextEditor::TabSettings &tabSettings) override;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
|
@@ -42,7 +42,8 @@ CppEditor::CppEditor()
|
||||
void CppEditor::decorateEditor(TextEditor::TextEditorWidget *editor)
|
||||
{
|
||||
editor->textDocument()->setSyntaxHighlighter(new CppHighlighter);
|
||||
editor->textDocument()->setIndenter(new CppTools::CppQtStyleIndenter);
|
||||
editor->textDocument()->setIndenter(
|
||||
new CppTools::CppQtStyleIndenter(editor->textDocument()->document()));
|
||||
editor->setAutoCompleter(new CppAutoCompleter);
|
||||
}
|
||||
|
||||
|
@@ -111,7 +111,7 @@ CppEditorDocument::CppEditorDocument()
|
||||
|
||||
ICodeStylePreferencesFactory *factory
|
||||
= TextEditorSettings::codeStyleFactory(CppTools::Constants::CPP_SETTINGS_ID);
|
||||
setIndenter(factory->createIndenter());
|
||||
setIndenter(factory->createIndenter(document()));
|
||||
|
||||
connect(this, &TextEditor::TextDocument::tabSettingsChanged,
|
||||
this, &CppEditorDocument::invalidateFormatterCache);
|
||||
@@ -243,6 +243,7 @@ void CppEditorDocument::onFilePathChanged(const Utils::FileName &oldPath,
|
||||
Q_UNUSED(oldPath);
|
||||
|
||||
if (!newPath.isEmpty()) {
|
||||
indenter()->setFileName(newPath);
|
||||
setMimeType(Utils::mimeTypeForFile(newPath.toFileInfo()).name());
|
||||
|
||||
connect(this, &Core::IDocument::contentsChanged,
|
||||
@@ -443,9 +444,7 @@ CppTools::BaseEditorDocumentProcessor *CppEditorDocument::processor()
|
||||
|
||||
TextEditor::TabSettings CppEditorDocument::tabSettings() const
|
||||
{
|
||||
return indenter()->hasTabSettings()
|
||||
? indenter()->tabSettings()
|
||||
: TextEditor::TextDocument::tabSettings();
|
||||
return indenter()->tabSettings().value_or(TextEditor::TextDocument::tabSettings());
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
|
@@ -111,9 +111,9 @@ QWidget *CppCodeStylePreferencesFactory::createEditor(TextEditor::ICodeStylePref
|
||||
return widget;
|
||||
}
|
||||
|
||||
TextEditor::Indenter *CppCodeStylePreferencesFactory::createIndenter() const
|
||||
TextEditor::Indenter *CppCodeStylePreferencesFactory::createIndenter(QTextDocument *doc) const
|
||||
{
|
||||
return new CppQtStyleIndenter();
|
||||
return new CppQtStyleIndenter(doc);
|
||||
}
|
||||
|
||||
QString CppCodeStylePreferencesFactory::snippetProviderGroupId() const
|
||||
|
@@ -41,7 +41,7 @@ public:
|
||||
TextEditor::ICodeStylePreferences *createCodeStyle() const override;
|
||||
QWidget *createEditor(TextEditor::ICodeStylePreferences *settings,
|
||||
QWidget *parent) const override;
|
||||
TextEditor::Indenter *createIndenter() const override;
|
||||
TextEditor::Indenter *createIndenter(QTextDocument *doc) const override;
|
||||
QString snippetProviderGroupId() const override;
|
||||
QString previewText() const override;
|
||||
};
|
||||
|
@@ -481,7 +481,7 @@ void CppCodeStylePreferencesWidget::updatePreview()
|
||||
QTextCursor tc = preview->textCursor();
|
||||
tc.beginEditBlock();
|
||||
while (block.isValid()) {
|
||||
preview->textDocument()->indenter()->indentBlock(doc, block, QChar::Null, ts);
|
||||
preview->textDocument()->indenter()->indentBlock(block, QChar::Null, ts);
|
||||
|
||||
block = block.next();
|
||||
}
|
||||
|
@@ -61,7 +61,6 @@
|
||||
#include <projectexplorer/projectexplorer.h>
|
||||
#include <projectexplorer/projectmacro.h>
|
||||
#include <projectexplorer/session.h>
|
||||
#include <texteditor/indenter.h>
|
||||
#include <utils/fileutils.h>
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
|
@@ -49,7 +49,6 @@ namespace CPlusPlus { class LookupContext; }
|
||||
namespace ProjectExplorer { class Project; }
|
||||
namespace TextEditor {
|
||||
class BaseHoverHandler;
|
||||
class Indenter;
|
||||
class TextDocument;
|
||||
} // namespace TextEditor
|
||||
|
||||
|
@@ -36,7 +36,8 @@
|
||||
|
||||
using namespace CppTools;
|
||||
|
||||
CppQtStyleIndenter::CppQtStyleIndenter()
|
||||
CppQtStyleIndenter::CppQtStyleIndenter(QTextDocument *doc)
|
||||
: TextEditor::TextIndenter(doc)
|
||||
{
|
||||
// Just for safety. setCodeStylePreferences should be called when the editor the
|
||||
// indenter belongs to gets initialized.
|
||||
@@ -67,13 +68,10 @@ static bool isElectricInLine(const QChar ch, const QString &text)
|
||||
return text.contains(QLatin1String("break"));
|
||||
case ':':
|
||||
// switch cases and access declarations should be reindented
|
||||
if (text.contains(QLatin1String("case"))
|
||||
|| text.contains(QLatin1String("default"))
|
||||
|| text.contains(QLatin1String("public"))
|
||||
|| text.contains(QLatin1String("private"))
|
||||
|| text.contains(QLatin1String("protected"))
|
||||
|| text.contains(QLatin1String("signals"))
|
||||
|| text.contains(QLatin1String("Q_SIGNALS"))) {
|
||||
if (text.contains(QLatin1String("case")) || text.contains(QLatin1String("default"))
|
||||
|| text.contains(QLatin1String("public")) || text.contains(QLatin1String("private"))
|
||||
|| text.contains(QLatin1String("protected")) || text.contains(QLatin1String("signals"))
|
||||
|| text.contains(QLatin1String("Q_SIGNALS"))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -93,13 +91,10 @@ static bool isElectricInLine(const QChar ch, const QString &text)
|
||||
return true;
|
||||
}
|
||||
|
||||
void CppQtStyleIndenter::indentBlock(QTextDocument *doc,
|
||||
const QTextBlock &block,
|
||||
void CppQtStyleIndenter::indentBlock(const QTextBlock &block,
|
||||
const QChar &typedChar,
|
||||
const TextEditor::TabSettings &tabSettings)
|
||||
{
|
||||
Q_UNUSED(doc)
|
||||
|
||||
QtStyleCodeFormatter codeFormatter(tabSettings, codeStyleSettings());
|
||||
|
||||
codeFormatter.updateStateUntil(block);
|
||||
@@ -124,15 +119,13 @@ void CppQtStyleIndenter::indentBlock(QTextDocument *doc,
|
||||
tabSettings.indentLine(block, indent + padding, padding);
|
||||
}
|
||||
|
||||
void CppQtStyleIndenter::indent(QTextDocument *doc,
|
||||
const QTextCursor &cursor,
|
||||
void CppQtStyleIndenter::indent(const QTextCursor &cursor,
|
||||
const QChar &typedChar,
|
||||
const TextEditor::TabSettings &tabSettings,
|
||||
bool /*autoTriggered*/)
|
||||
const TextEditor::TabSettings &tabSettings)
|
||||
{
|
||||
if (cursor.hasSelection()) {
|
||||
QTextBlock block = doc->findBlock(cursor.selectionStart());
|
||||
const QTextBlock end = doc->findBlock(cursor.selectionEnd()).next();
|
||||
QTextBlock block = m_doc->findBlock(cursor.selectionStart());
|
||||
const QTextBlock end = m_doc->findBlock(cursor.selectionEnd()).next();
|
||||
|
||||
QtStyleCodeFormatter codeFormatter(tabSettings, codeStyleSettings());
|
||||
codeFormatter.updateStateUntil(block);
|
||||
@@ -149,7 +142,7 @@ void CppQtStyleIndenter::indent(QTextDocument *doc,
|
||||
} while (block.isValid() && block != end);
|
||||
tc.endEditBlock();
|
||||
} else {
|
||||
indentBlock(doc, cursor.block(), typedChar, tabSettings);
|
||||
indentBlock(cursor.block(), typedChar, tabSettings);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,13 +153,14 @@ void CppQtStyleIndenter::setCodeStylePreferences(TextEditor::ICodeStylePreferenc
|
||||
m_cppCodeStylePreferences = cppCodeStylePreferences;
|
||||
}
|
||||
|
||||
void CppQtStyleIndenter::invalidateCache(QTextDocument *doc)
|
||||
void CppQtStyleIndenter::invalidateCache()
|
||||
{
|
||||
QtStyleCodeFormatter formatter;
|
||||
formatter.invalidateCache(doc);
|
||||
formatter.invalidateCache(m_doc);
|
||||
}
|
||||
|
||||
int CppQtStyleIndenter::indentFor(const QTextBlock &block, const TextEditor::TabSettings &tabSettings)
|
||||
int CppQtStyleIndenter::indentFor(const QTextBlock &block,
|
||||
const TextEditor::TabSettings &tabSettings)
|
||||
{
|
||||
QtStyleCodeFormatter codeFormatter(tabSettings, codeStyleSettings());
|
||||
|
||||
@@ -185,9 +179,8 @@ CppCodeStyleSettings CppQtStyleIndenter::codeStyleSettings() const
|
||||
return CppCodeStyleSettings();
|
||||
}
|
||||
|
||||
TextEditor::IndentationForBlock
|
||||
CppQtStyleIndenter::indentationForBlocks(const QVector<QTextBlock> &blocks,
|
||||
const TextEditor::TabSettings &tabSettings)
|
||||
TextEditor::IndentationForBlock CppQtStyleIndenter::indentationForBlocks(
|
||||
const QVector<QTextBlock> &blocks, const TextEditor::TabSettings &tabSettings)
|
||||
{
|
||||
QtStyleCodeFormatter codeFormatter(tabSettings, codeStyleSettings());
|
||||
|
||||
|
@@ -27,10 +27,9 @@
|
||||
|
||||
#include "cpptools_global.h"
|
||||
|
||||
#include <texteditor/indenter.h>
|
||||
#include <texteditor/textindenter.h>
|
||||
|
||||
namespace TextEditor
|
||||
{
|
||||
namespace TextEditor {
|
||||
class ICodeStylePreferences;
|
||||
}
|
||||
|
||||
@@ -38,32 +37,30 @@ namespace CppTools {
|
||||
class CppCodeStyleSettings;
|
||||
class CppCodeStylePreferences;
|
||||
|
||||
class CPPTOOLS_EXPORT CppQtStyleIndenter : public TextEditor::Indenter
|
||||
class CPPTOOLS_EXPORT CppQtStyleIndenter : public TextEditor::TextIndenter
|
||||
{
|
||||
public:
|
||||
CppQtStyleIndenter();
|
||||
explicit CppQtStyleIndenter(QTextDocument *doc);
|
||||
~CppQtStyleIndenter() override;
|
||||
|
||||
bool isElectricCharacter(const QChar &ch) const override;
|
||||
void indentBlock(QTextDocument *doc,
|
||||
const QTextBlock &block,
|
||||
void indentBlock(const QTextBlock &block,
|
||||
const QChar &typedChar,
|
||||
const TextEditor::TabSettings &tabSettings) override;
|
||||
|
||||
void indent(QTextDocument *doc,
|
||||
const QTextCursor &cursor,
|
||||
void indent(const QTextCursor &cursor,
|
||||
const QChar &typedChar,
|
||||
const TextEditor::TabSettings &tabSettings,
|
||||
bool autoTriggered = true) override;
|
||||
const TextEditor::TabSettings &tabSettings) override;
|
||||
|
||||
void setCodeStylePreferences(TextEditor::ICodeStylePreferences *preferences) override;
|
||||
void invalidateCache(QTextDocument *doc) override;
|
||||
void invalidateCache() override;
|
||||
int indentFor(const QTextBlock &block, const TextEditor::TabSettings &tabSettings) override;
|
||||
TextEditor::IndentationForBlock indentationForBlocks(const QVector<QTextBlock> &blocks,
|
||||
const TextEditor::TabSettings &tabSettings) override;
|
||||
TextEditor::IndentationForBlock indentationForBlocks(
|
||||
const QVector<QTextBlock> &blocks, const TextEditor::TabSettings &tabSettings) override;
|
||||
|
||||
private:
|
||||
CppCodeStyleSettings codeStyleSettings() const;
|
||||
CppCodeStylePreferences *m_cppCodeStylePreferences = nullptr;
|
||||
};
|
||||
|
||||
} // CppTools
|
||||
} // namespace CppTools
|
||||
|
@@ -56,8 +56,8 @@ public:
|
||||
const TextEditor::TabSettings &tabSettings =
|
||||
ProjectExplorer::actualTabSettings(fileName, textDocument);
|
||||
|
||||
CppQtStyleIndenter indenter;
|
||||
indenter.indent(selection.document(), selection, QChar::Null, tabSettings);
|
||||
CppQtStyleIndenter indenter(selection.document());
|
||||
indenter.indent(selection, QChar::Null, tabSettings);
|
||||
}
|
||||
|
||||
void reindentSelection(const QTextCursor &selection,
|
||||
@@ -67,8 +67,9 @@ public:
|
||||
const TextEditor::TabSettings &tabSettings =
|
||||
ProjectExplorer::actualTabSettings(fileName, textDocument);
|
||||
|
||||
CppQtStyleIndenter indenter;
|
||||
indenter.reindent(selection.document(), selection, tabSettings);
|
||||
CppQtStyleIndenter indenter(selection.document());
|
||||
indenter.reindent(selection,
|
||||
tabSettings);
|
||||
}
|
||||
|
||||
void fileChanged(const QString &fileName) override
|
||||
|
@@ -1633,7 +1633,7 @@ void FakeVimPluginPrivate::editorOpened(IEditor *editor)
|
||||
while (!cursor.atBlockEnd())
|
||||
cursor.deleteChar();
|
||||
} else {
|
||||
tew->textDocument()->indenter()->indentBlock(doc, block, typedChar, tabSettings);
|
||||
tew->textDocument()->indenter()->indentBlock(block, typedChar, tabSettings);
|
||||
}
|
||||
block = block.next();
|
||||
}
|
||||
|
@@ -319,7 +319,7 @@ GlslEditorFactory::GlslEditorFactory()
|
||||
|
||||
setDocumentCreator([]() { return new TextDocument(Constants::C_GLSLEDITOR_ID); });
|
||||
setEditorWidgetCreator([]() { return new GlslEditorWidget; });
|
||||
setIndenterCreator([]() { return new GlslIndenter; });
|
||||
setIndenterCreator([](QTextDocument *doc) { return new GlslIndenter(doc); });
|
||||
setSyntaxHighlighterCreator([]() { return new GlslHighlighter; });
|
||||
setCommentDefinition(Utils::CommentDefinition::CppStyle);
|
||||
setCompletionAssistProvider(new GlslCompletionAssistProvider);
|
||||
|
@@ -38,26 +38,25 @@
|
||||
namespace GlslEditor {
|
||||
namespace Internal {
|
||||
|
||||
GlslIndenter::GlslIndenter(QTextDocument *doc)
|
||||
: TextEditor::TextIndenter(doc)
|
||||
{}
|
||||
GlslIndenter::~GlslIndenter() = default;
|
||||
|
||||
bool GlslIndenter::isElectricCharacter(const QChar &ch) const
|
||||
{
|
||||
return ch == QLatin1Char('{')
|
||||
|| ch == QLatin1Char('}')
|
||||
|| ch == QLatin1Char(':')
|
||||
|| ch == QLatin1Char('#');
|
||||
return ch == QLatin1Char('{') || ch == QLatin1Char('}') || ch == QLatin1Char(':')
|
||||
|| ch == QLatin1Char('#');
|
||||
}
|
||||
|
||||
void GlslIndenter::indentBlock(QTextDocument *doc,
|
||||
const QTextBlock &block,
|
||||
void GlslIndenter::indentBlock(const QTextBlock &block,
|
||||
const QChar &typedChar,
|
||||
const TextEditor::TabSettings &tabSettings)
|
||||
{
|
||||
Q_UNUSED(doc)
|
||||
|
||||
// TODO: do something with it
|
||||
CppTools::QtStyleCodeFormatter codeFormatter(tabSettings,
|
||||
CppTools::CppToolsSettings::instance()->cppCodeStyle()->codeStyleSettings());
|
||||
CppTools::QtStyleCodeFormatter
|
||||
codeFormatter(tabSettings,
|
||||
CppTools::CppToolsSettings::instance()->cppCodeStyle()->codeStyleSettings());
|
||||
|
||||
codeFormatter.updateStateUntil(block);
|
||||
int indent;
|
||||
@@ -77,19 +76,19 @@ void GlslIndenter::indentBlock(QTextDocument *doc,
|
||||
tabSettings.indentLine(block, indent + padding, padding);
|
||||
}
|
||||
|
||||
void GlslIndenter::indent(QTextDocument *doc,
|
||||
const QTextCursor &cursor,
|
||||
void GlslIndenter::indent(const QTextCursor &cursor,
|
||||
const QChar &typedChar,
|
||||
const TextEditor::TabSettings &tabSettings,
|
||||
bool /*autoTriggered*/)
|
||||
const TextEditor::TabSettings &tabSettings)
|
||||
{
|
||||
if (cursor.hasSelection()) {
|
||||
QTextBlock block = doc->findBlock(cursor.selectionStart());
|
||||
const QTextBlock end = doc->findBlock(cursor.selectionEnd()).next();
|
||||
QTextBlock block = m_doc->findBlock(cursor.selectionStart());
|
||||
const QTextBlock end = m_doc->findBlock(cursor.selectionEnd()).next();
|
||||
|
||||
// TODO: do something with it
|
||||
CppTools::QtStyleCodeFormatter codeFormatter(tabSettings,
|
||||
CppTools::CppToolsSettings::instance()->cppCodeStyle()->codeStyleSettings());
|
||||
CppTools::CppToolsSettings::instance()
|
||||
->cppCodeStyle()
|
||||
->codeStyleSettings());
|
||||
codeFormatter.updateStateUntil(block);
|
||||
|
||||
QTextCursor tc = cursor;
|
||||
@@ -104,14 +103,15 @@ void GlslIndenter::indent(QTextDocument *doc,
|
||||
} while (block.isValid() && block != end);
|
||||
tc.endEditBlock();
|
||||
} else {
|
||||
indentBlock(doc, cursor.block(), typedChar, tabSettings);
|
||||
indentBlock(cursor.block(), typedChar, tabSettings);
|
||||
}
|
||||
}
|
||||
|
||||
int GlslIndenter::indentFor(const QTextBlock &block, const TextEditor::TabSettings &tabSettings)
|
||||
{
|
||||
CppTools::QtStyleCodeFormatter codeFormatter(tabSettings,
|
||||
CppTools::CppToolsSettings::instance()->cppCodeStyle()->codeStyleSettings());
|
||||
CppTools::QtStyleCodeFormatter
|
||||
codeFormatter(tabSettings,
|
||||
CppTools::CppToolsSettings::instance()->cppCodeStyle()->codeStyleSettings());
|
||||
|
||||
codeFormatter.updateStateUntil(block);
|
||||
int indent;
|
||||
@@ -121,12 +121,12 @@ int GlslIndenter::indentFor(const QTextBlock &block, const TextEditor::TabSettin
|
||||
return indent;
|
||||
}
|
||||
|
||||
TextEditor::IndentationForBlock
|
||||
GlslIndenter::indentationForBlocks(const QVector<QTextBlock> &blocks,
|
||||
const TextEditor::TabSettings &tabSettings)
|
||||
TextEditor::IndentationForBlock GlslIndenter::indentationForBlocks(
|
||||
const QVector<QTextBlock> &blocks, const TextEditor::TabSettings &tabSettings)
|
||||
{
|
||||
CppTools::QtStyleCodeFormatter codeFormatter(tabSettings,
|
||||
CppTools::CppToolsSettings::instance()->cppCodeStyle()->codeStyleSettings());
|
||||
CppTools::QtStyleCodeFormatter
|
||||
codeFormatter(tabSettings,
|
||||
CppTools::CppToolsSettings::instance()->cppCodeStyle()->codeStyleSettings());
|
||||
|
||||
codeFormatter.updateStateUntil(blocks.last());
|
||||
|
||||
|
@@ -25,31 +25,29 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <texteditor/indenter.h>
|
||||
#include <texteditor/textindenter.h>
|
||||
|
||||
namespace GlslEditor {
|
||||
namespace Internal {
|
||||
|
||||
class GlslIndenter : public TextEditor::Indenter
|
||||
class GlslIndenter : public TextEditor::TextIndenter
|
||||
{
|
||||
public:
|
||||
explicit GlslIndenter(QTextDocument *doc);
|
||||
~GlslIndenter() override;
|
||||
|
||||
bool isElectricCharacter(const QChar &ch) const override;
|
||||
void indentBlock(QTextDocument *doc,
|
||||
const QTextBlock &block,
|
||||
void indentBlock(const QTextBlock &block,
|
||||
const QChar &typedChar,
|
||||
const TextEditor::TabSettings &tabSettings) override;
|
||||
|
||||
void indent(QTextDocument *doc,
|
||||
const QTextCursor &cursor,
|
||||
void indent(const QTextCursor &cursor,
|
||||
const QChar &typedChar,
|
||||
const TextEditor::TabSettings &tabSettings,
|
||||
bool autoTriggered = true) override;
|
||||
const TextEditor::TabSettings &tabSettings) override;
|
||||
|
||||
int indentFor(const QTextBlock &block, const TextEditor::TabSettings &tabSettings) override;
|
||||
TextEditor::IndentationForBlock indentationForBlocks(const QVector<QTextBlock> &blocks,
|
||||
const TextEditor::TabSettings &tabSettings) override;
|
||||
TextEditor::IndentationForBlock indentationForBlocks(
|
||||
const QVector<QTextBlock> &blocks, const TextEditor::TabSettings &tabSettings) override;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
|
@@ -60,8 +60,8 @@ NimEditorFactory::NimEditorFactory()
|
||||
setDocumentCreator([]() {
|
||||
return new TextDocument(Constants::C_NIMEDITOR_ID);
|
||||
});
|
||||
setIndenterCreator([]() {
|
||||
return new NimIndenter;
|
||||
setIndenterCreator([](QTextDocument *doc) {
|
||||
return new NimIndenter(doc);
|
||||
});
|
||||
setSyntaxHighlighterCreator([]() {
|
||||
return new NimHighlighter;
|
||||
@@ -80,7 +80,7 @@ Core::IEditor *NimEditorFactory::createEditor()
|
||||
void NimEditorFactory::decorateEditor(TextEditorWidget *editor)
|
||||
{
|
||||
editor->textDocument()->setSyntaxHighlighter(new NimHighlighter());
|
||||
editor->textDocument()->setIndenter(new NimIndenter());
|
||||
editor->textDocument()->setIndenter(new NimIndenter(editor->textDocument()->document()));
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -37,7 +37,8 @@
|
||||
|
||||
namespace Nim {
|
||||
|
||||
NimIndenter::NimIndenter()
|
||||
NimIndenter::NimIndenter(QTextDocument *doc)
|
||||
: TextEditor::TextIndenter(doc)
|
||||
{}
|
||||
|
||||
bool NimIndenter::isElectricCharacter(const QChar &ch) const
|
||||
@@ -45,12 +46,10 @@ bool NimIndenter::isElectricCharacter(const QChar &ch) const
|
||||
return NimIndenter::electricCharacters().contains(ch);
|
||||
}
|
||||
|
||||
void NimIndenter::indentBlock(QTextDocument *document,
|
||||
const QTextBlock &block,
|
||||
void NimIndenter::indentBlock(const QTextBlock &block,
|
||||
const QChar &typedChar,
|
||||
const TextEditor::TabSettings &settings)
|
||||
{
|
||||
Q_UNUSED(document);
|
||||
Q_UNUSED(typedChar);
|
||||
|
||||
const QString currentLine = block.text();
|
||||
|
@@ -25,23 +25,24 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <texteditor/indenter.h>
|
||||
#include <texteditor/textindenter.h>
|
||||
|
||||
namespace TextEditor { class SimpleCodeStylePreferences; }
|
||||
namespace TextEditor {
|
||||
class SimpleCodeStylePreferences;
|
||||
}
|
||||
|
||||
namespace Nim {
|
||||
|
||||
class NimLexer;
|
||||
|
||||
class NimIndenter : public TextEditor::Indenter
|
||||
class NimIndenter : public TextEditor::TextIndenter
|
||||
{
|
||||
public:
|
||||
NimIndenter();
|
||||
explicit NimIndenter(QTextDocument *doc);
|
||||
|
||||
bool isElectricCharacter(const QChar &ch) const override;
|
||||
|
||||
void indentBlock(QTextDocument *document,
|
||||
const QTextBlock &block,
|
||||
void indentBlock(const QTextBlock &block,
|
||||
const QChar &typedChar,
|
||||
const TextEditor::TabSettings &settings) override;
|
||||
|
||||
@@ -51,9 +52,11 @@ private:
|
||||
bool startsBlock(const QString &line, int state) const;
|
||||
bool endsBlock(const QString &line, int state) const;
|
||||
|
||||
int calculateIndentationDiff(const QString &previousLine, int previousState, int indentSize) const;
|
||||
int calculateIndentationDiff(const QString &previousLine,
|
||||
int previousState,
|
||||
int indentSize) const;
|
||||
|
||||
static QString rightTrimmed(const QString& other);
|
||||
static QString rightTrimmed(const QString &other);
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace Nim
|
||||
|
@@ -67,9 +67,9 @@ QWidget *NimCodeStylePreferencesFactory::createEditor(TextEditor::ICodeStylePref
|
||||
return result;
|
||||
}
|
||||
|
||||
TextEditor::Indenter *NimCodeStylePreferencesFactory::createIndenter() const
|
||||
TextEditor::Indenter *NimCodeStylePreferencesFactory::createIndenter(QTextDocument *doc) const
|
||||
{
|
||||
return new NimIndenter();
|
||||
return new NimIndenter(doc);
|
||||
}
|
||||
|
||||
QString NimCodeStylePreferencesFactory::snippetProviderGroupId() const
|
||||
|
@@ -41,7 +41,7 @@ public:
|
||||
TextEditor::ICodeStylePreferences *createCodeStyle() const override;
|
||||
QWidget *createEditor(TextEditor::ICodeStylePreferences *settings,
|
||||
QWidget *parent) const override;
|
||||
TextEditor::Indenter *createIndenter() const override;
|
||||
TextEditor::Indenter *createIndenter(QTextDocument *doc) const override;
|
||||
QString snippetProviderGroupId() const override;
|
||||
QString previewText() const override;
|
||||
};
|
||||
|
@@ -96,8 +96,7 @@ void NimCodeStylePreferencesWidget::updatePreview()
|
||||
QTextCursor tc = m_ui->previewTextEdit->textCursor();
|
||||
tc.beginEditBlock();
|
||||
while (block.isValid()) {
|
||||
m_ui->previewTextEdit->textDocument()->indenter()
|
||||
->indentBlock(doc, block, QChar::Null, ts);
|
||||
m_ui->previewTextEdit->textDocument()->indenter()->indentBlock(block, QChar::Null, ts);
|
||||
block = block.next();
|
||||
}
|
||||
tc.endEditBlock();
|
||||
|
@@ -36,7 +36,6 @@
|
||||
#include <coreplugin/dialogs/promptoverwritedialog.h>
|
||||
#include <texteditor/icodestylepreferences.h>
|
||||
#include <texteditor/icodestylepreferencesfactory.h>
|
||||
#include <texteditor/indenter.h>
|
||||
#include <texteditor/normalindenter.h>
|
||||
#include <texteditor/storagesettings.h>
|
||||
#include <texteditor/tabsettings.h>
|
||||
@@ -94,18 +93,22 @@ bool JsonWizardGenerator::formatFile(const JsonWizard *wizard, GeneratedFile *fi
|
||||
auto baseProject = qobject_cast<Project *>(wizard->property("SelectedProject").value<QObject *>());
|
||||
ICodeStylePreferencesFactory *factory = TextEditorSettings::codeStyleFactory(languageId);
|
||||
|
||||
Indenter *indenter = nullptr;
|
||||
if (factory)
|
||||
indenter = factory->createIndenter();
|
||||
if (!indenter)
|
||||
indenter = new NormalIndenter();
|
||||
|
||||
ICodeStylePreferences *codeStylePrefs = codeStylePreferences(baseProject, languageId);
|
||||
indenter->setCodeStylePreferences(codeStylePrefs);
|
||||
QTextDocument doc(file->contents());
|
||||
QTextCursor cursor(&doc);
|
||||
Indenter *indenter = nullptr;
|
||||
if (factory) {
|
||||
indenter = factory->createIndenter(&doc);
|
||||
indenter->setFileName(Utils::FileName::fromString(file->path()));
|
||||
}
|
||||
if (!indenter)
|
||||
indenter = new NormalIndenter(&doc);
|
||||
ICodeStylePreferences *codeStylePrefs = codeStylePreferences(baseProject, languageId);
|
||||
indenter->setCodeStylePreferences(codeStylePrefs);
|
||||
|
||||
cursor.select(QTextCursor::Document);
|
||||
indenter->indent(&doc, cursor, QChar::Null, codeStylePrefs->currentTabSettings());
|
||||
indenter->indent(cursor,
|
||||
QChar::Null,
|
||||
codeStylePrefs->currentTabSettings());
|
||||
delete indenter;
|
||||
if (TextEditorSettings::storageSettings().m_cleanWhitespace) {
|
||||
QTextBlock block = doc.firstBlock();
|
||||
|
@@ -252,18 +252,23 @@ void ProjectFileWizardExtension::applyCodeStyle(GeneratedFile *file) const
|
||||
|
||||
ICodeStylePreferencesFactory *factory = TextEditorSettings::codeStyleFactory(languageId);
|
||||
|
||||
QTextDocument doc(file->contents());
|
||||
Indenter *indenter = nullptr;
|
||||
if (factory)
|
||||
indenter = factory->createIndenter();
|
||||
if (factory) {
|
||||
indenter = factory->createIndenter(&doc);
|
||||
indenter->setFileName(Utils::FileName::fromString(file->path()));
|
||||
}
|
||||
if (!indenter)
|
||||
indenter = new NormalIndenter();
|
||||
indenter = new NormalIndenter(&doc);
|
||||
|
||||
ICodeStylePreferences *codeStylePrefs = codeStylePreferences(baseProject, languageId);
|
||||
indenter->setCodeStylePreferences(codeStylePrefs);
|
||||
QTextDocument doc(file->contents());
|
||||
|
||||
QTextCursor cursor(&doc);
|
||||
cursor.select(QTextCursor::Document);
|
||||
indenter->indent(&doc, cursor, QChar::Null, codeStylePrefs->currentTabSettings());
|
||||
indenter->indent(cursor,
|
||||
QChar::Null,
|
||||
codeStylePrefs->currentTabSettings());
|
||||
delete indenter;
|
||||
if (TextEditorSettings::storageSettings().m_cleanWhitespace) {
|
||||
QTextBlock block = doc.firstBlock();
|
||||
|
@@ -54,7 +54,7 @@ PythonEditorFactory::PythonEditorFactory()
|
||||
| TextEditorActionHandler::FollowSymbolUnderCursor);
|
||||
|
||||
setDocumentCreator([] { return new TextDocument(Constants::C_PYTHONEDITOR_ID); });
|
||||
setIndenterCreator([] { return new PythonIndenter; });
|
||||
setIndenterCreator([](QTextDocument *doc) { return new PythonIndenter(doc); });
|
||||
setSyntaxHighlighterCreator([] { return new PythonHighlighter; });
|
||||
setCommentDefinition(Utils::CommentDefinition::HashStyle);
|
||||
setParenthesesMatchingEnabled(true);
|
||||
|
@@ -50,6 +50,10 @@ static QTextBlock previousNonEmptyBlock(const QTextBlock &block)
|
||||
return result;
|
||||
}
|
||||
|
||||
PythonIndenter::PythonIndenter(QTextDocument *doc)
|
||||
: TextEditor::TextIndenter(doc)
|
||||
{}
|
||||
|
||||
/**
|
||||
* @brief Does given character change indentation level?
|
||||
* @param ch Any value
|
||||
|
@@ -25,12 +25,14 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <texteditor/indenter.h>
|
||||
#include <texteditor/textindenter.h>
|
||||
|
||||
namespace PythonEditor {
|
||||
|
||||
class PythonIndenter : public TextEditor::Indenter
|
||||
class PythonIndenter : public TextEditor::TextIndenter
|
||||
{
|
||||
public:
|
||||
explicit PythonIndenter(QTextDocument *doc);
|
||||
private:
|
||||
bool isElectricCharacter(const QChar &ch) const override;
|
||||
int indentFor(const QTextBlock &block, const TextEditor::TabSettings &tabSettings) override;
|
||||
|
@@ -61,8 +61,8 @@ void BaseTextEditModifier::indentLines(int startLine, int endLine)
|
||||
QTextBlock start = textDocument->findBlockByNumber(i);
|
||||
|
||||
if (start.isValid()) {
|
||||
QmlJSEditor::Internal::Indenter indenter;
|
||||
indenter.indentBlock(textDocument, start, QChar::Null, tabSettings);
|
||||
QmlJSEditor::Internal::Indenter indenter(textDocument);
|
||||
indenter.indentBlock(start, QChar::Null, tabSettings);
|
||||
}
|
||||
}
|
||||
tc.endEditBlock();
|
||||
|
@@ -1071,7 +1071,7 @@ QmlJSEditorFactory::QmlJSEditorFactory()
|
||||
void QmlJSEditorFactory::decorateEditor(TextEditorWidget *editor)
|
||||
{
|
||||
editor->textDocument()->setSyntaxHighlighter(new QmlJSHighlighter);
|
||||
editor->textDocument()->setIndenter(new Indenter);
|
||||
editor->textDocument()->setIndenter(new Indenter(editor->textDocument()->document()));
|
||||
editor->setAutoCompleter(new AutoCompleter);
|
||||
}
|
||||
|
||||
|
@@ -653,7 +653,7 @@ QmlJSEditorDocument::QmlJSEditorDocument()
|
||||
connect(this, &TextEditor::TextDocument::tabSettingsChanged,
|
||||
d, &Internal::QmlJSEditorDocumentPrivate::invalidateFormatterCache);
|
||||
setSyntaxHighlighter(new QmlJSHighlighter(document()));
|
||||
setIndenter(new Internal::Indenter);
|
||||
setIndenter(new Internal::Indenter(document()));
|
||||
}
|
||||
|
||||
QmlJSEditorDocument::~QmlJSEditorDocument()
|
||||
|
@@ -421,8 +421,8 @@ void QuickToolBar::indentLines(int startLine, int endLine)
|
||||
QTextBlock start = m_editorWidget->document()->findBlockByNumber(i);
|
||||
|
||||
if (start.isValid()) {
|
||||
QmlJSEditor::Internal::Indenter indenterMy;
|
||||
indenterMy.indentBlock(m_editorWidget->document(), start, QChar::Null, tabSettings);
|
||||
QmlJSEditor::Internal::Indenter indenterMy(m_editorWidget->document());
|
||||
indenterMy.indentBlock(start, QChar::Null, tabSettings);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -80,9 +80,9 @@ QWidget *QmlJSCodeStylePreferencesFactory::createEditor(TextEditor::ICodeStylePr
|
||||
return widget;
|
||||
}
|
||||
|
||||
TextEditor::Indenter *QmlJSCodeStylePreferencesFactory::createIndenter() const
|
||||
TextEditor::Indenter *QmlJSCodeStylePreferencesFactory::createIndenter(QTextDocument *doc) const
|
||||
{
|
||||
return new QmlJSEditor::Internal::Indenter();
|
||||
return new QmlJSEditor::Internal::Indenter(doc);
|
||||
}
|
||||
|
||||
QString QmlJSCodeStylePreferencesFactory::snippetProviderGroupId() const
|
||||
|
@@ -39,7 +39,7 @@ public:
|
||||
TextEditor::ICodeStylePreferences *createCodeStyle() const override;
|
||||
QWidget *createEditor(TextEditor::ICodeStylePreferences *settings,
|
||||
QWidget *parent) const override;
|
||||
TextEditor::Indenter *createIndenter() const override;
|
||||
TextEditor::Indenter *createIndenter(QTextDocument *doc) const override;
|
||||
QString snippetProviderGroupId() const override;
|
||||
QString previewText() const override;
|
||||
};
|
||||
|
@@ -113,8 +113,7 @@ void QmlJSCodeStylePreferencesWidget::updatePreview()
|
||||
QTextCursor tc = m_ui->previewTextEdit->textCursor();
|
||||
tc.beginEditBlock();
|
||||
while (block.isValid()) {
|
||||
m_ui->previewTextEdit->textDocument()->indenter()
|
||||
->indentBlock(doc, block, QChar::Null, ts);
|
||||
m_ui->previewTextEdit->textDocument()->indenter()->indentBlock(block, QChar::Null, ts);
|
||||
block = block.next();
|
||||
}
|
||||
tc.endEditBlock();
|
||||
|
@@ -35,7 +35,9 @@
|
||||
using namespace QmlJSEditor;
|
||||
using namespace Internal;
|
||||
|
||||
Indenter::Indenter() = default;
|
||||
Indenter::Indenter(QTextDocument *doc)
|
||||
: TextEditor::TextIndenter(doc)
|
||||
{}
|
||||
|
||||
Indenter::~Indenter() = default;
|
||||
|
||||
@@ -49,13 +51,10 @@ bool Indenter::isElectricCharacter(const QChar &ch) const
|
||||
return false;
|
||||
}
|
||||
|
||||
void Indenter::indentBlock(QTextDocument *doc,
|
||||
const QTextBlock &block,
|
||||
void Indenter::indentBlock(const QTextBlock &block,
|
||||
const QChar &typedChar,
|
||||
const TextEditor::TabSettings &tabSettings)
|
||||
{
|
||||
Q_UNUSED(doc)
|
||||
|
||||
const int depth = indentFor(block, tabSettings);
|
||||
if (depth == -1)
|
||||
return;
|
||||
@@ -74,29 +73,24 @@ void Indenter::indentBlock(QTextDocument *doc,
|
||||
tabSettings.indentLine(block, depth);
|
||||
}
|
||||
|
||||
void Indenter::invalidateCache(QTextDocument *doc)
|
||||
void Indenter::invalidateCache()
|
||||
{
|
||||
QmlJSTools::CreatorCodeFormatter codeFormatter;
|
||||
codeFormatter.invalidateCache(doc);
|
||||
codeFormatter.invalidateCache(m_doc);
|
||||
}
|
||||
|
||||
|
||||
int Indenter::indentFor(const QTextBlock &block,
|
||||
const TextEditor::TabSettings &tabSettings)
|
||||
int Indenter::indentFor(const QTextBlock &block, const TextEditor::TabSettings &tabSettings)
|
||||
{
|
||||
QmlJSTools::CreatorCodeFormatter codeFormatter(tabSettings);
|
||||
codeFormatter.updateStateUntil(block);
|
||||
return codeFormatter.indentFor(block);
|
||||
}
|
||||
|
||||
|
||||
TextEditor::IndentationForBlock
|
||||
Indenter::indentationForBlocks(const QVector<QTextBlock> &blocks,
|
||||
const TextEditor::TabSettings &tabSettings)
|
||||
TextEditor::IndentationForBlock Indenter::indentationForBlocks(
|
||||
const QVector<QTextBlock> &blocks, const TextEditor::TabSettings &tabSettings)
|
||||
{
|
||||
QmlJSTools::CreatorCodeFormatter codeFormatter(tabSettings);
|
||||
|
||||
|
||||
codeFormatter.updateStateUntil(blocks.last());
|
||||
|
||||
TextEditor::IndentationForBlock ret;
|
||||
|
@@ -27,27 +27,26 @@
|
||||
|
||||
#include "qmljstools_global.h"
|
||||
|
||||
#include <texteditor/indenter.h>
|
||||
#include <texteditor/textindenter.h>
|
||||
|
||||
namespace QmlJSEditor {
|
||||
namespace Internal {
|
||||
|
||||
class QMLJSTOOLS_EXPORT Indenter : public TextEditor::Indenter
|
||||
class QMLJSTOOLS_EXPORT Indenter : public TextEditor::TextIndenter
|
||||
{
|
||||
public:
|
||||
Indenter();
|
||||
explicit Indenter(QTextDocument *doc);
|
||||
~Indenter() override;
|
||||
|
||||
bool isElectricCharacter(const QChar &ch) const override;
|
||||
void indentBlock(QTextDocument *doc,
|
||||
const QTextBlock &block,
|
||||
void indentBlock(const QTextBlock &block,
|
||||
const QChar &typedChar,
|
||||
const TextEditor::TabSettings &tabSettings) override;
|
||||
void invalidateCache(QTextDocument *doc) override;
|
||||
void invalidateCache() override;
|
||||
|
||||
int indentFor(const QTextBlock &block, const TextEditor::TabSettings &tabSettings) override;
|
||||
TextEditor::IndentationForBlock indentationForBlocks(const QVector<QTextBlock> &blocks,
|
||||
const TextEditor::TabSettings &tabSettings) override;
|
||||
TextEditor::IndentationForBlock indentationForBlocks(
|
||||
const QVector<QTextBlock> &blocks, const TextEditor::TabSettings &tabSettings) override;
|
||||
};
|
||||
|
||||
} // Internal
|
||||
|
@@ -77,8 +77,8 @@ public:
|
||||
const TextEditor::TabSettings &tabSettings =
|
||||
ProjectExplorer::actualTabSettings(fileName, textDocument);
|
||||
|
||||
QmlJSEditor::Internal::Indenter indenter;
|
||||
indenter.reindent(selection.document(), selection, tabSettings);
|
||||
QmlJSEditor::Internal::Indenter indenter(selection.document());
|
||||
indenter.reindent(selection, tabSettings);
|
||||
}
|
||||
|
||||
void fileChanged(const QString &fileName) override
|
||||
|
@@ -83,14 +83,15 @@ void CodeStyleEditor::updatePreview()
|
||||
{
|
||||
QTextDocument *doc = m_preview->document();
|
||||
|
||||
m_preview->textDocument()->indenter()->invalidateCache(doc);
|
||||
m_preview->textDocument()->indenter()->invalidateCache();
|
||||
|
||||
QTextBlock block = doc->firstBlock();
|
||||
QTextCursor tc = m_preview->textCursor();
|
||||
tc.beginEditBlock();
|
||||
while (block.isValid()) {
|
||||
m_preview->textDocument()->indenter()
|
||||
->indentBlock(doc, block, QChar::Null, m_codeStyle->currentTabSettings());
|
||||
m_preview->textDocument()->indenter()->indentBlock(block,
|
||||
QChar::Null,
|
||||
m_codeStyle->currentTabSettings());
|
||||
block = block.next();
|
||||
}
|
||||
tc.endEditBlock();
|
||||
|
@@ -27,13 +27,14 @@
|
||||
|
||||
#include "texteditor_global.h"
|
||||
|
||||
#include "indenter.h"
|
||||
|
||||
#include <QObject>
|
||||
|
||||
namespace Core { class Id; }
|
||||
namespace TextEditor {
|
||||
|
||||
class ICodeStylePreferences;
|
||||
class Indenter;
|
||||
class SnippetProvider;
|
||||
|
||||
class TEXTEDITOR_EXPORT ICodeStylePreferencesFactory : public QObject
|
||||
@@ -48,7 +49,7 @@ public:
|
||||
virtual QString displayName() = 0;
|
||||
virtual ICodeStylePreferences *createCodeStyle() const = 0;
|
||||
virtual QWidget *createEditor(ICodeStylePreferences *preferences, QWidget *parent) const = 0;
|
||||
virtual TextEditor::Indenter *createIndenter() const = 0;
|
||||
virtual TextEditor::Indenter *createIndenter(QTextDocument *doc) const = 0;
|
||||
virtual QString snippetProviderGroupId() const = 0;
|
||||
virtual QString previewText() const = 0;
|
||||
};
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Copyright (C) 2019 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
@@ -25,21 +25,16 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "texteditor_global.h"
|
||||
|
||||
#include "tabsettings.h"
|
||||
#include <utils/fileutils.h>
|
||||
#include <utils/optional.h>
|
||||
|
||||
#include <QMap>
|
||||
#include <QTextBlock>
|
||||
#include <vector>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QTextDocument;
|
||||
class QTextCursor;
|
||||
class QTextBlock;
|
||||
class QChar;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace Utils { class FileName; }
|
||||
namespace Utils {
|
||||
class FileName;
|
||||
}
|
||||
|
||||
namespace TextEditor {
|
||||
|
||||
@@ -48,7 +43,7 @@ class TabSettings;
|
||||
|
||||
using IndentationForBlock = QMap<int, int>;
|
||||
|
||||
class TEXTEDITOR_EXPORT Replacement
|
||||
class Replacement
|
||||
{
|
||||
public:
|
||||
Replacement(int offset, int length, const QString &text)
|
||||
@@ -63,50 +58,57 @@ public:
|
||||
|
||||
using Replacements = std::vector<Replacement>;
|
||||
|
||||
class TEXTEDITOR_EXPORT Indenter
|
||||
class Indenter
|
||||
{
|
||||
public:
|
||||
Indenter();
|
||||
virtual ~Indenter();
|
||||
explicit Indenter(QTextDocument *doc)
|
||||
: m_doc(doc)
|
||||
{}
|
||||
|
||||
void setFileName(const Utils::FileName &fileName) { m_fileName = fileName; }
|
||||
|
||||
virtual ~Indenter() = default;
|
||||
|
||||
// Returns true if key triggers an indent.
|
||||
virtual bool isElectricCharacter(const QChar &ch) const;
|
||||
virtual bool isElectricCharacter(const QChar & /*ch*/) const { return false; }
|
||||
|
||||
// Indent a text block based on previous line. Default does nothing
|
||||
virtual void indentBlock(QTextDocument *doc,
|
||||
const QTextBlock &block,
|
||||
const QChar &typedChar,
|
||||
const TabSettings &tabSettings);
|
||||
virtual void setCodeStylePreferences(ICodeStylePreferences * /*preferences*/) {}
|
||||
|
||||
// Indent at cursor. Calls indentBlock for selection or current line.
|
||||
virtual void indent(QTextDocument *doc,
|
||||
const QTextCursor &cursor,
|
||||
const QChar &typedChar,
|
||||
const TabSettings &tabSettings,
|
||||
bool autoTriggered = true);
|
||||
virtual void invalidateCache() {}
|
||||
|
||||
// By default just calls indent with default settings.
|
||||
virtual Replacements format(QTextDocument *doc,
|
||||
const Utils::FileName &fileName,
|
||||
const QTextCursor &cursor,
|
||||
const TabSettings &tabSettings);
|
||||
|
||||
// Reindent at cursor. Selection will be adjusted according to the indentation
|
||||
// change of the first block.
|
||||
virtual void reindent(QTextDocument *doc, const QTextCursor &cursor, const TabSettings &tabSettings);
|
||||
|
||||
virtual void setCodeStylePreferences(ICodeStylePreferences *preferences);
|
||||
|
||||
virtual void invalidateCache(QTextDocument *doc);
|
||||
|
||||
virtual int indentFor(const QTextBlock &block, const TextEditor::TabSettings &tabSettings);
|
||||
virtual int indentFor(const QTextBlock & /*block*/, const TabSettings & /*tabSettings*/)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Expects a list of blocks in order of occurrence in the document.
|
||||
virtual IndentationForBlock indentationForBlocks(const QVector<QTextBlock> &blocks,
|
||||
const TextEditor::TabSettings &tabSettings);
|
||||
const TabSettings & /*tabSettings*/)
|
||||
= 0;
|
||||
virtual Utils::optional<TabSettings> tabSettings() const = 0;
|
||||
|
||||
virtual bool hasTabSettings() const { return false; }
|
||||
virtual TabSettings tabSettings() const { return TabSettings(); }
|
||||
// Indent a text block based on previous line. Default does nothing
|
||||
virtual void indentBlock(const QTextBlock &block,
|
||||
const QChar &typedChar,
|
||||
const TabSettings &tabSettings)
|
||||
= 0;
|
||||
|
||||
// Indent at cursor. Calls indentBlock for selection or current line.
|
||||
virtual void indent(const QTextCursor &cursor,
|
||||
const QChar &typedChar,
|
||||
const TabSettings &tabSettings)
|
||||
= 0;
|
||||
|
||||
// By default just calls indent with default settings.
|
||||
virtual Replacements format(const QTextCursor &cursor, const TabSettings &tabSettings) = 0;
|
||||
|
||||
// Reindent at cursor. Selection will be adjusted according to the indentation
|
||||
// change of the first block.
|
||||
virtual void reindent(const QTextCursor &cursor, const TabSettings &tabSettings) = 0;
|
||||
|
||||
protected:
|
||||
QTextDocument *m_doc;
|
||||
Utils::FileName m_fileName;
|
||||
};
|
||||
|
||||
} // namespace TextEditor
|
||||
|
@@ -50,6 +50,10 @@
|
||||
|
||||
using namespace TextEditor;
|
||||
|
||||
NormalIndenter::NormalIndenter(QTextDocument *doc)
|
||||
: TextIndenter(doc)
|
||||
{}
|
||||
|
||||
int NormalIndenter::indentFor(const QTextBlock &block, const TabSettings &tabSettings)
|
||||
{
|
||||
Q_UNUSED(tabSettings);
|
||||
|
@@ -25,14 +25,14 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "indenter.h"
|
||||
#include "textindenter.h"
|
||||
|
||||
namespace TextEditor {
|
||||
|
||||
class TEXTEDITOR_EXPORT NormalIndenter : public Indenter
|
||||
class TEXTEDITOR_EXPORT NormalIndenter : public TextIndenter
|
||||
{
|
||||
public:
|
||||
NormalIndenter() = default;
|
||||
explicit NormalIndenter(QTextDocument *doc);
|
||||
~NormalIndenter() override = default;
|
||||
|
||||
int indentFor(const QTextBlock &block, const TabSettings &tabSettings) override;
|
||||
|
@@ -65,7 +65,7 @@ PlainTextEditorFactory::PlainTextEditorFactory()
|
||||
|
||||
setDocumentCreator([]() { return new TextDocument(Core::Constants::K_DEFAULT_TEXT_EDITOR_ID); });
|
||||
setEditorWidgetCreator([]() { return new PlainTextEditorWidget; });
|
||||
setIndenterCreator([]() { return new NormalIndenter; });
|
||||
setIndenterCreator([](QTextDocument *doc) { return new NormalIndenter(doc); });
|
||||
setUseGenericHighlighter(true);
|
||||
|
||||
setEditorActionHandlers(TextEditorActionHandler::Format |
|
||||
|
@@ -27,7 +27,7 @@
|
||||
|
||||
#include "extraencodingsettings.h"
|
||||
#include "fontsettings.h"
|
||||
#include "indenter.h"
|
||||
#include "textindenter.h"
|
||||
#include "storagesettings.h"
|
||||
#include "syntaxhighlighter.h"
|
||||
#include "tabsettings.h"
|
||||
@@ -78,7 +78,7 @@ class TextDocumentPrivate
|
||||
{
|
||||
public:
|
||||
TextDocumentPrivate()
|
||||
: m_indenter(new Indenter)
|
||||
: m_indenter(new TextIndenter(&m_document))
|
||||
{
|
||||
}
|
||||
|
||||
@@ -416,19 +416,19 @@ void TextDocument::setExtraEncodingSettings(const ExtraEncodingSettings &extraEn
|
||||
d->m_extraEncodingSettings = extraEncodingSettings;
|
||||
}
|
||||
|
||||
void TextDocument::autoIndent(const QTextCursor &cursor, QChar typedChar, bool autoTriggered)
|
||||
void TextDocument::autoIndent(const QTextCursor &cursor, QChar typedChar)
|
||||
{
|
||||
d->m_indenter->indent(&d->m_document, cursor, typedChar, tabSettings(), autoTriggered);
|
||||
d->m_indenter->indent(cursor, typedChar, tabSettings());
|
||||
}
|
||||
|
||||
void TextDocument::autoReindent(const QTextCursor &cursor)
|
||||
{
|
||||
d->m_indenter->reindent(&d->m_document, cursor, tabSettings());
|
||||
d->m_indenter->reindent(cursor, tabSettings());
|
||||
}
|
||||
|
||||
void TextDocument::autoFormat(const QTextCursor &cursor)
|
||||
{
|
||||
d->m_indenter->format(&d->m_document, filePath(), cursor, tabSettings());
|
||||
d->m_indenter->format(cursor, tabSettings());
|
||||
}
|
||||
|
||||
QTextCursor TextDocument::indent(const QTextCursor &cursor, bool blockSelection, int column,
|
||||
@@ -824,8 +824,8 @@ void TextDocument::cleanWhitespace(QTextCursor &cursor, bool cleanIndentation, b
|
||||
return;
|
||||
|
||||
const TabSettings currentTabSettings = tabSettings();
|
||||
const IndentationForBlock &indentations =
|
||||
d->m_indenter->indentationForBlocks(blocks, currentTabSettings);
|
||||
const IndentationForBlock &indentations
|
||||
= d->m_indenter->indentationForBlocks(blocks, currentTabSettings);
|
||||
|
||||
foreach (block, blocks) {
|
||||
QString blockText = block.text();
|
||||
|
@@ -26,6 +26,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "texteditor_global.h"
|
||||
#include "indenter.h"
|
||||
|
||||
#include <coreplugin/id.h>
|
||||
#include <coreplugin/textdocument.h>
|
||||
@@ -48,7 +49,6 @@ namespace TextEditor {
|
||||
class CompletionAssistProvider;
|
||||
class ExtraEncodingSettings;
|
||||
class FontSettings;
|
||||
class Indenter;
|
||||
class IAssistProvider;
|
||||
class StorageSettings;
|
||||
class SyntaxHighlighter;
|
||||
@@ -87,8 +87,7 @@ public:
|
||||
|
||||
void setIndenter(Indenter *indenter);
|
||||
Indenter *indenter() const;
|
||||
void autoIndent(const QTextCursor &cursor, QChar typedChar = QChar::Null,
|
||||
bool autoTriggered = true);
|
||||
void autoIndent(const QTextCursor &cursor, QChar typedChar = QChar::Null);
|
||||
void autoReindent(const QTextCursor &cursor);
|
||||
void autoFormat(const QTextCursor &cursor);
|
||||
QTextCursor indent(const QTextCursor &cursor, bool blockSelection = false, int column = 0,
|
||||
|
@@ -8738,7 +8738,7 @@ IEditor *TextEditorFactory::createEditor()
|
||||
TextDocumentPtr doc(d->m_documentCreator());
|
||||
|
||||
if (d->m_indenterCreator)
|
||||
doc->setIndenter(d->m_indenterCreator());
|
||||
doc->setIndenter(d->m_indenterCreator(doc->document()));
|
||||
|
||||
if (d->m_syntaxHighlighterCreator)
|
||||
doc->setSyntaxHighlighter(d->m_syntaxHighlighterCreator());
|
||||
|
@@ -27,6 +27,7 @@
|
||||
|
||||
#include "texteditor_global.h"
|
||||
#include "blockrange.h"
|
||||
#include "indenter.h"
|
||||
#include "codeassist/assistenums.h"
|
||||
|
||||
#include <coreplugin/editormanager/editormanager.h>
|
||||
@@ -85,7 +86,6 @@ class CompletionSettings;
|
||||
class DisplaySettings;
|
||||
class ExtraEncodingSettings;
|
||||
class FontSettings;
|
||||
class Indenter;
|
||||
class MarginSettings;
|
||||
class StorageSettings;
|
||||
class TypingSettings;
|
||||
@@ -635,7 +635,7 @@ public:
|
||||
using DocumentCreator = std::function<TextDocument *()>;
|
||||
using EditorWidgetCreator = std::function<TextEditorWidget *()>;
|
||||
using SyntaxHighLighterCreator = std::function<SyntaxHighlighter *()>;
|
||||
using IndenterCreator = std::function<Indenter *()>;
|
||||
using IndenterCreator = std::function<Indenter *(QTextDocument *)>;
|
||||
using AutoCompleterCreator = std::function<AutoCompleter *()>;
|
||||
|
||||
void setDocumentCreator(const DocumentCreator &creator);
|
||||
|
@@ -31,7 +31,7 @@ SOURCES += texteditorplugin.cpp \
|
||||
textdocumentlayout.cpp \
|
||||
completionsettings.cpp \
|
||||
normalindenter.cpp \
|
||||
indenter.cpp \
|
||||
textindenter.cpp \
|
||||
quickfix.cpp \
|
||||
syntaxhighlighter.cpp \
|
||||
highlighterutils.cpp \
|
||||
@@ -135,7 +135,7 @@ HEADERS += texteditorplugin.h \
|
||||
textdocumentlayout.h \
|
||||
completionsettings.h \
|
||||
normalindenter.h \
|
||||
indenter.h \
|
||||
textindenter.h \
|
||||
quickfix.h \
|
||||
syntaxhighlighter.h \
|
||||
highlighterutils.h \
|
||||
@@ -218,7 +218,8 @@ HEADERS += texteditorplugin.h \
|
||||
commentssettings.h \
|
||||
textstyles.h \
|
||||
formattexteditor.h \
|
||||
command.h
|
||||
command.h \
|
||||
indenter.h
|
||||
|
||||
FORMS += \
|
||||
displaysettingspage.ui \
|
||||
|
@@ -88,7 +88,6 @@ Project {
|
||||
"icodestylepreferences.h",
|
||||
"icodestylepreferencesfactory.cpp",
|
||||
"icodestylepreferencesfactory.h",
|
||||
"indenter.cpp",
|
||||
"indenter.h",
|
||||
"ioutlinewidget.h",
|
||||
"linenumberfilter.cpp",
|
||||
@@ -143,6 +142,8 @@ Project {
|
||||
"texteditorplugin.h",
|
||||
"texteditorsettings.cpp",
|
||||
"texteditorsettings.h",
|
||||
"textindenter.cpp",
|
||||
"textindenter.h",
|
||||
"textmark.cpp",
|
||||
"textmark.h",
|
||||
"textstyles.h",
|
||||
|
@@ -23,30 +23,32 @@
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "indenter.h"
|
||||
#include "tabsettings.h"
|
||||
#include "textdocumentlayout.h"
|
||||
#include "textindenter.h"
|
||||
|
||||
#include <QTextDocument>
|
||||
#include <QTextCursor>
|
||||
|
||||
using namespace TextEditor;
|
||||
|
||||
Indenter::Indenter() = default;
|
||||
TextIndenter::TextIndenter(QTextDocument *doc)
|
||||
: Indenter(doc)
|
||||
{}
|
||||
|
||||
Indenter::~Indenter() = default;
|
||||
TextIndenter::~TextIndenter() = default;
|
||||
|
||||
bool Indenter::isElectricCharacter(const QChar &) const
|
||||
IndentationForBlock TextIndenter::indentationForBlocks(const QVector<QTextBlock> &blocks,
|
||||
const TabSettings &tabSettings)
|
||||
{
|
||||
return false;
|
||||
IndentationForBlock ret;
|
||||
for (QTextBlock block : blocks)
|
||||
ret.insert(block.blockNumber(), indentFor(block, tabSettings));
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Indenter::indentBlock(QTextDocument *doc,
|
||||
const QTextBlock &block,
|
||||
const QChar &typedChar,
|
||||
const TabSettings &tabSettings)
|
||||
void TextIndenter::indentBlock(const QTextBlock &block,
|
||||
const QChar &typedChar,
|
||||
const TabSettings &tabSettings)
|
||||
{
|
||||
Q_UNUSED(doc);
|
||||
Q_UNUSED(typedChar);
|
||||
const int indent = indentFor(block, tabSettings);
|
||||
if (indent < 0)
|
||||
@@ -54,41 +56,39 @@ void Indenter::indentBlock(QTextDocument *doc,
|
||||
tabSettings.indentLine(block, indent);
|
||||
}
|
||||
|
||||
void Indenter::indent(QTextDocument *doc,
|
||||
const QTextCursor &cursor,
|
||||
const QChar &typedChar,
|
||||
const TabSettings &tabSettings,
|
||||
bool /*autoTriggered*/)
|
||||
void TextIndenter::indent(const QTextCursor &cursor,
|
||||
const QChar &typedChar,
|
||||
const TabSettings &tabSettings)
|
||||
{
|
||||
if (cursor.hasSelection()) {
|
||||
QTextBlock block = doc->findBlock(cursor.selectionStart());
|
||||
const QTextBlock end = doc->findBlock(cursor.selectionEnd()).next();
|
||||
QTextBlock block = m_doc->findBlock(cursor.selectionStart());
|
||||
const QTextBlock end = m_doc->findBlock(cursor.selectionEnd()).next();
|
||||
do {
|
||||
indentBlock(doc, block, typedChar, tabSettings);
|
||||
indentBlock(block, typedChar, tabSettings);
|
||||
block = block.next();
|
||||
} while (block.isValid() && block != end);
|
||||
} else {
|
||||
indentBlock(doc, cursor.block(), typedChar, tabSettings);
|
||||
indentBlock(cursor.block(), typedChar, tabSettings);
|
||||
}
|
||||
}
|
||||
|
||||
void Indenter::reindent(QTextDocument *doc, const QTextCursor &cursor, const TabSettings &tabSettings)
|
||||
void TextIndenter::reindent(const QTextCursor &cursor, const TabSettings &tabSettings)
|
||||
{
|
||||
if (cursor.hasSelection()) {
|
||||
QTextBlock block = doc->findBlock(cursor.selectionStart());
|
||||
const QTextBlock end = doc->findBlock(cursor.selectionEnd()).next();
|
||||
QTextBlock block = m_doc->findBlock(cursor.selectionStart());
|
||||
const QTextBlock end = m_doc->findBlock(cursor.selectionEnd()).next();
|
||||
|
||||
// skip empty blocks
|
||||
while (block.isValid() && block != end) {
|
||||
QString bt = block.text();
|
||||
if (tabSettings.firstNonSpace(bt) < bt.size())
|
||||
break;
|
||||
indentBlock(doc, block, QChar::Null, tabSettings);
|
||||
indentBlock(block, QChar::Null, tabSettings);
|
||||
block = block.next();
|
||||
}
|
||||
|
||||
int previousIndentation = tabSettings.indentationColumn(block.text());
|
||||
indentBlock(doc, block, QChar::Null, tabSettings);
|
||||
indentBlock(block, QChar::Null, tabSettings);
|
||||
int currentIndentation = tabSettings.indentationColumn(block.text());
|
||||
int delta = currentIndentation - previousIndentation;
|
||||
|
||||
@@ -98,40 +98,17 @@ void Indenter::reindent(QTextDocument *doc, const QTextCursor &cursor, const Tab
|
||||
block = block.next();
|
||||
}
|
||||
} else {
|
||||
indentBlock(doc, cursor.block(), QChar::Null, tabSettings);
|
||||
indentBlock(cursor.block(), QChar::Null, tabSettings);
|
||||
}
|
||||
}
|
||||
|
||||
Replacements Indenter::format(QTextDocument *doc,
|
||||
const Utils::FileName & /*fileName*/,
|
||||
const QTextCursor &cursor,
|
||||
const TabSettings &tabSettings)
|
||||
Replacements TextIndenter::format(const QTextCursor &cursor, const TabSettings &tabSettings)
|
||||
{
|
||||
indent(doc, cursor, QChar::Null, tabSettings);
|
||||
indent(cursor, QChar::Null, tabSettings);
|
||||
return Replacements();
|
||||
}
|
||||
|
||||
void Indenter::setCodeStylePreferences(ICodeStylePreferences *)
|
||||
Utils::optional<TabSettings> TextIndenter::tabSettings() const
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Indenter::invalidateCache(QTextDocument *)
|
||||
{
|
||||
}
|
||||
|
||||
int Indenter::indentFor(const QTextBlock &block, const TabSettings &tabSettings)
|
||||
{
|
||||
Q_UNUSED(block)
|
||||
Q_UNUSED(tabSettings)
|
||||
return -1;
|
||||
}
|
||||
|
||||
IndentationForBlock Indenter::indentationForBlocks(const QVector<QTextBlock> &blocks,
|
||||
const TabSettings &tabSettings)
|
||||
{
|
||||
IndentationForBlock ret;
|
||||
foreach (QTextBlock block, blocks)
|
||||
ret.insert(block.blockNumber(), indentFor(block, tabSettings));
|
||||
return ret;
|
||||
return Utils::optional<TabSettings>();
|
||||
}
|
63
src/plugins/texteditor/textindenter.h
Normal file
63
src/plugins/texteditor/textindenter.h
Normal file
@@ -0,0 +1,63 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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 "texteditor_global.h"
|
||||
|
||||
#include "indenter.h"
|
||||
#include "tabsettings.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QTextDocument;
|
||||
class QTextCursor;
|
||||
class QChar;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace TextEditor {
|
||||
|
||||
class TEXTEDITOR_EXPORT TextIndenter : public Indenter
|
||||
{
|
||||
public:
|
||||
explicit TextIndenter(QTextDocument *doc);
|
||||
~TextIndenter() override;
|
||||
|
||||
IndentationForBlock indentationForBlocks(const QVector<QTextBlock> &blocks,
|
||||
const TabSettings &tabSettings) override;
|
||||
void indentBlock(const QTextBlock &block,
|
||||
const QChar &typedChar,
|
||||
const TabSettings &tabSettings) override;
|
||||
|
||||
void indent(const QTextCursor &cursor,
|
||||
const QChar &typedChar,
|
||||
const TabSettings &tabSettings) override;
|
||||
|
||||
Replacements format(const QTextCursor &cursor, const TabSettings &tabSettings) override;
|
||||
|
||||
void reindent(const QTextCursor &cursor, const TabSettings &tabSettings) override;
|
||||
Utils::optional<TabSettings> tabSettings() const override;
|
||||
};
|
||||
|
||||
} // namespace TextEditor
|
@@ -153,11 +153,20 @@ LLVM_VERSION = $$extractVersion($$output)
|
||||
!isEmpty(LLVM_VERSION) {
|
||||
versionIsAtLeast($$LLVM_VERSION, 7, 0, 0): {
|
||||
CLANGFORMAT_LIBS=-lclangFormat -lclangToolingInclusions -lclangToolingCore -lclangRewrite -lclangLex -lclangBasic
|
||||
win32:CLANGFORMAT_LIBS += -lversion
|
||||
ALL_CLANG_LIBS=-lclangFormat -lclangToolingInclusions -lclangTooling -lclangToolingCore \
|
||||
-lclangRewrite -lclangIndex -lclangFrontend -lclangParse -lclangSerialization \
|
||||
-lclangSema -lclangEdit -lclangAnalysis -lclangDriver -lclangDynamicASTMatchers \
|
||||
-lclangASTMatchers -lclangAST -lclangLex -lclangBasic
|
||||
} else:versionIsAtLeast($$LLVM_VERSION, 6, 0, 0): {
|
||||
CLANGFORMAT_LIBS=-lclangFormat -lclangToolingCore -lclangRewrite -lclangLex -lclangBasic
|
||||
win32:CLANGFORMAT_LIBS += -lversion
|
||||
ALL_CLANG_LIBS=-lclangFormat -lclangTooling -lclangToolingCore \
|
||||
-lclangRewrite -lclangIndex -lclangFrontend -lclangParse -lclangSerialization \
|
||||
-lclangSema -lclangEdit -lclangAnalysis -lclangDriver -lclangDynamicASTMatchers \
|
||||
-lclangASTMatchers -lclangAST -lclangLex -lclangBasic
|
||||
}
|
||||
|
||||
win32:CLANGFORMAT_LIBS += -lversion
|
||||
win32:ALL_CLANG_LIBS += -lversion
|
||||
}
|
||||
|
||||
isEmpty(LLVM_VERSION) {
|
||||
@@ -223,6 +232,7 @@ isEmpty(LLVM_VERSION) {
|
||||
}
|
||||
|
||||
CLANGFORMAT_LIBS = -L$${LLVM_LIBDIR} $$CLANGFORMAT_LIBS $$LLVM_STATIC_LIBS
|
||||
ALL_CLANG_LIBS = -L$${LLVM_LIBDIR} $$ALL_CLANG_LIBS $$CLANG_LIB $$LLVM_STATIC_LIBS
|
||||
|
||||
contains(QMAKE_DEFAULT_INCDIRS, $$LLVM_INCLUDEPATH): LLVM_INCLUDEPATH =
|
||||
|
||||
|
@@ -9,8 +9,6 @@ include(../../../src/shared/clang/clang_defines.pri)
|
||||
DEFINES += CLANG_UNIT_TESTS
|
||||
INCLUDEPATH += $$LLVM_INCLUDEPATH
|
||||
win32 {
|
||||
LIBS += -lVersion
|
||||
|
||||
# set run path for clang.dll dependency
|
||||
bin_path = $$LLVM_BINDIR
|
||||
bin_path ~= s,/,\\,g
|
||||
@@ -18,7 +16,8 @@ include(../../../src/shared/clang/clang_defines.pri)
|
||||
check.commands = cd . & set PATH=$$bin_path;%PATH%& cmd /c
|
||||
}
|
||||
|
||||
LIBS += $$LIBTOOLING_LIBS $$LIBCLANG_LIBS
|
||||
LIBS += $$ALL_CLANG_LIBS
|
||||
|
||||
!contains(QMAKE_DEFAULT_LIBDIRS, $$LLVM_LIBDIR): QMAKE_RPATHDIR += $$LLVM_LIBDIR
|
||||
|
||||
LLVM_CXXFLAGS ~= s,-g\d?,
|
||||
|
106
tests/unit/unittest/clangformat-test.cpp
Normal file
106
tests/unit/unittest/clangformat-test.cpp
Normal file
@@ -0,0 +1,106 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 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 "googletest.h"
|
||||
|
||||
#include <clangformat/clangformatbaseindenter.h>
|
||||
#include <utils/fileutils.h>
|
||||
|
||||
#include <QTextDocument>
|
||||
|
||||
namespace TextEditor {
|
||||
class TabSettings
|
||||
{
|
||||
};
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class ClangFormatIndenter : public ClangFormat::ClangFormatBaseIndenter
|
||||
{
|
||||
public:
|
||||
ClangFormatIndenter(QTextDocument *doc)
|
||||
: ClangFormat::ClangFormatBaseIndenter(doc)
|
||||
{}
|
||||
|
||||
Utils::optional<TextEditor::TabSettings> tabSettings() const override
|
||||
{
|
||||
return Utils::optional<TextEditor::TabSettings>();
|
||||
}
|
||||
};
|
||||
|
||||
class ClangFormat : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
void SetUp() final
|
||||
{
|
||||
indenter.setFileName(Utils::FileName::fromString(TESTDATA_DIR"/test.cpp"));
|
||||
}
|
||||
|
||||
void insertLines(const std::vector<QString> &lines)
|
||||
{
|
||||
cursor.setPosition(0);
|
||||
for (size_t lineNumber = 1; lineNumber <= lines.size(); ++lineNumber) {
|
||||
if (lineNumber > 1)
|
||||
cursor.insertBlock();
|
||||
|
||||
cursor.insertText(lines[lineNumber - 1]);
|
||||
}
|
||||
cursor.setPosition(0);
|
||||
cursor.movePosition(QTextCursor::End, QTextCursor::KeepAnchor);
|
||||
}
|
||||
|
||||
std::vector<QString> documentLines()
|
||||
{
|
||||
std::vector<QString> result;
|
||||
const int lines = doc.blockCount();
|
||||
result.reserve(static_cast<size_t>(lines));
|
||||
for (int line = 0; line < lines; ++line)
|
||||
result.push_back(doc.findBlockByNumber(line).text());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
QTextDocument doc;
|
||||
ClangFormatIndenter indenter{&doc};
|
||||
QTextCursor cursor{&doc};
|
||||
};
|
||||
|
||||
TEST_F(ClangFormat, SimpleIndent)
|
||||
{
|
||||
insertLines({"int main()",
|
||||
"{",
|
||||
"int a;",
|
||||
"}"});
|
||||
|
||||
indenter.indent(cursor, QChar::Null, TextEditor::TabSettings());
|
||||
|
||||
ASSERT_THAT(documentLines(), ElementsAre("int main()",
|
||||
"{",
|
||||
" int a;",
|
||||
"}"));
|
||||
}
|
||||
|
||||
}
|
@@ -19,6 +19,7 @@ include(cplusplus.pri)
|
||||
include($$PWD/../../../src/shared/clang/clang_defines.pri)
|
||||
include($$PWD/../../../src/tools/clangbackend/source/clangbackendclangipc-source.pri)
|
||||
include($$PWD/../../../src/plugins/clangcodemodel/clangcodemodelunittestfiles.pri)
|
||||
include($$PWD/../../../src/plugins/clangformat/clangformat-source.pri)
|
||||
} else {
|
||||
DEFINES += CLANG_VERSION=\\\"6.0.0\\\"
|
||||
DEFINES += "\"CLANG_RESOURCE_DIR=\\\"/usr/include\\\"\""
|
||||
|
102
tests/unit/unittest/data/.clang-format
Normal file
102
tests/unit/unittest/data/.clang-format
Normal file
@@ -0,0 +1,102 @@
|
||||
---
|
||||
Language: Cpp
|
||||
AccessModifierOffset: -4
|
||||
AlignAfterOpenBracket: Align
|
||||
AlignConsecutiveAssignments: false
|
||||
AlignConsecutiveDeclarations: false
|
||||
AlignEscapedNewlines: DontAlign
|
||||
AlignOperands: true
|
||||
AlignTrailingComments: true
|
||||
AllowAllParametersOfDeclarationOnNextLine: true
|
||||
AllowShortBlocksOnASingleLine: false
|
||||
AllowShortCaseLabelsOnASingleLine: false
|
||||
AllowShortFunctionsOnASingleLine: Inline
|
||||
AllowShortIfStatementsOnASingleLine: false
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
AlwaysBreakAfterReturnType: None
|
||||
AlwaysBreakBeforeMultilineStrings: false
|
||||
AlwaysBreakTemplateDeclarations: true
|
||||
BinPackArguments: false
|
||||
BinPackParameters: false
|
||||
BraceWrapping:
|
||||
AfterClass: true
|
||||
AfterControlStatement: false
|
||||
AfterEnum: false
|
||||
AfterFunction: true
|
||||
AfterNamespace: false
|
||||
AfterObjCDeclaration: false
|
||||
AfterStruct: true
|
||||
AfterUnion: false
|
||||
BeforeCatch: false
|
||||
BeforeElse: false
|
||||
IndentBraces: false
|
||||
SplitEmptyFunction: false
|
||||
SplitEmptyRecord: false
|
||||
SplitEmptyNamespace: false
|
||||
BreakBeforeBinaryOperators: All
|
||||
BreakBeforeBraces: Custom
|
||||
BreakBeforeInheritanceComma: false
|
||||
BreakBeforeTernaryOperators: true
|
||||
BreakConstructorInitializersBeforeComma: false
|
||||
BreakConstructorInitializers: BeforeComma
|
||||
BreakAfterJavaFieldAnnotations: false
|
||||
BreakStringLiterals: true
|
||||
ColumnLimit: 100
|
||||
CommentPragmas: '^ IWYU pragma:'
|
||||
CompactNamespaces: false
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: false
|
||||
ConstructorInitializerIndentWidth: 4
|
||||
ContinuationIndentWidth: 4
|
||||
Cpp11BracedListStyle: true
|
||||
DerivePointerAlignment: false
|
||||
DisableFormat: false
|
||||
ExperimentalAutoDetectBinPacking: false
|
||||
FixNamespaceComments: true
|
||||
ForEachMacros:
|
||||
- forever # avoids { wrapped to next line
|
||||
- foreach
|
||||
- Q_FOREACH
|
||||
- BOOST_FOREACH
|
||||
IncludeCategories:
|
||||
- Regex: '^<Q.*'
|
||||
Priority: 200
|
||||
IncludeIsMainRegex: '(Test)?$'
|
||||
IndentCaseLabels: false
|
||||
IndentWidth: 4
|
||||
IndentWrappedFunctionNames: false
|
||||
JavaScriptQuotes: Leave
|
||||
JavaScriptWrapImports: true
|
||||
KeepEmptyLinesAtTheStartOfBlocks: false
|
||||
# Do not add QT_BEGIN_NAMESPACE/QT_END_NAMESPACE as this will indent lines in between.
|
||||
MacroBlockBegin: ""
|
||||
MacroBlockEnd: ""
|
||||
MaxEmptyLinesToKeep: 1
|
||||
NamespaceIndentation: None
|
||||
ObjCBlockIndentWidth: 4
|
||||
ObjCSpaceAfterProperty: false
|
||||
ObjCSpaceBeforeProtocolList: true
|
||||
PenaltyBreakAssignment: 150
|
||||
PenaltyBreakBeforeFirstCallParameter: 300
|
||||
PenaltyBreakComment: 500
|
||||
PenaltyBreakFirstLessLess: 400
|
||||
PenaltyBreakString: 600
|
||||
PenaltyExcessCharacter: 50
|
||||
PenaltyReturnTypeOnItsOwnLine: 300
|
||||
PointerAlignment: Right
|
||||
ReflowComments: false
|
||||
SortIncludes: true
|
||||
SortUsingDeclarations: true
|
||||
SpaceAfterCStyleCast: true
|
||||
SpaceAfterTemplateKeyword: false
|
||||
SpaceBeforeAssignmentOperators: true
|
||||
SpaceBeforeParens: ControlStatements
|
||||
SpaceInEmptyParentheses: false
|
||||
SpacesBeforeTrailingComments: 1
|
||||
SpacesInAngles: false
|
||||
SpacesInContainerLiterals: false
|
||||
SpacesInCStyleCastParentheses: false
|
||||
SpacesInParentheses: false
|
||||
SpacesInSquareBrackets: false
|
||||
Standard: Cpp11
|
||||
TabWidth: 4
|
||||
UseTab: Never
|
@@ -194,6 +194,10 @@ SOURCES += \
|
||||
builddependencycollector-test.cpp
|
||||
}
|
||||
|
||||
!isEmpty(CLANGFORMAT_LIBS) {
|
||||
SOURCES += clangformat-test.cpp
|
||||
}
|
||||
|
||||
exists($$GOOGLEBENCHMARK_DIR) {
|
||||
SOURCES += \
|
||||
smallstring-benchmark.cpp
|
||||
|
Reference in New Issue
Block a user