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:
Ivan Donchevskii
2019-01-16 09:37:54 +01:00
parent 8b5beeb952
commit d7058e1afe
71 changed files with 1200 additions and 818 deletions

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,8 @@
INCLUDEPATH += $$PWD
HEADERS += \
$$PWD/clangformatconstants.h \
$$PWD/clangformatbaseindenter.h
SOURCES += \
$$PWD/clangformatbaseindenter.cpp

View File

@@ -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 += \

View File

@@ -30,6 +30,8 @@ QtcPlugin {
cpp.rpaths: base.concat(libclang.llvmLibDir)
files: [
"clangformatbaseindenter.h",
"clangformatbaseindenter.cpp",
"clangformatconfigwidget.cpp",
"clangformatconfigwidget.h",
"clangformatconfigwidget.ui",

View 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 &currentBlock)
{
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 &currentBlock)
{
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 &currentBlock)
{
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

View 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

View File

@@ -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 &currentBlock)
{
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 &currentBlock)
{
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 &currentBlock)
{
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;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -49,7 +49,6 @@ namespace CPlusPlus { class LookupContext; }
namespace ProjectExplorer { class Project; }
namespace TextEditor {
class BaseHoverHandler;
class Indenter;
class TextDocument;
} // namespace TextEditor

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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

View File

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

View File

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

View 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;",
"}"));
}
}

View File

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

View 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

View File

@@ -194,6 +194,10 @@ SOURCES += \
builddependencycollector-test.cpp
}
!isEmpty(CLANGFORMAT_LIBS) {
SOURCES += clangformat-test.cpp
}
exists($$GOOGLEBENCHMARK_DIR) {
SOURCES += \
smallstring-benchmark.cpp