fakevimplugin.cpp 56.7 KB
Newer Older
1
/**************************************************************************
hjk's avatar
hjk committed
2
3
4
**
** This file is part of Qt Creator
**
hjk's avatar
hjk committed
5
** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
hjk's avatar
hjk committed
6
**
Eike Ziller's avatar
Eike Ziller committed
7
** Contact: http://www.qt-project.org/
hjk's avatar
hjk committed
8
9
**
**
10
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
11
**
hjk's avatar
hjk committed
12
13
14
15
16
17
** 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.
hjk's avatar
hjk committed
18
**
con's avatar
con committed
19
** In addition, as a special exception, Nokia gives you certain additional
hjk's avatar
hjk committed
20
** rights. These rights are described in the Nokia Qt LGPL Exception
con's avatar
con committed
21
22
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
23
24
25
26
27
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
hjk's avatar
hjk committed
28
**
29
**************************************************************************/
hjk's avatar
hjk committed
30
31
32

#include "fakevimplugin.h"

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

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

#include <projectexplorer/projectexplorerconstants.h>

55
#include <texteditor/basetextdocumentlayout.h>
hjk's avatar
hjk committed
56
57
58
#include <texteditor/basetexteditor.h>
#include <texteditor/basetextmark.h>
#include <texteditor/texteditorconstants.h>
Jarek Kobus's avatar
Jarek Kobus committed
59
#include <texteditor/typingsettings.h>
60
#include <texteditor/tabsettings.h>
Jarek Kobus's avatar
Jarek Kobus committed
61
#include <texteditor/icodestylepreferences.h>
62
#include <texteditor/texteditorsettings.h>
63
#include <texteditor/indenter.h>
Leandro Melo's avatar
Leandro Melo committed
64
65
66
67
68
69
#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
70

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

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>

81
82
83
84
85
86
87
#include <QAbstractTableModel>
#include <QDebug>
#include <QFile>
#include <QtPlugin>
#include <QObject>
#include <QSettings>
#include <QTextStream>
hjk's avatar
hjk committed
88

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

using namespace FakeVim::Internal;
using namespace TextEditor;
using namespace Core;

namespace FakeVim {
namespace Constants {

hjk's avatar
hjk committed
106
107
108
109
110
111
112
const char INSTALL_HANDLER[]                = "TextEditor.FakeVimHandler";
const char MINI_BUFFER[]                    = "TextEditor.FakeVimMiniBuffer";
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
113
114
115
116
117

} // namespace Constants
} // namespace FakeVim


hjk's avatar
hjk committed
118
119
120
namespace FakeVim {
namespace Internal {

121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
class MiniBuffer : public QLabel
{
    Q_OBJECT

public:
    void setContents(const QString &contents, int cursorPos)
    {
        QString msg = contents;
        if (cursorPos != -1)
            msg = contents.left(cursorPos) + QChar(10073) + contents.mid(cursorPos);
        setText("  " + msg);
    }
};

class MiniBuffer1 : public QLineEdit
{
    Q_OBJECT

public:
    MiniBuffer1()
    {
        setFrame(false);
    }
    void showEvent(QShowEvent *ev)
    {
        QLineEdit::showEvent(ev);
        QColor color = Qt::black;
        QPalette pal = parentWidget()->palette();
        pal.setBrush(QPalette::All, QPalette::WindowText, color);
        pal.setBrush(QPalette::All, QPalette::ButtonText, color);
        pal.setBrush(QPalette::All, QPalette::Foreground, color);
        pal.setBrush(QPalette::All, QPalette::Background, color);
        //color.setAlpha(100);
        //pal.setBrush(QPalette::Disabled, QPalette::WindowText, color);
        //pal.setBrush(QPalette::Disabled, QPalette::ButtonText, color);
        //pal.setBrush(QPalette::Disabled, QPalette::Foreground, color);
        setPalette(pal);
    }
    void setContents(const QString &contents, int cursorPos)
    {
        setText(contents);
        setCursorPosition(cursorPos);
    }
};

hjk's avatar
hjk committed
166
167
168
169
170
171
///////////////////////////////////////////////////////////////////////
//
// FakeVimOptionPage
//
///////////////////////////////////////////////////////////////////////

172
173
typedef QMap<QString, QRegExp> ExCommandMap;
typedef QMap<int, QString> UserCommandMap;
hjk's avatar
hjk committed
174
typedef QLatin1String _;
175

hjk's avatar
hjk committed
176
177
178
179
180
class FakeVimOptionPage : public Core::IOptionsPage
{
    Q_OBJECT

public:
181
182
183
184
185
186
187
188
    FakeVimOptionPage()
    {
        setId(_(Constants::SETTINGS_ID));
        setDisplayName(tr("General"));
        setCategory(_(Constants::SETTINGS_CATEGORY));
        setDisplayCategory(tr("FakeVim"));
        setCategoryIcon(_(Constants::SETTINGS_CATEGORY_FAKEVIM_ICON));
    }
hjk's avatar
hjk committed
189
190

