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

#include "fakevimplugin.h"

32
#include "fakevimhandler.h"
hjk's avatar
hjk committed
33
34
#include "ui_fakevimoptions.h"

35
#include <coreplugin/actionmanager/actioncontainer.h>
36
#include <coreplugin/actionmanager/actionmanager.h>
37
38
#include <coreplugin/actionmanager/command.h>
#include <coreplugin/actionmanager/command.h>
39
#include <coreplugin/actionmanager/commandmappings.h>
hjk's avatar
hjk committed
40
#include <coreplugin/coreconstants.h>
41
#include <coreplugin/dialogs/ioptionspage.h>
hjk's avatar
hjk committed
42
#include <coreplugin/editormanager/editormanager.h>
43
#include <coreplugin/editormanager/openeditorsmodel.h>
44
#include <coreplugin/documentmanager.h>
hjk's avatar
hjk committed
45
#include <coreplugin/icore.h>
46
#include <coreplugin/idocument.h>
hjk's avatar
hjk committed
47
#include <coreplugin/messagemanager.h>
48
#include <coreplugin/id.h>
49
50
#include <coreplugin/statusbarwidget.h>
#include <coreplugin/statusbarmanager.h>
hjk's avatar
hjk committed
51
52
53

#include <projectexplorer/projectexplorerconstants.h>

54
#include <texteditor/basetextdocumentlayout.h>
hjk's avatar
hjk committed
55
56
57
#include <texteditor/basetexteditor.h>
#include <texteditor/basetextmark.h>
#include <texteditor/texteditorconstants.h>
Jarek Kobus's avatar
Jarek Kobus committed
58
#include <texteditor/typingsettings.h>
59
#include <texteditor/tabsettings.h>
Jarek Kobus's avatar
Jarek Kobus committed
60
#include <texteditor/icodestylepreferences.h>
61
#include <texteditor/texteditorsettings.h>
62
#include <texteditor/indenter.h>
Leandro Melo's avatar
Leandro Melo committed
63
64
65
66
67
68
#include <texteditor/codeassist/basicproposalitem.h>
#include <texteditor/codeassist/basicproposalitemlistmodel.h>
#include <texteditor/codeassist/completionassistprovider.h>
#include <texteditor/codeassist/iassistprocessor.h>
#include <texteditor/codeassist/iassistinterface.h>
#include <texteditor/codeassist/genericproposal.h>
hjk's avatar
hjk committed
69

70
#include <find/findplugin.h>
71
#include <find/textfindconstants.h>
72
#include <find/ifindsupport.h>
73

hjk's avatar
hjk committed
74
#include <utils/qtcassert.h>
hjk's avatar
hjk committed
75
#include <utils/savedaction.h>
76
#include <utils/treewidgetcolumnstretcher.h>
77
#include <utils/stylehelper.h>
hjk's avatar
hjk committed
78

79
80
#include <cpptools/cpptoolsconstants.h>

Lukas Holecek's avatar
Lukas Holecek committed
81
82
#include <extensionsystem/pluginmanager.h>

83
84
85
86
87
88
#include <QAbstractTableModel>
#include <QDebug>
#include <QFile>
#include <QtPlugin>
#include <QObject>
#include <QSettings>
89
#include <QStackedWidget>
90
#include <QTextStream>
hjk's avatar
hjk committed
91

92
93
94
95
96
97
98
99
100
#include <QDesktopServices>
#include <QItemDelegate>
#include <QMessageBox>
#include <QPlainTextEdit>
#include <QShortcut>
#include <QTextBlock>
#include <QTextCursor>
#include <QTextEdit>
#include <QTreeWidgetItem>
hjk's avatar
hjk committed
101
102
103
104
105

using namespace TextEditor;
using namespace Core;

