clangformat.cpp 7.57 KB
Newer Older
1
/****************************************************************************
Lorenz Haas's avatar
Lorenz Haas committed
2
**
3 4
** Copyright (C) 2016 Lorenz Haas
** Contact: https://www.qt.io/licensing/
Lorenz Haas's avatar
Lorenz Haas committed
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.
Lorenz Haas's avatar
Lorenz Haas committed
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.
Lorenz Haas's avatar
Lorenz Haas committed
23 24 25
**
****************************************************************************/

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

Lorenz Haas's avatar
Lorenz Haas committed
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>
Lorenz Haas's avatar
Lorenz Haas committed
42
#include <coreplugin/editormanager/ieditor.h>
43
#include <coreplugin/idocument.h>
Lorenz Haas's avatar
Lorenz Haas committed
44
#include <cppeditor/cppeditorconstants.h>
45
#include <texteditor/texteditor.h>
46
#include <utils/algorithm.h>
47
#include <utils/fileutils.h>
Lorenz Haas's avatar
Lorenz Haas committed
48 49 50

#include <QAction>
#include <QMenu>
51
#include <QTextBlock>
Lorenz Haas's avatar
Lorenz Haas committed
52 53 54 55 56

namespace Beautifier {
namespace Internal {
namespace ClangFormat {

57
ClangFormat::ClangFormat(BeautifierPlugin *parent) :
Lorenz Haas's avatar
Lorenz Haas committed
58
    BeautifierAbstractTool(parent),
59
    m_beautifierPlugin(parent),
Lorenz Haas's avatar
Lorenz Haas committed
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);
}

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

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

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

Lorenz Haas's avatar
Lorenz Haas committed
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

Lorenz Haas's avatar
Lorenz Haas committed
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());
Lorenz Haas's avatar
Lorenz Haas committed
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)};
Lorenz Haas's avatar
Lorenz Haas committed
118 119 120 121
}

void ClangFormat::formatFile()
{
122
    m_beautifierPlugin->formatCurrentFile(command());
Lorenz Haas's avatar
Lorenz Haas committed
123 124 125 126
}

void ClangFormat::formatSelectedText()
{
127 128
    const TextEditor::TextEditorWidget *widget
            = TextEditor::TextEditorWidget::currentTextEditorWidget();
129
    if (!widget)
Lorenz Haas's avatar
Lorenz Haas committed
130 131
        return;

132
    const QTextCursor tc = widget->textCursor();
Lorenz Haas's avatar
Lorenz Haas committed
133 134 135
    if (tc.hasSelection()) {
        const int offset = tc.selectionStart();
        const int length = tc.selectionEnd() - offset;
136
        m_beautifierPlugin->formatCurrentFile(command(offset, length));
Lorenz Haas's avatar
Lorenz Haas committed
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
Lorenz Haas's avatar
Lorenz Haas committed
179
{
180 181 182 183
    Command command;
    command.setExecutable(m_settings->command());
    command.setProcessing(Command::PipeProcessing);

Lorenz Haas's avatar
Lorenz Haas committed
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");
Lorenz Haas's avatar
Lorenz Haas committed
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");
Lorenz Haas's avatar
Lorenz Haas committed
199
    }
200

Lorenz Haas's avatar
Lorenz Haas committed
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;
}

Lorenz Haas's avatar
Lorenz Haas committed
217 218 219
} // namespace ClangFormat
} // namespace Internal
} // namespace Beautifier