    QWidget *createPage(QWidget *parent);
hjk's avatar
hjk committed
191
    void apply() { m_group.apply(ICore::settings()); }
hjk's avatar
hjk committed
192
    void finish() { m_group.finish(); }
193
    virtual bool matches(const QString &) const;
hjk's avatar
hjk committed
194
195
196
197
198
199
200
201
202

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

private:
    friend class DebuggerPlugin;
    Ui::FakeVimOptionPage m_ui;
203
    QString m_searchKeywords;
204
    Utils::SavedActionSet m_group;
hjk's avatar
hjk committed
205
206
207
208
209
210
211
212
};

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

    m_group.clear();
213
    m_group.insert(theFakeVimSetting(ConfigUseFakeVim),
214
        m_ui.checkBoxUseFakeVim);
215
216
    m_group.insert(theFakeVimSetting(ConfigReadVimRc),
        m_ui.checkBoxReadVimRc);
hjk's avatar
hjk committed
217

218
    m_group.insert(theFakeVimSetting(ConfigExpandTab),
hjk's avatar
hjk committed
219
        m_ui.checkBoxExpandTab);
220
    m_group.insert(theFakeVimSetting(ConfigHlSearch),
hjk's avatar
hjk committed
221
        m_ui.checkBoxHlSearch);
222
    m_group.insert(theFakeVimSetting(ConfigShiftWidth),
223
        m_ui.spinBoxShiftWidth);
224
225
    m_group.insert(theFakeVimSetting(ConfigShowMarks),
        m_ui.checkBoxShowMarks);
hjk's avatar
hjk committed
226

227
    m_group.insert(theFakeVimSetting(ConfigSmartTab),
hjk's avatar
hjk committed
228
        m_ui.checkBoxSmartTab);
229
    m_group.insert(theFakeVimSetting(ConfigStartOfLine),
hjk's avatar
hjk committed
230
        m_ui.checkBoxStartOfLine);
231
    m_group.insert(theFakeVimSetting(ConfigTabStop),
232
        m_ui.spinBoxTabStop);
233
    m_group.insert(theFakeVimSetting(ConfigBackspace),
hjk's avatar
hjk committed
234
        m_ui.lineEditBackspace);
235
236
    m_group.insert(theFakeVimSetting(ConfigIsKeyword),
        m_ui.lineEditIsKeyword);
hjk's avatar
hjk committed
237

238
239
    m_group.insert(theFakeVimSetting(ConfigPassControlKey),
        m_ui.checkBoxPassControlKey);
240
    m_group.insert(theFakeVimSetting(ConfigAutoIndent),
hjk's avatar
hjk committed
241
        m_ui.checkBoxAutoIndent);
242
    m_group.insert(theFakeVimSetting(ConfigSmartIndent),
243
        m_ui.checkBoxSmartIndent);
244
    m_group.insert(theFakeVimSetting(ConfigIncSearch),
245
        m_ui.checkBoxIncSearch);
246
247
    m_group.insert(theFakeVimSetting(ConfigUseCoreSearch),
        m_ui.checkBoxUseCoreSearch);
hjk's avatar
hjk committed
248
249