namespace FakeVim {
hjk's avatar
hjk committed
106
namespace Internal {
hjk's avatar
hjk committed
107

hjk's avatar
hjk committed
108
109
110
111
112
113
const char INSTALL_HANDLER[]                = "TextEditor.FakeVimHandler";
const char SETTINGS_CATEGORY[]              = "D.FakeVim";
const char SETTINGS_CATEGORY_FAKEVIM_ICON[] = ":/core/images/category_fakevim.png";
const char SETTINGS_ID[]                    = "A.General";
const char SETTINGS_EX_CMDS_ID[]            = "B.ExCommands";
const char SETTINGS_USER_CMDS_ID[]          = "C.UserCommands";
hjk's avatar
hjk committed
114

115
class MiniBuffer : public QStackedWidget
116
117
118
119
{
    Q_OBJECT

public:
120
    MiniBuffer() : m_label(new QLabel(this)), m_edit(new QLineEdit(this)), m_eventFilter(0)
121
    {
122
123
124
125
126
127
128
        m_edit->installEventFilter(this);
        connect(m_edit, SIGNAL(textEdited(QString)), SLOT(changed()));
        connect(m_edit, SIGNAL(cursorPositionChanged(int,int)), SLOT(changed()));
        m_label->setTextInteractionFlags(Qt::TextSelectableByMouse);

        addWidget(m_label);
        addWidget(m_edit);
129
130
    }

131
132
133
134
135
136
137
138
139
140
141
    void setContents(const QString &contents, int cursorPos, int messageLevel, QObject *eventFilter)
    {
        if (cursorPos != -1) {
            m_edit->blockSignals(true);
            m_label->clear();
            m_edit->setText(contents);
            m_edit->setCursorPosition(cursorPos);
            m_edit->blockSignals(false);

            setCurrentWidget(m_edit);
            m_edit->setFocus();
142
        } else if (contents.isEmpty() && messageLevel != MessageShowCmd) {
Lukas Holecek's avatar
Lukas Holecek committed
143
            hide();
144
        } else {
Lukas Holecek's avatar
Lukas Holecek committed
145
            show();
146
147
148
149
150
151
152
153
154
            m_label->setText(messageLevel == MessageMode ? "-- " + contents + " --" : contents);

            QString css;
            if (messageLevel == MessageError) {
                css = QString("border:1px solid rgba(255,255,255,150);"
                              "background-color:rgba(255,0,0,100);");
            } else if (messageLevel == MessageWarning) {
                css = QString("border:1px solid rgba(255,255,255,120);"
                              "background-color:rgba(255,255,0,20);");
155
156
157
            } else if (messageLevel == MessageShowCmd) {
                css = QString("border:1px solid rgba(255,255,255,120);"
                              "background-color:rgba(100,255,100,30);");
158
159
160
            }
            m_label->setStyleSheet(QString(
                "*{border-radius:2px;padding-left:4px;padding-right:4px;%1}").arg(css));
161

162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
            if (m_edit->hasFocus())
                emit edited(QString(), -1);

            setCurrentWidget(m_label);
        }

        if (m_eventFilter != eventFilter) {
            if (m_eventFilter != 0) {
                m_edit->removeEventFilter(m_eventFilter);
                disconnect(SIGNAL(edited(QString,int)));
            }
            if (eventFilter != 0) {
                m_edit->installEventFilter(eventFilter);
                connect(this, SIGNAL(edited(QString,int)),
                        eventFilter, SLOT(miniBufferTextEdited(QString,int)));
            }
            m_eventFilter = eventFilter;
        }
    }

    QSize sizeHint() const
183
    {
184
185
186
        QSize size = QWidget::sizeHint();
        // reserve maximal width for line edit widget
        return currentWidget() == m_edit ? QSize(maximumWidth(), size.height()) : size;
187
    }
188
189
190
191
192
193

signals:
    void edited(const QString &text, int cursorPos);

private slots:
    void changed()
194
    {
195
        emit edited(m_edit->text(), m_edit->cursorPosition());
196
    }
197
198

    bool eventFilter(QObject *ob, QEvent *ev)
199
    {
200
201
202
203
204
205
206
207
        // cancel editing on escape
        if (m_eventFilter != 0 && ob == m_edit && ev->type() == QEvent::ShortcutOverride
            && static_cast<QKeyEvent*>(ev)->key() == Qt::Key_Escape) {
            emit edited(QString(), -1);
            ev->accept();
            return true;
        }
        return false;
208
    }
209
210
211
212
213

private:
    QLabel *m_label;
    QLineEdit *m_edit;
    QObject *m_eventFilter;
214
215
};

hjk's avatar
hjk committed
216
217
218
219
220
221
///////////////////////////////////////////////////////////////////////
//
// FakeVimOptionPage
//
///////////////////////////////////////////////////////////////////////

222
223
typedef QMap<QString, QRegExp> ExCommandMap;
typedef QMap<int, QString> UserCommandMap;
hjk's avatar
hjk committed
224
typedef QLatin1String _;
225

hjk's avatar
hjk committed
226
class FakeVimOptionPage : public IOptionsPage
hjk's avatar
hjk committed
227
228
229
230
{
    Q_OBJECT

public:
231
232
    FakeVimOptionPage()
    {
hjk's avatar
hjk committed
233
        setId(_(SETTINGS_ID));
234
        setDisplayName(tr("General"));
hjk's avatar
hjk committed
235
        setCategory(_(SETTINGS_CATEGORY));
236
        setDisplayCategory(tr("FakeVim"));
hjk's avatar
hjk committed
237
        setCategoryIcon(_(SETTINGS_CATEGORY_FAKEVIM_ICON));
238
    }
hjk's avatar
hjk committed
239
240

    QWidget *createPage(QWidget *parent);
hjk's avatar
hjk committed
241
    void apply() { m_group.apply(ICore::settings()); }
hjk's avatar
hjk committed
242
    void finish() { m_group.finish(); }
243
    virtual bool matches(const QString &) const;
hjk's avatar
hjk committed
244
245
246
247
248
249
250
251
252

private slots:
    void copyTextEditorSettings();
    void setQtStyle();
    void setPlainStyle();

private:
    friend class DebuggerPlugin;
    Ui::FakeVimOptionPage m_ui;
253
    QString m_searchKeywords;
254
    Utils::SavedActionSet m_group;
hjk's avatar
hjk committed
255
256
257
258
259
260
261
262
};

QWidget *FakeVimOptionPage::createPage(QWidget *parent)
{
    QWidget *w = new QWidget(parent);
    m_ui.setupUi(w);

    m_group.clear();
263
    m_group.insert(theFakeVimSetting(ConfigUseFakeVim),
264
        m_ui.checkBoxUseFakeVim);
265
266
    m_group.insert(theFakeVimSetting(ConfigReadVimRc),
        m_ui.checkBoxReadVimRc);
hjk's avatar
hjk committed
267

268
    m_group.insert(theFakeVimSetting(ConfigExpandTab),
hjk's avatar
hjk committed
269
        m_ui.checkBoxExpandTab);
270
    m_group.insert(theFakeVimSetting(ConfigHlSearch),
hjk's avatar
hjk committed
271
        m_ui.checkBoxHlSearch);
272
    m_group.insert(theFakeVimSetting(ConfigShiftWidth),
273
        m_ui.spinBoxShiftWidth);
274
275
    m_group.insert(theFakeVimSetting(ConfigShowMarks),
        m_ui.checkBoxShowMarks);
hjk's avatar
hjk committed
276

277
    m_group.insert(theFakeVimSetting(ConfigSmartTab),
hjk's avatar
hjk committed
278
        m_ui.checkBoxSmartTab);
279
    m_group.insert(theFakeVimSetting(ConfigStartOfLine),
hjk's avatar
hjk committed
280
        m_ui.checkBoxStartOfLine);
281
    m_group.insert(theFakeVimSetting(ConfigTabStop),
282
        m_ui.spinBoxTabStop);
283
    m_group.insert(theFakeVimSetting(ConfigBackspace),
hjk's avatar
hjk committed
284
        m_ui.lineEditBackspace);
285
286
    m_group.insert(theFakeVimSetting(ConfigIsKeyword),
        m_ui.lineEditIsKeyword);
hjk's avatar
hjk committed
287

288
289
    m_group.insert(theFakeVimSetting(ConfigPassControlKey),
        m_ui.checkBoxPassControlKey);
290
    m_group.insert(theFakeVimSetting(ConfigAutoIndent),
hjk's avatar
hjk committed
291
        m_ui.checkBoxAutoIndent);
292
    m_group.insert(theFakeVimSetting(ConfigSmartIndent),
293
        m_ui.checkBoxSmartIndent);
Lukas Holecek's avatar
Lukas Holecek committed
294

295
    m_group.insert(theFakeVimSetting(ConfigIncSearch),
296
        m_ui.checkBoxIncSearch);
297
298
    m_group.insert(theFakeVimSetting(ConfigUseCoreSearch),
        m_ui.checkBoxUseCoreSearch);
299
300
    m_group.insert(theFakeVimSetting(ConfigSmartCase),
        m_ui.checkBoxSmartCase);
Lukas Holecek's avatar
Lukas Holecek committed
301
302
    m_group.insert(theFakeVimSetting(ConfigWrapScan),
        m_ui.checkBoxWrapScan);
hjk's avatar
hjk committed
303

304
305
306
    m_group.insert(theFakeVimSetting(ConfigShowCmd),
        m_ui.checkBoxShowCmd);

hjk's avatar
hjk committed
307
    connect(m_ui.pushButtonCopyTextEditorSettings, SIGNAL(clicked()),
308
        SLOT(copyTextEditorSettings()));
hjk's avatar
hjk committed
309
    connect(m_ui.pushButtonSetQtStyle, SIGNAL(clicked()),
310
        SLOT(setQtStyle()));
hjk's avatar
hjk committed
311
    connect(m_ui.pushButtonSetPlainStyle, SIGNAL(clicked()),
312
313
        SLOT(setPlainStyle()));

314
    if (m_searchKeywords.isEmpty()) {
315
        QLatin1Char sep(' ');
316
        QTextStream(&m_searchKeywords)
317
318
319
320
321
322
323
324
325
326
                << sep << m_ui.checkBoxUseFakeVim->text()
                << sep << m_ui.checkBoxReadVimRc->text()
                << sep << m_ui.checkBoxAutoIndent->text()
                << sep << m_ui.checkBoxSmartIndent->text()
                << sep << m_ui.checkBoxExpandTab->text()
                << sep << m_ui.checkBoxSmartTab->text()
                << sep << m_ui.checkBoxHlSearch->text()
                << sep << m_ui.checkBoxIncSearch->text()
                << sep << m_ui.checkBoxStartOfLine->text()
                << sep << m_ui.checkBoxUseCoreSearch->text()
327
                << sep << m_ui.checkBoxSmartCase->text()
328
329
330
331
332
333
                << sep << m_ui.checkBoxShowMarks->text()
                << sep << m_ui.checkBoxPassControlKey->text()
                << sep << m_ui.labelShiftWidth->text()
                << sep << m_ui.labelTabulator->text()
                << sep << m_ui.labelBackspace->text()
                << sep << m_ui.labelIsKeyword->text();
334
335
        m_searchKeywords.remove(QLatin1Char('&'));
    }
hjk's avatar
hjk committed
336
337
338
339
340
    return w;
}

void FakeVimOptionPage::copyTextEditorSettings()
{
Jarek Kobus's avatar
Jarek Kobus committed
341
342
343
    TabSettings ts = TextEditorSettings::instance()->codeStyle()->tabSettings();
    TypingSettings tps = TextEditorSettings::instance()->typingSettings();
    m_ui.checkBoxExpandTab->setChecked(ts.m_tabPolicy != TabSettings::TabsOnlyTabPolicy);
344
345
    m_ui.spinBoxTabStop->setValue(ts.m_tabSize);
    m_ui.spinBoxShiftWidth->setValue(ts.m_indentSize);
346
    m_ui.checkBoxSmartTab->setChecked(
Jarek Kobus's avatar
Jarek Kobus committed
347
        tps.m_smartBackspaceBehavior == TypingSettings::BackspaceFollowsPreviousIndents);
348
    m_ui.checkBoxAutoIndent->setChecked(true);
Jarek Kobus's avatar
Jarek Kobus committed
349
    m_ui.checkBoxSmartIndent->setChecked(tps.m_autoIndent);
350
    m_ui.checkBoxIncSearch->setChecked(true);
hjk's avatar
hjk committed
351
352
353
354
355
}

void FakeVimOptionPage::setQtStyle()
{
    m_ui.checkBoxExpandTab->setChecked(true);
356
357
    m_ui.spinBoxTabStop->setValue(4);
    m_ui.spinBoxShiftWidth->setValue(4);
hjk's avatar
hjk committed
358
359
    m_ui.checkBoxSmartTab->setChecked(true);
    m_ui.checkBoxAutoIndent->setChecked(true);
360
    m_ui.checkBoxSmartIndent->setChecked(true);
361
    m_ui.checkBoxIncSearch->setChecked(true);
hjk's avatar
hjk committed
362
    m_ui.lineEditBackspace->setText(_("indent,eol,start"));
hjk's avatar
hjk committed
363
364
365
366
367
}

void FakeVimOptionPage::setPlainStyle()
{
    m_ui.checkBoxExpandTab->setChecked(false);
368
369
    m_ui.spinBoxTabStop->setValue(8);
    m_ui.spinBoxShiftWidth->setValue(8);
hjk's avatar
hjk committed
370
371
    m_ui.checkBoxSmartTab->setChecked(false);
    m_ui.checkBoxAutoIndent->setChecked(false);
372
    m_ui.checkBoxSmartIndent->setChecked(false);
373
    m_ui.checkBoxIncSearch->setChecked(false);
hjk's avatar
hjk committed
374
    m_ui.lineEditBackspace->setText(QString());
hjk's avatar
hjk committed
375
376
}

377
378
379
380
381
bool FakeVimOptionPage::matches(const QString &s) const
{
    return m_searchKeywords.contains(s, Qt::CaseInsensitive);
}

382
//const char *FAKEVIM_CONTEXT = "FakeVim";
hjk's avatar
hjk committed
383

384
385
386
387
388
389
///////////////////////////////////////////////////////////////////////
//
// FakeVimExCommandsPage
//
///////////////////////////////////////////////////////////////////////

390
enum { CommandRole = Qt::UserRole };
391

hjk's avatar
hjk committed
392
class FakeVimExCommandsPage : public CommandMappings
393
394
395
396
{
    Q_OBJECT

public:
397
398
399
    FakeVimExCommandsPage(FakeVimPluginPrivate *q)
        : m_q(q)
    {
hjk's avatar
hjk committed
400
        setId(_(SETTINGS_EX_CMDS_ID));
401
        setDisplayName(tr("Ex Command Mapping"));
hjk's avatar
hjk committed
402
        setCategory(_(SETTINGS_CATEGORY));
403
        setDisplayCategory(tr("FakeVim"));
hjk's avatar
hjk committed
404
        setCategoryIcon(_(SETTINGS_CATEGORY_FAKEVIM_ICON));
405
    }
406
407
408

    QWidget *createPage(QWidget *parent);
    void initialize();
409
410
    ExCommandMap &exCommandMap();
    ExCommandMap &defaultExCommandMap();
411
412
413

public slots:
    void commandChanged(QTreeWidgetItem *current);
414
415
416
    void targetIdentifierChanged();
    void resetTargetIdentifier();
    void removeTargetIdentifier();
417
418
419
    void defaultAction();

private:
420
    FakeVimPluginPrivate *m_q;
421
422
423
424
};

QWidget *FakeVimExCommandsPage::createPage(QWidget *parent)
{
425
426
427
    QWidget *w = CommandMappings::createPage(parent);
    setPageTitle(tr("Ex Command Mapping"));
    setTargetHeader(tr("Ex Trigger Expression"));
428
    setTargetLabelText(tr("Regular expression:"));
429
430
    setTargetEditTitle(tr("Ex Command"));
    setImportExportEnabled(false);
431
432
433
434
435
    return w;
}

void FakeVimExCommandsPage::initialize()
{
436
437
    QMap<QString, QTreeWidgetItem *> sections;

hjk's avatar
hjk committed
438
    foreach (Command *c, ActionManager::commands()) {
439
440
441
        if (c->action() && c->action()->isSeparator())
            continue;

442
        QTreeWidgetItem *item = new QTreeWidgetItem;
443
        const QString name = c->id().toString();
444
445
        const int pos = name.indexOf(QLatin1Char('.'));
        const QString section = name.left(pos);
446
        const QString subId = name.mid(pos + 1);
447
        item->setData(0, CommandRole, name);
448
449

        if (!sections.contains(section)) {
450
451
            QTreeWidgetItem *categoryItem =
                new QTreeWidgetItem(commandList(), QStringList() << section);
452
453
454
455
            QFont f = categoryItem->font(0);
            f.setBold(true);
            categoryItem->setFont(0, f);
            sections.insert(section, categoryItem);
456
            commandList()->expandItem(categoryItem);
457
458
459
460
        }
        sections[section]->addChild(item);

        item->setText(0, subId);
461
        item->setText(1, c->description());
462

463
464
465
466
        QString regex;
        if (exCommandMap().contains(name))
            regex = exCommandMap()[name].pattern();
        item->setText(2, regex);
467

468
        if (regex != defaultExCommandMap()[name].pattern())
469
            setModified(item, true);
470
471
472
473
474
475
476
    }

    commandChanged(0);
}

void FakeVimExCommandsPage::commandChanged(QTreeWidgetItem *current)
{
477
    CommandMappings::commandChanged(current);
478
479
    if (current)
        targetEdit()->setText(current->text(2));
480
481
}

482
void FakeVimExCommandsPage::targetIdentifierChanged()
483
{
484
    QTreeWidgetItem *current = commandList()->currentItem();
485
486
487
    if (!current)
        return;

488
    const QString name =  current->data(0, CommandRole).toString();
489
    const QString regex = targetEdit()->text();
490
491

    if (current->data(0, Qt::UserRole).isValid()) {
492
493
        current->setText(2, regex);
        exCommandMap()[name] = QRegExp(regex);
494
    }
495

496
    setModified(current, regex != defaultExCommandMap()[name].pattern());
497
498
}

499
void FakeVimExCommandsPage::resetTargetIdentifier()
500
{
501
    QTreeWidgetItem *current = commandList()->currentItem();
502
503
    if (!current)
        return;
504
    const QString name = current->data(0, CommandRole).toString();
505
506
507
508
    QString regex;
    if (defaultExCommandMap().contains(name))
        regex = defaultExCommandMap()[name].pattern();
    targetEdit()->setText(regex);
509
510
}

511
void FakeVimExCommandsPage::removeTargetIdentifier()
512
{
513
    targetEdit()->clear();
514
515
516
517
}

void FakeVimExCommandsPage::defaultAction()
{
hjk's avatar
hjk committed
518
519
520
521
522
523
    int n = commandList()->topLevelItemCount();
    for (int i = 0; i != n; ++i) {
        QTreeWidgetItem *section = commandList()->topLevelItem(i);
        int m = section->childCount();
        for (int j = 0; j != m; ++j) {
            QTreeWidgetItem *item = section->child(j);
524
            const QString name = item->data(0, CommandRole).toString();
hjk's avatar
hjk committed
525
526
527
528
529
530
531
532
            QString regex;
            if (defaultExCommandMap().contains(name))
                regex = defaultExCommandMap()[name].pattern();
            setModified(item, false);
            item->setText(2, regex);
            if (item == commandList()->currentItem())
                commandChanged(item);
        }
533
534
535
    }
}

536
537
538
539
540
541
542
543
///////////////////////////////////////////////////////////////////////
//
// FakeVimUserCommandsPage
//
///////////////////////////////////////////////////////////////////////

class FakeVimUserCommandsModel : public QAbstractTableModel
{
Friedemann Kleint's avatar
Friedemann Kleint committed
544
    Q_OBJECT
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
public:
    FakeVimUserCommandsModel(FakeVimPluginPrivate *q) : m_q(q) {}
    ~FakeVimUserCommandsModel() {}

