fakevimplugin.cpp 56.4 KB
Newer Older
1
/**************************************************************************
hjk's avatar
hjk committed
2 3 4
**
** This file is part of Qt Creator
**
con's avatar
con committed
5
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
hjk's avatar
hjk committed
6
**
hjk's avatar
hjk committed
7
** Contact: Nokia Corporation (info@qt.nokia.com)
hjk's avatar
hjk committed
8 9
**
**
10
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
11
**
hjk's avatar
hjk committed
12 13 14 15 16 17
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this file.
** Please review the following information to ensure the GNU Lesser General
** Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
hjk's avatar
hjk committed
18
**
con's avatar
con committed
19
** In addition, as a special exception, Nokia gives you certain additional
hjk's avatar
hjk committed
20
** rights. These rights are described in the Nokia Qt LGPL Exception
con's avatar
con committed
21 22
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
23 24 25 26 27
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
con's avatar
con committed
28 29
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
hjk's avatar
hjk committed
30
**
31
**************************************************************************/
hjk's avatar
hjk committed
32 33 34

#include "fakevimplugin.h"

35
#include "fakevimhandler.h"
hjk's avatar
hjk committed
36 37
#include "ui_fakevimoptions.h"

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

#include <projectexplorer/projectexplorerconstants.h>

57
#include <texteditor/basetextdocumentlayout.h>
hjk's avatar
hjk committed
58 59
#include <texteditor/basetexteditor.h>
#include <texteditor/basetextmark.h>
hjk's avatar
hjk committed
60
#include <texteditor/completionsupport.h>
hjk's avatar
hjk committed
61
#include <texteditor/texteditorconstants.h>
62 63
#include <texteditor/tabsettings.h>
#include <texteditor/texteditorsettings.h>
64
#include <texteditor/indenter.h>
65
#include <texteditor/icompletioncollector.h>
hjk's avatar
hjk committed
66

67
#include <find/findplugin.h>
68 69
#include <find/textfindconstants.h>

hjk's avatar
hjk committed
70
#include <utils/qtcassert.h>
hjk's avatar
hjk committed
71
#include <utils/savedaction.h>
72
#include <utils/treewidgetcolumnstretcher.h>
hjk's avatar
hjk committed
73

74 75
#include <cppeditor/cppeditorconstants.h>

76 77
#include <cpptools/cpptoolsconstants.h>

78
#include <QtCore/QAbstractTableModel>
hjk's avatar
hjk committed
79
#include <QtCore/QDebug>
80
#include <QtCore/QFile>
81
#include <QtCore/QtPlugin>
hjk's avatar
hjk committed
82 83
#include <QtCore/QObject>
#include <QtCore/QSettings>
84
#include <QtCore/QTextStream>
hjk's avatar
hjk committed
85

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

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

namespace FakeVim {
namespace Constants {

con's avatar
con committed
103 104 105 106 107 108 109
const char * const INSTALL_HANDLER                = "TextEditor.FakeVimHandler";
const char * const MINI_BUFFER                    = "TextEditor.FakeVimMiniBuffer";
const char * const INSTALL_KEY                    = "Alt+V,Alt+V";
const char * const SETTINGS_CATEGORY              = "D.FakeVim";
const char * const SETTINGS_CATEGORY_FAKEVIM_ICON = ":/core/images/category_fakevim.png";
const char * const SETTINGS_ID                    = "A.General";
const char * const SETTINGS_EX_CMDS_ID            = "B.ExCommands";
110
const char * const SETTINGS_USER_CMDS_ID          = "C.UserCommands";
hjk's avatar
hjk committed
111 112 113 114 115

} // namespace Constants
} // namespace FakeVim


hjk's avatar
hjk committed
116 117 118
namespace FakeVim {
namespace Internal {

hjk's avatar
hjk committed
119 120 121 122 123 124
///////////////////////////////////////////////////////////////////////
//
// FakeVimOptionPage
//
///////////////////////////////////////////////////////////////////////

125 126
typedef QMap<QString, QRegExp> ExCommandMap;
typedef QMap<int, QString> UserCommandMap;
hjk's avatar
hjk committed
127
typedef QLatin1String _;
128

hjk's avatar
hjk committed
129 130 131 132 133 134 135 136
class FakeVimOptionPage : public Core::IOptionsPage
{
    Q_OBJECT

public:
    FakeVimOptionPage() {}