    connect(m_ui.pushButtonCopyTextEditorSettings, SIGNAL(clicked()),
250
        SLOT(copyTextEditorSettings()));
hjk's avatar
hjk committed
251
    connect(m_ui.pushButtonSetQtStyle, SIGNAL(clicked()),
252
        SLOT(setQtStyle()));
hjk's avatar
hjk committed
253
    connect(m_ui.pushButtonSetPlainStyle, SIGNAL(clicked()),
254
255
        SLOT(setPlainStyle()));

256
    if (m_searchKeywords.isEmpty()) {
257
        QLatin1Char sep(' ');
258
        QTextStream(&m_searchKeywords)
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
                << 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()
                << 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();
275
276
        m_searchKeywords.remove(QLatin1Char('&'));
    }
hjk's avatar
hjk committed
277
278
279
280
281
    return w;
}

void FakeVimOptionPage::copyTextEditorSettings()
{
Jarek Kobus's avatar
Jarek Kobus committed
282
283
284
    TabSettings ts = TextEditorSettings::instance()->codeStyle()->tabSettings();
    TypingSettings tps = TextEditorSettings::instance()->typingSettings();
    m_ui.checkBoxExpandTab->setChecked(ts.m_tabPolicy != TabSettings::TabsOnlyTabPolicy);
285
286
    m_ui.spinBoxTabStop->setValue(ts.m_tabSize);
    m_ui.spinBoxShiftWidth->setValue(ts.m_indentSize);
287
    m_ui.checkBoxSmartTab->setChecked(
Jarek Kobus's avatar
Jarek Kobus committed
288
        tps.m_smartBackspaceBehavior == TypingSettings::BackspaceFollowsPreviousIndents);
289
    m_ui.checkBoxAutoIndent->setChecked(true);
Jarek Kobus's avatar
Jarek Kobus committed
290
    m_ui.checkBoxSmartIndent->setChecked(tps.m_autoIndent);
291
    m_ui.checkBoxIncSearch->setChecked(true);
hjk's avatar
hjk committed
292
293
294
295
296
}

void FakeVimOptionPage::setQtStyle()
{
    m_ui.checkBoxExpandTab->setChecked(true);
297
298
    m_ui.spinBoxTabStop->setValue(4);
    m_ui.spinBoxShiftWidth->setValue(4);
hjk's avatar
hjk committed
299
300
    m_ui.checkBoxSmartTab->setChecked(true);
    m_ui.checkBoxAutoIndent->setChecked(true);
301
    m_ui.checkBoxSmartIndent->setChecked(true);
302
    m_ui.checkBoxIncSearch->setChecked(true);
hjk's avatar
hjk committed
303
    m_ui.lineEditBackspace->setText(_("indent,eol,start"));
hjk's avatar
hjk committed
304
305
306
307
308
}

void FakeVimOptionPage::setPlainStyle()
{
    m_ui.checkBoxExpandTab->setChecked(false);
309
310
    m_ui.spinBoxTabStop->setValue(8);
    m_ui.spinBoxShiftWidth->setValue(8);
hjk's avatar
hjk committed
311
312
    m_ui.checkBoxSmartTab->setChecked(false);
    m_ui.checkBoxAutoIndent->setChecked(false);
313
    m_ui.checkBoxSmartIndent->setChecked(false);
314
    m_ui.checkBoxIncSearch->setChecked(false);
hjk's avatar
hjk committed
315
    m_ui.lineEditBackspace->setText(QString());
hjk's avatar
hjk committed
316
317
}

318
319
320
321
322
bool FakeVimOptionPage::matches(const QString &s) const
{
    return m_searchKeywords.contains(s, Qt::CaseInsensitive);
}

323
//const char *FAKEVIM_CONTEXT = "FakeVim";
hjk's avatar
hjk committed
324

325
326
327
328
329
330
///////////////////////////////////////////////////////////////////////
//
// FakeVimExCommandsPage
//
///////////////////////////////////////////////////////////////////////

331
enum { CommandRole = Qt::UserRole };
332

333
class FakeVimExCommandsPage : public Core::CommandMappings
334
335
336
337
{
    Q_OBJECT

public:
338
339
340
341
342
343
344
345
346
    FakeVimExCommandsPage(FakeVimPluginPrivate *q)
        : m_q(q)
    {
        setId(_(Constants::SETTINGS_EX_CMDS_ID));
        setDisplayName(tr("Ex Command Mapping"));
        setCategory(_(Constants::SETTINGS_CATEGORY));
        setDisplayCategory(tr("FakeVim"));
        setCategoryIcon(_(Constants::SETTINGS_CATEGORY_FAKEVIM_ICON));
    }
347
348
349

    QWidget *createPage(QWidget *parent);
    void initialize();
350
351
    ExCommandMap &exCommandMap();
    ExCommandMap &defaultExCommandMap();
352
353
354

public slots:
    void commandChanged(QTreeWidgetItem *current);
355
356
357
    void targetIdentifierChanged();
    void resetTargetIdentifier();
    void removeTargetIdentifier();
358
359
360
    void defaultAction();

private:
361
    FakeVimPluginPrivate *m_q;
362
363
364
365
};

QWidget *FakeVimExCommandsPage::createPage(QWidget *parent)
{
366
367
368
    QWidget *w = CommandMappings::createPage(parent);
    setPageTitle(tr("Ex Command Mapping"));
    setTargetHeader(tr("Ex Trigger Expression"));
369
    setTargetLabelText(tr("Regular expression:"));
370
371
    setTargetEditTitle(tr("Ex Command"));
    setImportExportEnabled(false);
372
373
374
375
376
    return w;
}

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

Eike Ziller's avatar
Eike Ziller committed
379
    foreach (Command *c, Core::ActionManager::commands()) {
380
381
382
        if (c->action() && c->action()->isSeparator())
            continue;

383
        QTreeWidgetItem *item = new QTreeWidgetItem;
384
        const QString name = c->id().toString();
385
386
        const int pos = name.indexOf(QLatin1Char('.'));
        const QString section = name.left(pos);
387
        const QString subId = name.mid(pos + 1);
388
        item->setData(0, CommandRole, name);
389
390

        if (!sections.contains(section)) {
391
392
            QTreeWidgetItem *categoryItem =
                new QTreeWidgetItem(commandList(), QStringList() << section);
393
394
395
396
            QFont f = categoryItem->font(0);
            f.setBold(true);
            categoryItem->setFont(0, f);
            sections.insert(section, categoryItem);
397
            commandList()->expandItem(categoryItem);
398
399
400
401
        }
        sections[section]->addChild(item);

        item->setText(0, subId);
402
        item->setText(1, c->description());
403

404
405
406
407
        QString regex;
        if (exCommandMap().contains(name))
            regex = exCommandMap()[name].pattern();
        item->setText(2, regex);
408

409
        if (regex != defaultExCommandMap()[name].pattern())
410
            setModified(item, true);
411
412
413
414
415
416
417
    }

