fakevimplugin.cpp 64.7 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 142 143 144 145 146 147 148 149 150 151 152 153 154
    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();
        } else {
            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);");
            }
            m_label->setStyleSheet(QString(
                "*{border-radius:2px;padding-left:4px;padding-right:4px;%1}").arg(css));
155

156 157 158 159 160 161 162 163
            if (m_edit->hasFocus())
                emit edited(QString(), -1);

            setCurrentWidget(m_label);
        }

        if (m_eventFilter != eventFilter) {
            if (m_eventFilter != 0) {
164
                m_label->setText(QString());
165 166 167 168 169 170 171 172 173 174 175 176 177
                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
178
    {
179 180 181
        QSize size = QWidget::sizeHint();
        // reserve maximal width for line edit widget
        return currentWidget() == m_edit ? QSize(maximumWidth(), size.height()) : size;
182
    }
183 184 185 186 187 188

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

private slots:
    void changed()
189
    {
190
        emit edited(m_edit->text(), m_edit->cursorPosition());
191
    }
192 193

    bool eventFilter(QObject *ob, QEvent *ev)
194
    {
195 196 197 198 199 200 201 202
        // 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;
203
    }
204 205 206 207 208

private:
    QLabel *m_label;
    QLineEdit *m_edit;
    QObject *m_eventFilter;
209 210
};

hjk's avatar
hjk committed
211 212 213 214 215 216
///////////////////////////////////////////////////////////////////////
//
// FakeVimOptionPage
//
///////////////////////////////////////////////////////////////////////

217 218
typedef QMap<QString, QRegExp> ExCommandMap;
typedef QMap<int, QString> UserCommandMap;
hjk's avatar
hjk committed
219
typedef QLatin1String _;
220

hjk's avatar
hjk committed
221
class FakeVimOptionPage : public IOptionsPage
hjk's avatar
hjk committed
222 223 224 225
{
    Q_OBJECT

public:
226 227
    FakeVimOptionPage()
    {
hjk's avatar
hjk committed
228
        setId(_(SETTINGS_ID));
229
        setDisplayName(tr("General"));
hjk's avatar
hjk committed
230
        setCategory(_(SETTINGS_CATEGORY));
231
        setDisplayCategory(tr("FakeVim"));
hjk's avatar
hjk committed
232
        setCategoryIcon(_(SETTINGS_CATEGORY_FAKEVIM_ICON));
233
    }
hjk's avatar
hjk committed
234 235

    QWidget *createPage(QWidget *parent);
hjk's avatar
hjk committed
236
    void apply() { m_group.apply(ICore::settings()); }
hjk's avatar
hjk committed
237
    void finish() { m_group.finish(); }
238
    virtual bool matches(const QString &) const;
hjk's avatar
hjk committed
239 240 241 242 243 244 245 246 247

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

private:
    friend class DebuggerPlugin;
    Ui::FakeVimOptionPage m_ui;
248
    QString m_searchKeywords;
249
    Utils::SavedActionSet m_group;
hjk's avatar
hjk committed
250 251 252 253 254 255 256 257
};

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

    m_group.clear();
258
    m_group.insert(theFakeVimSetting(ConfigUseFakeVim),
259
        m_ui.checkBoxUseFakeVim);
260 261
    m_group.insert(theFakeVimSetting(ConfigReadVimRc),
        m_ui.checkBoxReadVimRc);
hjk's avatar
hjk committed
262

263
    m_group.insert(theFakeVimSetting(ConfigExpandTab),
hjk's avatar
hjk committed
264
        m_ui.checkBoxExpandTab);
265
    m_group.insert(theFakeVimSetting(ConfigHlSearch),
hjk's avatar
hjk committed
266
        m_ui.checkBoxHlSearch);
267
    m_group.insert(theFakeVimSetting(ConfigShiftWidth),
268
        m_ui.spinBoxShiftWidth);
269 270
    m_group.insert(theFakeVimSetting(ConfigShowMarks),
        m_ui.checkBoxShowMarks);
hjk's avatar
hjk committed
271

272
    m_group.insert(theFakeVimSetting(ConfigSmartTab),
hjk's avatar
hjk committed
273
        m_ui.checkBoxSmartTab);
274
    m_group.insert(theFakeVimSetting(ConfigStartOfLine),
hjk's avatar
hjk committed
275
        m_ui.checkBoxStartOfLine);
276
    m_group.insert(theFakeVimSetting(ConfigTabStop),
277
        m_ui.spinBoxTabStop);
278
    m_group.insert(theFakeVimSetting(ConfigBackspace),
hjk's avatar
hjk committed
279
        m_ui.lineEditBackspace);
280 281
    m_group.insert(theFakeVimSetting(ConfigIsKeyword),
        m_ui.lineEditIsKeyword);
hjk's avatar
hjk committed
282

283 284
    m_group.insert(theFakeVimSetting(ConfigPassControlKey),
        m_ui.checkBoxPassControlKey);
285
    m_group.insert(theFakeVimSetting(ConfigAutoIndent),
hjk's avatar
hjk committed
286
        m_ui.checkBoxAutoIndent);
287
    m_group.insert(theFakeVimSetting(ConfigSmartIndent),
288
        m_ui.checkBoxSmartIndent);
Lukas Holecek's avatar
Lukas Holecek committed
289

290
    m_group.insert(theFakeVimSetting(ConfigIncSearch),
291
        m_ui.checkBoxIncSearch);
292 293
    m_group.insert(theFakeVimSetting(ConfigUseCoreSearch),
        m_ui.checkBoxUseCoreSearch);
294 295
    m_group.insert(theFakeVimSetting(ConfigSmartCase),
        m_ui.checkBoxSmartCase);
Lukas Holecek's avatar
Lukas Holecek committed
296 297
    m_group.insert(theFakeVimSetting(ConfigWrapScan),
        m_ui.checkBoxWrapScan);
hjk's avatar
hjk committed
298 299

    connect(m_ui.pushButtonCopyTextEditorSettings, SIGNAL(clicked()),
300
        SLOT(copyTextEditorSettings()));
hjk's avatar
hjk committed
301
    connect(m_ui.pushButtonSetQtStyle, SIGNAL(clicked()),
302
        SLOT(setQtStyle()));
hjk's avatar
hjk committed
303
    connect(m_ui.pushButtonSetPlainStyle, SIGNAL(clicked()),
304 305
        SLOT(setPlainStyle()));

306
    if (m_searchKeywords.isEmpty()) {
307
        QLatin1Char sep(' ');
308
        QTextStream(&m_searchKeywords)
309 310 311 312 313 314 315 316 317 318
                << 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()
319
                << sep << m_ui.checkBoxSmartCase->text()
320 321 322 323 324 325
                << 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();
326 327
        m_searchKeywords.remove(QLatin1Char('&'));
    }
hjk's avatar
hjk committed
328 329 330 331 332
    return w;
}