    int rowCount(const QModelIndex &parent) const;
    int columnCount(const QModelIndex &parent) const;
    QVariant data(const QModelIndex &index, int role) const;
    bool setData(const QModelIndex &index, const QVariant &data, int role);
    QVariant headerData(int section, Qt::Orientation orientation, int role) const;
    Qt::ItemFlags flags(const QModelIndex &index) const;

private:
    FakeVimPluginPrivate *m_q;
};

int FakeVimUserCommandsModel::rowCount(const QModelIndex &parent) const
{
    return parent.isValid() ? 0 : 9;
}

int FakeVimUserCommandsModel::columnCount(const QModelIndex &parent) const
{
    return parent.isValid() ? 0 : 2;
}


QVariant FakeVimUserCommandsModel::headerData(int section,
    Qt::Orientation orient, int role) const
{
    if (orient == Qt::Horizontal && role == Qt::DisplayRole) {
        switch (section) {
            case 0: return tr("Action");
            case 1: return tr("Command");
        };
    }
    return QVariant();
}

Qt::ItemFlags FakeVimUserCommandsModel::flags(const QModelIndex &index) const
{
    if (index.column() == 1)
        return QAbstractTableModel::flags(index) | Qt::ItemIsEditable;
    return QAbstractTableModel::flags(index);
}

class FakeVimUserCommandsDelegate : public QItemDelegate
{
public:
    explicit FakeVimUserCommandsDelegate(QObject *parent)
        : QItemDelegate(parent)
    {}

    QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &,
        const QModelIndex &) const
    {
        QLineEdit *lineEdit = new QLineEdit(parent);
        lineEdit->setFrame(false);
        return lineEdit;
    }

    void setModelData(QWidget *editor, QAbstractItemModel *model,
                      const QModelIndex &index) const
    {
        QLineEdit *lineEdit = qobject_cast<QLineEdit *>(editor);
        QTC_ASSERT(lineEdit, return);
        model->setData(index, lineEdit->text(), Qt::EditRole);
    }
};

hjk's avatar
hjk committed
614
class FakeVimUserCommandsPage : public IOptionsPage
615
616
617
618
{
    Q_OBJECT

public:
619
620
621
    FakeVimUserCommandsPage(FakeVimPluginPrivate *q)
        : m_q(q)
    {
hjk's avatar
hjk committed
622
        setId(_(SETTINGS_USER_CMDS_ID));
623
        setDisplayName(tr("User Command Mapping"));
hjk's avatar
hjk committed
624
        setCategory(_(SETTINGS_CATEGORY));
625
        setDisplayCategory(tr("FakeVim"));
hjk's avatar
hjk committed
626
        setCategoryIcon(_(SETTINGS_CATEGORY_FAKEVIM_ICON));
627
628
    }

629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
    void apply();
    void finish() {}

    QWidget *createPage(QWidget *parent);
    void initialize() {}
    UserCommandMap &userCommandMap();
    UserCommandMap &defaultUserCommandMap();

private:
    FakeVimPluginPrivate *m_q;
};

QWidget *FakeVimUserCommandsPage::createPage(QWidget *parent)
{
    QGroupBox *box = new QGroupBox(parent);

    FakeVimUserCommandsModel *model = new FakeVimUserCommandsModel(m_q);
    QTreeView *widget = new QTreeView;
    widget->setModel(model);
    widget->resizeColumnToContents(0);

    FakeVimUserCommandsDelegate *delegate = new FakeVimUserCommandsDelegate(widget);
    widget->setItemDelegateForColumn(1, delegate);

    QGridLayout *layout = new QGridLayout(box);
    layout->addWidget(widget, 0, 0);
    box->setLayout(layout);

    return box;
}

void FakeVimUserCommandsPage::apply()
{
    //m_q->writeSettings();
}

hjk's avatar
hjk committed
665
666
667
668
669
670
671

///////////////////////////////////////////////////////////////////////
//
// WordCompletion
//
///////////////////////////////////////////////////////////////////////

hjk's avatar
hjk committed
672
class FakeVimCompletionAssistProvider : public CompletionAssistProvider
hjk's avatar
hjk committed
673
674
{
public:
hjk's avatar
hjk committed
675
    bool supportsEditor(const Id &) const
hjk's avatar
hjk committed
676
    {
Leandro Melo's avatar
Leandro Melo committed
677
        return false;
hjk's avatar
hjk committed
678
679
    }

hjk's avatar
hjk committed
680
    IAssistProcessor *createProcessor() const;
Leandro Melo's avatar
Leandro Melo committed
681
682

    void setActive(const QString &needle, bool forward, FakeVimHandler *handler)
hjk's avatar
hjk committed
683
    {
Leandro Melo's avatar
Leandro Melo committed
684
685
686
687
688
689
690
691
692
693
694
695
        Q_UNUSED(forward);
        m_handler = handler;
        if (!m_handler)
            return;

        BaseTextEditorWidget *editor = qobject_cast<BaseTextEditorWidget *>(handler->widget());
        if (!editor)
            return;

        //qDebug() << "ACTIVATE: " << needle << forward;
        m_needle = needle;
        editor->invokeAssist(Completion, this);
hjk's avatar
hjk committed
696
697
    }

Leandro Melo's avatar
Leandro Melo committed
698
    void setInactive()
hjk's avatar
hjk committed
699
    {
Leandro Melo's avatar
Leandro Melo committed
700
701
        m_needle.clear();
        m_handler = 0;
hjk's avatar
hjk committed
702
703
    }

Leandro Melo's avatar
Leandro Melo committed
704
    const QString &needle() const
705
    {
Leandro Melo's avatar
Leandro Melo committed
706
        return m_needle;
707
    }
hjk's avatar
hjk committed
708

Leandro Melo's avatar
Leandro Melo committed
709
    void appendNeedle(const QChar &c)
710
    {
Leandro Melo's avatar
Leandro Melo committed
711
        m_needle.append(c);
712
713
    }

Leandro Melo's avatar
Leandro Melo committed
714
    FakeVimHandler *handler() const
715
    {
Leandro Melo's avatar
Leandro Melo committed
716
        return m_handler;
717
    }
hjk's avatar
hjk committed
718

Leandro Melo's avatar
Leandro Melo committed
719
720
721
722
723
724
725
726
727
728
729
730
731
private:
    FakeVimHandler *m_handler;
    QString m_needle;
};

class FakeVimAssistProposalItem : public BasicProposalItem
{
public:
    FakeVimAssistProposalItem(const FakeVimCompletionAssistProvider *provider)
        : m_provider(const_cast<FakeVimCompletionAssistProvider *>(provider))
    {}