    commandChanged(0);
}

void FakeVimExCommandsPage::commandChanged(QTreeWidgetItem *current)
{
418
    CommandMappings::commandChanged(current);
419
420
    if (current)
        targetEdit()->setText(current->text(2));
421
422
}

423
void FakeVimExCommandsPage::targetIdentifierChanged()
424
{
425
    QTreeWidgetItem *current = commandList()->currentItem();
426
427
428
    if (!current)
        return;

429
    const QString name =  current->data(0, CommandRole).toString();
430
    const QString regex = targetEdit()->text();
431
432

    if (current->data(0, Qt::UserRole).isValid()) {
433
434
        current->setText(2, regex);
        exCommandMap()[name] = QRegExp(regex);
435
    }
436

437
    setModified(current, regex != defaultExCommandMap()[name].pattern());
438
439
}

440
void FakeVimExCommandsPage::resetTargetIdentifier()
441
{
442
    QTreeWidgetItem *current = commandList()->currentItem();
443
444
    if (!current)
        return;
445
    const QString name = current->data(0, CommandRole).toString();
446
447
448
449
    QString regex;
    if (defaultExCommandMap().contains(name))
        regex = defaultExCommandMap()[name].pattern();
    targetEdit()->setText(regex);
450
451
}

452
void FakeVimExCommandsPage::removeTargetIdentifier()
453
{
454
    targetEdit()->clear();
455
456
457
458
}

void FakeVimExCommandsPage::defaultAction()
{
hjk's avatar
hjk committed
459
460
461
462
463
464
    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);
465
            const QString name = item->data(0, CommandRole).toString();
hjk's avatar
hjk committed
466
467
468
469
470
471
472
473
            QString regex;
            if (defaultExCommandMap().contains(name))
                regex = defaultExCommandMap()[name].pattern();
            setModified(item, false);
            item->setText(2, regex);
            if (item == commandList()->currentItem())
                commandChanged(item);
        }
474
475
476
    }
}

477
478
479
480
481
482
483
484
///////////////////////////////////////////////////////////////////////
//
// FakeVimUserCommandsPage
//
///////////////////////////////////////////////////////////////////////

class FakeVimUserCommandsModel : public QAbstractTableModel
{
Friedemann Kleint's avatar
Friedemann Kleint committed
485
    Q_OBJECT
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
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);
    }
};

