// Copyright (C) 2021 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "clangformatfile.h" #include "clangformatsettings.h" #include "clangformatutils.h" #include #include #include #include #include using namespace ClangFormat; ClangFormatFile::ClangFormatFile(Utils::FilePath filePath) : m_filePath(filePath) { if (!m_filePath.exists()) { // create file and folder m_filePath.parentDir().createDir(); std::fstream newStyleFile(m_filePath.path().toStdString(), std::fstream::out); if (newStyleFile.is_open()) { newStyleFile.close(); } resetStyleToQtC(); return; } m_style.Language = clang::format::FormatStyle::LK_Cpp; const std::error_code error = clang::format::parseConfiguration(m_filePath.fileContents() .value_or(QByteArray()) .toStdString(), &m_style); if (error.value() != static_cast(clang::format::ParseError::Success)) { resetStyleToQtC(); } } clang::format::FormatStyle ClangFormatFile::style() { return m_style; } Utils::FilePath ClangFormatFile::filePath() { return m_filePath; } void ClangFormatFile::setStyle(clang::format::FormatStyle style) { m_style = style; saveNewFormat(); } bool ClangFormatFile::isReadOnly() const { return m_isReadOnly; } void ClangFormatFile::setIsReadOnly(bool isReadOnly) { m_isReadOnly = isReadOnly; } void ClangFormatFile::resetStyleToQtC() { m_style = qtcStyle(); saveStyleToFile(m_style, m_filePath); } void ClangFormatFile::setBasedOnStyle(QString styleName) { changeField({"BasedOnStyle", styleName}); saveNewFormat(); } QString ClangFormatFile::setStyle(QString style) { const std::error_code error = clang::format::parseConfiguration(style.toStdString(), &m_style); if (error.value() != static_cast(clang::format::ParseError::Success)) { return QString::fromStdString(error.message()); } saveNewFormat(style.toUtf8()); return ""; } QString ClangFormatFile::changeField(Field field) { return changeFields({field}); } QString ClangFormatFile::changeFields(QList fields) { std::stringstream content; content << "---" << "\n"; for (const auto &field : fields) { content << field.first.toStdString() << ": " << field.second.toStdString() << "\n"; } return setStyle(QString::fromStdString(content.str())); } void ClangFormatFile::saveNewFormat() { if (m_isReadOnly) return; saveStyleToFile(m_style, m_filePath); } void ClangFormatFile::saveNewFormat(QByteArray style) { if (m_isReadOnly) return; m_filePath.writeFileContents(style); } void ClangFormatFile::saveStyleToFile(clang::format::FormatStyle style, Utils::FilePath filePath) { std::string styleStr = clang::format::configurationAsText(style); // workaround: configurationAsText() add comment "# " before BasedOnStyle line const int pos = styleStr.find("# BasedOnStyle"); if (pos != int(std::string::npos)) styleStr.erase(pos, 2); styleStr.append("\n"); filePath.writeFileContents(QByteArray::fromStdString(styleStr)); } CppEditor::CppCodeStyleSettings ClangFormatFile::toCppCodeStyleSettings( ProjectExplorer::Project *project) const { using namespace clang::format; auto settings = CppEditor::CppCodeStyleSettings::getProjectCodeStyle(project); FormatStyle style; style.Language = clang::format::FormatStyle::LK_Cpp; const std::error_code error = parseConfiguration(m_filePath.fileContents().value_or(QByteArray()).toStdString(), &style); QTC_ASSERT(error.value() == static_cast(ParseError::Success), return settings); // Modifier offset should be opposite to indent width in order indentAccessSpecifiers // to be false settings.indentAccessSpecifiers = (style.AccessModifierOffset != -1 * int(style.IndentWidth)); settings.indentNamespaceBody = style.NamespaceIndentation == FormatStyle::NamespaceIndentationKind::NI_All; settings.indentNamespaceBraces = settings.indentNamespaceBody; settings.indentClassBraces = style.BreakBeforeBraces == FormatStyle::BS_Whitesmiths; settings.indentEnumBraces = settings.indentClassBraces; settings.indentBlockBraces = settings.indentClassBraces; settings.indentFunctionBraces = settings.indentClassBraces; settings.indentSwitchLabels = style.IndentCaseLabels; #if LLVM_VERSION_MAJOR >= 11 settings.indentBlocksRelativeToSwitchLabels = style.IndentCaseBlocks; settings.indentStatementsRelativeToSwitchLabels = style.IndentCaseBlocks; settings.indentControlFlowRelativeToSwitchLabels = style.IndentCaseBlocks; #endif if (style.DerivePointerAlignment && ClangFormatSettings::instance().mode() == ClangFormatSettings::Mode::Formatting) { settings.bindStarToIdentifier = style.PointerAlignment == FormatStyle::PAS_Right; settings.bindStarToTypeName = style.PointerAlignment == FormatStyle::PAS_Left; settings.bindStarToLeftSpecifier = style.PointerAlignment == FormatStyle::PAS_Left; settings.bindStarToRightSpecifier = style.PointerAlignment == FormatStyle::PAS_Right; } return settings; } void ClangFormatFile::fromCppCodeStyleSettings(const CppEditor::CppCodeStyleSettings &settings) { using namespace clang::format; if (settings.indentAccessSpecifiers) m_style.AccessModifierOffset = 0; else m_style.AccessModifierOffset = -1 * m_style.IndentWidth; if (settings.indentNamespaceBody || settings.indentNamespaceBraces) m_style.NamespaceIndentation = FormatStyle::NamespaceIndentationKind::NI_All; else m_style.NamespaceIndentation = FormatStyle::NamespaceIndentationKind::NI_None; if (settings.indentClassBraces || settings.indentEnumBraces || settings.indentBlockBraces || settings.indentFunctionBraces) m_style.BreakBeforeBraces = FormatStyle::BS_Whitesmiths; m_style.IndentCaseLabels = settings.indentSwitchLabels; #if LLVM_VERSION_MAJOR >= 11 m_style.IndentCaseBlocks = settings.indentBlocksRelativeToSwitchLabels || settings.indentStatementsRelativeToSwitchLabels || settings.indentControlFlowRelativeToSwitchLabels; #endif if (settings.alignAssignments) m_style.BreakBeforeBinaryOperators = FormatStyle::BOS_NonAssignment; if (settings.extraPaddingForConditionsIfConfusingAlign) m_style.BreakBeforeBinaryOperators = FormatStyle::BOS_All; m_style.DerivePointerAlignment = settings.bindStarToIdentifier || settings.bindStarToTypeName || settings.bindStarToLeftSpecifier || settings.bindStarToRightSpecifier; if ((settings.bindStarToIdentifier || settings.bindStarToRightSpecifier) && ClangFormatSettings::instance().mode() == ClangFormatSettings::Mode::Formatting) m_style.PointerAlignment = FormatStyle::PAS_Right; if ((settings.bindStarToTypeName || settings.bindStarToLeftSpecifier) && ClangFormatSettings::instance().mode() == ClangFormatSettings::Mode::Formatting) m_style.PointerAlignment = FormatStyle::PAS_Left; saveNewFormat(); } TextEditor::TabSettings ClangFormatFile::toTabSettings(ProjectExplorer::Project *project) const { using namespace clang::format; auto settings = CppEditor::CppCodeStyleSettings::getProjectTabSettings(project); FormatStyle style; style.Language = clang::format::FormatStyle::LK_Cpp; const std::error_code error = parseConfiguration(m_filePath.fileContents().value_or(QByteArray()).toStdString(), &style); QTC_ASSERT(error.value() == static_cast(ParseError::Success), return settings); settings.m_indentSize = style.IndentWidth; settings.m_tabSize = style.TabWidth; switch (style.UseTab) { #if LLVM_VERSION_MAJOR >= 11 case FormatStyle::UT_AlignWithSpaces: #endif case FormatStyle::UT_ForIndentation: case FormatStyle::UT_ForContinuationAndIndentation: settings.m_tabPolicy = TextEditor::TabSettings::TabPolicy::MixedTabPolicy; break; case FormatStyle::UT_Never: settings.m_tabPolicy = TextEditor::TabSettings::TabPolicy::SpacesOnlyTabPolicy; break; case FormatStyle::UT_Always: settings.m_tabPolicy = TextEditor::TabSettings::TabPolicy::TabsOnlyTabPolicy; break; } return settings; } void ClangFormatFile::fromTabSettings(const TextEditor::TabSettings &settings) { using namespace clang::format; m_style.IndentWidth = settings.m_indentSize; m_style.TabWidth = settings.m_tabSize; switch (settings.m_tabPolicy) { case TextEditor::TabSettings::TabPolicy::MixedTabPolicy: m_style.UseTab = FormatStyle::UT_ForContinuationAndIndentation; break; case TextEditor::TabSettings::TabPolicy::SpacesOnlyTabPolicy: m_style.UseTab = FormatStyle::UT_Never; break; case TextEditor::TabSettings::TabPolicy::TabsOnlyTabPolicy: m_style.UseTab = FormatStyle::UT_Always; break; } saveNewFormat(); }