clangformat.cpp 7.57 KB
Newer Older
1
/****************************************************************************
2
**
3 4
** Copyright (C) 2016 Lorenz Haas
** Contact: https://www.qt.io/licensing/
5 6 7 8 9 10 11
**
** 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
12 13 14
** 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.
15
**
16 17 18 19 20 21 22
** 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.
23 24 25
**
****************************************************************************/

26 27
// Tested with version 3.3, 3.4 and 3.4.1

28 29 30 31 32 33 34 35 36 37 38 39 40
#include "clangformat.h"

#include "clangformatconstants.h"
#include "clangformatoptionspage.h"
#include "clangformatsettings.h"

#include "../beautifierconstants.h"
#include "../beautifierplugin.h"

#include <coreplugin/actionmanager/actioncontainer.h>
#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/actionmanager/command.h>
#include <coreplugin/coreconstants.h>
41
#include <coreplugin/editormanager/editormanager.h>
42
#include <coreplugin/editormanager/ieditor.h>
43
#include <coreplugin/idocument.h>
44
#include <cppeditor/cppeditorconstants.h>
45
#include <texteditor/texteditor.h>
46
#include <utils/algorithm.h>
47
#include <utils/fileutils.h>
48 49 50

#include <QAction>
#include <QMenu>
51
#include <QTextBlock>
52 53 54 55 56

namespace Beautifier {
namespace Internal {
namespace ClangFormat {

57
ClangFormat::ClangFormat(BeautifierPlugin *parent) :
58
    BeautifierAbstractTool(parent),
59
    m_beautifierPlugin(parent),
60 61 62 63 64 65 66 67 68
    m_settings(new ClangFormatSettings)
{
}

ClangFormat::~ClangFormat()
{
    delete m_settings;
}

69 70 71 72 73
QString ClangFormat::id() const
{
    return QLatin1String(Constants::ClangFormat::DISPLAY_NAME);
}

74 75 76
bool ClangFormat::initialize()
{
    Core::ActionContainer *menu = Core::ActionManager::createMenu(Constants::ClangFormat::MENU_ID);
77
    menu->menu()->setTitle(tr("&ClangFormat"));
78

79
    m_formatFile = new QAction(BeautifierPlugin::msgFormatCurrentFile(), this);
80 81
    Core::Command *cmd
            = Core::ActionManager::registerAction(m_formatFile,
82
                                                  Constants::ClangFormat::ACTION_FORMATFILE);
83
    menu->addAction(cmd);
Montel Laurent's avatar
Montel Laurent committed
84
    connect(m_formatFile, &QAction::triggered, this, &ClangFormat::formatFile);
85

86
    m_formatRange = new QAction(BeautifierPlugin::msgFormatSelectedText(), this);
87
    cmd = Core::ActionManager::registerAction(m_formatRange,
88
                                              Constants::ClangFormat::ACTION_FORMATSELECTED);
89
    menu->addAction(cmd);
Montel Laurent's avatar
Montel Laurent committed
90
    connect(m_formatRange, &QAction::triggered, this, &ClangFormat::formatSelectedText);
91

92 93 94 95 96 97 98 99
    m_disableFormattingSelectedText
        = new QAction(BeautifierPlugin::msgDisableFormattingSelectedText(), this);
    cmd = Core::ActionManager::registerAction(
        m_disableFormattingSelectedText, Constants::ClangFormat::ACTION_DISABLEFORMATTINGSELECTED);
    menu->addAction(cmd);
    connect(m_disableFormattingSelectedText, &QAction::triggered,
            this, &ClangFormat::disableFormattingSelectedText);

100 101
    Core::ActionManager::actionContainer(Constants::MENU_ID)->addMenu(menu);

102
    connect(m_settings, &ClangFormatSettings::supportedMimeTypesChanged,
Lorenz Haas's avatar
Lorenz Haas committed
103
            [this] { updateActions(Core::EditorManager::currentEditor()); });
104

105 106 107 108 109
    return true;
}

void ClangFormat::updateActions(Core::IEditor *editor)
{
Lorenz Haas's avatar
Lorenz Haas committed
110
    const bool enabled = editor && m_settings->isApplicable(editor->document());
111 112 113 114 115 116
    m_formatFile->setEnabled(enabled);
    m_formatRange->setEnabled(enabled);
}

QList<QObject *> ClangFormat::autoReleaseObjects()
{
117
    return {new ClangFormatOptionsPage(m_settings, this)};
118 119 120 121
}

void ClangFormat::formatFile()
{
122
    m_beautifierPlugin->formatCurrentFile(command());
123 124 125 126
}

void ClangFormat::formatSelectedText()
{
127 128
    const TextEditor::TextEditorWidget *widget
            = TextEditor::TextEditorWidget::currentTextEditorWidget();
129
    if (!widget)
130 131
        return;

132
    const QTextCursor tc = widget->textCursor();
133 134 135
    if (tc.hasSelection()) {
        const int offset = tc.selectionStart();
        const int length = tc.selectionEnd() - offset;
136
        m_beautifierPlugin->formatCurrentFile(command(offset, length));
137 138 139 140 141
    } else if (m_settings->formatEntireFileFallback()) {
        formatFile();
    }
}

142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
void ClangFormat::disableFormattingSelectedText()
{
    TextEditor::TextEditorWidget *widget = TextEditor::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();
    m_beautifierPlugin->formatCurrentFile(command(selectionStartBlock.position(),
                                                  reformatTextLength));
}

178
Command ClangFormat::command() const
179
{
180 181 182 183
    Command command;
    command.setExecutable(m_settings->command());
    command.setProcessing(Command::PipeProcessing);

184
    if (m_settings->usePredefinedStyle()) {
185 186 187 188 189 190 191 192
        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);
        }

193
        command.addOption("-assume-filename=%file");
194
    } else {
195
        command.addOption("-style=file");
196 197
        const QString path =
                QFileInfo(m_settings->styleFileName(m_settings->customStyle())).absolutePath();
198
        command.addOption("-assume-filename=" + path + QDir::separator() + "%filename");
199
    }
200

201 202 203
    return command;
}

204 205 206 207 208
bool ClangFormat::isApplicable(const Core::IDocument *document) const
{
    return m_settings->isApplicable(document);
}

209 210 211 212 213 214 215 216
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;
}

217 218 219
} // namespace ClangFormat
} // namespace Internal
} // namespace Beautifier