    virtual bool implicitlyApplies() const
hjk's avatar
hjk committed
732
    {
Leandro Melo's avatar
Leandro Melo committed
733
        return false;
hjk's avatar
hjk committed
734
735
    }

Leandro Melo's avatar
Leandro Melo committed
736
    virtual bool prematurelyApplies(const QChar &c) const
hjk's avatar
hjk committed
737
    {
Leandro Melo's avatar
Leandro Melo committed
738
739
        m_provider->appendNeedle(c);
        return text() == m_provider->needle();
hjk's avatar
hjk committed
740
741
    }

Leandro Melo's avatar
Leandro Melo committed
742
    virtual void applyContextualContent(BaseTextEditor *, int) const
hjk's avatar
hjk committed
743
    {
Leandro Melo's avatar
Leandro Melo committed
744
745
746
        QTC_ASSERT(m_provider->handler(), return);
        m_provider->handler()->handleReplay(text().mid(m_provider->needle().size()));
        const_cast<FakeVimCompletionAssistProvider *>(m_provider)->setInactive();
hjk's avatar
hjk committed
747
748
    }

Leandro Melo's avatar
Leandro Melo committed
749
750
751
752
753
754
755
756
757
758
759
760
761
private:
    FakeVimCompletionAssistProvider *m_provider;
};


class FakeVimAssistProposalModel : public BasicProposalItemListModel
{
public:
    FakeVimAssistProposalModel(const QList<BasicProposalItem *> &items)
        : BasicProposalItemListModel(items)
    {}