    // IOptionsPage
hjk's avatar
hjk committed
137
    QString id() const { return _(Constants::SETTINGS_ID); }
138
    QString displayName() const { return tr("General"); }
hjk's avatar
hjk committed
139
    QString category() const { return _(Constants::SETTINGS_CATEGORY); }
140
    QString displayCategory() const { return tr("FakeVim"); }
hjk's avatar
hjk committed
141 142
    QIcon categoryIcon() const
        { return QIcon(_(Constants::SETTINGS_CATEGORY_FAKEVIM_ICON)); }
hjk's avatar
hjk committed
143 144 145 146

    QWidget *createPage(QWidget *parent);
    void apply() { m_group.apply(ICore::instance()->settings()); }
    void finish() { m_group.finish(); }
147
    virtual bool matches(const QString &) const;
hjk's avatar
hjk committed
148 149 150 151 152 153 154 155 156

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

private:
    friend class DebuggerPlugin;
    Ui::FakeVimOptionPage m_ui;
157
    QString m_searchKeywords;
158
    Utils::SavedActionSet m_group;
hjk's avatar
hjk committed
159 160 161 162 163 164 165 166
};

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

    m_group.clear();
167
    m_group.insert(theFakeVimSetting(ConfigUseFakeVim),
168
        m_ui.checkBoxUseFakeVim);
169 170
    m_group.insert(theFakeVimSetting(ConfigReadVimRc),
        m_ui.checkBoxReadVimRc);
hjk's avatar
hjk committed
171

172
    m_group.insert(theFakeVimSetting(ConfigExpandTab),
hjk's avatar
hjk committed
173
        m_ui.checkBoxExpandTab);
174
    m_group.insert(theFakeVimSetting(ConfigHlSearch),
hjk's avatar
hjk committed
175
        m_ui.checkBoxHlSearch);
176
    m_group.insert(theFakeVimSetting(ConfigShiftWidth),
177
        m_ui.spinBoxShiftWidth);
178 179
    m_group.insert(theFakeVimSetting(ConfigShowMarks),
        m_ui.checkBoxShowMarks);
hjk's avatar
hjk committed
180

181
    m_group.insert(theFakeVimSetting(ConfigSmartTab),
hjk's avatar
hjk committed
182
        m_ui.checkBoxSmartTab);
183
    m_group.insert(theFakeVimSetting(ConfigStartOfLine),
hjk's avatar
hjk committed
184
        m_ui.checkBoxStartOfLine);
185
    m_group.insert(theFakeVimSetting(ConfigTabStop),
186
        m_ui.spinBoxTabStop);
187
    m_group.insert(theFakeVimSetting(ConfigBackspace),
hjk's avatar
hjk committed
188
        m_ui.lineEditBackspace);
189 190
    m_group.insert(theFakeVimSetting(ConfigIsKeyword),
        m_ui.lineEditIsKeyword);
hjk's avatar
hjk committed
191

192 193
    m_group.insert(theFakeVimSetting(ConfigPassControlKey),
        m_ui.checkBoxPassControlKey);
194
    m_group.insert(theFakeVimSetting(ConfigAutoIndent),
hjk's avatar
hjk committed
195
        m_ui.checkBoxAutoIndent);
196
    m_group.insert(theFakeVimSetting(ConfigSmartIndent),
197
        m_ui.checkBoxSmartIndent);
198
    m_group.insert(theFakeVimSetting(ConfigIncSearch),
199
        m_ui.checkBoxIncSearch);
200 201
    m_group.insert(theFakeVimSetting(ConfigUseCoreSearch),
        m_ui.checkBoxUseCoreSearch);
hjk's avatar
hjk committed
202 203

