fakevimplugin.cpp 68.3 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
        m_edit->installEventFilter(this);
        connect(m_edit, SIGNAL(textEdited(QString)), SLOT(changed()));
        connect(m_edit, SIGNAL(cursorPositionChanged(int,int)), SLOT(changed()));
126
        connect(m_edit, SIGNAL(selectionChanged()), SLOT(changed()));
127 128 129 130
        m_label->setTextInteractionFlags(Qt::TextSelectableByMouse);

        addWidget(m_label);
        addWidget(m_edit);
131 132
    }

133 134
    void setContents(const QString &contents, int cursorPos, int anchorPos,
                     int messageLevel, QObject *eventFilter)
135 136 137 138 139
    {
        if (cursorPos != -1) {
            m_edit->blockSignals(true);
            m_label->clear();
            m_edit->setText(contents);
140 141 142 143
            if (anchorPos != -1 && anchorPos != cursorPos)
                m_edit->setSelection(anchorPos, cursorPos - anchorPos);
            else
                m_edit->setCursorPosition(cursorPos);
144 145 146
            m_edit->blockSignals(false);
            setCurrentWidget(m_edit);
            m_edit->setFocus();
147
        } else if (contents.isEmpty() && messageLevel != MessageShowCmd) {
Lukas Holecek's avatar
Lukas Holecek committed
148
            hide();
149
        } else {
Lukas Holecek's avatar
Lukas Holecek committed
150
            show();
151
            m_label->setText(messageLevel == MessageMode ? _("-- ") + contents + _(" --") : contents);
152 153 154

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

167
            if (m_edit->hasFocus())
168
                emit edited(QString(), -1, -1);
169 170 171 172 173 174 175

            setCurrentWidget(m_label);
        }

        if (m_eventFilter != eventFilter) {
            if (m_eventFilter != 0) {
                m_edit->removeEventFilter(m_eventFilter);
176
                disconnect(SIGNAL(edited(QString,int,int)));
177 178 179
            }
            if (eventFilter != 0) {
                m_edit->installEventFilter(eventFilter);
180 181
                connect(this, SIGNAL(edited(QString,int,int)),
                        eventFilter, SLOT(miniBufferTextEdited(QString,int,int)));
182 183 184 185 186 187
            }
            m_eventFilter = eventFilter;
        }
    }

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

signals:
195
    void edited(const QString &text, int cursorPos, int anchorPos);
196 197 198

private slots:
    void changed()
199
    {
200 201 202 203 204
        const int cursorPos = m_edit->cursorPosition();
        int anchorPos = m_edit->selectionStart();
        if (anchorPos == cursorPos)
            anchorPos = cursorPos + m_edit->selectedText().length();
        emit edited(m_edit->text(), cursorPos, anchorPos);
205
    }
206 207

    bool eventFilter(QObject *ob, QEvent *ev)
208
    {
209 210 211
        // cancel editing on escape
        if (m_eventFilter != 0 && ob == m_edit && ev->type() == QEvent::ShortcutOverride
            && static_cast<QKeyEvent*>(ev)->key() == Qt::Key_Escape) {
212
            emit edited(QString(), -1, -1);
213 214 215 216
            ev->accept();
            return true;
        }
        return false;
217
    }
218 219 220 221 222

private:
    QLabel *m_label;
    QLineEdit *m_edit;
    QObject *m_eventFilter;
223 224
};

hjk's avatar
hjk committed
225 226 227 228 229 230
///////////////////////////////////////////////////////////////////////
//
// FakeVimOptionPage
//
///////////////////////////////////////////////////////////////////////

231 232
typedef QMap<QString, QRegExp> ExCommandMap;
typedef QMap<int, QString> UserCommandMap;
233

