cppqtstyleindenter.cpp 6.16 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
2
**
Eike Ziller's avatar
Eike Ziller committed
3 4
** Copyright (C) 2015 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
7
**
hjk's avatar
hjk committed
8 9 10 11
** 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
Eike Ziller's avatar
Eike Ziller committed
12 13
** a written agreement between you and The Qt Company.  For licensing terms and
** conditions see http://www.qt.io/terms-conditions.  For further information
Eike Ziller's avatar
Eike Ziller committed
14
** use the contact form at http://www.qt.io/contact-us.
15 16
**
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
17
** Alternatively, this file may be used under the terms of the GNU Lesser
Eike Ziller's avatar
Eike Ziller committed
18 19 20 21 22 23
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file.  Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
hjk's avatar
hjk committed
24
**
Eike Ziller's avatar
Eike Ziller committed
25 26
** In addition, as a special exception, The Qt Company gives you certain additional
** rights.  These rights are described in The Qt Company LGPL Exception
con's avatar
con committed
27 28
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
29
****************************************************************************/
30 31 32

#include "cppqtstyleindenter.h"

33 34 35
#include "cppcodeformatter.h"
#include "cpptoolssettings.h"
#include "cppcodestylepreferences.h"
36

37 38 39 40
#include <QChar>
#include <QTextDocument>
#include <QTextBlock>
#include <QTextCursor>
41

42
using namespace CppTools;
43 44

CppQtStyleIndenter::CppQtStyleIndenter()
45 46
    : m_cppCodeStylePreferences(0)
{
47 48
    // Just for safety. setCodeStylePreferences should be called when the editor the
    // indenter belongs to gets initialized.
Jarek Kobus's avatar
Jarek Kobus committed
49
    m_cppCodeStylePreferences = CppToolsSettings::instance()->cppCodeStyle();
50
}
51 52 53 54

CppQtStyleIndenter::~CppQtStyleIndenter()
{}

55
bool CppQtStyleIndenter::isElectricCharacter(const QChar &ch) const
56
{
Orgad Shaneh's avatar
Orgad Shaneh committed
57 58 59 60 61
    switch (ch.toLatin1()) {
    case '{':
    case '}':
    case ':':
    case '#':
62 63
    case '<':
    case '>':
64
    case ';':
65 66 67 68 69
        return true;
    }
    return false;
}

70
static bool isElectricInLine(const QChar ch, const QString &text)
71
{
72
    switch (ch.toLatin1()) {
73 74
    case ';':
        return text.contains(QLatin1String("break"));
75 76 77 78 79 80 81 82 83 84 85
    case ':':
        // switch cases and access declarations should be reindented
        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 true;
        }
86

87 88 89 90
        // fall-through
        // lines that start with : might have a constructor initializer list
    case '<':
    case '>': {
91 92 93 94 95 96
        // Electric if at line beginning (after space indentation)
        for (int i = 0, len = text.count(); i < len; ++i) {
            if (!text.at(i).isSpace())
                return text.at(i) == ch;
        }
        return false;
97 98
    }
    }
99

100
    return true;
101 102
}

103 104 105
void CppQtStyleIndenter::indentBlock(QTextDocument *doc,
                                     const QTextBlock &block,
                                     const QChar &typedChar,
106
                                     const TextEditor::TabSettings &tabSettings)
107 108 109
{
    Q_UNUSED(doc)

110
    QtStyleCodeFormatter codeFormatter(tabSettings, codeStyleSettings());
111 112 113 114 115 116 117

    codeFormatter.updateStateUntil(block);
    int indent;
    int padding;
    codeFormatter.indentFor(block, &indent, &padding);

    if (isElectricCharacter(typedChar)) {
118
        // : should not be electric for labels
119
        if (!isElectricInLine(typedChar, block.text()))
120 121 122 123
            return;

        // only reindent the current line when typing electric characters if the
        // indent is the same it would be if the line were empty
124 125 126
        int newlineIndent;
        int newlinePadding;
        codeFormatter.indentForNewLineAfter(block.previous(), &newlineIndent, &newlinePadding);
127
        if (tabSettings.indentationColumn(block.text()) != newlineIndent + newlinePadding)
128 129 130
            return;
    }

131
    tabSettings.indentLine(block, indent + padding, padding);
132 133
}

134 135 136
void CppQtStyleIndenter::indent(QTextDocument *doc,
                                const QTextCursor &cursor,
                                const QChar &typedChar,
137
                                const TextEditor::TabSettings &tabSettings)
138 139 140 141 142
{
    if (cursor.hasSelection()) {
        QTextBlock block = doc->findBlock(cursor.selectionStart());
        const QTextBlock end = doc->findBlock(cursor.selectionEnd()).next();

143
        QtStyleCodeFormatter codeFormatter(tabSettings, codeStyleSettings());
144 145
        codeFormatter.updateStateUntil(block);

146
        QTextCursor tc = cursor;
147 148 149 150 151
        tc.beginEditBlock();
        do {
            int indent;
            int padding;
            codeFormatter.indentFor(block, &indent, &padding);
152
            tabSettings.indentLine(block, indent + padding, padding);
153 154 155 156 157
            codeFormatter.updateLineStateChange(block);
            block = block.next();
        } while (block.isValid() && block != end);
        tc.endEditBlock();
    } else {
158
        indentBlock(doc, cursor.block(), typedChar, tabSettings);
159 160
    }
}
161

Jarek Kobus's avatar
Jarek Kobus committed
162
void CppQtStyleIndenter::setCodeStylePreferences(TextEditor::ICodeStylePreferences *preferences)
163
{
164 165
    CppCodeStylePreferences *cppCodeStylePreferences
            = qobject_cast<CppCodeStylePreferences *>(preferences);
166 167 168 169
    if (cppCodeStylePreferences)
        m_cppCodeStylePreferences = cppCodeStylePreferences;
}

Jarek Kobus's avatar
Jarek Kobus committed
170 171
void CppQtStyleIndenter::invalidateCache(QTextDocument *doc)
{
172
    QtStyleCodeFormatter formatter;
Jarek Kobus's avatar
Jarek Kobus committed
173 174 175
    formatter.invalidateCache(doc);
}

176 177 178
CppCodeStyleSettings CppQtStyleIndenter::codeStyleSettings() const
{
    if (m_cppCodeStylePreferences)
Jarek Kobus's avatar
Jarek Kobus committed
179
        return m_cppCodeStylePreferences->currentCodeStyleSettings();
180 181
    return CppCodeStyleSettings();
}