    connect(m_ui.pushButtonCopyTextEditorSettings, SIGNAL(clicked()),
204
        SLOT(copyTextEditorSettings()));
hjk's avatar
hjk committed
205
    connect(m_ui.pushButtonSetQtStyle, SIGNAL(clicked()),
206
        SLOT(setQtStyle()));
hjk's avatar
hjk committed
207
    connect(m_ui.pushButtonSetPlainStyle, SIGNAL(clicked()),
208 209
        SLOT(setPlainStyle()));

210
    if (m_searchKeywords.isEmpty()) {
211
        QLatin1Char sep(' ');
212
        QTextStream(&m_searchKeywords)
213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228
                << sep << m_ui.checkBoxUseFakeVim->text()
                << sep << m_ui.checkBoxReadVimRc->text()
                << sep << m_ui.checkBoxAutoIndent->text()
                << sep << m_ui.checkBoxSmartIndent->text()
                << sep << m_ui.checkBoxExpandTab->text()
                << sep << m_ui.checkBoxSmartTab->text()
                << sep << m_ui.checkBoxHlSearch->text()
                << sep << m_ui.checkBoxIncSearch->text()
                << sep << m_ui.checkBoxStartOfLine->text()
                << sep << m_ui.checkBoxUseCoreSearch->text()
                << sep << m_ui.checkBoxShowMarks->text()
                << sep << m_ui.checkBoxPassControlKey->text()
                << sep << m_ui.labelShiftWidth->text()
                << sep << m_ui.labelTabulator->text()
                << sep << m_ui.labelBackspace->text()
                << sep << m_ui.labelIsKeyword->text();
229 230
        m_searchKeywords.remove(QLatin1Char('&'));
    }
hjk's avatar
hjk committed
231 232 233 234 235
    return w;
}

void FakeVimOptionPage::copyTextEditorSettings()
{
hjk's avatar
hjk committed
236
    TabSettings ts = TextEditorSettings::instance()->tabSettings();
hjk's avatar
hjk committed
237
    m_ui.checkBoxExpandTab->setChecked(ts.m_spacesForTabs);
238 239
    m_ui.spinBoxTabStop->setValue(ts.m_tabSize);
    m_ui.spinBoxShiftWidth->setValue(ts.m_indentSize);
hjk's avatar
hjk committed
240
    m_ui.checkBoxSmartTab->setChecked(ts.m_smartBackspace);
241 242
    m_ui.checkBoxAutoIndent->setChecked(true);
    m_ui.checkBoxSmartIndent->setChecked(ts.m_autoIndent);
243
    m_ui.checkBoxIncSearch->setChecked(true);
hjk's avatar
hjk committed
244 245 246 247 248
}

void FakeVimOptionPage::setQtStyle()
{
    m_ui.checkBoxExpandTab->setChecked(true);
249 250
    m_ui.spinBoxTabStop->setValue(4);
    m_ui.spinBoxShiftWidth->setValue(4);
hjk's avatar
hjk committed
251 252
    m_ui.checkBoxSmartTab->setChecked(true);
    m_ui.checkBoxAutoIndent->setChecked(true);
253
    m_ui.checkBoxSmartIndent->setChecked(true);
254
    m_ui.checkBoxIncSearch->setChecked(true);
hjk's avatar
hjk committed
255
    m_ui.lineEditBackspace->setText(_("indent,eol,start"));
hjk's avatar
hjk committed
256 257 258 259 260
}

void FakeVimOptionPage::setPlainStyle()
{
    m_ui.checkBoxExpandTab->setChecked(false);
261 262
    m_ui.spinBoxTabStop->setValue(8);
    m_ui.spinBoxShiftWidth->setValue(8);
hjk's avatar
hjk committed
263 264
    m_ui.checkBoxSmartTab->setChecked(false);
    m_ui.checkBoxAutoIndent->setChecked(false);
265
    m_ui.checkBoxSmartIndent->setChecked(false);
266
    m_ui.checkBoxIncSearch->setChecked(false);
hjk's avatar
hjk committed
267
    m_ui.lineEditBackspace->setText(QString());
hjk's avatar
hjk committed
268 269
}

270 271 272 273 274
bool FakeVimOptionPage::matches(const QString &s) const
{
    return m_searchKeywords.contains(s, Qt::CaseInsensitive);
}

275
//const char *FAKEVIM_CONTEXT = "FakeVim";
hjk's avatar
hjk committed
276

277 278 279 280 281 282
///////////////////////////////////////////////////////////////////////
//
// FakeVimExCommandsPage
//
///////////////////////////////////////////////////////////////////////

283
enum { CommandRole = Qt::UserRole };
284

285
class FakeVimExCommandsPage : public Core::CommandMappings
286 287 288 289
{
    Q_OBJECT

public:
290
    FakeVimExCommandsPage(FakeVimPluginPrivate *q) : m_q(q) {}
291
    ~FakeVimExCommandsPage() {}
292 293

    // IOptionsPage
hjk's avatar
hjk committed
294
    QString id() const { return _(Constants::SETTINGS_EX_CMDS_ID); }
295
    QString displayName() const { return tr("Ex Command Mapping"); }
hjk's avatar
hjk committed
296
    QString category() const { return _(Constants::SETTINGS_CATEGORY); }
297
    QString displayCategory() const { return tr("FakeVim"); }
298
    QIcon categoryIcon() const { return QIcon(); } // TODO: Icon for FakeVim
299 300 301

    QWidget *createPage(QWidget *parent);
    void initialize();
302 303
    ExCommandMap &exCommandMap();
    ExCommandMap &defaultExCommandMap();
304 305 306

public slots:
    void commandChanged(QTreeWidgetItem *current);
307 308 309
    void targetIdentifierChanged();
    void resetTargetIdentifier();
    void removeTargetIdentifier();
310 311 312
    void defaultAction();

private:
313
    FakeVimPluginPrivate *m_q;
314 315 316 317
};

QWidget *FakeVimExCommandsPage::createPage(QWidget *parent)
{
318 319 320
    QWidget *w = CommandMappings::createPage(parent);
    setPageTitle(tr("Ex Command Mapping"));
    setTargetHeader(tr("Ex Trigger Expression"));
321
    setTargetLabelText(tr("Regular expression:"));
322 323
    setTargetEditTitle(tr("Ex Command"));
    setImportExportEnabled(false);
324 325 326 327 328
    return w;
}

void FakeVimExCommandsPage::initialize()
{
hjk's avatar
hjk committed
329
    ActionManager *am = ICore::instance()->actionManager();
330 331 332 333
    QTC_ASSERT(am, return);
    UniqueIDManager *uidm = UniqueIDManager::instance();
    QTC_ASSERT(uidm, return);

334 335
    QMap<QString, QTreeWidgetItem *> sections;

336 337 338 339
    foreach (Command *c, am->commands()) {
        if (c->action() && c->action()->isSeparator())
            continue;

340
        QTreeWidgetItem *item = new QTreeWidgetItem;
341
        item->setData(0, CommandRole, int(c->id()));
342 343

        const QString name = uidm->stringForUniqueIdentifier(c->id());
344 345
        const int pos = name.indexOf(QLatin1Char('.'));
        const QString section = name.left(pos);
346
        const QString subId = name.mid(pos + 1);
347 348

        if (!sections.contains(section)) {
349 350
            QTreeWidgetItem *categoryItem =
                new QTreeWidgetItem(commandList(), QStringList() << section);
351 352 353 354
            QFont f = categoryItem->font(0);
            f.setBold(true);
            categoryItem->setFont(0, f);
            sections.insert(section, categoryItem);
355
            commandList()->expandItem(categoryItem);
356 357 358 359
        }
        sections[section]->addChild(item);

        item->setText(0, subId);
360 361

        if (c->action()) {
362 363 364
            QString text = c->hasAttribute(Command::CA_UpdateText)
                    && !c->defaultText().isNull()
                ? c->defaultText() : c->action()->text();
365 366 367 368 369 370
            text.remove(QRegExp("&(?!&)"));
            item->setText(1, text);
        } else {
            item->setText(1, c->shortcut()->whatsThis());
        }

371 372 373 374
        QString regex;
        if (exCommandMap().contains(name))
            regex = exCommandMap()[name].pattern();
        item->setText(2, regex);
375

376
        if (regex != defaultExCommandMap()[name].pattern())
377
            setModified(item, true);
378 379 380 381 382 383 384
    }

    commandChanged(0);
}

void FakeVimExCommandsPage::commandChanged(QTreeWidgetItem *current)
{
385
    CommandMappings::commandChanged(current);
386 387
    if (current)
        targetEdit()->setText(current->text(2));
388 389
}

390
void FakeVimExCommandsPage::targetIdentifierChanged()
391
{
392
    QTreeWidgetItem *current = commandList()->currentItem();
393 394 395 396
    if (!current)
        return;

    UniqueIDManager *uidm = UniqueIDManager::instance();
397 398 399
    int id = current->data(0, CommandRole).toInt();
    const QString name = uidm->stringForUniqueIdentifier(id);
    const QString regex = targetEdit()->text();
400 401

    if (current->data(0, Qt::UserRole).isValid()) {
402 403
        current->setText(2, regex);
        exCommandMap()[name] = QRegExp(regex);
404
    }
405

406
    setModified(current, regex != defaultExCommandMap()[name].pattern());
407 408
}

409
void FakeVimExCommandsPage::resetTargetIdentifier()
410
{
411
    QTreeWidgetItem *current = commandList()->currentItem();
412 413 414 415 416 417 418 419 420
    if (!current)
        return;
    UniqueIDManager *uidm = UniqueIDManager::instance();
    int id = current->data(0, CommandRole).toInt();
    const QString name = uidm->stringForUniqueIdentifier(id);
    QString regex;
    if (defaultExCommandMap().contains(name))
        regex = defaultExCommandMap()[name].pattern();
    targetEdit()->setText(regex);
421 422
}

423
void FakeVimExCommandsPage::removeTargetIdentifier()
424
{
425
    targetEdit()->clear();
426 427 428 429 430
}

void FakeVimExCommandsPage::defaultAction()
{
    UniqueIDManager *uidm = UniqueIDManager::instance();
hjk's avatar
hjk committed
431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446
    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);
            const int id = item->data(0, CommandRole).toInt();
            const QString name = uidm->stringForUniqueIdentifier(id);
            QString regex;
            if (defaultExCommandMap().contains(name))
                regex = defaultExCommandMap()[name].pattern();
            setModified(item, false);
            item->setText(2, regex);
            if (item == commandList()->currentItem())
                commandChanged(item);
        }
