fakevimplugin.cpp 65.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";
hjk's avatar
hjk committed
114

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

public:
120
    MiniBuffer() : m_label(new QLabel(this)), m_edit(new QLineEdit(this)), m_eventFilter(0)
121
    {
122 123 124 125 126 127 128
        m_edit->installEventFilter(this);
        connect(m_edit, SIGNAL(textEdited(QString)), SLOT(changed()));
        connect(m_edit, SIGNAL(cursorPositionChanged(int,int)), SLOT(changed()));
        m_label->setTextInteractionFlags(Qt::TextSelectableByMouse);

        addWidget(m_label);
        addWidget(m_edit);
129 130
    }

131 132 133 134 135 136 137 138 139 140 141
    void setContents(const QString &contents, int cursorPos, int messageLevel, QObject *eventFilter)
    {
        if (cursorPos != -1) {
            m_edit->blockSignals(true);
            m_label->clear();
            m_edit->setText(contents);
            m_edit->setCursorPosition(cursorPos);
            m_edit->blockSignals(false);

            setCurrentWidget(m_edit);
            m_edit->setFocus();
Lukas Holecek's avatar
Lukas Holecek committed
142 143
        } else if (contents.isEmpty()) {
            hide();
144
        } else {
Lukas Holecek's avatar
Lukas Holecek committed
145
            show();
146 147 148 149 150 151 152 153 154 155 156 157
            m_label->setText(messageLevel == MessageMode ? "-- " + contents + " --" : contents);

            QString css;
            if (messageLevel == MessageError) {
                css = QString("border:1px solid rgba(255,255,255,150);"
                              "background-color:rgba(255,0,0,100);");
            } else if (messageLevel == MessageWarning) {
                css = QString("border:1px solid rgba(255,255,255,120);"
                              "background-color:rgba(255,255,0,20);");
            }
            m_label->setStyleSheet(QString(
                "*{border-radius:2px;padding-left:4px;padding-right:4px;%1}").arg(css));
158

159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179
            if (m_edit->hasFocus())
                emit edited(QString(), -1);

            setCurrentWidget(m_label);
        }

        if (m_eventFilter != eventFilter) {
            if (m_eventFilter != 0) {
                m_edit->removeEventFilter(m_eventFilter);
                disconnect(SIGNAL(edited(QString,int)));
            }
            if (eventFilter != 0) {
                m_edit->installEventFilter(eventFilter);
                connect(this, SIGNAL(edited(QString,int)),
                        eventFilter, SLOT(miniBufferTextEdited(QString,int)));
            }
            m_eventFilter = eventFilter;
        }
    }

    QSize sizeHint() const
180
    {
181 182 183
        QSize size = QWidget::sizeHint();
        // reserve maximal width for line edit widget
        return currentWidget() == m_edit ? QSize(maximumWidth(), size.height()) : size;
184
    }
185 186 187 188 189 190

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

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

    bool eventFilter(QObject *ob, QEvent *ev)
196
    {
197 198 199 200 201 202 203 204
        // cancel editing on escape
        if (m_eventFilter != 0 && ob == m_edit && ev->type() == QEvent::ShortcutOverride
            && static_cast<QKeyEvent*>(ev)->key() == Qt::Key_Escape) {
            emit edited(QString(), -1);
            ev->accept();
            return true;
        }
        return false;
205
    }
206 207 208 209 210

private:
    QLabel *m_label;
    QLineEdit *m_edit;
    QObject *m_eventFilter;
211 212
};

hjk's avatar
hjk committed
213 214 215 216 217 218
///////////////////////////////////////////////////////////////////////
//
// FakeVimOptionPage
//
///////////////////////////////////////////////////////////////////////

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

hjk's avatar
hjk committed
223
class FakeVimOptionPage : public IOptionsPage
hjk's avatar
hjk committed
224 225 226 227
{
    Q_OBJECT

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

376
//const char *FAKEVIM_CONTEXT = "FakeVim";
hjk's avatar
hjk committed
377

378 379 380 381 382 383
///////////////////////////////////////////////////////////////////////
//
// FakeVimExCommandsPage
//
///////////////////////////////////////////////////////////////////////

384
enum { CommandRole = Qt::UserRole };
385

hjk's avatar
hjk committed
386
class FakeVimExCommandsPage : public CommandMappings
387 388 389 390
{
    Q_OBJECT

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

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

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

private:
414
    FakeVimPluginPrivate *m_q;
415 416 417 418
};

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

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

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

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

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

        item->setText(0, subId);
455
        item->setText(1, c->description());
456

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

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

    commandChanged(0);
}

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

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

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

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

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

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

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

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

530 531 532 533 534 535 536 537
///////////////////////////////////////////////////////////////////////
//
// FakeVimUserCommandsPage
//
///////////////////////////////////////////////////////////////////////

class FakeVimUserCommandsModel : public QAbstractTableModel
{
Friedemann Kleint's avatar
Friedemann Kleint committed
538
    Q_OBJECT
539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607
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
608
class FakeVimUserCommandsPage : public IOptionsPage
609 610 611 612
{
    Q_OBJECT

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

623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658
    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
659 660 661 662 663 664 665

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

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

hjk's avatar
hjk committed
674
    IAssistProcessor *createProcessor() const;
Leandro Melo's avatar
Leandro Melo committed
675 676

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

814

hjk's avatar
hjk committed
815 816
///////////////////////////////////////////////////////////////////////
//
817
// FakeVimPluginPrivate
hjk's avatar
hjk committed
818 819 820
//
///////////////////////////////////////////////////////////////////////

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

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

833
    bool initialize();
834
    void aboutToShutdown();
835 836

private slots:
837
    void onCoreAboutToClose();
838 839
    void editorOpened(Core::IEditor *);
    void editorAboutToClose(Core::IEditor *);
840

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

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

869 870 871
    void writeSettings();
    void readSettings();

872 873
    void handleDelayedQuitAll(bool forced);
    void handleDelayedQuit(bool forced, Core::IEditor *editor);
874
    void userActionTriggered();
875

876 877
    void switchToFile(int n);
    int currentFile() const;
878