fakevimplugin.cpp 56.5 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 72
#include <find/textfindconstants.h>

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

78 79
#include <cpptools/cpptoolsconstants.h>

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

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

using namespace TextEditor;
using namespace Core;

namespace FakeVim {
hjk's avatar
hjk committed
102
namespace Internal {
hjk's avatar
hjk committed
103

hjk's avatar
hjk committed
104 105 106 107 108 109
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
110

111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
class MiniBuffer : public QLabel
{
    Q_OBJECT

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

class MiniBuffer1 : public QLineEdit
{
    Q_OBJECT

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

hjk's avatar
hjk committed
156 157 158 159 160 161
///////////////////////////////////////////////////////////////////////
//
// FakeVimOptionPage
//
///////////////////////////////////////////////////////////////////////

162 163
typedef QMap<QString, QRegExp> ExCommandMap;
typedef QMap<int, QString> UserCommandMap;
hjk's avatar
hjk committed
164
typedef QLatin1String _;
165

hjk's avatar
hjk committed
166
class FakeVimOptionPage : public IOptionsPage
hjk's avatar
hjk committed
167 168 169 170
{
    Q_OBJECT

public:
171 172
    FakeVimOptionPage()
    {
hjk's avatar
hjk committed
173
        setId(_(SETTINGS_ID));
174
        setDisplayName(tr("General"));
hjk's avatar
hjk committed
175
        setCategory(_(SETTINGS_CATEGORY));
176
        setDisplayCategory(tr("FakeVim"));
hjk's avatar
hjk committed
177
        setCategoryIcon(_(SETTINGS_CATEGORY_FAKEVIM_ICON));
178
    }
hjk's avatar
hjk committed
179 180

    QWidget *createPage(QWidget *parent);
hjk's avatar
hjk committed
181
    void apply() { m_group.apply(ICore::settings()); }
hjk's avatar
hjk committed
182
    void finish() { m_group.finish(); }
183
    virtual bool matches(const QString &) const;
hjk's avatar
hjk committed
184 185 186 187 188 189 190 191 192

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

private:
    friend class DebuggerPlugin;
    Ui::FakeVimOptionPage m_ui;
193
    QString m_searchKeywords;
194
    Utils::SavedActionSet m_group;
hjk's avatar
hjk committed
195 196 197 198 199 200 201 202
};

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

    m_group.clear();
203
    m_group.insert(theFakeVimSetting(ConfigUseFakeVim),
204
        m_ui.checkBoxUseFakeVim);
205 206
    m_group.insert(theFakeVimSetting(ConfigReadVimRc),
        m_ui.checkBoxReadVimRc);
hjk's avatar
hjk committed
207

208
    m_group.insert(theFakeVimSetting(ConfigExpandTab),
hjk's avatar
hjk committed
209
        m_ui.checkBoxExpandTab);
210
    m_group.insert(theFakeVimSetting(ConfigHlSearch),
hjk's avatar
hjk committed
211
        m_ui.checkBoxHlSearch);
212
    m_group.insert(theFakeVimSetting(ConfigShiftWidth),
213
        m_ui.spinBoxShiftWidth);
214 215
    m_group.insert(theFakeVimSetting(ConfigShowMarks),
        m_ui.checkBoxShowMarks);
hjk's avatar
hjk committed
216

217
    m_group.insert(theFakeVimSetting(ConfigSmartTab),
hjk's avatar
hjk committed
218
        m_ui.checkBoxSmartTab);
219
    m_group.insert(theFakeVimSetting(ConfigStartOfLine),
hjk's avatar
hjk committed
220
        m_ui.checkBoxStartOfLine);
221
    m_group.insert(theFakeVimSetting(ConfigTabStop),
222
        m_ui.spinBoxTabStop);
223
    m_group.insert(theFakeVimSetting(ConfigBackspace),
hjk's avatar
hjk committed
224
        m_ui.lineEditBackspace);
225 226
    m_group.insert(theFakeVimSetting(ConfigIsKeyword),
        m_ui.lineEditIsKeyword);
hjk's avatar
hjk committed
227

228 229
    m_group.insert(theFakeVimSetting(ConfigPassControlKey),
        m_ui.checkBoxPassControlKey);
230
    m_group.insert(theFakeVimSetting(ConfigAutoIndent),
hjk's avatar
hjk committed
231
        m_ui.checkBoxAutoIndent);
232
    m_group.insert(theFakeVimSetting(ConfigSmartIndent),
233
        m_ui.checkBoxSmartIndent);
234
    m_group.insert(theFakeVimSetting(ConfigIncSearch),
235
        m_ui.checkBoxIncSearch);
236 237
    m_group.insert(theFakeVimSetting(ConfigUseCoreSearch),
        m_ui.checkBoxUseCoreSearch);
238 239
    m_group.insert(theFakeVimSetting(ConfigSmartCase),
        m_ui.checkBoxSmartCase);
hjk's avatar
hjk committed
240 241