447 448 449
    }
}

450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 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 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
///////////////////////////////////////////////////////////////////////
//
// FakeVimUserCommandsPage
//
///////////////////////////////////////////////////////////////////////

class FakeVimUserCommandsModel : public QAbstractTableModel
{
public:
    FakeVimUserCommandsModel(FakeVimPluginPrivate *q) : m_q(q) {}
    ~FakeVimUserCommandsModel() {}

    int rowCount(const QModelIndex &parent) const;
    int columnCount(const QModelIndex &parent) const;
    QVariant data(const QModelIndex &index, int role) const;
    bool setData(const QModelIndex &index, const QVariant &data, int role);
    QVariant headerData(int section, Qt::Orientation orientation, int role) const;
    Qt::ItemFlags flags(const QModelIndex &index) const;

private:
    FakeVimPluginPrivate *m_q;
};

int FakeVimUserCommandsModel::rowCount(const QModelIndex &parent) const
{
    return parent.isValid() ? 0 : 9;
}

int FakeVimUserCommandsModel::columnCount(const QModelIndex &parent) const
{
    return parent.isValid() ? 0 : 2;
}


QVariant FakeVimUserCommandsModel::headerData(int section,
    Qt::Orientation orient, int role) const
{
    if (orient == Qt::Horizontal && role == Qt::DisplayRole) {
        switch (section) {
            case 0: return tr("Action");
            case 1: return tr("Command");
        };
    }
    return QVariant();
}