void FakeVimOptionPage::copyTextEditorSettings()
{
Jarek Kobus's avatar
Jarek Kobus committed
333 334 335
    TabSettings ts = TextEditorSettings::instance()->codeStyle()->tabSettings();
    TypingSettings tps = TextEditorSettings::instance()->typingSettings();
    m_ui.checkBoxExpandTab->setChecked(ts.m_tabPolicy != TabSettings::TabsOnlyTabPolicy);
336 337
    m_ui.spinBoxTabStop->setValue(ts.m_tabSize);
    m_ui.spinBoxShiftWidth->setValue(ts.m_indentSize);
338
    m_ui.checkBoxSmartTab->setChecked(
Jarek Kobus's avatar
Jarek Kobus committed
339
        tps.m_smartBackspaceBehavior == TypingSettings::BackspaceFollowsPreviousIndents);
340
    m_ui.checkBoxAutoIndent->setChecked(true);
Jarek Kobus's avatar
Jarek Kobus committed
341
    m_ui.checkBoxSmartIndent->setChecked(tps.m_autoIndent);
342
    m_ui.checkBoxIncSearch->setChecked(true);
hjk's avatar
hjk committed
343 344 345 346 347
}

void FakeVimOptionPage::setQtStyle()
{
    m_ui.checkBoxExpandTab->setChecked(true);
348 349
    m_ui.spinBoxTabStop->setValue(4);
    m_ui.spinBoxShiftWidth->setValue(4);
hjk's avatar
hjk committed
350 351
    m_ui.checkBoxSmartTab->setChecked(true);
    m_ui.checkBoxAutoIndent->setChecked(true);
352
    m_ui.checkBoxSmartIndent->setChecked(true);
353
    m_ui.checkBoxIncSearch->setChecked(true);
hjk's avatar
hjk committed
354
    m_ui.lineEditBackspace->setText(_("indent,eol,start"));
hjk's avatar
hjk committed
355 356 357 358 359
}