    virtual bool supportsPrefixExpansion() const
hjk's avatar
hjk committed
762
    {
Leandro Melo's avatar
Leandro Melo committed
763
        return false;
hjk's avatar
hjk committed
764
    }
Leandro Melo's avatar
Leandro Melo committed
765
};
hjk's avatar
hjk committed
766

Leandro Melo's avatar
Leandro Melo committed
767
768
769
class FakeVimCompletionAssistProcessor : public IAssistProcessor
{
public:
hjk's avatar
hjk committed
770
    FakeVimCompletionAssistProcessor(const IAssistProvider *provider)
Leandro Melo's avatar
Leandro Melo committed
771
772
773
        : m_provider(static_cast<const FakeVimCompletionAssistProvider *>(provider))
    {}

hjk's avatar
hjk committed
774
    IAssistProposal *perform(const IAssistInterface *interface)
hjk's avatar
hjk committed
775
    {
Leandro Melo's avatar
Leandro Melo committed
776
777
778
779
        const QString &needle = m_provider->needle();

        const int basePosition = interface->position() - needle.size();

780
        QTextCursor tc(interface->textDocument());
Leandro Melo's avatar
Leandro Melo committed
781
        tc.setPosition(interface->position());
hjk's avatar
hjk committed
782
783
        tc.movePosition(QTextCursor::Start, QTextCursor::MoveAnchor);

Leandro Melo's avatar
Leandro Melo committed
784
        QList<BasicProposalItem *> items;
hjk's avatar
hjk committed
785
786
787
        QSet<QString> seen;
        QTextDocument::FindFlags flags = QTextDocument::FindCaseSensitively;
        while (1) {
Leandro Melo's avatar
Leandro Melo committed
788
            tc = tc.document()->find(needle, tc.position(), flags);
hjk's avatar
hjk committed
789
790
791
792
793
794
            if (tc.isNull())
                break;
            QTextCursor sel = tc;
            sel.select(QTextCursor::WordUnderCursor);
            QString found = sel.selectedText();
            // Only add "real" completions.
Leandro Melo's avatar
Leandro Melo committed
795
            if (found.startsWith(needle)
hjk's avatar
hjk committed
796
                    && !seen.contains(found)
Leandro Melo's avatar
Leandro Melo committed
797
                    && sel.anchor() != basePosition) {
hjk's avatar
hjk committed
798
                seen.insert(found);
Leandro Melo's avatar
Leandro Melo committed
799
800
801
                BasicProposalItem *item = new FakeVimAssistProposalItem(m_provider);
                item->setText(found);
                items.append(item);
hjk's avatar
hjk committed
802
803
804
805
806
            }
            tc.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor);
        }
        //qDebug() << "COMPLETIONS" << completions->size();

Leandro Melo's avatar
Leandro Melo committed
807
808
        delete interface;
        return new GenericProposal(basePosition, new FakeVimAssistProposalModel(items));
hjk's avatar
hjk committed
809
810
811
    }

private:
Leandro Melo's avatar
Leandro Melo committed
812
    const FakeVimCompletionAssistProvider *m_provider;
hjk's avatar
hjk committed
813
};
814