Qt::ItemFlags FakeVimUserCommandsModel::flags(const QModelIndex &index) const
{
    if (index.column() == 1)
        return QAbstractTableModel::flags(index) | Qt::ItemIsEditable;
    return QAbstractTableModel::flags(index);
}

class FakeVimUserCommandsDelegate : public QItemDelegate
{
public:
    explicit FakeVimUserCommandsDelegate(QObject *parent)
        : QItemDelegate(parent)
    {}

    QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &,
        const QModelIndex &) const
    {
        QLineEdit *lineEdit = new QLineEdit(parent);
        lineEdit->setFrame(false);
        return lineEdit;
    }

    void setModelData(QWidget *editor, QAbstractItemModel *model,
                      const QModelIndex &index) const
    {
        QLineEdit *lineEdit = qobject_cast<QLineEdit *>(editor);
        QTC_ASSERT(lineEdit, return);
        model->setData(index, lineEdit->text(), Qt::EditRole);
    }
};

class FakeVimUserCommandsPage : public Core::IOptionsPage
{
    Q_OBJECT

public:
    FakeVimUserCommandsPage(FakeVimPluginPrivate *q) : m_q(q) {}
    ~FakeVimUserCommandsPage() {}

    // IOptionsPage
    QString id() const { return _(Constants::SETTINGS_USER_CMDS_ID); }
    QString displayName() const { return tr("User Command Mapping"); }
    QString category() const { return _(Constants::SETTINGS_CATEGORY); }
    QString displayCategory() const { return tr("FakeVim"); }
    QIcon categoryIcon() const { return QIcon(); }
    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
577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600

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

class WordCompletion : public ICompletionCollector
{
    Q_OBJECT

public:
    WordCompletion()
    {
        m_editable = 0;
        m_editor = 0;
    }