class FakeVimUserCommandsPage : public Core::IOptionsPage
{
    Q_OBJECT

public:
560
561
562
563
564
565
566
567
568
569
    FakeVimUserCommandsPage(FakeVimPluginPrivate *q)
        : m_q(q)
    {
        setId(_(Constants::SETTINGS_USER_CMDS_ID));
        setDisplayName(tr("User Command Mapping"));
        setCategory(_(Constants::SETTINGS_CATEGORY));
        setDisplayCategory(tr("FakeVim"));
        setCategoryIcon(_(Constants::SETTINGS_CATEGORY_FAKEVIM_ICON));
    }

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
    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
606
607
608
609
610
611
612

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

Leandro Melo's avatar
Leandro Melo committed
613
class FakeVimCompletionAssistProvider : public TextEditor::CompletionAssistProvider
hjk's avatar
hjk committed
614
615
{
public:
hjk's avatar
hjk committed
616
    bool supportsEditor(const Core::Id &) const
hjk's avatar
hjk committed
617
    {
Leandro Melo's avatar
Leandro Melo committed
618
        return false;
hjk's avatar
hjk committed
619
620
    }

hjk's avatar
hjk committed
621
    TextEditor::IAssistProcessor *createProcessor() const;
Leandro Melo's avatar
Leandro Melo committed
622
623

    void setActive(const QString &needle, bool forward, FakeVimHandler *handler)
hjk's avatar
hjk committed
624
    {
Leandro Melo's avatar
Leandro Melo committed
625
626
627
628
629
630
631
632
633
634
635
636
        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
637
638
    }

Leandro Melo's avatar
Leandro Melo committed
639
    void setInactive()
hjk's avatar
hjk committed
640
    {
Leandro Melo's avatar
Leandro Melo committed
641
642
        m_needle.clear();
        m_handler = 0;
hjk's avatar
hjk committed
643
644
    }

Leandro Melo's avatar
Leandro Melo committed
645
    const QString &needle() const
646
    {
Leandro Melo's avatar
Leandro Melo committed
647
        return m_needle;
648
    }
hjk's avatar
hjk committed
649

Leandro Melo's avatar
Leandro Melo committed
650
    void appendNeedle(const QChar &c)
651
    {
Leandro Melo's avatar
Leandro Melo committed
652
        m_needle.append(c);
653
654
    }

Leandro Melo's avatar
Leandro Melo committed
655
    FakeVimHandler *handler() const
656
    {
Leandro Melo's avatar
Leandro Melo committed
657
        return m_handler;
658
    }
hjk's avatar
hjk committed
659

Leandro Melo's avatar
Leandro Melo committed
660
661
662
663
664
665
666
667
668
669
670
671
672
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
673
    {
Leandro Melo's avatar
Leandro Melo committed
674
        return false;
hjk's avatar
hjk committed
675
676
    }

Leandro Melo's avatar
Leandro Melo committed
677
    virtual bool prematurelyApplies(const QChar &c) const
hjk's avatar
hjk committed
678
    {
Leandro Melo's avatar
Leandro Melo committed
679
680
        m_provider->appendNeedle(c);
        return text() == m_provider->needle();
hjk's avatar
hjk committed
681
682
    }

Leandro Melo's avatar
Leandro Melo committed
683
    virtual void applyContextualContent(BaseTextEditor *, int) const
hjk's avatar
hjk committed
684
    {
Leandro Melo's avatar
Leandro Melo committed
685
686
687
        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
688
689
    }

Leandro Melo's avatar
Leandro Melo committed
690
691
692
693
694
695
696
697
698
699
700
701
702
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
703
    {
Leandro Melo's avatar
Leandro Melo committed
704
        return false;
hjk's avatar
hjk committed
705
    }
Leandro Melo's avatar
Leandro Melo committed
706
};
hjk's avatar
hjk committed
707

Leandro Melo's avatar
Leandro Melo committed
708
709
710
711
712
713
714
715
class FakeVimCompletionAssistProcessor : public IAssistProcessor
{
public:
    FakeVimCompletionAssistProcessor(const TextEditor::IAssistProvider *provider)
        : m_provider(static_cast<const FakeVimCompletionAssistProvider *>(provider))
    {}