Leandro Melo's avatar
Leandro Melo committed
815
816
817
818
819
IAssistProcessor *FakeVimCompletionAssistProvider::createProcessor() const
{
    return new FakeVimCompletionAssistProcessor(this);
}

820

hjk's avatar
hjk committed
821
822
///////////////////////////////////////////////////////////////////////
//
823
// FakeVimPluginPrivate
hjk's avatar
hjk committed
824
825
826
//
///////////////////////////////////////////////////////////////////////

827
class FakeVimPluginPrivate : public QObject
hjk's avatar
hjk committed
828
{
829
830
831
832
833
834
    Q_OBJECT

public:
    FakeVimPluginPrivate(FakeVimPlugin *);
    ~FakeVimPluginPrivate();
    friend class FakeVimPlugin;
835
    friend class FakeVimExCommandsPage;
836
837
    friend class FakeVimUserCommandsPage;
    friend class FakeVimUserCommandsModel;
838

839
    bool initialize();
840
    void aboutToShutdown();
841
842

private slots:
843
    void onCoreAboutToClose();
844
845
    void editorOpened(Core::IEditor *);
    void editorAboutToClose(Core::IEditor *);
846

847
    void setUseFakeVim(const QVariant &value);
848
    void setUseFakeVimInternal(bool on);
849
    void quitFakeVim();
850
    void triggerCompletions();
851
    void triggerSimpleCompletions(const QString &needle, bool forward);
hjk's avatar
hjk committed
852
    void windowCommand(int key);
853
    void find(bool reverse);
854
    void findNext(bool reverse);
855
856
857
    void foldToggle(int depth);
    void foldAll(bool fold);
    void fold(int depth, bool fold);
858
    void foldGoTo(int count, bool current);
Lukas Holecek's avatar
Lukas Holecek committed
859
    void jumpToGlobalMark(QChar mark, bool backTickMode, const QString &fileName);
hjk's avatar
hjk committed
860
    void showSettingsDialog();
861
    void maybeReadVimRc();
hjk's avatar
hjk committed
862
863
    void setBlockSelection(bool);
    void hasBlockSelection(bool*);
864

865
866
867
    void resetCommandBuffer();
    void showCommandBuffer(const QString &contents, int cursorPos, int messageLevel,
                           QObject *eventFilter);
868
869
    void showExtraInformation(const QString &msg);
    void changeSelection(const QList<QTextEdit::ExtraSelection> &selections);
870
    void highlightMatches(const QString &needle);
871
    void moveToMatchingParenthesis(bool *moved, bool *forward, QTextCursor *cursor);
872
    void checkForElectricCharacter(bool *result, QChar c);
Lukas Holecek's avatar
Lukas Holecek committed
873
    void indentRegion(int beginBlock, int endBlock, QChar typedChar);
874
    void handleExCommand(bool *handled, const ExCommand &cmd);
875

876
877
878
    void writeSettings();
    void readSettings();

879
880
    void handleDelayedQuitAll(bool forced);
    void handleDelayedQuit(bool forced, Core::IEditor *editor);
881
    void userActionTriggered();
882

883
884
    void switchToFile(int n);
    int currentFile() const;
885

886
887
888
signals:
    void delayedQuitRequested(bool forced, Core::IEditor *editor);
    void delayedQuitAllRequested(bool forced);
889
890
891

private:
    FakeVimPlugin *q;
hjk's avatar
hjk committed
892
    FakeVimOptionPage *m_fakeVimOptionsPage;
893
    FakeVimExCommandsPage *m_fakeVimExCommandsPage;
894
    FakeVimUserCommandsPage *m_fakeVimUserCommandsPage;
hjk's avatar
hjk committed
895
    QHash<IEditor *, FakeVimHandler *> m_editorToHandler;
896

hjk's avatar
hjk committed
897
898
    void triggerAction(const Id &id);
    void setActionChecked(const Id &id, bool check);
899

900
901
902
    typedef int (*DistFunction)(const QRect &cursor, const QRect &other);
    void moveSomewhere(DistFunction f);

903
904
905
906
907
908
909
910
911
912
    ExCommandMap &exCommandMap() { return m_exCommandMap; }
    ExCommandMap &defaultExCommandMap() { return m_defaultExCommandMap; }
    ExCommandMap m_exCommandMap;
    ExCommandMap m_defaultExCommandMap;

    UserCommandMap &userCommandMap() { return m_userCommandMap; }
    UserCommandMap &defaultUserCommandMap() { return m_defaultUserCommandMap; }
    UserCommandMap m_userCommandMap;
    UserCommandMap m_defaultUserCommandMap;

hjk's avatar
hjk committed
913
    StatusBarWidget *m_statusBar;
Leandro Melo's avatar
Leandro Melo committed
914
915
916
    // @TODO: Delete
    //WordCompletion *m_wordCompletion;
    FakeVimCompletionAssistProvider *m_wordProvider;
917
918
};