    virtual bool shouldRestartCompletion()
    {
        //qDebug() << "SHOULD RESTART COMPLETION?";
        return false;
    }

601
    virtual ITextEditor *editor() const
hjk's avatar
hjk committed
602 603 604 605 606
    {
        //qDebug() << "NO EDITOR?";
        return m_editable;
    }

607 608 609 610
    virtual int startPosition() const
    {
        return m_startPosition;
    }
hjk's avatar
hjk committed
611

612
    virtual bool supportsEditor(ITextEditor *) const
613 614 615 616 617 618 619 620
    {
        return true;
    }

    virtual bool supportsPolicy(CompletionPolicy policy) const
    {
        return policy == TextCompletion;
    }
hjk's avatar
hjk committed
621

622
    virtual bool triggersCompletion(ITextEditor *editable)
hjk's avatar
hjk committed
623 624 625 626 627 628
    {
        //qDebug() << "TRIGGERS?";
        QTC_ASSERT(m_editable == editable, /**/);
        return true;
    }

629
    virtual int startCompletion(ITextEditor *editable)
hjk's avatar
hjk committed
630 631 632 633 634 635 636 637 638 639 640 641 642
    {
        //qDebug() << "START COMPLETION";
        QTC_ASSERT(m_editor, return -1);
        QTC_ASSERT(m_editable == editable, return -1);
        return m_editor->textCursor().position();
    }

    void setActive(const QString &needle, bool forward, FakeVimHandler *handler)
    {
        Q_UNUSED(forward);
        m_handler = handler;
        if (!m_handler)
            return;
643
        m_editor = qobject_cast<BaseTextEditorWidget *>(handler->widget());
hjk's avatar
hjk committed
644 645 646 647
        if (!m_editor)
            return;
        //qDebug() << "ACTIVATE: " << needle << forward;
        m_needle = needle;
648
        m_editable = m_editor->editor();
hjk's avatar
hjk committed
649 650
        m_startPosition = m_editor->textCursor().position() - needle.size();

651
        CompletionSupport::instance()->complete(m_editable, TextCompletion, false);
hjk's avatar
hjk committed
652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724
    }

    void setInactive()
    {
        m_needle.clear();
        m_editable = 0;
        m_editor = 0;
        m_handler = 0;
        m_startPosition = -1;
    }

    virtual void completions(QList<CompletionItem> *completions)
    {
        QTC_ASSERT(m_editor, return);
        QTC_ASSERT(completions, return);
        QTextCursor tc = m_editor->textCursor();
        tc.movePosition(QTextCursor::Start, QTextCursor::MoveAnchor);

        QSet<QString> seen;

        QTextDocument::FindFlags flags = QTextDocument::FindCaseSensitively;
        while (1) {
            tc = tc.document()->find(m_needle, tc.position(), flags);
            if (tc.isNull())
                break;
            QTextCursor sel = tc;
            sel.select(QTextCursor::WordUnderCursor);
            QString found = sel.selectedText();
            // Only add "real" completions.
            if (found.startsWith(m_needle)
                    && !seen.contains(found)
                    && sel.anchor() != m_startPosition) {
                seen.insert(found);
                CompletionItem item;
                item.collector = this;
                item.text = found;
                completions->append(item);
            }
            tc.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor);
        }
        //qDebug() << "COMPLETIONS" << completions->size();
    }

    virtual bool typedCharCompletes(const CompletionItem &item, QChar typedChar)
    {
        m_needle += typedChar;
        //qDebug() << "COMPLETE? " << typedChar << item.text << m_needle;
        return item.text == m_needle;
    }

    virtual void complete(const CompletionItem &item, QChar typedChar)
    {
        Q_UNUSED(typedChar);
        //qDebug() << "COMPLETE: " << item.text;
        QTC_ASSERT(m_handler, return);
        m_handler->handleReplay(item.text.mid(m_needle.size()));
        setInactive();
    }

    virtual bool partiallyComplete(const QList<CompletionItem> &completionItems)
    {
        //qDebug() << "PARTIALLY";
        Q_UNUSED(completionItems);
        return false;
    }

    virtual void cleanup() {}

private:
    int findStartOfName(int pos = -1) const;
    bool isInComment() const;

    FakeVimHandler *m_handler;
725 726
    BaseTextEditorWidget *m_editor;
    ITextEditor *m_editable;
hjk's avatar
hjk committed
727 728 729 730 731
    QString m_needle;
    QString m_currentPrefix;
    QList<CompletionItem> m_items;
    int m_startPosition;
};
732 733


hjk's avatar
hjk committed
734 735
///////////////////////////////////////////////////////////////////////
//
736
// FakeVimPluginPrivate
hjk's avatar
hjk committed
737 738 739
//
///////////////////////////////////////////////////////////////////////