    connect(m_ui.pushButtonCopyTextEditorSettings, SIGNAL(clicked()),
242
        SLOT(copyTextEditorSettings()));
hjk's avatar
hjk committed
243
    connect(m_ui.pushButtonSetQtStyle, SIGNAL(clicked()),
244
        SLOT(setQtStyle()));
hjk's avatar
hjk committed
245
    connect(m_ui.pushButtonSetPlainStyle, SIGNAL(clicked()),
246 247
        SLOT(setPlainStyle()));

248
    if (m_searchKeywords.isEmpty()) {
249
        QLatin1Char sep(' ');
250
        QTextStream(&m_searchKeywords)
251 252 253 254 255 256 257 258 259 260
                << 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()
261
                << sep << m_ui.checkBoxSmartCase->text()
262 263 264 265 266 267
                << 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();
268 269
        m_searchKeywords.remove(QLatin1Char('&'));
    }
hjk's avatar
hjk committed
270 271 272 273 274
    return w;
}

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

void FakeVimOptionPage::setQtStyle()
{
    m_ui.checkBoxExpandTab->setChecked(true);
290 291
    m_ui.spinBoxTabStop->setValue(4);
    m_ui.spinBoxShiftWidth->setValue(4);
hjk's avatar
hjk committed
292 293
    m_ui.checkBoxSmartTab->setChecked(true);
    m_ui.checkBoxAutoIndent->setChecked(true);
294
    m_ui.checkBoxSmartIndent->setChecked(true);
295
    m_ui.checkBoxIncSearch->setChecked(true);
hjk's avatar
hjk committed
296
    m_ui.lineEditBackspace->setText(_("indent,eol,start"));
hjk's avatar
hjk committed
297 298 299 300 301
}

void FakeVimOptionPage::setPlainStyle()
{
    m_ui.checkBoxExpandTab->setChecked(false);
302 303
    m_ui.spinBoxTabStop->setValue(8);
    m_ui.spinBoxShiftWidth->setValue(8);
hjk's avatar
hjk committed
304 305
    m_ui.checkBoxSmartTab->setChecked(false);
    m_ui.checkBoxAutoIndent->setChecked(false);
306
    m_ui.checkBoxSmartIndent->setChecked(false);
307
    m_ui.checkBoxIncSearch->setChecked(false);
hjk's avatar
hjk committed
308
    m_ui.lineEditBackspace->setText(QString());
hjk's avatar
hjk committed
309 310
}

311 312 313 314 315
bool FakeVimOptionPage::matches(const QString &s) const
{
    return m_searchKeywords.contains(s, Qt::CaseInsensitive);
}

316
//const char *FAKEVIM_CONTEXT = "FakeVim";
hjk's avatar
hjk committed
317

318 319 320 321 322 323
///////////////////////////////////////////////////////////////////////
//
// FakeVimExCommandsPage
//
///////////////////////////////////////////////////////////////////////

324
enum { CommandRole = Qt::UserRole };
325

hjk's avatar
hjk committed
326
class FakeVimExCommandsPage : public CommandMappings
327 328 329 330
{
    Q_OBJECT

public:
331 332 333
    FakeVimExCommandsPage(FakeVimPluginPrivate *q)
        : m_q(q)
    {
hjk's avatar
hjk committed
334
        setId(_(SETTINGS_EX_CMDS_ID));
335
        setDisplayName(tr("Ex Command Mapping"));
hjk's avatar
hjk committed
336
        setCategory(_(SETTINGS_CATEGORY));
337
        setDisplayCategory(tr("FakeVim"));
hjk's avatar
hjk committed
338
        setCategoryIcon(_(SETTINGS_CATEGORY_FAKEVIM_ICON));
339
    }
340 341 342

    QWidget *createPage(QWidget *parent);
    void initialize();
343 344
    ExCommandMap &exCommandMap();
    ExCommandMap &defaultExCommandMap();
345 346 347

public slots:
    void commandChanged(QTreeWidgetItem *current);
348 349 350
    void targetIdentifierChanged();
    void resetTargetIdentifier();
    void removeTargetIdentifier();
351 352 353
    void defaultAction();

private:
354
    FakeVimPluginPrivate *m_q;
355 356 357 358
};

QWidget *FakeVimExCommandsPage::createPage(QWidget *parent)
{
359 360 361
    QWidget *w = CommandMappings::createPage(parent);
    setPageTitle(tr("Ex Command Mapping"));
    setTargetHeader(tr("Ex Trigger Expression"));
362
    setTargetLabelText(tr("Regular expression:"));
363 364
    setTargetEditTitle(tr("Ex Command"));
    setImportExportEnabled(false);
365 366 367 368 369
    return w;
}

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

hjk's avatar
hjk committed
372
    foreach (Command *c, ActionManager::commands()) {
373 374 375
        if (c->action() && c->action()->isSeparator())
            continue;

376
        QTreeWidgetItem *item = new QTreeWidgetItem;
377
        const QString name = c->id().toString();
378 379
        const int pos = name.indexOf(QLatin1Char('.'));
        const QString section = name.left(pos);
380
        const QString subId = name.mid(pos + 1);
381
        item->setData(0, CommandRole, name);
382 383

        if (!sections.contains(section)) {
384 385
            QTreeWidgetItem *categoryItem =
                new QTreeWidgetItem(commandList(), QStringList() << section);
386 387 388 389
            QFont f = categoryItem->font(0);
            f.setBold(true);
            categoryItem->setFont(0, f);
            sections.insert(section, categoryItem);
390
            commandList()->expandItem(categoryItem);
391 392 393 394
        }
        sections[section]->addChild(item);

        item->setText(0, subId);
395
        item->setText(1, c->description());
396

397 398 399 400
        QString regex;
        if (exCommandMap().contains(name))
            regex = exCommandMap()[name].pattern();
        item->setText(2, regex);
401

402
        if (regex != defaultExCommandMap()[name].pattern())
403
            setModified(item, true);
404 405 406 407 408 409 410
    }