919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
QVariant FakeVimUserCommandsModel::data(const QModelIndex &index, int role) const
{
    if (!index.isValid())
        return QVariant();

    if (role == Qt::DisplayRole || role == Qt::EditRole) {
        switch (index.column()) {
        case 0: // Action
            return tr("User command #%1").arg(index.row() + 1);
        case 1: // Command
            return m_q->userCommandMap().value(index.row() + 1);
        }
    }

    return QVariant();
}

bool FakeVimUserCommandsModel::setData(const QModelIndex &index,
    const QVariant &data, int role)
{
    if (role == Qt::DisplayRole || role == Qt::EditRole)
        if (index.column() == 1)
            m_q->userCommandMap()[index.row() + 1] = data.toString();
    return true;
}

945
FakeVimPluginPrivate::FakeVimPluginPrivate(FakeVimPlugin *plugin)
946
{
947
    q = plugin;
hjk's avatar
hjk committed
948
    m_fakeVimOptionsPage = 0;
949
    m_fakeVimExCommandsPage = 0;
950
    m_fakeVimUserCommandsPage = 0;
951
    defaultExCommandMap()[CppTools::Constants::SWITCH_HEADER_SOURCE] =
952
        QRegExp("^A$");
953
    defaultExCommandMap()["Coreplugin.OutputPane.previtem"] =
954
        QRegExp("^(cN(ext)?|cp(revious)?)!?( (.*))?$");
955
    defaultExCommandMap()["Coreplugin.OutputPane.nextitem"] =
956
        QRegExp("^cn(ext)?!?( (.*))?$");
957
    defaultExCommandMap()[TextEditor::Constants::FOLLOW_SYMBOL_UNDER_CURSOR] =
958
        QRegExp("^tag?$");
959
    defaultExCommandMap()[Core::Constants::GO_BACK] =
960
        QRegExp("^pop?$");
hjk's avatar
hjk committed
961
    defaultExCommandMap()[_("QtCreator.Locate")] =
962
        QRegExp("^e$");
963
964
965
966
967
968

    for (int i = 1; i < 10; ++i) {
        QString cmd = QString::fromLatin1(":echo User command %1 executed.<CR>");
        defaultUserCommandMap().insert(i, cmd.arg(i));
    }

969
    m_statusBar = 0;
hjk's avatar
hjk committed
970
971
}

972
FakeVimPluginPrivate::~FakeVimPluginPrivate()
hjk's avatar
hjk committed
973
{
hjk's avatar
hjk committed
974
975
976
    q->removeObject(m_fakeVimOptionsPage);
    delete m_fakeVimOptionsPage;
    m_fakeVimOptionsPage = 0;
dt's avatar
dt committed
977
    delete theFakeVimSettings();
978
979
980
981

    q->removeObject(m_fakeVimExCommandsPage);
    delete m_fakeVimExCommandsPage;
    m_fakeVimExCommandsPage = 0;
982
983
984
985

    q->removeObject(m_fakeVimUserCommandsPage);
    delete m_fakeVimUserCommandsPage;
    m_fakeVimUserCommandsPage = 0;
986
987
}

988
989
void FakeVimPluginPrivate::onCoreAboutToClose()
{
990
    // Don't attach to editors anymore.
hjk's avatar
hjk committed
991
    disconnect(ICore::editorManager(), SIGNAL(editorOpened(Core::IEditor*)),
992
993
994
        this, SLOT(editorOpened(Core::IEditor*)));
}

995
996
void FakeVimPluginPrivate::aboutToShutdown()
{
hjk's avatar
hjk committed
997
998
}

999
bool FakeVimPluginPrivate::initialize()
hjk's avatar
hjk committed
1000
{
hjk's avatar
hjk committed
1001
    EditorManager *editorManager = ICore::editorManager();
hjk's avatar
hjk committed
1002

Leandro Melo's avatar
Leandro Melo committed
1003
1004
1005
1006
    //m_wordCompletion = new WordCompletion;
    //q->addAutoReleasedObject(m_wordCompletion);
    m_wordProvider = new FakeVimCompletionAssistProvider;

hjk's avatar
hjk committed
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
/*
    // Set completion settings and keep them up to date.
    TextEditorSettings *textEditorSettings = TextEditorSettings::instance();
    completion->setCompletionSettings(textEditorSettings->completionSettings());
    connect(textEditorSettings,
        SIGNAL(completionSettingsChanged(TextEditor::CompletionSettings)),
        completion,
        SLOT(setCompletionSettings(TextEditor::CompletionSettings)));
*/

hjk's avatar
hjk committed
1017
    Context globalcontext(<