hjk's avatar
hjk committed
234
class FakeVimOptionPage : public IOptionsPage
hjk's avatar
hjk committed
235 236 237 238
{
    Q_OBJECT

public:
239 240
    FakeVimOptionPage()
    {
hjk's avatar
hjk committed
241
        setId(_(SETTINGS_ID));
242
        setDisplayName(tr("General"));
hjk's avatar
hjk committed
243
        setCategory(SETTINGS_CATEGORY);
244
        setDisplayCategory(tr("FakeVim"));
hjk's avatar
hjk committed
245
        setCategoryIcon(_(SETTINGS_CATEGORY_FAKEVIM_ICON));
246
    }
hjk's avatar
hjk committed
247 248

    QWidget *createPage(QWidget *parent);
hjk's avatar
hjk committed
249
    void apply() { m_group.apply(ICore::settings()); }
hjk's avatar
hjk committed
250
    void finish() { m_group.finish(); }
251
    virtual bool matches(const QString &) const;
hjk's avatar
hjk committed
252 253 254 255 256 257 258 259 260

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

private:
    friend class DebuggerPlugin;
    Ui::FakeVimOptionPage m_ui;
261
    QString m_searchKeywords;
262
    Utils::SavedActionSet m_group;
hjk's avatar
hjk committed
263 264 265 266 267 268 269 270
};

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

    m_group.clear();
271
    m_group.insert(theFakeVimSetting(ConfigUseFakeVim),
272
        m_ui.checkBoxUseFakeVim);
273 274
    m_group.insert(theFakeVimSetting(ConfigReadVimRc),
        m_ui.checkBoxReadVimRc);
hjk's avatar
hjk committed
275

276
    m_group.insert(theFakeVimSetting(ConfigExpandTab),
hjk's avatar
hjk committed
277
        m_ui.checkBoxExpandTab);
278
    m_group.insert(theFakeVimSetting(ConfigHlSearch),
hjk's avatar
hjk committed
279
        m_ui.checkBoxHlSearch);
280
    m_group.insert(theFakeVimSetting(ConfigShiftWidth),
281
        m_ui.spinBoxShiftWidth);
282 283
    m_group.insert(theFakeVimSetting(ConfigShowMarks),
        m_ui.checkBoxShowMarks);
hjk's avatar
hjk committed
284

285
    m_group.insert(theFakeVimSetting(ConfigSmartTab),
hjk's avatar
hjk committed
286
        m_ui.checkBoxSmartTab);
287
    m_group.insert(theFakeVimSetting(ConfigStartOfLine),
hjk's avatar
hjk committed
288
        m_ui.checkBoxStartOfLine);
289
    m_group.insert(theFakeVimSetting(ConfigTabStop),
290
        m_ui.spinBoxTabStop);
291
    m_group.insert(theFakeVimSetting(ConfigBackspace),
hjk's avatar
hjk committed
292
        m_ui.lineEditBackspace);
293 294
    m_group.insert(theFakeVimSetting(ConfigIsKeyword),
        m_ui.lineEditIsKeyword);
hjk's avatar
hjk committed
295

296 297
    m_group.insert(theFakeVimSetting(ConfigPassControlKey),
        m_ui.checkBoxPassControlKey);
298
    m_group.insert(theFakeVimSetting(ConfigAutoIndent),
hjk's avatar
hjk committed
299
        m_ui.checkBoxAutoIndent);
300
    m_group.insert(theFakeVimSetting(ConfigSmartIndent),
301
        m_ui.checkBoxSmartIndent);
Lukas Holecek's avatar
Lukas Holecek committed
302

303
    m_group.insert(theFakeVimSetting(ConfigIncSearch),
304
        m_ui.checkBoxIncSearch);
305 306
    m_group.insert(theFakeVimSetting(ConfigUseCoreSearch),
        m_ui.checkBoxUseCoreSearch);
307 308
    m_group.insert(theFakeVimSetting(ConfigSmartCase),
        m_ui.checkBoxSmartCase);
Lukas Holecek's avatar
Lukas Holecek committed
309 310
    m_group.insert(theFakeVimSetting(ConfigWrapScan),
        m_ui.checkBoxWrapScan);
hjk's avatar
hjk committed
311

312 313 314
    m_group.insert(theFakeVimSetting(ConfigShowCmd),
        m_ui.checkBoxShowCmd);

hjk's avatar
hjk committed
315
    connect(m_ui.pushButtonCopyTextEditorSettings, SIGNAL(clicked()),
316
        SLOT(copyTextEditorSettings()));
hjk's avatar
hjk committed
317
    connect(m_ui.pushButtonSetQtStyle, SIGNAL(clicked()),
318
        SLOT(setQtStyle()));
hjk's avatar
hjk committed
319
    connect(m_ui.pushButtonSetPlainStyle, SIGNAL(clicked()),
320 321
        SLOT(setPlainStyle()));