    virtual TextEditor::IAssistProposal *perform(const IAssistInterface *interface)
hjk's avatar
hjk committed
716
    {
Leandro Melo's avatar
Leandro Melo committed
717
718
719
720
        const QString &needle = m_provider->needle();

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

721
        QTextCursor tc(interface->textDocument());
Leandro Melo's avatar
Leandro Melo committed
722
        tc.setPosition(interface->position());
hjk's avatar
hjk committed
723
724
        tc.movePosition(QTextCursor::Start, QTextCursor::MoveAnchor);

Leandro Melo's avatar
Leandro Melo committed
725
        QList<BasicProposalItem *> items;
hjk's avatar
hjk committed
726
727
728
        QSet<QString> seen;
        QTextDocument::FindFlags flags = QTextDocument::FindCaseSensitively;
        while (1) {
Leandro Melo's avatar
Leandro Melo committed
729
            tc = tc.document()->find(needle, tc.position(), flags);
hjk's avatar
hjk committed
730
731
732
733
734
735
            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
736
            if (found.startsWith(needle)
hjk's avatar
hjk committed
737
                    && !seen.contains(found)
Leandro Melo's avatar
Leandro Melo committed
738
                    && sel.anchor() != basePosition) {
hjk's avatar
hjk committed
739
                seen.insert(found);
Leandro Melo's avatar
Leandro Melo committed
740
741
742
                BasicProposalItem *item = new FakeVimAssistProposalItem(m_provider);
                item->setText(found);
                items.append(item);
hjk's avatar
hjk committed
743
744
745
746
747
            }
            tc.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor);
        }
        //qDebug() << "COMPLETIONS" << completions->size();

Leandro Melo's avatar
Leandro Melo committed
748
749
        delete interface;
        return new GenericProposal(basePosition, new FakeVimAssistProposalModel(items));
hjk's avatar
hjk committed
750
751
752
    }

private:
Leandro Melo's avatar
Leandro Melo committed
753
    const FakeVimCompletionAssistProvider *m_provider;
hjk's avatar
hjk committed
754
};
755

Leandro Melo's avatar
Leandro Melo committed
756
757
758
759
760
IAssistProcessor *FakeVimCompletionAssistProvider::createProcessor() const
{
    return new FakeVimCompletionAssistProcessor(this);
}

761

hjk's avatar
hjk committed
762
763
///////////////////////////////////////////////////////////////////////
//
764
// FakeVimPluginPrivate
hjk's avatar
hjk committed
765
766
767
//
///////////////////////////////////////////////////////////////////////

768
class FakeVimPluginPrivate : public QObject
hjk's avatar
hjk committed
769
{
770
771
772
773
774
775
    Q_OBJECT

public:
    FakeVimPluginPrivate(FakeVimPlugin *);
    ~FakeVimPluginPrivate();
    friend class FakeVimPlugin;
776
    friend class FakeVimExCommandsPage;
777
778
    friend class FakeVimUserCommandsPage;
    friend class FakeVimUserCommandsModel;
779

780
    bool initialize();
781
    void aboutToShutdown();
782
783

private slots:
784
    void onCoreAboutToClose();
785
786
    void editorOpened(Core::IEditor *);
    void editorAboutToClose(Core::IEditor *);
787

788
    void setUseFakeVim(const QVariant &value);
789
    void setUseFakeVimInternal(bool on);
790
    void quitFakeVim();
791
    void triggerCompletions();
792
    void triggerSimpleCompletions(const QString &needle, bool forward);
hjk's avatar
hjk committed
793
    void windowCommand(int key);
794
    void find(bool reverse);
795
    void findNext(bool reverse);
hjk's avatar
hjk committed
796
    void showSettingsDialog();
797
    void maybeReadVimRc();
hjk's avatar
hjk committed
798
799
    void setBlockSelection(bool);
    void hasBlockSelection(bool*);
800

801
    void showCommandBuffer(const QString &contents, int cursorPos);
802
803
    void showExtraInformation(const QString &msg);
    void changeSelection(const QList<QTextEdit::ExtraSelection> &selections);
804
    void moveToMatchingParenthesis(bool *moved, bool *forward, QTextCursor *cursor);
805
    void checkForElectricCharacter(bool *result, QChar c);
806
    void indentRegion(int beginLine, int endLine, QChar typedChar);
807
    void handleExCommand(bool *handled, const ExCommand &cmd);
808

809
810
811
    void writeSettings();
    void readSettings();

812
813
    void handleDelayedQuitAll(bool forced);
    void handleDelayedQuit(bool forced, Core::IEditor *editor);
814
    void userActionTriggered();
815

816
817
    void switchToFile(int n);
    int currentFile() const;
818

819
820
821
signals:
    void delayedQuitRequested(bool forced, Core::IEditor *editor);
    void delayedQuitAllRequested(bool forced);
822
823
824

private:
    FakeVimPlugin *q;
hjk's avatar
hjk committed
825
    FakeVimOptionPage *m_fakeVimOptionsPage;
826
    FakeVimExCommandsPage *m_fakeVimExCommandsPage;
827
    FakeVimUserCommandsPage *m_fakeVimUserCommandsPage;
828
    QHash<Core::IEditor *, FakeVimHandler *> m_editorToHandler;
829

hjk's avatar
hjk committed
830
831
    void triggerAction(const Core::Id &id);
    void setActionChecked(const Core::Id &id, bool check);
832

833
834
835
    typedef int (*DistFunction)(const QRect &cursor, const QRect &other);
    void moveSomewhere(DistFunction f);

836
837
838
839
840
841
842
843
844
845
    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;

846
    Core::StatusBarWidget *m_statusBar;
Leandro Melo's avatar
Leandro Melo committed
847
848
849
    // @TODO: Delete
    //WordCompletion *m_wordCompletion;
    FakeVimCompletionAssistProvider *m_wordProvider;
850
851
};

