diff options
author | Ivan Donchevskii <ivan.donchevskii@qt.io> | 2019-01-16 09:37:54 +0100 |
---|---|---|
committer | Ivan Donchevskii <ivan.donchevskii@qt.io> | 2019-01-22 09:52:15 +0000 |
commit | d7058e1afedfe609ff6e81222bd2137922bf7de3 (patch) | |
tree | 62d16136c8336646b8ef0ad4d220b7e0e8331203 /src | |
parent | 8b5beeb952448540a3834333b694919563d81ee2 (diff) | |
download | qt-creator-d7058e1afedfe609ff6e81222bd2137922bf7de3.tar.gz |
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>
Diffstat (limited to 'src')
66 files changed, 978 insertions, 808 deletions
diff --git a/src/plugins/android/javaeditor.cpp b/src/plugins/android/javaeditor.cpp index 8b023b1703..85ea22442b 100644 --- a/src/plugins/android/javaeditor.cpp +++ b/src/plugins/android/javaeditor.cpp @@ -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; } diff --git a/src/plugins/android/javaindenter.cpp b/src/plugins/android/javaindenter.cpp index 6b6d45d7ce..270e9cf988 100644 --- a/src/plugins/android/javaindenter.cpp +++ b/src/plugins/android/javaindenter.cpp @@ -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()) diff --git a/src/plugins/android/javaindenter.h b/src/plugins/android/javaindenter.h index 2b40851fd4..8a93058e83 100644 --- a/src/plugins/android/javaindenter.h +++ b/src/plugins/android/javaindenter.h @@ -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; diff --git a/src/plugins/clangformat/clangformat-source.pri b/src/plugins/clangformat/clangformat-source.pri new file mode 100644 index 0000000000..4aafbe77bf --- /dev/null +++ b/src/plugins/clangformat/clangformat-source.pri @@ -0,0 +1,8 @@ +INCLUDEPATH += $$PWD + +HEADERS += \ + $$PWD/clangformatconstants.h \ + $$PWD/clangformatbaseindenter.h + +SOURCES += \ + $$PWD/clangformatbaseindenter.cpp diff --git a/src/plugins/clangformat/clangformat.pro b/src/plugins/clangformat/clangformat.pro index 6696b3b0de..2e168fdbc5 100644 --- a/src/plugins/clangformat/clangformat.pro +++ b/src/plugins/clangformat/clangformat.pro @@ -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 += \ diff --git a/src/plugins/clangformat/clangformat.qbs b/src/plugins/clangformat/clangformat.qbs index 6c23ee0430..cbfaffe657 100644 --- a/src/plugins/clangformat/clangformat.qbs +++ b/src/plugins/clangformat/clangformat.qbs @@ -30,6 +30,8 @@ QtcPlugin { cpp.rpaths: base.concat(libclang.llvmLibDir) files: [ + "clangformatbaseindenter.h", + "clangformatbaseindenter.cpp", "clangformatconfigwidget.cpp", "clangformatconfigwidget.h", "clangformatconfigwidget.ui", diff --git a/src/plugins/clangformat/clangformatbaseindenter.cpp b/src/plugins/clangformat/clangformatbaseindenter.cpp new file mode 100644 index 0000000000..dde629cdd0 --- /dev/null +++ b/src/plugins/clangformat/clangformatbaseindenter.cpp @@ -0,0 +1,500 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include "clangformatbaseindenter.h" + +#include <clang/Tooling/Core/Replacement.h> + +#include <utils/fileutils.h> +#include <utils/textutils.h> +#include <utils/qtcassert.h> + +#include <QTextDocument> + +namespace ClangFormat { + +static void adjustFormatStyleForLineBreak(clang::format::FormatStyle &style) +{ + style.DisableFormat = false; + style.ColumnLimit = 0; +#ifdef KEEP_LINE_BREAKS_FOR_NON_EMPTY_LINES_BACKPORTED + style.KeepLineBreaksForNonEmptyLines = true; +#endif + style.MaxEmptyLinesToKeep = 2; + style.SortIncludes = false; + style.SortUsingDeclarations = false; +} + +static llvm::StringRef clearExtraNewline(llvm::StringRef text) +{ + while (text.startswith("\n\n")) + text = text.drop_front(); + return text; +} + +static clang::tooling::Replacements filteredReplacements( + const clang::tooling::Replacements &replacements, + int offset, + int extraOffsetToAdd, + bool onlyIndention) +{ + clang::tooling::Replacements filtered; + for (const clang::tooling::Replacement &replacement : replacements) { + int replacementOffset = static_cast<int>(replacement.getOffset()); + if (onlyIndention && replacementOffset != offset - 1) + continue; + + if (replacementOffset + 1 >= offset) + replacementOffset += extraOffsetToAdd; + + llvm::StringRef text = onlyIndention ? clearExtraNewline(replacement.getReplacementText()) + : replacement.getReplacementText(); + + llvm::Error error = filtered.add( + clang::tooling::Replacement(replacement.getFilePath(), + static_cast<unsigned int>(replacementOffset), + replacement.getLength(), + text)); + // Throws if error is not checked. + if (error) + break; + } + return filtered; +} + +static void trimFirstNonEmptyBlock(const QTextBlock ¤tBlock) +{ + QTextBlock prevBlock = currentBlock.previous(); + while (prevBlock.position() > 0 && prevBlock.text().trimmed().isEmpty()) + prevBlock = prevBlock.previous(); + + if (prevBlock.text().trimmed().isEmpty()) + return; + + const QString initialText = prevBlock.text(); + if (!initialText.at(initialText.size() - 1).isSpace()) + return; + + auto lastNonSpace = std::find_if_not(initialText.rbegin(), + initialText.rend(), + [](const QChar &letter) { return letter.isSpace(); }); + const int extraSpaceCount = static_cast<int>(std::distance(initialText.rbegin(), lastNonSpace)); + + QTextCursor cursor(prevBlock); + cursor.beginEditBlock(); + cursor.movePosition(QTextCursor::Right, + QTextCursor::MoveAnchor, + initialText.size() - extraSpaceCount); + cursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, extraSpaceCount); + cursor.removeSelectedText(); + cursor.endEditBlock(); +} + +static void trimCurrentBlock(const QTextBlock ¤tBlock) +{ + if (currentBlock.text().trimmed().isEmpty()) { + // Clear the block containing only spaces + QTextCursor cursor(currentBlock); + cursor.beginEditBlock(); + cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor); + cursor.removeSelectedText(); + cursor.endEditBlock(); + } +} + +// Returns the total langth of previous lines with pure whitespace. +static int previousEmptyLinesLength(const QTextBlock ¤tBlock) +{ + int length{0}; + QTextBlock prevBlock = currentBlock.previous(); + while (prevBlock.position() > 0 && prevBlock.text().trimmed().isEmpty()) { + length += prevBlock.text().length() + 1; + prevBlock = prevBlock.previous(); + } + + return length; +} + +static void modifyToIndentEmptyLines( + QByteArray &buffer, int offset, int &length, const QTextBlock &block, bool secondTry) +{ + const QString blockText = block.text().trimmed(); + const bool closingParenBlock = blockText.startsWith(')'); + if (blockText.isEmpty() || closingParenBlock) { + //This extra text works for the most cases. + QByteArray dummyText("a;"); + + // Search for previous character + QTextBlock prevBlock = block.previous(); + bool prevBlockIsEmpty = prevBlock.position() > 0 && prevBlock.text().trimmed().isEmpty(); + while (prevBlockIsEmpty) { + prevBlock = prevBlock.previous(); + prevBlockIsEmpty = prevBlock.position() > 0 && prevBlock.text().trimmed().isEmpty(); + } + if (prevBlock.text().endsWith(',')) + dummyText = "int a"; + + if (closingParenBlock) { + if (prevBlock.text().endsWith(',')) + dummyText = "int a"; + else + dummyText = "&& a"; + } + + length += dummyText.length(); + buffer.insert(offset, dummyText); + } + + if (secondTry) { + int nextLinePos = buffer.indexOf('\n', offset); + if (nextLinePos > 0) { + // If first try was not successful try to put ')' in the end of the line to close possibly + // unclosed parentheses. + // TODO: Does it help to add different endings depending on the context? + buffer.insert(nextLinePos, ')'); + length += 1; + } + } +} + +static const int kMaxLinesFromCurrentBlock = 200; + +static Utils::LineColumn utf16LineColumn(const QTextBlock &block, + int blockOffsetUtf8, + const QByteArray &utf8Buffer, + int utf8Offset) +{ + // If lastIndexOf('\n') returns -1 then we are fine to add 1 and get 0 offset. + const int lineStartUtf8Offset = utf8Buffer.lastIndexOf('\n', utf8Offset - 1) + 1; + int line = block.blockNumber() + 1; // Init with the line corresponding the block. + + if (utf8Offset < blockOffsetUtf8) { + line -= static_cast<int>(std::count(utf8Buffer.begin() + lineStartUtf8Offset, + utf8Buffer.begin() + blockOffsetUtf8, + '\n')); + } else { + line += static_cast<int>(std::count(utf8Buffer.begin() + blockOffsetUtf8, + utf8Buffer.begin() + lineStartUtf8Offset, + '\n')); + } + + const QByteArray lineText = utf8Buffer.mid(lineStartUtf8Offset, + utf8Offset - lineStartUtf8Offset); + return Utils::LineColumn(line, QString::fromUtf8(lineText).size() + 1); +} + +static TextEditor::Replacements utf16Replacements(const QTextBlock &block, + int blockOffsetUtf8, + const QByteArray &utf8Buffer, + const clang::tooling::Replacements &replacements) +{ + TextEditor::Replacements convertedReplacements; + convertedReplacements.reserve(replacements.size()); + for (const clang::tooling::Replacement &replacement : replacements) { + const Utils::LineColumn lineColUtf16 = utf16LineColumn(block, + blockOffsetUtf8, + utf8Buffer, + static_cast<int>( + replacement.getOffset())); + if (!lineColUtf16.isValid()) + continue; + const int utf16Offset = Utils::Text::positionInText(block.document(), + lineColUtf16.line, + lineColUtf16.column); + const int utf16Length = QString::fromUtf8( + utf8Buffer.mid(static_cast<int>(replacement.getOffset()), + static_cast<int>(replacement.getLength()))) + .size(); + convertedReplacements.emplace_back(utf16Offset, + utf16Length, + QString::fromStdString(replacement.getReplacementText())); + } + + return convertedReplacements; +} + +static void applyReplacements(const QTextBlock &block, const TextEditor::Replacements &replacements) +{ + if (replacements.empty()) + return; + + int fullOffsetShift = 0; + QTextCursor editCursor(block); + for (const TextEditor::Replacement &replacement : replacements) { + editCursor.beginEditBlock(); + editCursor.setPosition(replacement.offset + fullOffsetShift); + editCursor.movePosition(QTextCursor::NextCharacter, + QTextCursor::KeepAnchor, + replacement.length); + editCursor.removeSelectedText(); + editCursor.insertText(replacement.text); + editCursor.endEditBlock(); + fullOffsetShift += replacement.text.length() - replacement.length; + } +} + +static QString selectedLines(QTextDocument *doc, + const QTextBlock &startBlock, + const QTextBlock &endBlock) +{ + QString text = Utils::Text::textAt(QTextCursor(doc), + startBlock.position(), + std::max(0, + endBlock.position() + endBlock.length() + - startBlock.position() - 1)); + while (!text.isEmpty() && text.rbegin()->isSpace()) + text.chop(1); + return text; +} + +ClangFormatBaseIndenter::ClangFormatBaseIndenter(QTextDocument *doc) + : TextEditor::Indenter(doc) +{} + +TextEditor::IndentationForBlock ClangFormatBaseIndenter::indentationForBlocks( + const QVector<QTextBlock> &blocks, const TextEditor::TabSettings & /*tabSettings*/) +{ + TextEditor::IndentationForBlock ret; + for (QTextBlock block : blocks) + ret.insert(block.blockNumber(), indentFor(block)); + return ret; +} + +void ClangFormatBaseIndenter::indent(const QTextCursor &cursor, const QChar &typedChar) +{ + if (cursor.hasSelection()) { + // Calling currentBlock.next() might be unsafe because we change the document. + // Let's operate with block numbers instead. + const int startNumber = m_doc->findBlock(cursor.selectionStart()).blockNumber(); + const int endNumber = m_doc->findBlock(cursor.selectionEnd()).blockNumber(); + for (int currentBlockNumber = startNumber; currentBlockNumber <= endNumber; + ++currentBlockNumber) { + const QTextBlock currentBlock = m_doc->findBlockByNumber(currentBlockNumber); + if (currentBlock.isValid()) { + const int blocksAmount = m_doc->blockCount(); + indentBlock(currentBlock, typedChar); + QTC_CHECK(blocksAmount == m_doc->blockCount() + && "ClangFormat plugin indentation changed the amount of blocks."); + } + } + } else { + indentBlock(cursor.block(), typedChar); + } +} + +void ClangFormatBaseIndenter::indent(const QTextCursor &cursor, + const QChar &typedChar, + const TextEditor::TabSettings & /*tabSettings*/) +{ + indent(cursor, typedChar); +} + +void ClangFormatBaseIndenter::reindent(const QTextCursor &cursor, + const TextEditor::TabSettings & /*tabSettings*/) +{ + indent(cursor, QChar::Null); +} + +TextEditor::Replacements ClangFormatBaseIndenter::format( + const QTextCursor &cursor, const TextEditor::TabSettings & /*tabSettings*/) +{ + int utf8Offset; + int utf8Length; + const QByteArray buffer = m_doc->toPlainText().toUtf8(); + QTextBlock block = cursor.block(); + if (cursor.hasSelection()) { + block = m_doc->findBlock(cursor.selectionStart()); + const QTextBlock end = m_doc->findBlock(cursor.selectionEnd()); + utf8Offset = Utils::Text::utf8NthLineOffset(m_doc, buffer, block.blockNumber() + 1); + QTC_ASSERT(utf8Offset >= 0, return TextEditor::Replacements();); + utf8Length = selectedLines(m_doc, block, end).toUtf8().size(); + + } else { + const QTextBlock block = cursor.block(); + utf8Offset = Utils::Text::utf8NthLineOffset(m_doc, buffer, block.blockNumber() + 1); + QTC_ASSERT(utf8Offset >= 0, return TextEditor::Replacements();); + utf8Length = block.text().toUtf8().size(); + } + + const TextEditor::Replacements toReplace + = replacements(buffer, utf8Offset, utf8Length, block, QChar::Null, false); + applyReplacements(block, toReplace); + + return toReplace; +} + +void ClangFormatBaseIndenter::indentBlock(const QTextBlock &block, const QChar &typedChar) +{ + trimFirstNonEmptyBlock(block); + trimCurrentBlock(block); + const QByteArray buffer = m_doc->toPlainText().toUtf8(); + const int utf8Offset = Utils::Text::utf8NthLineOffset(m_doc, buffer, block.blockNumber() + 1); + QTC_ASSERT(utf8Offset >= 0, return;); + + applyReplacements(block, replacements(buffer, utf8Offset, 0, block, typedChar)); +} + +void ClangFormatBaseIndenter::indentBlock(const QTextBlock &block, + const QChar &typedChar, + const TextEditor::TabSettings & /*tabSettings*/) +{ + indentBlock(block, typedChar); +} + +int ClangFormatBaseIndenter::indentFor(const QTextBlock &block) +{ + trimFirstNonEmptyBlock(block); + trimCurrentBlock(block); + const QByteArray buffer = m_doc->toPlainText().toUtf8(); + const int utf8Offset = Utils::Text::utf8NthLineOffset(m_doc, buffer, block.blockNumber() + 1); + QTC_ASSERT(utf8Offset >= 0, return 0;); + + const TextEditor::Replacements toReplace = replacements(buffer, utf8Offset, 0, block); + + if (toReplace.empty()) + return -1; + + const TextEditor::Replacement &replacement = toReplace.front(); + int afterLineBreak = replacement.text.lastIndexOf('\n'); + afterLineBreak = (afterLineBreak < 0) ? 0 : afterLineBreak + 1; + return static_cast<int>(replacement.text.size() - afterLineBreak); +} + +int ClangFormatBaseIndenter::indentFor(const QTextBlock &block, + const TextEditor::TabSettings & /*tabSettings*/) +{ + return indentFor(block); +} + +bool ClangFormatBaseIndenter::isElectricCharacter(const QChar &ch) const +{ + switch (ch.toLatin1()) { + case '{': + case '}': + case ':': + case '#': + case '<': + case '>': + case ';': + case '(': + case ')': + case ',': + case '.': + return true; + } + return false; +} + +clang::format::FormatStyle ClangFormatBaseIndenter::styleForFile() const +{ + llvm::Expected<clang::format::FormatStyle> style + = clang::format::getStyle("file", m_fileName.toString().toStdString(), "none"); + if (style) + return *style; + + handleAllErrors(style.takeError(), [](const llvm::ErrorInfoBase &) { + // do nothing + }); + + return clang::format::getLLVMStyle(); +} + +TextEditor::Replacements ClangFormatBaseIndenter::replacements(QByteArray buffer, + int utf8Offset, + int utf8Length, + const QTextBlock &block, + const QChar &typedChar, + bool onlyIndention, + bool secondTry) const +{ + clang::format::FormatStyle style = styleForFile(); + + int originalOffsetUtf8 = utf8Offset; + int originalLengthUtf8 = utf8Length; + QByteArray originalBuffer = buffer; + + int extraOffset = 0; + if (onlyIndention) { + if (block.blockNumber() > kMaxLinesFromCurrentBlock) { + extraOffset = Utils::Text::utf8NthLineOffset(block.document(), + buffer, + block.blockNumber() + - kMaxLinesFromCurrentBlock); + } + int endOffset = Utils::Text::utf8NthLineOffset(block.document(), + buffer, + block.blockNumber() + + kMaxLinesFromCurrentBlock); + if (endOffset == -1) + endOffset = buffer.size(); + + buffer = buffer.mid(extraOffset, endOffset - extraOffset); + utf8Offset -= extraOffset; + + const int emptySpaceLength = previousEmptyLinesLength(block); + utf8Offset -= emptySpaceLength; + buffer.remove(utf8Offset, emptySpaceLength); + + extraOffset += emptySpaceLength; + + adjustFormatStyleForLineBreak(style); + if (typedChar == QChar::Null) + modifyToIndentEmptyLines(buffer, utf8Offset, utf8Length, block, secondTry); + } + + std::vector<clang::tooling::Range> ranges{ + {static_cast<unsigned int>(utf8Offset), static_cast<unsigned int>(utf8Length)}}; + clang::format::FormattingAttemptStatus status; + + clang::tooling::Replacements clangReplacements = reformat(style, + buffer.data(), + ranges, + m_fileName.toString().toStdString(), + &status); + + if (!status.FormatComplete) + return TextEditor::Replacements(); + + const clang::tooling::Replacements filtered = filteredReplacements(clangReplacements, + utf8Offset, + extraOffset, + onlyIndention); + const bool canTryAgain = onlyIndention && typedChar == QChar::Null && !secondTry; + if (canTryAgain && filtered.empty()) { + return replacements(originalBuffer, + originalOffsetUtf8, + originalLengthUtf8, + block, + typedChar, + onlyIndention, + true); + } + + return utf16Replacements(block, originalOffsetUtf8, originalBuffer, filtered); +} + +} // namespace ClangFormat diff --git a/src/plugins/clangformat/clangformatbaseindenter.h b/src/plugins/clangformat/clangformatbaseindenter.h new file mode 100644 index 0000000000..47137420d7 --- /dev/null +++ b/src/plugins/clangformat/clangformatbaseindenter.h @@ -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 diff --git a/src/plugins/clangformat/clangformatindenter.cpp b/src/plugins/clangformat/clangformatindenter.cpp index e5d14392e4..b52b4e9ce6 100644 --- a/src/plugins/clangformat/clangformatindenter.cpp +++ b/src/plugins/clangformat/clangformatindenter.cpp @@ -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 { - -void adjustFormatStyleForLineBreak(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; -} - -StringRef clearExtraNewline(StringRef text) -{ - while (text.startswith("\n\n")) - text = text.drop_front(); - return text; -} - -ClangReplacements filteredReplacements(const ClangReplacements &replacements, - int offset, - int extraOffsetToAdd, - bool onlyIndention) -{ - ClangReplacements filtered; - for (const ClangReplacement &replacement : replacements) { - int replacementOffset = static_cast<int>(replacement.getOffset()); - if (onlyIndention && replacementOffset != offset - 1) - continue; - - if (replacementOffset + 1 >= offset) - replacementOffset += extraOffsetToAdd; - - StringRef text = onlyIndention ? clearExtraNewline(replacement.getReplacementText()) - : replacement.getReplacementText(); - - Error error = filtered.add(ClangReplacement(replacement.getFilePath(), - static_cast<unsigned int>(replacementOffset), - replacement.getLength(), - text)); - // Throws if error is not checked. - if (error) - break; - } - return filtered; -} - -void trimFirstNonEmptyBlock(const QTextBlock ¤tBlock) -{ - QTextBlock prevBlock = currentBlock.previous(); - while (prevBlock.position() > 0 && prevBlock.text().trimmed().isEmpty()) - prevBlock = prevBlock.previous(); - - if (prevBlock.text().trimmed().isEmpty()) - return; - - const QString initialText = prevBlock.text(); - if (!initialText.at(initialText.size() - 1).isSpace()) - return; - - auto lastNonSpace = std::find_if_not(initialText.rbegin(), - initialText.rend(), - [](const QChar &letter) { - return letter.isSpace(); - }); - const int extraSpaceCount = static_cast<int>(std::distance(initialText.rbegin(), lastNonSpace)); - - QTextCursor cursor(prevBlock); - cursor.beginEditBlock(); - cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, - initialText.size() - extraSpaceCount); - cursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, extraSpaceCount); - cursor.removeSelectedText(); - cursor.endEditBlock(); -} - -void trimCurrentBlock(const QTextBlock ¤tBlock) -{ - if (currentBlock.text().trimmed().isEmpty()) { - // Clear the block containing only spaces - QTextCursor cursor(currentBlock); - cursor.beginEditBlock(); - cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor); - cursor.removeSelectedText(); - cursor.endEditBlock(); - } -} - -// Returns the total langth of previous lines with pure whitespace. -int previousEmptyLinesLength(const QTextBlock ¤tBlock) -{ - int length{0}; - QTextBlock prevBlock = currentBlock.previous(); - while (prevBlock.position() > 0 && prevBlock.text().trimmed().isEmpty()) { - length += prevBlock.text().length() + 1; - prevBlock = prevBlock.previous(); - } - - return length; -} - -void modifyToIndentEmptyLines( - QByteArray &buffer, int offset, int &length, const QTextBlock &block, bool secondTry) -{ - const QString blockText = block.text().trimmed(); - const bool closingParenBlock = blockText.startsWith(')'); - if (blockText.isEmpty() || closingParenBlock) { - //This extra text works for the most cases. - QByteArray dummyText("a;"); - - // Search for previous character - QTextBlock prevBlock = block.previous(); - bool prevBlockIsEmpty = prevBlock.position() > 0 && prevBlock.text().trimmed().isEmpty(); - while (prevBlockIsEmpty) { - prevBlock = prevBlock.previous(); - prevBlockIsEmpty = prevBlock.position() > 0 && prevBlock.text().trimmed().isEmpty(); - } - if (prevBlock.text().endsWith(',')) - dummyText = "int a"; - - if (closingParenBlock) { - if (prevBlock.text().endsWith(',')) - dummyText = "int a"; - else - dummyText = "&& a"; - } - - length += dummyText.length(); - buffer.insert(offset, dummyText); - } +ClangFormatIndenter::ClangFormatIndenter(QTextDocument *doc) + : ClangFormatBaseIndenter(doc) +{} - 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) +FormatStyle ClangFormatIndenter::styleForFile() const { - 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); + return ClangFormat::styleForFile(m_fileName); } -TabSettings ClangFormatIndenter::tabSettings() const +Utils::optional<TabSettings> ClangFormatIndenter::tabSettings() const { FormatStyle style = currentProjectStyle(); TabSettings tabSettings; diff --git a/src/plugins/clangformat/clangformatindenter.h b/src/plugins/clangformat/clangformatindenter.h index a12ffaee44..5631e32db1 100644 --- a/src/plugins/clangformat/clangformatindenter.h +++ b/src/plugins/clangformat/clangformatindenter.h @@ -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; - - bool isElectricCharacter(const QChar &ch) const override; + ClangFormatIndenter(QTextDocument *doc); + Utils::optional<TextEditor::TabSettings> tabSettings() const override; - bool hasTabSettings() const override { return true; } - TextEditor::TabSettings tabSettings() const override; +private: + clang::format::FormatStyle styleForFile() const override; }; } // namespace ClangFormat diff --git a/src/plugins/clangformat/clangformatplugin.cpp b/src/plugins/clangformat/clangformatplugin.cpp index d5882aab58..2d848d9501 100644 --- a/src/plugins/clangformat/clangformatplugin.cpp +++ b/src/plugins/clangformat/clangformatplugin.cpp @@ -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); } }; diff --git a/src/plugins/clangtools/clangfixitsrefactoringchanges.cpp b/src/plugins/clangtools/clangfixitsrefactoringchanges.cpp index feff3a8e2c..88b17fb42a 100644 --- a/src/plugins/clangtools/clangfixitsrefactoringchanges.cpp +++ b/src/plugins/clangtools/clangfixitsrefactoringchanges.cpp @@ -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()) diff --git a/src/plugins/clangtools/clangfixitsrefactoringchanges.h b/src/plugins/clangtools/clangfixitsrefactoringchanges.h index 63aea839b1..c1fcf38c09 100644 --- a/src/plugins/clangtools/clangfixitsrefactoringchanges.h +++ b/src/plugins/clangtools/clangfixitsrefactoringchanges.h @@ -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 { diff --git a/src/plugins/cmakeprojectmanager/cmakeeditor.cpp b/src/plugins/cmakeprojectmanager/cmakeeditor.cpp index 122569616e..c5c0fe5b74 100644 --- a/src/plugins/cmakeprojectmanager/cmakeeditor.cpp +++ b/src/plugins/cmakeprojectmanager/cmakeeditor.cpp @@ -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); diff --git a/src/plugins/cmakeprojectmanager/cmakeindenter.cpp b/src/plugins/cmakeprojectmanager/cmakeindenter.cpp index 882f0277a6..3ba47a634d 100644 --- a/src/plugins/cmakeprojectmanager/cmakeindenter.cpp +++ b/src/plugins/cmakeprojectmanager/cmakeindenter.cpp @@ -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) diff --git a/src/plugins/cmakeprojectmanager/cmakeindenter.h b/src/plugins/cmakeprojectmanager/cmakeindenter.h index a47bdcced5..f7d80b0e0b 100644 --- a/src/plugins/cmakeprojectmanager/cmakeindenter.h +++ b/src/plugins/cmakeprojectmanager/cmakeindenter.h @@ -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 diff --git a/src/plugins/cppeditor/cppeditor.cpp b/src/plugins/cppeditor/cppeditor.cpp index a2b622a2f9..e7064d3165 100644 --- a/src/plugins/cppeditor/cppeditor.cpp +++ b/src/plugins/cppeditor/cppeditor.cpp @@ -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); } diff --git a/src/plugins/cppeditor/cppeditordocument.cpp b/src/plugins/cppeditor/cppeditordocument.cpp index 68e3e6547e..c8951c12eb 100644 --- a/src/plugins/cppeditor/cppeditordocument.cpp +++ b/src/plugins/cppeditor/cppeditordocument.cpp @@ -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 diff --git a/src/plugins/cpptools/cppcodestylepreferencesfactory.cpp b/src/plugins/cpptools/cppcodestylepreferencesfactory.cpp index 00da046edc..4c0ca8d9a5 100644 --- a/src/plugins/cpptools/cppcodestylepreferencesfactory.cpp +++ b/src/plugins/cpptools/cppcodestylepreferencesfactory.cpp @@ -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 diff --git a/src/plugins/cpptools/cppcodestylepreferencesfactory.h b/src/plugins/cpptools/cppcodestylepreferencesfactory.h index 233022a31a..eefe3adce2 100644 --- a/src/plugins/cpptools/cppcodestylepreferencesfactory.h +++ b/src/plugins/cpptools/cppcodestylepreferencesfactory.h @@ -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; }; diff --git a/src/plugins/cpptools/cppcodestylesettingspage.cpp b/src/plugins/cpptools/cppcodestylesettingspage.cpp index c80537db9b..f803516358 100644 --- a/src/plugins/cpptools/cppcodestylesettingspage.cpp +++ b/src/plugins/cpptools/cppcodestylesettingspage.cpp @@ -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(); } diff --git a/src/plugins/cpptools/cppmodelmanager.cpp b/src/plugins/cpptools/cppmodelmanager.cpp index 45ab82efec..f5c257b6d2 100644 --- a/src/plugins/cpptools/cppmodelmanager.cpp +++ b/src/plugins/cpptools/cppmodelmanager.cpp @@ -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> diff --git a/src/plugins/cpptools/cppmodelmanager.h b/src/plugins/cpptools/cppmodelmanager.h index 0eac5abf87..302e447ade 100644 --- a/src/plugins/cpptools/cppmodelmanager.h +++ b/src/plugins/cpptools/cppmodelmanager.h @@ -49,7 +49,6 @@ namespace CPlusPlus { class LookupContext; } namespace ProjectExplorer { class Project; } namespace TextEditor { class BaseHoverHandler; -class Indenter; class TextDocument; } // namespace TextEditor diff --git a/src/plugins/cpptools/cppqtstyleindenter.cpp b/src/plugins/cpptools/cppqtstyleindenter.cpp index 54cb3cc9ed..97ffd666e3 100644 --- a/src/plugins/cpptools/cppqtstyleindenter.cpp +++ b/src/plugins/cpptools/cppqtstyleindenter.cpp @@ -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()); diff --git a/src/plugins/cpptools/cppqtstyleindenter.h b/src/plugins/cpptools/cppqtstyleindenter.h index 05a36c883e..3b8fb07168 100644 --- a/src/plugins/cpptools/cppqtstyleindenter.h +++ b/src/plugins/cpptools/cppqtstyleindenter.h @@ -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 diff --git a/src/plugins/cpptools/cpprefactoringchanges.cpp b/src/plugins/cpptools/cpprefactoringchanges.cpp index 929a6ec020..138a5a3693 100644 --- a/src/plugins/cpptools/cpprefactoringchanges.cpp +++ b/src/plugins/cpptools/cpprefactoringchanges.cpp @@ -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 diff --git a/src/plugins/fakevim/fakevimplugin.cpp b/src/plugins/fakevim/fakevimplugin.cpp index 537e47b18a..1807efb910 100644 --- a/src/plugins/fakevim/fakevimplugin.cpp +++ b/src/plugins/fakevim/fakevimplugin.cpp @@ -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(); } diff --git a/src/plugins/glsleditor/glsleditor.cpp b/src/plugins/glsleditor/glsleditor.cpp index 33a4dafa47..c018e15f90 100644 --- a/src/plugins/glsleditor/glsleditor.cpp +++ b/src/plugins/glsleditor/glsleditor.cpp @@ -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); diff --git a/src/plugins/glsleditor/glslindenter.cpp b/src/plugins/glsleditor/glslindenter.cpp index b2c533f1e2..87b12f98e9 100644 --- a/src/plugins/glsleditor/glslindenter.cpp +++ b/src/plugins/glsleditor/glslindenter.cpp @@ -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()); diff --git a/src/plugins/glsleditor/glslindenter.h b/src/plugins/glsleditor/glslindenter.h index 75495f8303..beb6aaa849 100644 --- a/src/plugins/glsleditor/glslindenter.h +++ b/src/plugins/glsleditor/glslindenter.h @@ -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 diff --git a/src/plugins/nim/editor/nimeditorfactory.cpp b/src/plugins/nim/editor/nimeditorfactory.cpp index 91a57adcb2..c6f4f17dff 100644 --- a/src/plugins/nim/editor/nimeditorfactory.cpp +++ b/src/plugins/nim/editor/nimeditorfactory.cpp @@ -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())); } } diff --git a/src/plugins/nim/editor/nimindenter.cpp b/src/plugins/nim/editor/nimindenter.cpp index 8adf4a0730..0b61a4536b 100644 --- a/src/plugins/nim/editor/nimindenter.cpp +++ b/src/plugins/nim/editor/nimindenter.cpp @@ -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(); diff --git a/src/plugins/nim/editor/nimindenter.h b/src/plugins/nim/editor/nimindenter.h index adf09c6a62..0342eda753 100644 --- a/src/plugins/nim/editor/nimindenter.h +++ b/src/plugins/nim/editor/nimindenter.h @@ -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 diff --git a/src/plugins/nim/settings/nimcodestylepreferencesfactory.cpp b/src/plugins/nim/settings/nimcodestylepreferencesfactory.cpp index 997622278e..299c6d13e5 100644 --- a/src/plugins/nim/settings/nimcodestylepreferencesfactory.cpp +++ b/src/plugins/nim/settings/nimcodestylepreferencesfactory.cpp @@ -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 diff --git a/src/plugins/nim/settings/nimcodestylepreferencesfactory.h b/src/plugins/nim/settings/nimcodestylepreferencesfactory.h index 987ad253e3..e3d7ce3251 100644 --- a/src/plugins/nim/settings/nimcodestylepreferencesfactory.h +++ b/src/plugins/nim/settings/nimcodestylepreferencesfactory.h @@ -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; }; diff --git a/src/plugins/nim/settings/nimcodestylepreferenceswidget.cpp b/src/plugins/nim/settings/nimcodestylepreferenceswidget.cpp index 2f98932cc2..b4e735ff48 100644 --- a/src/plugins/nim/settings/nimcodestylepreferenceswidget.cpp +++ b/src/plugins/nim/settings/nimcodestylepreferenceswidget.cpp @@ -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(); diff --git a/src/plugins/projectexplorer/jsonwizard/jsonwizardgeneratorfactory.cpp b/src/plugins/projectexplorer/jsonwizard/jsonwizardgeneratorfactory.cpp index 013de06c0a..e0a06c824e 100644 --- a/src/plugins/projectexplorer/jsonwizard/jsonwizardgeneratorfactory.cpp +++ b/src/plugins/projectexplorer/jsonwizard/jsonwizardgeneratorfactory.cpp @@ -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); + QTextDocument doc(file->contents()); + QTextCursor cursor(&doc); 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(); diff --git a/src/plugins/projectexplorer/projectfilewizardextension.cpp b/src/plugins/projectexplorer/projectfilewizardextension.cpp index 4b4ecaeb30..5dab7bbf99 100644 --- a/src/plugins/projectexplorer/projectfilewizardextension.cpp +++ b/src/plugins/projectexplorer/projectfilewizardextension.cpp @@ -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(); diff --git a/src/plugins/pythoneditor/pythoneditor.cpp b/src/plugins/pythoneditor/pythoneditor.cpp index 4c8b95ad92..d0f27ae63c 100644 --- a/src/plugins/pythoneditor/pythoneditor.cpp +++ b/src/plugins/pythoneditor/pythoneditor.cpp @@ -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); diff --git a/src/plugins/pythoneditor/pythonindenter.cpp b/src/plugins/pythoneditor/pythonindenter.cpp index 035f1aef7c..ee7813f327 100644 --- a/src/plugins/pythoneditor/pythonindenter.cpp +++ b/src/plugins/pythoneditor/pythonindenter.cpp @@ -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 diff --git a/src/plugins/pythoneditor/pythonindenter.h b/src/plugins/pythoneditor/pythonindenter.h index 414c5dcb25..8ce10ba4fe 100644 --- a/src/plugins/pythoneditor/pythonindenter.h +++ b/src/plugins/pythoneditor/pythonindenter.h @@ -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; diff --git a/src/plugins/qmldesigner/designercore/model/basetexteditmodifier.cpp b/src/plugins/qmldesigner/designercore/model/basetexteditmodifier.cpp index 612f3003e5..a5fddccba9 100644 --- a/src/plugins/qmldesigner/designercore/model/basetexteditmodifier.cpp +++ b/src/plugins/qmldesigner/designercore/model/basetexteditmodifier.cpp @@ -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(); diff --git a/src/plugins/qmljseditor/qmljseditor.cpp b/src/plugins/qmljseditor/qmljseditor.cpp index dadd9bee60..ee3f30b2fe 100644 --- a/src/plugins/qmljseditor/qmljseditor.cpp +++ b/src/plugins/qmljseditor/qmljseditor.cpp @@ -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); } diff --git a/src/plugins/qmljseditor/qmljseditordocument.cpp b/src/plugins/qmljseditor/qmljseditordocument.cpp index ae18e6a573..2f30f324cb 100644 --- a/src/plugins/qmljseditor/qmljseditordocument.cpp +++ b/src/plugins/qmljseditor/qmljseditordocument.cpp @@ -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() diff --git a/src/plugins/qmljseditor/quicktoolbar.cpp b/src/plugins/qmljseditor/quicktoolbar.cpp index 89c6dc349b..15dec5a80c 100644 --- a/src/plugins/qmljseditor/quicktoolbar.cpp +++ b/src/plugins/qmljseditor/quicktoolbar.cpp @@ -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); } } } diff --git a/src/plugins/qmljstools/qmljscodestylepreferencesfactory.cpp b/src/plugins/qmljstools/qmljscodestylepreferencesfactory.cpp index f49f88929f..3ca3bbc999 100644 --- a/src/plugins/qmljstools/qmljscodestylepreferencesfactory.cpp +++ b/src/plugins/qmljstools/qmljscodestylepreferencesfactory.cpp @@ -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 diff --git a/src/plugins/qmljstools/qmljscodestylepreferencesfactory.h b/src/plugins/qmljstools/qmljscodestylepreferencesfactory.h index 18f40a98d3..111103f243 100644 --- a/src/plugins/qmljstools/qmljscodestylepreferencesfactory.h +++ b/src/plugins/qmljstools/qmljscodestylepreferencesfactory.h @@ -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; }; diff --git a/src/plugins/qmljstools/qmljscodestylesettingspage.cpp b/src/plugins/qmljstools/qmljscodestylesettingspage.cpp index f13ac29f75..2de503b782 100644 --- a/src/plugins/qmljstools/qmljscodestylesettingspage.cpp +++ b/src/plugins/qmljstools/qmljscodestylesettingspage.cpp @@ -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(); diff --git a/src/plugins/qmljstools/qmljsindenter.cpp b/src/plugins/qmljstools/qmljsindenter.cpp index 416267e695..3e156d756e 100644 --- a/src/plugins/qmljstools/qmljsindenter.cpp +++ b/src/plugins/qmljstools/qmljsindenter.cpp @@ -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; diff --git a/src/plugins/qmljstools/qmljsindenter.h b/src/plugins/qmljstools/qmljsindenter.h index 1c82a1b18c..3a51790556 100644 --- a/src/plugins/qmljstools/qmljsindenter.h +++ b/src/plugins/qmljstools/qmljsindenter.h @@ -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 diff --git a/src/plugins/qmljstools/qmljsrefactoringchanges.cpp b/src/plugins/qmljstools/qmljsrefactoringchanges.cpp index b27631c2d0..3fda024e4e 100644 --- a/src/plugins/qmljstools/qmljsrefactoringchanges.cpp +++ b/src/plugins/qmljstools/qmljsrefactoringchanges.cpp @@ -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 diff --git a/src/plugins/texteditor/codestyleeditor.cpp b/src/plugins/texteditor/codestyleeditor.cpp index cbf74c0a12..edfc40d12b 100644 --- a/src/plugins/texteditor/codestyleeditor.cpp +++ b/src/plugins/texteditor/codestyleeditor.cpp @@ -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(); diff --git a/src/plugins/texteditor/icodestylepreferencesfactory.h b/src/plugins/texteditor/icodestylepreferencesfactory.h index 43cc6edffd..2acc0a5fa4 100644 --- a/src/plugins/texteditor/icodestylepreferencesfactory.h +++ b/src/plugins/texteditor/icodestylepreferencesfactory.h @@ -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; }; diff --git a/src/plugins/texteditor/indenter.h b/src/plugins/texteditor/indenter.h index 300e92a135..5b4b97fbfa 100644 --- a/src/plugins/texteditor/indenter.h +++ b/src/plugins/texteditor/indenter.h @@ -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; } + + virtual void setCodeStylePreferences(ICodeStylePreferences * /*preferences*/) {} + + virtual void invalidateCache() {} + + 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 TabSettings & /*tabSettings*/) + = 0; + virtual Utils::optional<TabSettings> tabSettings() const = 0; // Indent a text block based on previous line. Default does nothing - virtual void indentBlock(QTextDocument *doc, - const QTextBlock &block, + virtual void indentBlock(const QTextBlock &block, const QChar &typedChar, - const TabSettings &tabSettings); + const TabSettings &tabSettings) + = 0; // Indent at cursor. Calls indentBlock for selection or current line. - virtual void indent(QTextDocument *doc, - const QTextCursor &cursor, + virtual void indent(const QTextCursor &cursor, const QChar &typedChar, - const TabSettings &tabSettings, - bool autoTriggered = true); + const TabSettings &tabSettings) + = 0; // By default just calls indent with default settings. - virtual Replacements format(QTextDocument *doc, - const Utils::FileName &fileName, - const QTextCursor &cursor, - const TabSettings &tabSettings); + 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(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); - - // Expects a list of blocks in order of occurrence in the document. - virtual IndentationForBlock indentationForBlocks(const QVector<QTextBlock> &blocks, - const TextEditor::TabSettings &tabSettings); + virtual void reindent(const QTextCursor &cursor, const TabSettings &tabSettings) = 0; - virtual bool hasTabSettings() const { return false; } - virtual TabSettings tabSettings() const { return TabSettings(); } +protected: + QTextDocument *m_doc; + Utils::FileName m_fileName; }; } // namespace TextEditor diff --git a/src/plugins/texteditor/normalindenter.cpp b/src/plugins/texteditor/normalindenter.cpp index d1956d83d8..7faea84c48 100644 --- a/src/plugins/texteditor/normalindenter.cpp +++ b/src/plugins/texteditor/normalindenter.cpp @@ -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); diff --git a/src/plugins/texteditor/normalindenter.h b/src/plugins/texteditor/normalindenter.h index 119c2b8f66..ece41fa52d 100644 --- a/src/plugins/texteditor/normalindenter.h +++ b/src/plugins/texteditor/normalindenter.h @@ -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; diff --git a/src/plugins/texteditor/plaintexteditorfactory.cpp b/src/plugins/texteditor/plaintexteditorfactory.cpp index f585115e7f..02e89b4227 100644 --- a/src/plugins/texteditor/plaintexteditorfactory.cpp +++ b/src/plugins/texteditor/plaintexteditorfactory.cpp @@ -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 | diff --git a/src/plugins/texteditor/textdocument.cpp b/src/plugins/texteditor/textdocument.cpp index 7edbdd1488..ef504447a5 100644 --- a/src/plugins/texteditor/textdocument.cpp +++ b/src/plugins/texteditor/textdocument.cpp @@ -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(); diff --git a/src/plugins/texteditor/textdocument.h b/src/plugins/texteditor/textdocument.h index f293b3aeaa..eaa102ec74 100644 --- a/src/plugins/texteditor/textdocument.h +++ b/src/plugins/texteditor/textdocument.h @@ -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, diff --git a/src/plugins/texteditor/texteditor.cpp b/src/plugins/texteditor/texteditor.cpp index e2d46141fb..d7ceb83144 100644 --- a/src/plugins/texteditor/texteditor.cpp +++ b/src/plugins/texteditor/texteditor.cpp @@ -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()); diff --git a/src/plugins/texteditor/texteditor.h b/src/plugins/texteditor/texteditor.h index 8615f15295..8ea2606bd2 100644 --- a/src/plugins/texteditor/texteditor.h +++ b/src/plugins/texteditor/texteditor.h @@ -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); diff --git a/src/plugins/texteditor/texteditor.pro b/src/plugins/texteditor/texteditor.pro index 9b698b7340..d9f99ccc9d 100644 --- a/src/plugins/texteditor/texteditor.pro +++ b/src/plugins/texteditor/texteditor.pro @@ -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 \ diff --git a/src/plugins/texteditor/texteditor.qbs b/src/plugins/texteditor/texteditor.qbs index 052a8f13fb..01fbcc68ab 100644 --- a/src/plugins/texteditor/texteditor.qbs +++ b/src/plugins/texteditor/texteditor.qbs @@ -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", diff --git a/src/plugins/texteditor/indenter.cpp b/src/plugins/texteditor/textindenter.cpp index 6f0dce63cf..bca7b1ae18 100644 --- a/src/plugins/texteditor/indenter.cpp +++ b/src/plugins/texteditor/textindenter.cpp @@ -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 *) -{ - -} - -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) +Utils::optional<TabSettings> TextIndenter::tabSettings() const { - IndentationForBlock ret; - foreach (QTextBlock block, blocks) - ret.insert(block.blockNumber(), indentFor(block, tabSettings)); - return ret; + return Utils::optional<TabSettings>(); } diff --git a/src/plugins/texteditor/textindenter.h b/src/plugins/texteditor/textindenter.h new file mode 100644 index 0000000000..8a87b9214d --- /dev/null +++ b/src/plugins/texteditor/textindenter.h @@ -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 diff --git a/src/shared/clang/clang_installation.pri b/src/shared/clang/clang_installation.pri index acd555dd06..4629aedaab 100644 --- a/src/shared/clang/clang_installation.pri +++ b/src/shared/clang/clang_installation.pri @@ -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 = |