322
    if (m_searchKeywords.isEmpty()) {
323
        QLatin1Char sep(' ');
324
        QTextStream(&m_searchKeywords)
325 326 327 328 329 330 331 332 333 334
                << 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()
335
                << sep << m_ui.checkBoxSmartCase->text()
336 337 338 339 340 341
                << 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();
342 343
        m_searchKeywords.remove(QLatin1Char('&'));
    }
hjk's avatar
hjk committed
344 345 346 347 348
    return w;
}

void FakeVimOptionPage::copyTextEditorSettings()
{
Jarek Kobus's avatar
Jarek Kobus committed
349 350 351
    TabSettings ts = TextEditorSettings::instance()->codeStyle()->tabSettings();
    TypingSettings tps = TextEditorSettings::instance()->typingSettings();
    m_ui.checkBoxExpandTab->setChecked(ts.m_tabPolicy != TabSettings::TabsOnlyTabPolicy);
352 353
    m_ui.spinBoxTabStop->setValue(ts.m_tabSize);
    m_ui.spinBoxShiftWidth->setValue(ts.m_indentSize);
354
    m_ui.checkBoxSmartTab->setChecked(
Jarek Kobus's avatar
Jarek Kobus committed
355
        tps.m_smartBackspaceBehavior == TypingSettings::BackspaceFollowsPreviousIndents);
356
    m_ui.checkBoxAutoIndent->setChecked(true);
Jarek Kobus's avatar
Jarek Kobus committed
357
    m_ui.checkBoxSmartIndent->setChecked(tps.m_autoIndent);
358
    m_ui.checkBoxIncSearch->setChecked(true);
hjk's avatar
hjk committed
359 360 361 362 363
}

void FakeVimOptionPage::setQtStyle()
{
    m_ui.checkBoxExpandTab->setChecked(true);
364 365
    m_ui.spinBoxTabStop->setValue(4);
    m_ui.spinBoxShiftWidth->setValue(4);
hjk's avatar
hjk committed
366 367
    m_ui.checkBoxSmartTab->setChecked(true);
    m_ui.checkBoxAutoIndent->setChecked(true);
368
    m_ui.checkBoxSmartIndent->setChecked(true);
369
    m_ui.checkBoxIncSearch->setChecked(true);
hjk's avatar
hjk committed
370
    m_ui.lineEditBackspace->setText(_("indent,eol,start"));
hjk's avatar
hjk committed
371 372 373 374 375
}

void FakeVimOptionPage::setPlainStyle()
{
    m_ui.checkBoxExpandTab->setChecked(false);
376 377
    m_ui.spinBoxTabStop->setValue(8);
    m_ui.spinBoxShiftWidth->setValue(8);
hjk's avatar
hjk committed
378 379
    m_ui.checkBoxSmartTab->setChecked(false);
    m_ui.checkBoxAutoIndent->setChecked(false);
380
    m_ui.checkBoxSmartIndent->setChecked(false);
381
    m_ui.checkBoxIncSearch->setChecked(false);
hjk's avatar
hjk committed
382
    m_ui.lineEditBackspace->setText(QString());
hjk's avatar
hjk committed
383 384
}

385 386 387 388 389
bool FakeVimOptionPage::matches(const QString &s) const
{
    return m_searchKeywords.contains(s, Qt::CaseInsensitive);
}

390
//const char *FAKEVIM_CONTEXT = "FakeVim";
hjk's avatar
hjk committed
391

392 393 394 395 396 397
///////////////////////////////////////////////////////////////////////
//
// FakeVimExCommandsPage
//
///////////////////////////////////////////////////////////////////////

398
enum { CommandRole = Qt::UserRole };
399

