fakevimplugin.cpp 67.8 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";
114
typedef QLatin1String _;
hjk's avatar
hjk committed
115

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

public:
121
    MiniBuffer() : m_label(new QLabel(this)), m_edit(new QLineEdit(this)), m_eventFilter(0)
122
    {
123 124 125 126 127 128 129
        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);
130 131
    }

132 133 134 135 136 137 138 139 140 141 142
    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();
143
        } else if (contents.isEmpty() && messageLevel != MessageShowCmd) {
Lukas Holecek's avatar
Lukas Holecek committed
144
            hide();
145
        } else {
Lukas Holecek's avatar
Lukas Holecek committed
146
            show();
147
            m_label->setText(messageLevel == MessageMode ? _("-- ") + contents + _(" --") : contents);
148 149 150

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

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

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

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

    bool eventFilter(QObject *ob, QEvent *ev)
200
    {
201 202 203 204 205 206 207 208
        // 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;
209
    }
210 211 212 213 214

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

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

223 224
typedef QMap<QString, QRegExp> ExCommandMap;
typedef QMap<int, QString> UserCommandMap;
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);