740
class FakeVimPluginPrivate : public QObject
hjk's avatar
hjk committed
741
{
742 743 744 745 746 747
    Q_OBJECT

public:
    FakeVimPluginPrivate(FakeVimPlugin *);
    ~FakeVimPluginPrivate();
    friend class FakeVimPlugin;
748
    friend class FakeVimExCommandsPage;
749 750
    friend class FakeVimUserCommandsPage;
    friend class FakeVimUserCommandsModel;
751

752
    bool initialize();
753
    void aboutToShutdown();
754 755

private slots:
756
    void onCoreAboutToClose();
757 758
    void editorOpened(Core::IEditor *);
    void editorAboutToClose(Core::IEditor *);
759

760
    void setUseFakeVim(const QVariant &value);
761
    void setUseFakeVimInternal(bool on);
762
    void quitFakeVim();
763
    void triggerCompletions();
764
    void triggerSimpleCompletions(const QString &needle, bool forward);
hjk's avatar
hjk committed
765
    void windowCommand(int key);
766
    void find(bool reverse);
767
    void findNext(bool reverse);
hjk's avatar
hjk committed
768
    void showSettingsDialog();
769
    void maybeReadVimRc();
hjk's avatar
hjk committed
770 771
    void setBlockSelection(bool);
    void hasBlockSelection(bool*);
772 773 774 775

    void showCommandBuffer(const QString &contents);
    void showExtraInformation(const QString &msg);
    void changeSelection(const QList<QTextEdit::ExtraSelection> &selections);
776
    void moveToMatchingParenthesis(bool *moved, bool *forward, QTextCursor *cursor);
777
    void checkForElectricCharacter(bool *result, QChar c);
778
    void indentRegion(int beginLine, int endLine, QChar typedChar);
779
    void handleExCommand(bool *handled, const ExCommand &cmd);
780

781 782 783
    void writeSettings();
    void readSettings();

784 785
    void handleDelayedQuitAll(bool forced);
    void handleDelayedQuit(bool forced, Core::IEditor *editor);
786
    void userActionTriggered();
787

788 789
    void switchToFile(int n);
    int currentFile() const;
790

791 792 793
signals:
    void delayedQuitRequested(bool forced, Core::IEditor *editor);
    void delayedQuitAllRequested(bool forced);
794 795 796

private:
    FakeVimPlugin *q;
hjk's avatar
hjk committed
797
    FakeVimOptionPage *m_fakeVimOptionsPage;
798
    FakeVimExCommandsPage *m_fakeVimExCommandsPage;
799
    FakeVimUserCommandsPage *m_fakeVimUserCommandsPage;
800
    QHash<Core::IEditor *, FakeVimHandler *> m_editorToHandler;
hjk's avatar
hjk committed
801 802 803 804
    QPointer<Core::ICore> m_core;
    QPointer<Core::EditorManager> m_editorManager;
    QPointer<Core::ActionManager> m_actionManager;
    ICore *core() const { return m_core; }
hjk's avatar
hjk committed
805
    EditorManager *editorManager() const { return m_editorManager; }
hjk's avatar
hjk committed
806
    ActionManager *actionManager() const { return m_actionManager; }
807

808 809
    void triggerAction(const QString &code);
    void setActionChecked(const QString &code, bool check);
810

811 812 813
    typedef int (*DistFunction)(const QRect &cursor, const QRect &other);
    void moveSomewhere(DistFunction f);

814 815 816 817 818 819 820 821 822 823
    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;

824
    Core::StatusBarWidget *m_statusBar;
hjk's avatar
hjk committed
825
    WordCompletion *m_wordCompletion;
826 827
};

828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853
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;
}

854
FakeVimPluginPrivate::FakeVimPluginPrivate(FakeVimPlugin *plugin)
855
{
856
    q = plugin;
hjk's avatar
hjk committed
857
    m_fakeVimOptionsPage = 0;
858
    m_fakeVimExCommandsPage = 0;
859
    m_fakeVimUserCommandsPage = 0;
860
    defaultExCommandMap()[CppTools::Constants::SWITCH_HEADER_SOURCE] =
861
        QRegExp("^A$");
862
    defaultExCommandMap()["Coreplugin.OutputPane.previtem"] =
863
        QRegExp("^(cN(ext)?|cp(revious)?)!?( (.*))?$");
864
    defaultExCommandMap()["Coreplugin.OutputPane.nextitem"] =
865
        QRegExp("^cn(ext)?!?( (.*))?$");
866
    defaultExCommandMap()[CppEditor::Constants::JUMP_TO_DEFINITION] =
867
        QRegExp("^tag?$");
868
    defaultExCommandMap()[Core::Constants::GO_BACK] =
869
        QRegExp("^pop?$");
hjk's avatar
hjk committed
870
    defaultExCommandMap()[_("QtCreator.Locate")] =
871
        QRegExp("^e$");
872 873 874 875 876 877

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

878
    m_statusBar = 0;
hjk's avatar
hjk committed
879 880
}