void FakeVimOptionPage::setPlainStyle()
{
    m_ui.checkBoxExpandTab->setChecked(false);
360 361
    m_ui.spinBoxTabStop->setValue(8);
    m_ui.spinBoxShiftWidth->setValue(8);
hjk's avatar
hjk committed
362 363
    m_ui.checkBoxSmartTab->setChecked(false);
    m_ui.checkBoxAutoIndent->setChecked(false);
364
    m_ui.checkBoxSmartIndent->setChecked(false);
365
    m_ui.checkBoxIncSearch->setChecked(false);
hjk's avatar
hjk committed
366
    m_ui.lineEditBackspace->setText(QString());
hjk's avatar
hjk committed
367 368
}

369 370 371 372 373
bool FakeVimOptionPage::matches(const QString &s) const
{
    return m_searchKeywords.contains(s, Qt::CaseInsensitive);
}

374
//const char *FAKEVIM_CONTEXT = "FakeVim";
hjk's avatar
hjk committed
375

376 377 378 379 380 381
///////////////////////////////////////////////////////////////////////
//
// FakeVimExCommandsPage
//
///////////////////////////////////////////////////////////////////////

382
enum { CommandRole = Qt::UserRole };
383

hjk's avatar
hjk committed
384
class FakeVimExCommandsPage : public CommandMappings
385 386 387 388
{
    Q_OBJECT

public:
389 390 391
    FakeVimExCommandsPage(FakeVimPluginPrivate *q)
        : m_q(q)
    {
hjk's avatar
hjk committed
392
        setId(_(SETTINGS_EX_CMDS_ID));
393
        setDisplayName(tr("Ex Command Mapping"));
hjk's avatar
hjk committed
394
        setCategory(_(SETTINGS_CATEGORY));
395
        setDisplayCategory(tr("FakeVim"));
hjk's avatar
hjk committed
396
        setCategoryIcon(_(SETTINGS_CATEGORY_FAKEVIM_ICON));
397
    }
398 399 400

    QWidget *createPage(QWidget *parent);
    void initialize();
401 402
    ExCommandMap &exCommandMap();
    ExCommandMap &defaultExCommandMap();
403 404 405

public slots:
    void commandChanged(QTreeWidgetItem *current);
406 407 408
    void targetIdentifierChanged();
    void resetTargetIdentifier();
    void removeTargetIdentifier();
409 410 411
    void defaultAction();

private:
412
    FakeVimPluginPrivate *m_q;
413 414 415 416
};

QWidget *FakeVimExCommandsPage::createPage(QWidget *parent)
{
417 418 419
    QWidget *w = CommandMappings::createPage(parent);
    setPageTitle(tr("Ex Command Mapping"));
    setTargetHeader(tr("Ex Trigger Expression"));
420
    setTargetLabelText(tr("Regular expression:"));
421 422
    setTargetEditTitle(tr("Ex Command"));
    setImportExportEnabled(false);
423 424 425 426 427
    return w;
}

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

hjk's avatar
hjk committed
430
    foreach (Command *c, ActionManager::commands()) {
431 432 433
        if (c->action() && c->action()->isSeparator())
            continue;

434
        QTreeWidgetItem *item = new QTreeWidgetItem;
435
        const QString name = c->id().toString();
436 437
        const int pos = name.indexOf(QLatin1Char('.'));
        const QString section = name.left(pos);
438
        const QString subId = name.mid(pos + 1);
439
        item->setData(0, CommandRole, name);
440 441

        if (!sections.contains(section)) {
442 443
            QTreeWidgetItem *categoryItem =
                new QTreeWidgetItem(commandList(), QStringList() << section);
444 445 446 447
            QFont f = categoryItem->font(0);
            f.setBold(true);
            categoryItem->setFont(0, f);
            sections.insert(section, categoryItem);
448
            commandList()->expandItem(categoryItem);
449 450 451 452
        }
        sections[section]->addChild(item);

        item->setText(0, subId);
453
        item->setText(1, c->description());
454

455 456 457 458
        QString regex;
        if (exCommandMap().contains(name))
            regex = exCommandMap()[name].pattern();
        item->setText(2, regex);
459

460
        if (regex != defaultExCommandMap()[name].pattern())
461
            setModified(item, true);
462 463 464 465 466 467 468
    }

    commandChanged(0);
}

