// Copyright (C) 2019 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "clangformatbaseindenter.h" #include "clangformatutils.h" #include "llvmfilesystem.h" #include #include #include #include #include #include #include #include #include #include #include #include #include namespace ClangFormat { Internal::LlvmFileSystemAdapter llvmFileSystemAdapter = {}; namespace { void adjustFormatStyleForLineBreak(clang::format::FormatStyle &style, ReplacementsToKeep replacementsToKeep) { style.MaxEmptyLinesToKeep = 100; #if LLVM_VERSION_MAJOR >= 13 style.SortIncludes = clang::format::FormatStyle::SI_Never; #else style.SortIncludes = false; #endif #if LLVM_VERSION_MAJOR >= 16 style.SortUsingDeclarations = clang::format::FormatStyle::SUD_Never; #else style.SortUsingDeclarations = false; #endif // This is a separate pass, don't do it unless it's the full formatting. style.FixNamespaceComments = false; #if LLVM_VERSION_MAJOR >= 16 style.AlignTrailingComments = {clang::format::FormatStyle::TCAS_Never, 0}; #else style.AlignTrailingComments = false; #endif if (replacementsToKeep == ReplacementsToKeep::IndentAndBefore) return; style.ColumnLimit = 0; } llvm::StringRef clearExtraNewline(llvm::StringRef text) { while (text.startswith("\n\n")) text = text.drop_front(); return text; } clang::tooling::Replacements filteredReplacements(const QByteArray &buffer, const clang::tooling::Replacements &replacements, int utf8Offset, int utf8Length, ReplacementsToKeep replacementsToKeep) { clang::tooling::Replacements filtered; for (const clang::tooling::Replacement &replacement : replacements) { int replacementOffset = static_cast(replacement.getOffset()); // Skip everything after. if (replacementOffset >= utf8Offset + utf8Length) return filtered; const bool isNotIndentOrInRange = replacementOffset < utf8Offset - 1 || buffer.at(replacementOffset) != '\n'; if (isNotIndentOrInRange && replacementsToKeep == ReplacementsToKeep::OnlyIndent) continue; llvm::StringRef text = replacementsToKeep == ReplacementsToKeep::OnlyIndent ? clearExtraNewline(replacement.getReplacementText()) : replacement.getReplacementText(); if (replacementsToKeep == ReplacementsToKeep::OnlyIndent && int(text.count('\n')) != buffer.mid(replacementOffset, replacement.getLength()).count('\n')) { continue; } llvm::Error error = filtered.add( clang::tooling::Replacement(replacement.getFilePath(), static_cast(replacementOffset), replacement.getLength(), text)); // Throws if error is not checked. if (error) { error = llvm::handleErrors(std::move(error), [](const llvm::ErrorInfoBase &) -> llvm::Error { return llvm::Error::success(); }); QTC_CHECK(!error && "Error must be a \"success\" at this point"); break; } } return filtered; } void trimRHSWhitespace(const QTextBlock &block) { const QString initialText = block.text(); if (!initialText.rbegin()->isSpace()) return; auto lastNonSpace = std::find_if_not(initialText.rbegin(), initialText.rend(), [](const QChar &letter) { return letter.isSpace(); }); const int extraSpaceCount = static_cast(std::distance(initialText.rbegin(), lastNonSpace)); QTextCursor cursor(block); cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, initialText.size() - extraSpaceCount); cursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, extraSpaceCount); cursor.removeSelectedText(); } QTextBlock reverseFindLastEmptyBlock(QTextBlock start) { if (start.position() > 0) { start = start.previous(); while (start.position() > 0 && start.text().trimmed().isEmpty()) start = start.previous(); if (!start.text().trimmed().isEmpty()) start = start.next(); } return start; } QTextBlock reverseFindLastBlockWithSymbol(QTextBlock start, QChar ch) { if (start.position() > 0) { start = start.previous(); while (start.position() > 0 && !start.text().contains(ch)) start = start.previous(); } return start; } enum class CharacterContext { AfterComma, LastAfterComma, NewStatementOrContinuation, IfOrElseWithoutScope, Unknown }; QChar findFirstNonWhitespaceCharacter(const QTextBlock ¤tBlock) { const QTextDocument *doc = currentBlock.document(); int currentPos = currentBlock.position(); while (currentPos < doc->characterCount() && doc->characterAt(currentPos).isSpace()) ++currentPos; return currentPos < doc->characterCount() ? doc->characterAt(currentPos) : QChar::Null; } int findMatchingOpeningParen(const QTextBlock &blockEndingWithClosingParen) { const QTextDocument *doc = blockEndingWithClosingParen.document(); int currentPos = blockEndingWithClosingParen.position() + blockEndingWithClosingParen.text().lastIndexOf(')'); int parenBalance = 1; while (currentPos > 0 && parenBalance > 0) { --currentPos; if (doc->characterAt(currentPos) == ')') ++parenBalance; if (doc->characterAt(currentPos) == '(') --parenBalance; } if (parenBalance == 0) return currentPos; return -1; } bool comesDirectlyAfterIf(const QTextDocument *doc, int pos) { --pos; while (pos > 0 && doc->characterAt(pos).isSpace()) --pos; return pos > 0 && doc->characterAt(pos) == 'f' && doc->characterAt(pos - 1) == 'i'; } CharacterContext characterContext(const QTextBlock ¤tBlock) { QTextBlock previousNonEmptyBlock = reverseFindLastEmptyBlock(currentBlock); if (previousNonEmptyBlock.position() > 0) previousNonEmptyBlock = previousNonEmptyBlock.previous(); const QString prevLineText = previousNonEmptyBlock.text().trimmed(); if (prevLineText.isEmpty()) return CharacterContext::NewStatementOrContinuation; const QChar firstNonWhitespaceChar = findFirstNonWhitespaceCharacter(currentBlock); if (prevLineText.endsWith(',')) { if (firstNonWhitespaceChar == '}') { if (reverseFindLastBlockWithSymbol(currentBlock, '{').text().trimmed().last(1) == '{') return CharacterContext::NewStatementOrContinuation; return CharacterContext::LastAfterComma; } if (firstNonWhitespaceChar == ')') { if (reverseFindLastBlockWithSymbol(currentBlock, '(').text().trimmed().last(1) == '(') return CharacterContext::NewStatementOrContinuation; return CharacterContext::LastAfterComma; } return CharacterContext::AfterComma; } if (prevLineText.endsWith("else")) return CharacterContext::IfOrElseWithoutScope; if (prevLineText.endsWith(')')) { const int pos = findMatchingOpeningParen(previousNonEmptyBlock); if (pos >= 0 && comesDirectlyAfterIf(previousNonEmptyBlock.document(), pos)) return CharacterContext::IfOrElseWithoutScope; } return CharacterContext::NewStatementOrContinuation; } bool nextBlockExistsAndEmpty(const QTextBlock ¤tBlock) { QTextBlock nextBlock = currentBlock.next(); if (!nextBlock.isValid() || nextBlock.position() == currentBlock.position()) return false; return nextBlock.text().trimmed().isEmpty(); } QByteArray dummyTextForContext(CharacterContext context, bool closingBraceBlock) { if (closingBraceBlock && context == CharacterContext::NewStatementOrContinuation) return QByteArray(); switch (context) { case CharacterContext::AfterComma: return "a,"; case CharacterContext::LastAfterComma: return "a"; case CharacterContext::IfOrElseWithoutScope: return ";"; case CharacterContext::NewStatementOrContinuation: return "/*//*/"; case CharacterContext::Unknown: default: QTC_ASSERT(false, return "";); } } // Add extra text in case of the empty line or the line starting with ')'. // Track such extra pieces of text in isInsideDummyTextInLine(). int forceIndentWithExtraText(QByteArray &buffer, CharacterContext &charContext, const QTextBlock &block, bool secondTry) { if (!block.isValid()) return 0; auto tmpcharContext = characterContext(block); if (charContext == CharacterContext::LastAfterComma && tmpcharContext == CharacterContext::LastAfterComma) { charContext = CharacterContext::AfterComma; } else { charContext = tmpcharContext; } const QString blockText = block.text(); int firstNonWhitespace = Utils::indexOf(blockText, [](const QChar &ch) { return !ch.isSpace(); }); int utf8Offset = Utils::Text::utf8NthLineOffset(block.document(), buffer, block.blockNumber() + 1); if (firstNonWhitespace >= 0) utf8Offset += firstNonWhitespace; else utf8Offset += blockText.length(); const bool closingParenBlock = firstNonWhitespace >= 0 && blockText.at(firstNonWhitespace) == ')'; const bool closingBraceBlock = firstNonWhitespace >= 0 && blockText.at(firstNonWhitespace) == '}'; int extraLength = 0; QByteArray dummyText; if (firstNonWhitespace < 0 && charContext != CharacterContext::Unknown && nextBlockExistsAndEmpty(block)) { // If the next line is also empty it's safer to use a comment line. dummyText = "//"; } else if (firstNonWhitespace < 0 || closingParenBlock || closingBraceBlock) { dummyText = dummyTextForContext(charContext, closingBraceBlock); } // A comment at the end of the line appears to prevent clang-format from removing line breaks. if (dummyText == "/*//*/" || dummyText.isEmpty()) { if (block.previous().isValid()) { const int prevEndOffset = Utils::Text::utf8NthLineOffset(block.document(), buffer, block.blockNumber()) + block.previous().text().length(); buffer.insert(prevEndOffset, " //"); extraLength += 3; } } buffer.insert(utf8Offset + extraLength, dummyText); extraLength += dummyText.length(); if (secondTry) { int nextLinePos = buffer.indexOf('\n', utf8Offset); if (nextLinePos < 0) nextLinePos = buffer.size() - 1; if (nextLinePos > 0) { // If first try was not successful try to put ')' in the end of the line to close possibly // unclosed parenthesis. // TODO: Does it help to add different endings depending on the context? buffer.insert(nextLinePos, ')'); extraLength += 1; } } return extraLength; } bool isInsideDummyTextInLine(const QString &originalLine, const QString &modifiedLine, int column) { // Detect the cases when we have inserted extra text into the line to get the indentation. return originalLine.length() < modifiedLine.length() && column != modifiedLine.length() + 1 && (column > originalLine.length() || originalLine.trimmed().isEmpty() || !modifiedLine.startsWith(originalLine)); } static Utils::Text::Position utf16LineColumn(const QByteArray &utf8Buffer, int utf8Offset) { Utils::Text::Position position; position.line = static_cast(std::count(utf8Buffer.begin(), utf8Buffer.begin() + utf8Offset, '\n')) + 1; const int startOfLineOffset = utf8Offset ? (utf8Buffer.lastIndexOf('\n', utf8Offset - 1) + 1) : 0; position.column = QString::fromUtf8(utf8Buffer.mid(startOfLineOffset, utf8Offset - startOfLineOffset)).length(); return position; } Utils::Text::Replacements utf16Replacements(const QTextDocument *doc, const QByteArray &utf8Buffer, const clang::tooling::Replacements &replacements) { Utils::Text::Replacements convertedReplacements; convertedReplacements.reserve(replacements.size()); for (const clang::tooling::Replacement &replacement : replacements) { Utils::Text::Position lineColUtf16 = utf16LineColumn( utf8Buffer, static_cast(replacement.getOffset())); if (!lineColUtf16.isValid()) continue; const QString lineText = doc->findBlockByNumber(lineColUtf16.line - 1).text(); const QString bufferLineText = Utils::Text::utf16LineTextInUtf8Buffer(utf8Buffer, static_cast(replacement.getOffset())); if (isInsideDummyTextInLine(lineText, bufferLineText, lineColUtf16.column + 1)) continue; lineColUtf16.column = std::min(lineColUtf16.column, int(lineText.length())); const int utf16Offset = Utils::Text::positionInText(doc, lineColUtf16.line, lineColUtf16.column + 1); const int utf16Length = QString::fromUtf8( utf8Buffer.mid(static_cast(replacement.getOffset()), static_cast(replacement.getLength()))) .size(); convertedReplacements.emplace_back(utf16Offset, utf16Length, QString::fromStdString(replacement.getReplacementText().str())); } return convertedReplacements; } QString selectedLines(QTextDocument *doc, const QTextBlock &startBlock, const QTextBlock &endBlock) { return Utils::Text::textAt(QTextCursor(doc), startBlock.position(), std::max(0, endBlock.position() + endBlock.length() - startBlock.position() - 1)); } int indentationForBlock(const Utils::Text::Replacements &toReplace, const QByteArray &buffer, const QTextBlock ¤tBlock) { const int utf8Offset = Utils::Text::utf8NthLineOffset(currentBlock.document(), buffer, currentBlock.blockNumber() + 1); auto replacementIt = std::find_if(toReplace.begin(), toReplace.end(), [utf8Offset](const Utils::Text::Replacement &replacement) { return replacement.offset == utf8Offset - 1; }); if (replacementIt == toReplace.end()) return -1; int afterLineBreak = replacementIt->text.lastIndexOf('\n'); afterLineBreak = (afterLineBreak < 0) ? 0 : afterLineBreak + 1; return static_cast(replacementIt->text.size() - afterLineBreak); } bool doNotIndentInContext(QTextDocument *doc, int pos) { const QChar character = doc->characterAt(pos); const QTextBlock currentBlock = doc->findBlock(pos); const QString text = currentBlock.text().left(pos - currentBlock.position()); // NOTE: check if "<<" and ">>" always work correctly. switch (character.toLatin1()) { default: break; case ':': // Do not indent when it's the first ':' and it's not the 'case' line. 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 false; } if (pos > 0 && doc->characterAt(pos - 1) != ':') return true; break; } return false; } int formattingRangeStart(const QTextBlock ¤tBlock, const QByteArray &buffer, int documentRevision) { QTextBlock prevBlock = currentBlock.previous(); while ((prevBlock.position() > 0 || prevBlock.length() > 0) && prevBlock.revision() != documentRevision) { // Find the first block with not matching revision. prevBlock = prevBlock.previous(); } if (prevBlock.revision() == documentRevision) prevBlock = prevBlock.next(); return Utils::Text::utf8NthLineOffset(prevBlock.document(), buffer, prevBlock.blockNumber() + 1); } } // namespace ClangFormatBaseIndenter::ClangFormatBaseIndenter(QTextDocument *doc) : TextEditor::Indenter(doc) {} Utils::Text::Replacements ClangFormatBaseIndenter::replacements(QByteArray buffer, const QTextBlock &startBlock, const QTextBlock &endBlock, int cursorPositionInEditor, ReplacementsToKeep replacementsToKeep, const QChar &typedChar, bool secondTry) const { QTC_ASSERT(replacementsToKeep != ReplacementsToKeep::All, return Utils::Text::Replacements()); QTC_ASSERT(!m_fileName.isEmpty(), return {}); QByteArray originalBuffer = buffer; int utf8Offset = Utils::Text::utf8NthLineOffset(m_doc, buffer, startBlock.blockNumber() + 1); QTC_ASSERT(utf8Offset >= 0, return Utils::Text::Replacements();); int utf8Length = selectedLines(m_doc, startBlock, endBlock).toUtf8().size(); int rangeStart = 0; if (replacementsToKeep == ReplacementsToKeep::IndentAndBefore) rangeStart = formattingRangeStart(startBlock, buffer, lastSaveRevision()); clang::format::FormatStyle style = styleForFile(); adjustFormatStyleForLineBreak(style, replacementsToKeep); if (replacementsToKeep == ReplacementsToKeep::OnlyIndent) { CharacterContext currentCharContext = CharacterContext::Unknown; // Iterate backwards to reuse the same dummy text for all empty lines. for (int index = endBlock.blockNumber(); index >= startBlock.blockNumber(); --index) { utf8Length += forceIndentWithExtraText(buffer, currentCharContext, m_doc->findBlockByNumber(index), secondTry); } } if (replacementsToKeep != ReplacementsToKeep::IndentAndBefore || utf8Offset < rangeStart) rangeStart = utf8Offset; unsigned int rangeLength = static_cast(utf8Offset + utf8Length - rangeStart); std::vector ranges{{static_cast(rangeStart), rangeLength}}; clang::format::FormattingAttemptStatus status; clang::tooling::Replacements clangReplacements = clang::format::reformat( style, buffer.data(), ranges, m_fileName.toFSPathString().toStdString(), &status); clang::tooling::Replacements filtered; if (status.FormatComplete) { filtered = filteredReplacements(buffer, clangReplacements, utf8Offset, utf8Length, replacementsToKeep); } const bool canTryAgain = replacementsToKeep == ReplacementsToKeep::OnlyIndent && typedChar == QChar::Null && !secondTry; if (canTryAgain && filtered.empty()) { return replacements(originalBuffer, startBlock, endBlock, cursorPositionInEditor, replacementsToKeep, typedChar, true); } return utf16Replacements(m_doc, buffer, filtered); } Utils::Text::Replacements ClangFormatBaseIndenter::format( const TextEditor::RangesInLines &rangesInLines) { QTC_ASSERT(!m_fileName.isEmpty(), return {}); if (rangesInLines.empty()) return Utils::Text::Replacements(); const QByteArray buffer = m_doc->toPlainText().toUtf8(); std::vector ranges; ranges.reserve(rangesInLines.size()); for (auto &range : rangesInLines) { const int utf8StartOffset = Utils::Text::utf8NthLineOffset(m_doc, buffer, range.startLine); int utf8RangeLength = m_doc->findBlockByNumber(range.endLine - 1).text().toUtf8().size(); if (range.endLine > range.startLine) { utf8RangeLength += Utils::Text::utf8NthLineOffset(m_doc, buffer, range.endLine) - utf8StartOffset; } ranges.emplace_back(static_cast(utf8StartOffset), static_cast(utf8RangeLength)); } clang::format::FormatStyle style = styleForFile(); const std::string assumedFileName = m_fileName.toFSPathString().toStdString(); clang::tooling::Replacements clangReplacements = clang::format::sortIncludes(style, buffer.data(), ranges, assumedFileName); auto changedCode = clang::tooling::applyAllReplacements(buffer.data(), clangReplacements); QTC_ASSERT(changedCode, { qDebug() << QString::fromStdString(llvm::toString(changedCode.takeError())); return Utils::Text::Replacements(); }); ranges = clang::tooling::calculateRangesAfterReplacements(clangReplacements, ranges); clang::format::FormattingAttemptStatus status; const clang::tooling::Replacements formatReplacements = clang::format::reformat(style, *changedCode, ranges, assumedFileName, &status); clangReplacements = clangReplacements.merge(formatReplacements); const Utils::Text::Replacements toReplace = utf16Replacements(m_doc, buffer, clangReplacements); Utils::Text::applyReplacements(m_doc, toReplace); return toReplace; } Utils::Text::Replacements ClangFormatBaseIndenter::indentsFor(QTextBlock startBlock, const QTextBlock &endBlock, const QChar &typedChar, int cursorPositionInEditor, bool trimTrailingWhitespace) { if (typedChar != QChar::Null && cursorPositionInEditor > 0 && m_doc->characterAt(cursorPositionInEditor - 1) == typedChar && doNotIndentInContext(m_doc, cursorPositionInEditor - 1)) { return Utils::Text::Replacements(); } startBlock = reverseFindLastEmptyBlock(startBlock); const int startBlockPosition = startBlock.position(); if (trimTrailingWhitespace && startBlockPosition > 0) { trimRHSWhitespace(startBlock.previous()); if (cursorPositionInEditor >= 0) cursorPositionInEditor += startBlock.position() - startBlockPosition; } const QByteArray buffer = m_doc->toPlainText().toUtf8(); ReplacementsToKeep replacementsToKeep = ReplacementsToKeep::OnlyIndent; if (formatWhileTyping() && (cursorPositionInEditor == -1 || cursorPositionInEditor >= startBlockPosition) && (typedChar == ';' || typedChar == '}')) { // Format before current position only in case the cursor is inside the indented block. // So if cursor position is less then the block position then the current line is before // the indented block - don't trigger extra formatting in this case. // cursorPositionInEditor == -1 means the condition matches automatically. // Format only before complete statement not to break code. replacementsToKeep = ReplacementsToKeep::IndentAndBefore; } return replacements(buffer, startBlock, endBlock, cursorPositionInEditor, replacementsToKeep, typedChar); } void ClangFormatBaseIndenter::indentBlocks(const QTextBlock &startBlock, const QTextBlock &endBlock, const QChar &typedChar, int cursorPositionInEditor) { applyReplacements(m_doc, indentsFor(startBlock, endBlock, typedChar, cursorPositionInEditor)); } void ClangFormatBaseIndenter::indent(const QTextCursor &cursor, const QChar &typedChar, int cursorPositionInEditor) { if (cursor.hasSelection()) { indentBlocks(m_doc->findBlock(cursor.selectionStart()), m_doc->findBlock(cursor.selectionEnd()), typedChar, cursorPositionInEditor); } else { indentBlocks(cursor.block(), cursor.block(), typedChar, cursorPositionInEditor); } } void ClangFormatBaseIndenter::indent(const QTextCursor &cursor, const QChar &typedChar, const TextEditor::TabSettings & /*tabSettings*/, int cursorPositionInEditor) { indent(cursor, typedChar, cursorPositionInEditor); } void ClangFormatBaseIndenter::reindent(const QTextCursor &cursor, const TextEditor::TabSettings & /*tabSettings*/, int cursorPositionInEditor) { indent(cursor, QChar::Null, cursorPositionInEditor); } void ClangFormatBaseIndenter::indentBlock(const QTextBlock &block, const QChar &typedChar, const TextEditor::TabSettings & /*tabSettings*/, int cursorPositionInEditor) { indentBlocks(block, block, typedChar, cursorPositionInEditor); } int ClangFormatBaseIndenter::indentFor(const QTextBlock &block, const TextEditor::TabSettings & /*tabSettings*/, int cursorPositionInEditor) { Utils::Text::Replacements toReplace = indentsFor(block, block, QChar::Null, cursorPositionInEditor, false); if (toReplace.empty()) return -1; const QByteArray buffer = m_doc->toPlainText().toUtf8(); return indentationForBlock(toReplace, buffer, block); } TextEditor::IndentationForBlock ClangFormatBaseIndenter::indentationForBlocks( const QVector &blocks, const TextEditor::TabSettings & /*tabSettings*/, int cursorPositionInEditor) { TextEditor::IndentationForBlock ret; if (blocks.isEmpty()) return ret; Utils::Text::Replacements toReplace = indentsFor(blocks.front(), blocks.back(), QChar::Null, cursorPositionInEditor); const QByteArray buffer = m_doc->toPlainText().toUtf8(); for (const QTextBlock &block : blocks) ret.insert(block.blockNumber(), indentationForBlock(toReplace, buffer, block)); return ret; } bool ClangFormatBaseIndenter::isElectricCharacter(const QChar &ch) const { switch (ch.toLatin1()) { case '{': case '}': case ':': case '#': case '<': case '>': case ';': case '(': case ')': return true; } return false; } std::optional ClangFormat::ClangFormatBaseIndenter::margin() const { return styleForFile().ColumnLimit; } void ClangFormatBaseIndenter::autoIndent(const QTextCursor &cursor, const TextEditor::TabSettings & /*tabSettings*/, int cursorPositionInEditor) { if (formatCodeInsteadOfIndent()) { QTextBlock start; QTextBlock end; if (cursor.hasSelection()) { start = m_doc->findBlock(cursor.selectionStart()); end = m_doc->findBlock(cursor.selectionEnd()); } else { start = end = cursor.block(); } format({{start.blockNumber() + 1, end.blockNumber() + 1}}); } else { indent(cursor, QChar::Null, cursorPositionInEditor); } } clang::format::FormatStyle overrideStyle(const Utils::FilePath &fileName) { const ProjectExplorer::Project *projectForFile = ProjectExplorer::ProjectManager::projectForFile(fileName); const TextEditor::ICodeStylePreferences *preferences = projectForFile ? projectForFile->editorConfiguration()->codeStyle("Cpp")->currentPreferences() : TextEditor::TextEditorSettings::codeStyle("Cpp")->currentPreferences(); Utils::FilePath filePath = filePathToCurrentSettings(preferences); if (!filePath.exists()) return qtcStyle(); clang::format::FormatStyle currentSettingsStyle; currentSettingsStyle.Language = clang::format::FormatStyle::LK_Cpp; const std::error_code error = clang::format::parseConfiguration(filePath.fileContents() .value_or(QByteArray()) .toStdString(), ¤tSettingsStyle); QTC_ASSERT(error.value() == static_cast(clang::format::ParseError::Success), return qtcStyle()); return currentSettingsStyle; } clang::format::FormatStyle ClangFormatBaseIndenter::styleForFile() const { if (getCurrentOverriddenSettings(m_fileName)) return overrideStyle(m_fileName); llvm::Expected styleFromProjectFolder = clang::format::getStyle("file", m_fileName.toFSPathString().toStdString(), "none", "", &llvmFileSystemAdapter); if (styleFromProjectFolder && !(*styleFromProjectFolder == clang::format::getNoStyle())) { addQtcStatementMacros(*styleFromProjectFolder); return *styleFromProjectFolder; } handleAllErrors(styleFromProjectFolder.takeError(), [](const llvm::ErrorInfoBase &) { // do nothing }); return qtcStyle(); } } // namespace ClangFormat