852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
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;
}

878
FakeVimPluginPrivate::FakeVimPluginPrivate(FakeVimPlugin *plugin)
879
{
880
    q = plugin;
hjk's avatar
hjk committed
881
    m_fakeVimOptionsPage = 0;
882
    m_fakeVimExCommandsPage = 0;
883
    m_fakeVimUserCommandsPage = 0;
884
    defaultExCommandMap()[CppTools::Constants::SWITCH_HEADER_SOURCE] =
885
        QRegExp("^A$");
886
    defaultExCommandMap()["Coreplugin.OutputPane.previtem"] =
887
        QRegExp("^(cN(ext)?|cp(revious)?)!?( (.*))?$");
888
    defaultExCommandMap()["Coreplugin.OutputPane.nextitem"] =
889
        QRegExp("^cn(ext)?!?( (.*))?$");
890
    defaultExCommandMap()[TextEditor::Constants::FOLLOW_SYMBOL_UNDER_CURSOR] =
891
        QRegExp("^tag?$");
892
    defaultExCommandMap()[Core::Constants::GO_BACK] =
893
        QRegExp("^pop?$");
hjk's avatar
hjk committed
894
    defaultExCommandMap()[_("QtCreator.Locate")] =
895
        QRegExp("^e$");
896
897
898
899
900
901

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

902
    m_statusBar = 0;
hjk's avatar
hjk committed
903
904
}

905
FakeVimPluginPrivate::~FakeVimPluginPrivate()
hjk's avatar
hjk committed
906
{
hjk's avatar
hjk committed
907
908
909
    q->removeObject(m_fakeVimOptionsPage);
    delete m_fakeVimOptionsPage;
    m_fakeVimOptionsPage = 0;
dt's avatar
dt committed
910
    delete theFakeVimSettings();
911
912
913
914

    q->removeObject(m_fakeVimExCommandsPage);
    delete m_fakeVimExCommandsPage;
    m_fakeVimExCommandsPage = 0;
915
916
917
918

    q->removeObject(m_fakeVimUserCommandsPage);
    delete m_fakeVimUserCommandsPage;
    m_fakeVimUserCommandsPage = 0;
919
920
}

921
922
void FakeVimPluginPrivate::onCoreAboutToClose()
{
923
    // Don't attach to editors anymore.
hjk's avatar
hjk committed
924
    disconnect(ICore::editorManager(), SIGNAL(editorOpened(Core::IEditor*)),
925
926
927
        this, SLOT(editorOpened(Core::IEditor*)));
}

928
929
void FakeVimPluginPrivate::aboutToShutdown()
{
hjk's avatar
hjk committed
930
931
}