881
FakeVimPluginPrivate::~FakeVimPluginPrivate()
hjk's avatar
hjk committed
882
{
hjk's avatar
hjk committed
883 884 885
    q->removeObject(m_fakeVimOptionsPage);
    delete m_fakeVimOptionsPage;
    m_fakeVimOptionsPage = 0;
dt's avatar
dt committed
886
    delete theFakeVimSettings();
887 888 889 890

    q->removeObject(m_fakeVimExCommandsPage);
    delete m_fakeVimExCommandsPage;
    m_fakeVimExCommandsPage = 0;
891 892 893 894

    q->removeObject(m_fakeVimUserCommandsPage);
    delete m_fakeVimUserCommandsPage;
    m_fakeVimUserCommandsPage = 0;
895 896
}

897 898 899 900 901 902 903
void FakeVimPluginPrivate::onCoreAboutToClose()
{
    // don't attach to editors any more
    disconnect(editorManager(), SIGNAL(editorOpened(Core::IEditor*)),
        this, SLOT(editorOpened(Core::IEditor*)));
}

904 905
void FakeVimPluginPrivate::aboutToShutdown()
{
hjk's avatar
hjk committed
906 907
}

908
bool FakeVimPluginPrivate::initialize()
hjk's avatar
hjk committed
909
{
hjk's avatar
hjk committed
910 911 912 913
    m_core = Core::ICore::instance();
    m_editorManager = core()->editorManager();
    m_actionManager = core()->actionManager();
    QTC_ASSERT(actionManager(), return false);
hjk's avatar
hjk committed
914

hjk's avatar
hjk committed
915 916 917 918 919 920 921 922 923 924 925 926
    m_wordCompletion = new WordCompletion;
    q->addAutoReleasedObject(m_wordCompletion);
/*
    // Set completion settings and keep them up to date.
    TextEditorSettings *textEditorSettings = TextEditorSettings::instance();
    completion->setCompletionSettings(textEditorSettings->completionSettings());
    connect(textEditorSettings,
        SIGNAL(completionSettingsChanged(TextEditor::CompletionSettings)),
        completion,
        SLOT(setCompletionSettings(TextEditor::CompletionSettings)));
*/

hjk's avatar
hjk committed
927
    Context globalcontext(Core::Constants::C_GLOBAL);
hjk's avatar
hjk committed
928

hjk's avatar
hjk committed
929 930
    m_fakeVimOptionsPage = new FakeVimOptionPage;
    q->addObject(m_fakeVimOptionsPage);
931

932
    m_fakeVimExCommandsPage = new FakeVimExCommandsPage(this);
933
    q->addObject(m_fakeVimExCommandsPage);
934 935 936 937

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

938
    readSettings();
939

con's avatar
con committed
940
    Core::Command *cmd = 0;
hjk's avatar
hjk committed
941
    cmd = actionManager()->registerAction(theFakeVimSetting(ConfigUseFakeVim),
hjk's avatar
hjk committed
942 943 944
        Constants::INSTALL_HANDLER, globalcontext);
    cmd->setDefaultKeySequence(QKeySequence(Constants::INSTALL_KEY));

945 946 947 948 949 950 951 952 953 954 955
    for (int i = 1; i < 10; ++i) {
        QAction *act = new QAction(this);
        act->setText(QString("Execute User Action #%1").arg(i));
        act->setData(i);
        QString id = QString("FakeVim.UserAction%1").arg(i);
        QString keys = QString("Alt+V,%1").arg(i);
        cmd = actionManager()->registerAction(act, id, globalcontext);
        cmd->setDefaultKeySequence(QKeySequence(keys));
        connect(act, SIGNAL(triggered()), SLOT(userActionTriggered()));
    }

956
    ActionContainer *advancedMenu =
hjk's avatar
hjk committed
957
        actionManager()->actionContainer(Core::Constants::M_EDIT_ADVANCED);