/**************************************************************************** ** ** Copyright (C) 2016 Lorenz Haas ** 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. ** ****************************************************************************/ // Tested with version 3.3, 3.4 and 3.4.1 #include "clangformat.h" #include "clangformatconstants.h" #include "clangformatoptionspage.h" #include "../beautifierconstants.h" #include "../beautifierplugin.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace TextEditor; namespace Beautifier { namespace Internal { ClangFormat::ClangFormat() { Core::ActionContainer *menu = Core::ActionManager::createMenu("ClangFormat.Menu"); menu->menu()->setTitle(tr("&ClangFormat")); m_formatFile = new QAction(BeautifierPlugin::msgFormatCurrentFile(), this); Core::Command *cmd = Core::ActionManager::registerAction(m_formatFile, "ClangFormat.FormatFile"); menu->addAction(cmd); connect(m_formatFile, &QAction::triggered, this, &ClangFormat::formatFile); m_formatRange = new QAction(BeautifierPlugin::msgFormatAtCursor(), this); cmd = Core::ActionManager::registerAction(m_formatRange, "ClangFormat.FormatAtCursor"); menu->addAction(cmd); connect(m_formatRange, &QAction::triggered, this, &ClangFormat::formatAtCursor); m_disableFormattingSelectedText = new QAction(BeautifierPlugin::msgDisableFormattingSelectedText(), this); cmd = Core::ActionManager::registerAction( m_disableFormattingSelectedText, "ClangFormat.DisableFormattingSelectedText"); menu->addAction(cmd); connect(m_disableFormattingSelectedText, &QAction::triggered, this, &ClangFormat::disableFormattingSelectedText); Core::ActionManager::actionContainer(Constants::MENU_ID)->addMenu(menu); connect(&m_settings, &ClangFormatSettings::supportedMimeTypesChanged, [this] { updateActions(Core::EditorManager::currentEditor()); }); } QString ClangFormat::id() const { return QLatin1String(Constants::CLANGFORMAT_DISPLAY_NAME); } void ClangFormat::updateActions(Core::IEditor *editor) { const bool enabled = editor && m_settings.isApplicable(editor->document()); m_formatFile->setEnabled(enabled); m_formatRange->setEnabled(enabled); } void ClangFormat::formatFile() { formatCurrentFile(command()); } void ClangFormat::formatAtCursor() { const TextEditorWidget *widget = TextEditorWidget::currentTextEditorWidget(); if (!widget) return; const QTextCursor tc = widget->textCursor(); if (tc.hasSelection()) { const int offset = tc.selectionStart(); const int length = tc.selectionEnd() - offset; formatCurrentFile(command(offset, length)); } else { // Pretend that the current line was selected. // Note that clang-format will extend the range to the next bigger // syntactic construct if needed. const QTextBlock block = tc.block(); const int offset = block.position(); const int length = block.length(); formatCurrentFile(command(offset, length)); } } void ClangFormat::disableFormattingSelectedText() { TextEditorWidget *widget = TextEditorWidget::currentTextEditorWidget(); if (!widget) return; const QTextCursor tc = widget->textCursor(); if (!tc.hasSelection()) return; // Insert start marker const QTextBlock selectionStartBlock = tc.document()->findBlock(tc.selectionStart()); QTextCursor insertCursor(tc.document()); insertCursor.beginEditBlock(); insertCursor.setPosition(selectionStartBlock.position()); insertCursor.insertText("// clang-format off\n"); const int positionToRestore = tc.position(); // Insert end marker QTextBlock selectionEndBlock = tc.document()->findBlock(tc.selectionEnd()); insertCursor.setPosition(selectionEndBlock.position() + selectionEndBlock.length() - 1); insertCursor.insertText("\n// clang-format on"); insertCursor.endEditBlock(); // Reset the cursor position in order to clear the selection. QTextCursor restoreCursor(tc.document()); restoreCursor.setPosition(positionToRestore); widget->setTextCursor(restoreCursor); // The indentation of these markers might be undesired, so reformat. // This is not optimal because two undo steps will be needed to remove the markers. const int reformatTextLength = insertCursor.position() - selectionStartBlock.position(); formatCurrentFile(command(selectionStartBlock.position(), reformatTextLength)); } Command ClangFormat::command() const { Command command; command.setExecutable(m_settings.command().toString()); command.setProcessing(Command::PipeProcessing); if (m_settings.usePredefinedStyle()) { const QString predefinedStyle = m_settings.predefinedStyle(); command.addOption("-style=" + predefinedStyle); if (predefinedStyle == "File") { const QString fallbackStyle = m_settings.fallbackStyle(); if (fallbackStyle != "Default") command.addOption("-fallback-style=" + fallbackStyle); } command.addOption("-assume-filename=%file"); } else { command.addOption("-style=file"); const QString path = QFileInfo(m_settings.styleFileName(m_settings.customStyle())).absolutePath(); command.addOption("-assume-filename=" + path + QDir::separator() + "%filename"); } return command; } bool ClangFormat::isApplicable(const Core::IDocument *document) const { return m_settings.isApplicable(document); } Command ClangFormat::command(int offset, int length) const { Command c = command(); c.addOption("-offset=" + QString::number(offset)); c.addOption("-length=" + QString::number(length)); return c; } } // namespace Internal } // namespace Beautifier