932
bool FakeVimPluginPrivate::initialize()
hjk's avatar
hjk committed
933
{
hjk's avatar
hjk committed
934
    EditorManager *editorManager = ICore::editorManager();
hjk's avatar
hjk committed
935

Leandro Melo's avatar
Leandro Melo committed
936
937
938
939
    //m_wordCompletion = new WordCompletion;
    //q->addAutoReleasedObject(m_wordCompletion);
    m_wordProvider = new FakeVimCompletionAssistProvider;

hjk's avatar
hjk committed
940
941
942
943
944
945
946
947
948
949
/*
    // 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
950
    Context globalcontext(Core::Constants::C_GLOBAL);
hjk's avatar
hjk committed
951

hjk's avatar
hjk committed
952
953
    m_fakeVimOptionsPage = new FakeVimOptionPage;
    q->addObject(m_fakeVimOptionsPage);
954

955
    m_fakeVimExCommandsPage = new FakeVimExCommandsPage(this);
956
    q->addObject(m_fakeVimExCommandsPage);
957
958
959
960

    m_fakeVimUserCommandsPage = new FakeVimUserCommandsPage(this);
    q->addObject(m_fakeVimUserCommandsPage);

961
    readSettings();
962

con's avatar
con committed
963
    Core::Command *cmd = 0;
Eike Ziller's avatar
Eike Ziller committed
964
    cmd = Core::ActionManager::registerAction(theFakeVimSetting(ConfigUseFakeVim),
hjk's avatar
hjk committed
965
        Constants::INSTALL_HANDLER, globalcontext, true);
966
    cmd->setDefaultKeySequence(QKeySequence(Core::UseMacShortcuts ? tr("Meta+V,Meta+V") : tr("Alt+V,Alt+V")));
hjk's avatar
hjk committed
967

968
    ActionContainer *advancedMenu =
Eike Ziller's avatar
Eike Ziller committed
969
        Core::ActionManager::actionContainer(Core::Constants::M_EDIT_ADVANCED);
970
971
    advancedMenu->addAction(cmd, Core::Constants::G_EDIT_EDITOR);

972
973
    for (int i = 1; i < 10; ++i) {
        QAction *act = new QAction(this);
Robert Loehning's avatar
Robert Loehning committed
974
        act->setText(tr("Execute User Action #%1").arg(i));
975
976
        act->setData(i);
        QString id = QString("FakeVim.UserAction%1").arg(i);
Eike Ziller's avatar
Eike Ziller committed
977
        cmd = Core::ActionManager::registerAction(act, Core::Id(id), globalcontext);
978
        cmd->setDefaultKeySequence(QKeySequence((Core::UseMacShortcuts ? tr("Meta+V,%1") : tr("Alt+V,%1")).arg(i)));
979
980
981
        connect(act, SIGNAL(triggered()), SLOT(userActionTriggered()));
    }

hjk's avatar
hjk committed
982
    connect(ICore::instance(), SIGNAL(coreAboutToClose()), this, SLOT(onCoreAboutToClose()));
983

984
    // EditorManager
985
    connect(editorManager, SIGNAL(editorAboutToClose(Core::IEditor*)),
986
        this, SLOT(editorAboutToClose(Core::IEditor*)));
987
    connect(editorManager, SIGNAL(editorOpened(Core::IEditor*)),
988
989
        this, SLOT(editorOpened(Core::IEditor*)));

990
991
    connect(theFakeVimSetting(ConfigUseFakeVim), SIGNAL(valueChanged(QVariant)),
        this, SLOT(setUseFakeVim(QVariant)));
992
993
    connect(theFakeVimSetting(ConfigReadVimRc), SIGNAL(valueChanged(QVariant)),
        this, SLOT(maybeReadVimRc()));
hjk's avatar
hjk committed
994

995
    // Delayed operations.
996
997
998
999
    connect(this, SIGNAL(delayedQuitRequested(bool,Core::IEditor*)),
        this, SLOT(handleDelayedQuit(bool,Core::IEditor*)), Qt::QueuedConnection);
    connect(this, SIGNAL(delayedQuitAllRequested(bool)),
        this, SLOT(handleDelayedQuitAll(bool)), Qt::QueuedConnection);
1000
1001
    maybeReadVimRc();
    //    << "MODE: " << theFakeVimSetting(ConfigUseFakeVim)->value();
1002

hjk's avatar
hjk committed
1003
1004
1005
    return true;
}

1006
1007
1008
1009
1010
1011
1012
1013
1014
void FakeVimPluginPrivate::userActionTriggered()
{
    QAction *act = qobject_cast<QAction *>(sender());
    if (!act)
        return;
    const int key = act->data().toInt();
    if (!key)
        return;
    QString cmd = userCommandMap().value(key);
hjk's avatar
hjk committed
1015
    IEditor *editor = EditorManager::currentEditor();
1016
1017
1018
1019
1020
    FakeVimHandler *handler = m_editorToHandler[editor];
    if (handler)
        handler->handleInput(cmd);
}

hjk's avatar
hjk committed
1021
1022
1023
1024
1025
static const char exCommandMapGroup[] = "FakeVimExCommand";
static const char userCommandMapGroup[] = "FakeVimUserCommand";
static const char reKey[] = "RegEx";
static const char cmdKey[] = "Cmd";
static const char idKey[] = "Command";
1026

1027
void FakeVimPluginPrivate::writeSettings()
1028
{
hjk's avatar
hjk committed
1029
    QSettings *settings = ICore::settings();
1030
1031

    theFakeVimSettings()->writeSettings(settings);
1032

1033
    { // block
1034
    settings->beginWriteArray(_(exCommandMapGroup));
1035
    int count = 0;
1036
    typedef ExCommandMap::const_iterator Iterator;
1037
1038
    const Iterator end = exCommandMap().constEnd();
    for (Iterator it = exCommandMap().constBegin(); it != end; ++it) {
1039
1040
        const QString id = it.key();
        const QRegExp re = it.value();
1041

1042
1043
        if ((defaultExCommandMap().contains(id) && defaultExCommandMap