    commandChanged(0);
}

void FakeVimExCommandsPage::commandChanged(QTreeWidgetItem *current)
{
411
    CommandMappings::commandChanged(current);
412 413
    if (current)
        targetEdit()->setText(current->text(2));
414 415
}

416
void FakeVimExCommandsPage::targetIdentifierChanged()
417
{
418
    QTreeWidgetItem *current = commandList()->currentItem();
419 420 421
    if (!current)
        return;

422
    const QString name =  current->data(0, CommandRole).toString();
423
    const QString regex = targetEdit()->text();
424 425

    if (current->data(0, Qt::UserRole).isValid()) {
426 427
        current->setText(2, regex);
        exCommandMap()[name] = QRegExp(regex);
428
    }
429

430
    setModified(current, regex != defaultExCommandMap()[name].pattern());
431 432
}

433
void FakeVimExCommandsPage::resetTargetIdentifier()
434
{
435
    QTreeWidgetItem *current = commandList()->currentItem();
436 437
    if (!current)
        return;
438
    const QString name = current->data(0, CommandRole).toString();
439 440 441 442
    QString regex;
    if (defaultExCommandMap().contains(name))
        regex = defaultExCommandMap()[name].pattern();
    targetEdit()->setText(regex);
443 444
}

445
void FakeVimExCommandsPage::removeTargetIdentifier()
446
{
447
    targetEdit()->clear();
448 449 450 451
}

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

470 471 472 473 474 475 476 477
///////////////////////////////////////////////////////////////////////
//
// FakeVimUserCommandsPage
//
///////////////////////////////////////////////////////////////////////

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

public:
553 554 555
    FakeVimUserCommandsPage(FakeVimPluginPrivate *q)
        : m_q(q)
    {
hjk's avatar
hjk committed
556
        setId(_(SETTINGS_USER_CMDS_ID));
557
        setDisplayName(tr("User Command Mapping"));
hjk's avatar
hjk committed
558
        setCategory(_(SETTINGS_CATEGORY));
559
        setDisplayCategory(tr("FakeVim"));
hjk's avatar
hjk committed
560
        setCategoryIcon(_(SETTINGS_CATEGORY_FAKEVIM_ICON));
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
    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
599 600 601 602 603 604 605

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

hjk's avatar
hjk committed
606
class FakeVimCompletionAssistProvider : public CompletionAssistProvider
hjk's avatar
hjk committed
607 608
{
public:
hjk's avatar
hjk committed
609
    bool supportsEditor(const Id &) const
hjk's avatar
hjk committed
610
    {
Leandro Melo's avatar
Leandro Melo committed
611
        return false;
hjk's avatar
hjk committed
612 613
    }

hjk's avatar
hjk committed
614
    IAssistProcessor *createProcessor() const;
Leandro Melo's avatar
Leandro Melo committed
615 616

    void setActive(const QString &needle, bool forward, FakeVimHandler *handler)
hjk's avatar
hjk committed
617
    {
Leandro Melo's avatar
Leandro Melo committed
618 619 620 621 622 623 624 625 626 627 628 629
        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
630 631
    }

Leandro Melo's avatar
Leandro Melo committed
632
    void setInactive()
hjk's avatar
hjk committed
633
    {
Leandro Melo's avatar
Leandro Melo committed
634 635
        m_needle.clear();
        m_handler = 0;
hjk's avatar
hjk committed
636 637
    }

Leandro Melo's avatar
Leandro Melo committed
638
    const QString &needle() const
639
    {
Leandro Melo's avatar
Leandro Melo committed
640
        return m_needle;
641
    }
hjk's avatar
hjk committed
642

Leandro Melo's avatar
Leandro Melo committed
643
    void appendNeedle(const QChar &c)
644
    {
Leandro Melo's avatar
Leandro Melo committed
645
        m_needle.append(c);
646 647
    }

Leandro Melo's avatar
Leandro Melo committed
648
    FakeVimHandler *handler() const
649
    {
Leandro Melo's avatar
Leandro Melo committed
650
        return m_handler;
651
    }
hjk's avatar
hjk committed
652

Leandro Melo's avatar
Leandro Melo committed
653 654 655 656 657 658 659 660 661 662 663 664 665
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
666
    {
Leandro Melo's avatar
Leandro Melo committed
667
        return false;
hjk's avatar
hjk committed
668 669
    }

Leandro Melo's avatar
Leandro Melo committed
670
    virtual bool prematurelyApplies(const QChar &c) const
hjk's avatar
hjk committed
671
    {
Leandro Melo's avatar
Leandro Melo committed
672 673
        m_provider->appendNeedle(c);
        return text() == m_provider->needle();
hjk's avatar
hjk committed
674 675
    }

Leandro Melo's avatar
Leandro Melo committed
676
    virtual void applyContextualContent(BaseTextEditor *, int) const
hjk's avatar
hjk committed
677
    {
Leandro Melo's avatar
Leandro Melo committed
678 679 680
        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
681 682
    }

Leandro Melo's avatar
Leandro Melo committed
683 684 685 686 687 688 689 690 691 692 693 694 695
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
696
    {
Leandro Melo's avatar
Leandro Melo committed
697
        return false;
hjk's avatar
hjk committed
698
    }
Leandro Melo's avatar
Leandro Melo committed
699
};
hjk's avatar
hjk committed
700

Leandro Melo's avatar
Leandro Melo committed
701 702 703
class FakeVimCompletionAssistProcessor : public IAssistProcessor
{
public:
hjk's avatar
hjk committed
704
    FakeVimCompletionAssistProcessor(const IAssistProvider *provider)
Leandro Melo's avatar
Leandro Melo committed
705 706 707
        : m_provider(static_cast<const FakeVimCompletionAssistProvider *>(provider))
    {}

hjk's avatar
hjk committed
708
    IAssistProposal *perform(const IAssistInterface *interface)
hjk's avatar
hjk committed
709
    {
Leandro Melo's avatar
Leandro Melo committed
710 711 712 713
        const QString &needle = m_provider->needle();

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

714
        QTextCursor tc(interface->textDocument());
Leandro Melo's avatar
Leandro Melo committed
715
        tc.setPosition(interface->position());
hjk's avatar
hjk committed
716 717
        tc.movePosition(QTextCursor::Start, QTextCursor::MoveAnchor);

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

Leandro Melo's avatar
Leandro Melo committed
741 742
        delete interface;
        return new GenericProposal(basePosition, new FakeVimAssistProposalModel(items));
hjk's avatar
hjk committed
743 744 745
    }

private:
Leandro Melo's avatar
Leandro Melo committed
746
    const FakeVimCompletionAssistProvider *m_provider;
hjk's avatar
hjk committed
747
};
748

Leandro Melo's avatar
Leandro Melo committed
749 750 751 752 753
IAssistProcessor *FakeVimCompletionAssistProvider::createProcessor() const
{
    return new FakeVimCompletionAssistProcessor(this);
}

754

hjk's avatar
hjk committed
755 756
///////////////////////////////////////////////////////////////////////
//
757
// FakeVimPluginPrivate
hjk's avatar
hjk committed
758 759 760
//
///////////////////////////////////////////////////////////////////////

761
class FakeVimPluginPrivate : public QObject
hjk's avatar
hjk committed
762
{
763 764 765 766 767 768
    Q_OBJECT

public:
    FakeVimPluginPrivate(FakeVimPlugin *);
    ~FakeVimPluginPrivate();
    friend class FakeVimPlugin;
769
    friend class FakeVimExCommandsPage;
770 771
    friend class FakeVimUserCommandsPage;
    friend class FakeVimUserCommandsModel;
772

773
    bool initialize();
774
    void aboutToShutdown();
775 776

private slots:
777
    void onCoreAboutToClose();
778 779
    void editorOpened(Core::IEditor *);
    void editorAboutToClose(Core::IEditor *);
780

781
    void setUseFakeVim(const QVariant &value);
782
    void setUseFakeVimInternal(bool on);
783
    void quitFakeVim();
784
    void triggerCompletions();
785
    void triggerSimpleCompletions(const QString &needle, bool forward);
hjk's avatar
hjk committed
786
    void windowCommand(int key);
787
    void find(bool reverse);
788
    void findNext(bool reverse);
hjk's avatar
hjk committed
789
    void showSettingsDialog();
790
    void maybeReadVimRc();
hjk's avatar
hjk committed
791 792
    void setBlockSelection(bool);
    void hasBlockSelection(bool*);
793

794
    void showCommandBuffer(const QString &contents, int cursorPos);
795 796
    void showExtraInformation(const QString &msg);
    void changeSelection(const QList<QTextEdit::ExtraSelection> &selections);
797
    void moveToMatchingParenthesis(bool *moved, bool *forward, QTextCursor *cursor);
798
    void checkForElectricCharacter(bool *result, QChar c);
799
    void indentRegion(int beginLine, int endLine, QChar typedChar);
800
    void handleExCommand(bool *handled, const ExCommand &cmd);
801

802 803 804
    void writeSettings();
    void readSettings();

805 806
    void handleDelayedQuitAll(bool forced);
    void handleDelayedQuit(bool forced, Core::IEditor *editor);
807
    void userActionTriggered();
808

809 810
    void switchToFile(int n);
    int currentFile() const;
811

812 813 814
signals:
    void delayedQuitRequested(bool forced, Core::IEditor *editor);
    void delayedQuitAllRequested(bool forced);
815 816 817

private:
    FakeVimPlugin *q;
hjk's avatar
hjk committed
818
    FakeVimOptionPage *m_fakeVimOptionsPage;
819
    FakeVimExCommandsPage *m_fakeVimExCommandsPage;
820
    FakeVimUserCommandsPage *m_fakeVimUserCommandsPage;
hjk's avatar
hjk committed
821
    QHash<IEditor *, FakeVimHandler *> m_editorToHandler;
822

hjk's avatar
hjk committed
823 824
    void triggerAction(const Id &id);
    void setActionChecked(const Id &id, bool check);
825

826 827 828
    typedef int (*DistFunction)(const QRect &cursor, const QRect &other);
    void moveSomewhere(DistFunction f);

829 830 831 832 833 834 835 836 837 838
    ExCommandMap &exCommandMap() { return m_exCommandMap; }
    ExCommandMap &defaultExCommandMap() { return m_defaultExCommandMap; }
    ExCommandMap m_exCommandMap;
    ExCommandMap m_defaultExCommandMap;

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

hjk's avatar
hjk committed
839
    StatusBarWidget *m_statusBar;
Leandro Melo's avatar
Leandro Melo committed
840 841 842
    // @TODO: Delete
    //WordCompletion *m_wordCompletion;
    FakeVimCompletionAssistProvider *m_wordProvider;
843 844
};

845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870
QVariant FakeVimUserCommandsModel::data(const QModelIndex &index, int role) const
{
    if (!index.isValid())
        return QVariant();

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

    return QVariant();
}

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

871
FakeVimPluginPrivate::FakeVimPluginPrivate(FakeVimPlugin *plugin)
872
{
873
    q = plugin;
hjk's avatar
hjk committed
874
    m_fakeVimOptionsPage = 0;
875
    m_fakeVimExCommandsPage = 0;
876
    m_fakeVimUserCommandsPage = 0;
877
    defaultExCommandMap()[CppTools::Constants::SWITCH_HEADER_SOURCE] =
878
        QRegExp("^A$");
879
    defaultExCommandMap()["Coreplugin.OutputPane.previtem"] =
880
        QRegExp("^(cN(ext)?|cp(revious)?)!?( (.*))?$");
881
    defaultExCommandMap()["Coreplugin.OutputPane.nextitem"] =
882
        QRegExp("^cn(ext)?!?( (.*))?$");
883
    defaultExCommandMap()[TextEditor::Constants::FOLLOW_SYMBOL_UNDER_CURSOR] =
884
        QRegExp("^tag?$");
885
    defaultExCommandMap()[Core::Constants::GO_BACK] =
886
        QRegExp("^pop?$");
hjk's avatar
hjk committed
887
    defaultExCommandMap()[_("QtCreator.Locate")] =