hjk's avatar
hjk committed
400
class FakeVimExCommandsPage : public CommandMappings
401 402 403 404
{
    Q_OBJECT

public:
405 406 407
    FakeVimExCommandsPage(FakeVimPluginPrivate *q)
        : m_q(q)
    {
hjk's avatar
hjk committed
408
        setId(_(SETTINGS_EX_CMDS_ID));
409
        setDisplayName(tr("Ex Command Mapping"));
hjk's avatar
hjk committed
410
        setCategory(SETTINGS_CATEGORY);
411
        setDisplayCategory(tr("FakeVim"));
hjk's avatar
hjk committed
412
        setCategoryIcon(_(SETTINGS_CATEGORY_FAKEVIM_ICON));
413
    }
414 415 416

    QWidget *createPage(QWidget *parent);
    void initialize();
417 418
    ExCommandMap &exCommandMap();
    ExCommandMap &defaultExCommandMap();
419 420 421

public slots:
    void commandChanged(QTreeWidgetItem *current);
422 423 424
    void targetIdentifierChanged();
    void resetTargetIdentifier();
    void removeTargetIdentifier();
425 426 427
    void defaultAction();

private:
428
    FakeVimPluginPrivate *m_q;
429 430 431 432
};

QWidget *FakeVimExCommandsPage::createPage(QWidget *parent)
{
433 434 435
    QWidget *w = CommandMappings::createPage(parent);
    setPageTitle(tr("Ex Command Mapping"));
    setTargetHeader(tr("Ex Trigger Expression"));
436
    setTargetLabelText(tr("Regular expression:"));
437 438
    setTargetEditTitle(tr("Ex Command"));
    setImportExportEnabled(false);
439 440 441 442 443
    return w;
}

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

hjk's avatar
hjk committed
446
    foreach (Command *c, ActionManager::commands()) {
447 448 449
        if (c->action() && c->action()->isSeparator())
            continue;

450
        QTreeWidgetItem *item = new QTreeWidgetItem;
451
        const QString name = c->id().toString();
452 453
        const int pos = name.indexOf(QLatin1Char('.'));
        const QString section = name.left(pos);
454
        const QString subId = name.mid(pos + 1);
455
        item->setData(0, CommandRole, name);
456 457

        if (!sections.contains(section)) {
458 459
            QTreeWidgetItem *categoryItem =
                new QTreeWidgetItem(commandList(), QStringList() << section);
460 461 462 463
            QFont f = categoryItem->font(0);
            f.setBold(true);
            categoryItem->setFont(0, f);
            sections.insert(section, categoryItem);
464
            commandList()->expandItem(categoryItem);
465 466 467 468
        }
        sections[section]->addChild(item);

        item->setText(0, subId);
469
        item->setText(1, c->description());
470

471 472 473 474
        QString regex;
        if (exCommandMap().contains(name))
            regex = exCommandMap()[name].pattern();
        item->setText(2, regex);
475

476
        if (regex != defaultExCommandMap()[name].pattern())
477
            setModified(item, true);
478 479 480 481 482 483 484
    }

    commandChanged(0);
}

void FakeVimExCommandsPage::commandChanged(QTreeWidgetItem *current)
{
485
    CommandMappings::commandChanged(current);
486 487
    if (current)
        targetEdit()->setText(current->text(2));
488 489
}

490
void FakeVimExCommandsPage::targetIdentifierChanged()
491
{
492
    QTreeWidgetItem *current = commandList()->currentItem();
493 494 495
    if (!current)
        return;

496
    const QString name =  current->data(0, CommandRole).toString();
497
    const QString regex = targetEdit()->text();
498 499

    if (current->data(0, Qt::UserRole).isValid()) {
500 501
        current->setText(2, regex);
        exCommandMap()[name] = QRegExp(regex);
502
    }
503

504
    setModified(current, regex != defaultExCommandMap()[name].pattern());
505 506
}

507
void FakeVimExCommandsPage::resetTargetIdentifier()
508
{
509
    QTreeWidgetItem *current = commandList()->currentItem();
510 511
    if (!current)
        return;
512
    const QString name = current->data(0, CommandRole).toString();
513 514 515 516
    QString regex;
    if (defaultExCommandMap().contains(name))
        regex = defaultExCommandMap()[name].pattern();
    targetEdit()->setText(regex);
517 518
}

519
void FakeVimExCommandsPage::removeTargetIdentifier()
520
{
521
    targetEdit()->clear();
522 523 524 525
}

void FakeVimExCommandsPage::defaultAction()
{
hjk's avatar
hjk committed
526 527 528 529 530 531
    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);
532
            const QString name = item->data(0, CommandRole).toString();