void FakeVimExCommandsPage::commandChanged(QTreeWidgetItem *current)
{
469
    CommandMappings::commandChanged(current);
470 471
    if (current)
        targetEdit()->setText(current->text(2));
472 473
}

474
void FakeVimExCommandsPage::targetIdentifierChanged()
475
{
476
    QTreeWidgetItem *current = commandList()->currentItem();
477 478 479
    if (!current)
        return;

480
    const QString name =  current->data(0, CommandRole).toString();
481
    const QString regex = targetEdit()->text();
482 483

    if (current->data(0, Qt::UserRole).isValid()) {
484 485
        current->setText(2, regex);
        exCommandMap()[name] = QRegExp(regex);
486
    }
487

488
    setModified(current, regex != defaultExCommandMap()[name].pattern());
489 490
}

491
void FakeVimExCommandsPage::resetTargetIdentifier()
492
{
493
    QTreeWidgetItem *current = commandList()->currentItem();
494 495
    if (!current)
        return;
496
    const QString name = current->data(0, CommandRole).toString();
497 498 499 500
    QString regex;
    if (defaultExCommandMap().contains(name))
        regex = defaultExCommandMap()[name].pattern();
    targetEdit()->setText(regex);
501 502
}

503
void FakeVimExCommandsPage::removeTargetIdentifier()
504
{
505
    targetEdit()->clear();
506 507 508 509
}

void FakeVimExCommandsPage::defaultAction()
{
hjk's avatar
hjk committed
510 511 512 513 514 515
    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);
516
            const QString name = item->data(0, CommandRole).toString();
hjk's avatar
hjk committed
517 518 519 520 521 522 523 524
            QString regex;
            if (defaultExCommandMap().contains(name))
                regex = defaultExCommandMap()[name].pattern();
            setModified(item, false);
            item->setText(2, regex);
            if (item == commandList()->currentItem())
                commandChanged(item);
        }
525 526 527
    }
}

528 529 530 531 532 533 534 535
///////////////////////////////////////////////////////////////////////
//
// FakeVimUserCommandsPage
//
///////////////////////////////////////////////////////////////////////