hjk's avatar
hjk committed
533 534 535 536 537 538 539 540
            QString regex;
            if (defaultExCommandMap().contains(name))
                regex = defaultExCommandMap()[name].pattern();
            setModified(item, false);
            item->setText(2, regex);
            if (item == commandList()->currentItem())
                commandChanged(item);
        }
541 542 543
    }
}

544 545 546 547 548 549 550 551
///////////////////////////////////////////////////////////////////////
//
// FakeVimUserCommandsPage
//
///////////////////////////////////////////////////////////////////////

class FakeVimUserCommandsModel : public QAbstractTableModel
{
Friedemann Kleint's avatar
Friedemann Kleint committed
552
    Q_OBJECT
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 614 615 616 617 618 619 620 621
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
622
class FakeVimUserCommandsPage : public IOptionsPage
623 624 625 626
{
    Q_OBJECT

public:
627 628 629
    FakeVimUserCommandsPage(FakeVimPluginPrivate *q)
        : m_q(q)
    {
hjk's avatar
hjk committed
630
        setId(_(SETTINGS_USER_CMDS_ID));
631
        setDisplayName(tr("User Command Mapping"));
hjk's avatar
hjk committed
632
        setCategory(SETTINGS_CATEGORY);
633
        setDisplayCategory(tr("FakeVim"));
hjk's avatar
hjk committed
634
        setCategoryIcon(_(SETTINGS_CATEGORY_FAKEVIM_ICON));
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 665 666 667 668 669 670 671 672
    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
673 674 675 676 677 678 679

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

hjk's avatar
hjk committed
680
class FakeVimCompletionAssistProvider : public CompletionAssistProvider
hjk's avatar
hjk committed
681 682
{
public:
hjk's avatar
hjk committed
683
    bool supportsEditor(const Id &) const
hjk's avatar
hjk committed
684
    {
Leandro Melo's avatar
Leandro Melo committed
685
        return false;
hjk's avatar
hjk committed
686 687
    }

hjk's avatar
hjk committed
688
    IAssistProcessor *createProcessor() const;
Leandro Melo's avatar
Leandro Melo committed
689 690

    void setActive(const QString &needle, bool forward, FakeVimHandler *handler)
hjk's avatar
hjk committed
691
    {
Leandro Melo's avatar
Leandro Melo committed
692 693 694 695 696 697 698 699 700 701 702 703
        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
704 705
    }

Leandro Melo's avatar
Leandro Melo committed
706
    void setInactive()
hjk's avatar
hjk committed
707
    {
Leandro Melo's avatar
Leandro Melo committed
708 709
        m_needle.clear();
        m_handler = 0;
hjk's avatar
hjk committed
710 711
    }

Leandro Melo's avatar
Leandro Melo committed
712
    const QString &needle() const
713
    {
Leandro Melo's avatar
Leandro Melo committed
714
        return m_needle;
715
    }
hjk's avatar
hjk committed
716

Leandro Melo's avatar
Leandro Melo committed
717
    void appendNeedle(const QChar &c)
718
    {
Leandro Melo's avatar
Leandro Melo committed
719
        m_needle.append(c);
720 721
    }

Leandro Melo's avatar
Leandro Melo committed
722
    FakeVimHandler *handler() const
723
    {
Leandro Melo's avatar
Leandro Melo committed
724
        return m_handler;
725
    }
hjk's avatar
hjk committed
726

Leandro Melo's avatar
Leandro Melo committed
727 728 729 730 731 732 733 734 735 736 737 738 739
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
740
    {
Leandro Melo's avatar
Leandro Melo committed
741
        return false;
hjk's avatar
hjk committed
742 743
    }

Leandro Melo's avatar
Leandro Melo committed
744
    virtual bool prematurelyApplies(const QChar &c) const
hjk's avatar
hjk committed
745
    {
Leandro Melo's avatar
Leandro Melo committed
746 747
        m_provider->appendNeedle(c);
        return text() == m_provider->needle();
hjk's avatar
hjk committed
748 749
    }

Leandro Melo's avatar
Leandro Melo committed
750
    virtual void applyContextualContent(BaseTextEditor *, int) const
hjk's avatar
hjk committed
751
    {
Leandro Melo's avatar
Leandro Melo committed
752 753 754
        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
755 756
    }

Leandro Melo's avatar
Leandro Melo committed
757 758 759 760 761 762 763 764 765 766 767 768 769
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
770
    {
Leandro Melo's avatar
Leandro Melo committed
771
        return false;
hjk's avatar
hjk committed
772
    }
Leandro Melo's avatar
Leandro Melo committed
773
};
hjk's avatar
hjk committed
774

Leandro Melo's avatar
Leandro Melo committed
775 776 777
class FakeVimCompletionAssistProcessor : public IAssistProcessor
{
public:
hjk's avatar
hjk committed
778
    FakeVimCompletionAssistProcessor(const IAssistProvider *provider)
Leandro Melo's avatar
Leandro Melo committed
779 780 781
        : m_provider(static_cast<const FakeVimCompletionAssistProvider *>(provider))
    {}

hjk's avatar
hjk committed
782
    IAssistProposal *perform(const IAssistInterface *interface)
hjk's avatar
hjk committed
783
    {
Leandro Melo's avatar
Leandro Melo committed
784 785 786 787
        const QString &needle = m_provider->needle();

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

788
        QTextCursor tc(interface->textDocument());
Leandro Melo's avatar
Leandro Melo committed
789
        tc.setPosition(interface->position());
hjk's avatar
hjk committed
790 791
        tc.movePosition(QTextCursor::Start, QTextCursor::MoveAnchor);

Leandro Melo's avatar
Leandro Melo committed
792
        QList<BasicProposalItem *> items;
hjk's avatar
hjk committed
793 794 795
        QSet<QString> seen;
        QTextDocument::FindFlags flags = QTextDocument::FindCaseSensitively;
        while (1) {
Leandro Melo's avatar
Leandro Melo committed
796
            tc = tc.document()->find(needle, tc.position(), flags);
hjk's avatar
hjk committed
797 798 799 800 801 802
            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
803
            if (found.startsWith(needle)
hjk's avatar
hjk committed
804
                    && !seen.contains(found)
Leandro Melo's avatar
Leandro Melo committed
805
                    && sel.anchor() != basePosition) {
hjk's avatar
hjk committed
806
                seen.insert(found);
Leandro Melo's avatar
Leandro Melo committed
807 808 809
                BasicProposalItem *item = new FakeVimAssistProposalItem(m_provider);
                item->setText(found);
                items.append(item);
hjk's avatar
hjk committed
810 811 812 813 814
            }
            tc.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor);
        }
        //qDebug() << "COMPLETIONS" << completions->size();

Leandro Melo's avatar
Leandro Melo committed
815 816
        delete interface;
        return new GenericProposal(basePosition, new FakeVimAssistProposalModel(items));
hjk's avatar
hjk committed
817 818 819
    }

private:
Leandro Melo's avatar
Leandro Melo committed
820
    const FakeVimCompletionAssistProvider *m_provider;
hjk's avatar
hjk committed
821
};
822

Leandro Melo's avatar
Leandro Melo committed
823 824 825 826 827
IAssistProcessor *FakeVimCompletionAssistProvider::createProcessor() const
{
    return new FakeVimCompletionAssistProcessor(this);
}

828

hjk's avatar
hjk committed
829 830
///////////////////////////////////////////////////////////////////////
//
831
// FakeVimPluginPrivate
hjk's avatar
hjk committed
832 833 834
//
///////////////////////////////////////////////////////////////////////

835
class FakeVimPluginPrivate : public QObject
hjk's avatar
hjk committed
836
{
837 838 839 840 841 842
    Q_OBJECT

public:
    FakeVimPluginPrivate(FakeVimPlugin *);
    ~FakeVimPluginPrivate();
    friend class FakeVimPlugin;
843
    friend class FakeVimExCommandsPage;
844 845
    friend class FakeVimUserCommandsPage;
    friend class FakeVimUserCommandsModel;
846

847
    bool initialize();
848
    void aboutToShutdown();
849 850

private slots:
851
    void onCoreAboutToClose();
852 853
    void editorOpened(Core::IEditor *);
    void editorAboutToClose(Core::IEditor *);
854

855
    void setUseFakeVim(const QVariant &value);
856
    void setUseFakeVimInternal(bool on);
857
    void quitFakeVim();
858
    void triggerCompletions();
859
    void triggerSimpleCompletions(const QString &needle, bool forward);
hjk's avatar
hjk committed
860
    void windowCommand(int key);