class FakeVimUserCommandsModel : public QAbstractTableModel
{
Friedemann Kleint's avatar
Friedemann Kleint committed
536
    Q_OBJECT
537 538 539 540 541 542 543 544 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
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
606
class FakeVimUserCommandsPage : public IOptionsPage
607 608 609 610
{
    Q_OBJECT

public:
611 612 613
    FakeVimUserCommandsPage(FakeVimPluginPrivate *q)
        : m_q(q)
    {
hjk's avatar
hjk committed
614
        setId(_(SETTINGS_USER_CMDS_ID));
615
        setDisplayName(tr("User Command Mapping"));
hjk's avatar
hjk committed
616
        setCategory(_(SETTINGS_CATEGORY));
617
        setDisplayCategory(tr("FakeVim"));
hjk's avatar
hjk committed
618
        setCategoryIcon(_(SETTINGS_CATEGORY_FAKEVIM_ICON));
619 620
    }

621 622 623 624 625 626 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
    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
657 658 659 660 661 662 663

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

hjk's avatar
hjk committed
664
class FakeVimCompletionAssistProvider : public CompletionAssistProvider
hjk's avatar
hjk committed
665 666
{
public:
hjk's avatar
hjk committed
667
    bool supportsEditor(const Id &) const
hjk's avatar
hjk committed
668
    {
Leandro Melo's avatar
Leandro Melo committed
669
        return false;
hjk's avatar
hjk committed
670 671
    }

hjk's avatar
hjk committed
672
    IAssistProcessor *createProcessor() const;
Leandro Melo's avatar
Leandro Melo committed
673 674

    void setActive(const QString &needle, bool forward, FakeVimHandler *handler)
hjk's avatar
hjk committed
675
    {
Leandro Melo's avatar
Leandro Melo committed
676 677 678 679 680 681 682 683 684 685 686 687
        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
688 689
    }

Leandro Melo's avatar
Leandro Melo committed
690
    void setInactive()
hjk's avatar
hjk committed
691
    {
Leandro Melo's avatar
Leandro Melo committed
692 693
        m_needle.clear();
        m_handler = 0;
hjk's avatar
hjk committed
694 695
    }

Leandro Melo's avatar
Leandro Melo committed
696
    const QString &needle() const
697
    {
Leandro Melo's avatar
Leandro Melo committed
698
        return m_needle;
699
    }
hjk's avatar
hjk committed
700

Leandro Melo's avatar
Leandro Melo committed
701
    void appendNeedle(const QChar &c)
702
    {
Leandro Melo's avatar
Leandro Melo committed
703
        m_needle.append(c);
704 705
    }

Leandro Melo's avatar
Leandro Melo committed
706
    FakeVimHandler *handler() const
707
    {
Leandro Melo's avatar
Leandro Melo committed
708
        return m_handler;
709
    }
hjk's avatar
hjk committed
710

Leandro Melo's avatar
Leandro Melo committed
711 712 713 714 715 716 717 718 719 720 721 722 723
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
724
    {
Leandro Melo's avatar
Leandro Melo committed
725
        return false;
hjk's avatar
hjk committed
726 727
    }

Leandro Melo's avatar
Leandro Melo committed
728
    virtual bool prematurelyApplies(const QChar &c) const
hjk's avatar
hjk committed
729
    {
Leandro Melo's avatar
Leandro Melo committed
730 731
        m_provider->appendNeedle(c);
        return text() == m_provider->needle();
hjk's avatar
hjk committed
732 733
    }

Leandro Melo's avatar
Leandro Melo committed
734
    virtual void applyContextualContent(BaseTextEditor *, int) const
hjk's avatar
hjk committed
735
    {
Leandro Melo's avatar
Leandro Melo committed
736 737 738
        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
739 740
    }

Leandro Melo's avatar
Leandro Melo committed
741 742 743 744 745 746 747 748 749 750 751 752 753
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
754
    {
Leandro Melo's avatar
Leandro Melo committed
755
        return false;
hjk's avatar
hjk committed
756
    }
Leandro Melo's avatar
Leandro Melo committed
757
};
hjk's avatar
hjk committed
758

Leandro Melo's avatar
Leandro Melo committed
759 760 761
class FakeVimCompletionAssistProcessor : public IAssistProcessor
{
public:
hjk's avatar
hjk committed
762
    FakeVimCompletionAssistProcessor(const IAssistProvider *provider)
Leandro Melo's avatar
Leandro Melo committed
763 764 765
        : m_provider(static_cast<const FakeVimCompletionAssistProvider *>(provider))
    {}

hjk's avatar
hjk committed
766
    IAssistProposal *perform(const IAssistInterface *interface)
hjk's avatar
hjk committed
767
    {
Leandro Melo's avatar
Leandro Melo committed
768 769 770 771
        const QString &needle = m_provider->needle();

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

772
        QTextCursor tc(interface->textDocument());
Leandro Melo's avatar
Leandro Melo committed
773
        tc.setPosition(interface->position());
hjk's avatar
hjk committed
774 775
        tc.movePosition(QTextCursor::Start, QTextCursor::MoveAnchor);

Leandro Melo's avatar
Leandro Melo committed
776
        QList<BasicProposalItem *> items;
hjk's avatar
hjk committed
777 778 779
        QSet<QString> seen;
        QTextDocument::FindFlags flags = QTextDocument::FindCaseSensitively;
        while (1) {
Leandro Melo's avatar
Leandro Melo committed
780
            tc = tc.document()->find(needle, tc.position(), flags);
hjk's avatar
hjk committed
781 782 783 784 785 786
            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
787
            if (found.startsWith(needle)
hjk's avatar
hjk committed
788
                    && !seen.contains(found)
Leandro Melo's avatar
Leandro Melo committed
789
                    && sel.anchor() != basePosition) {
hjk's avatar
hjk committed
790
                seen.insert(found);
Leandro Melo's avatar
Leandro Melo committed
791 792 793
                BasicProposalItem *item = new FakeVimAssistProposalItem(m_provider);
                item->setText(found);
                items.append(item);
hjk's avatar
hjk committed
794 795 796 797 798
            }
            tc.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor);
        }
        //qDebug() << "COMPLETIONS" << completions->size();

Leandro Melo's avatar
Leandro Melo committed
799 800
        delete interface;
        return new GenericProposal(basePosition, new FakeVimAssistProposalModel(items));
hjk's avatar
hjk committed
801 802 803
    }

private:
Leandro Melo's avatar
Leandro Melo committed
804
    const FakeVimCompletionAssistProvider *m_provider;
hjk's avatar
hjk committed
805
};
806

Leandro Melo's avatar
Leandro Melo committed
807 808 809 810 811
IAssistProcessor *FakeVimCompletionAssistProvider::createProcessor() const
{
    return new FakeVimCompletionAssistProcessor(this);
}

812

hjk's avatar
hjk committed
813 814
///////////////////////////////////////////////////////////////////////
//
815
// FakeVimPluginPrivate
hjk's avatar
hjk committed
816 817 818
//
///////////////////////////////////////////////////////////////////////

819
class FakeVimPluginPrivate : public QObject
hjk's avatar
hjk committed
820
{
821 822 823 824 825 826
    Q_OBJECT

public:
    FakeVimPluginPrivate(FakeVimPlugin *);
    ~FakeVimPluginPrivate();
    friend class FakeVimPlugin;
827
    friend class FakeVimExCommandsPage;
828 829
    friend class FakeVimUserCommandsPage;
    friend class FakeVimUserCommandsModel;
830

831
    bool initialize();
832
    void aboutToShutdown();
833 834

private slots:
835
    void onCoreAboutToClose();
836 837
    void editorOpened(Core::IEditor *);
    void editorAboutToClose(Core::IEditor *);
838

839
    void setUseFakeVim(const QVariant &value);
840
    void setUseFakeVimInternal(bool on);
841
    void quitFakeVim();
842
    void triggerCompletions();
843
    void triggerSimpleCompletions(const QString &needle, bool forward);
hjk's avatar
hjk committed
844
    void windowCommand(int key);
845
    void find(bool reverse);
846
    void findNext(bool reverse);
847 848 849
    void foldToggle(int depth);
    void foldAll(bool fold);
    void fold(int depth, bool fold);
hjk's avatar
hjk committed
850
    void showSettingsDialog();
851
    void maybeReadVimRc();
hjk's avatar
hjk committed
852 853
    void setBlockSelection(bool);
    void hasBlockSelection(bool*);
854

855 856 857
    void resetCommandBuffer();
    void showCommandBuffer(const QString &contents, int cursorPos, int messageLevel,
                           QObject *eventFilter);
858 859
    void showExtraInformation(const QString &msg);
    void changeSelection(const QList<QTextEdit::ExtraSelection> &selections);
860
    void highlightMatches(const QString &needle);
861
    void moveToMatchingParenthesis(bool *moved, bool *forward, QTextCursor *cursor);
862
    void checkForElectricCharacter(bool *result, QChar c);
Lukas Holecek's avatar
Lukas Holecek committed
863
    void indentRegion(int beginBlock, int endBlock, QChar typedChar);
864
    void handleExCommand(bool *handled, const ExCommand &cmd);
865

866 867 868
    void writeSettings();
    void readSettings();

869 870
    void handleDelayedQuitAll(bool forced);
    void handleDelayedQuit(bool forced, Core::IEditor *editor);
871
    void userActionTriggered();
872

873 874
    void switchToFile(int n);
    int currentFile() const;
875

876 877 878
signals:
    void delayedQuitRequested(bool forced, Core::IEditor *editor);
    void delayedQuitAllRequested(bool forced);
879 880 881

private:
    FakeVimPlugin *q;
hjk's avatar