fakevimplugin.cpp 69.5 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
hjk's avatar
hjk committed
2
**
3
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
hjk's avatar
hjk committed
4
** 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 "fakevimactions.h"
33
#include "fakevimhandler.h"
hjk's avatar
hjk committed
34 35
#include "ui_fakevimoptions.h"

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

#include <projectexplorer/projectexplorerconstants.h>

55
#include <texteditor/basetextdocumentlayout.h>
hjk's avatar
hjk committed
56 57 58
#include <texteditor/basetexteditor.h>
#include <texteditor/basetextmark.h>
#include <texteditor/texteditorconstants.h>
Jarek Kobus's avatar
Jarek Kobus committed
59
#include <texteditor/typingsettings.h>
60
#include <texteditor/tabsettings.h>
Jarek Kobus's avatar
Jarek Kobus committed
61
#include <texteditor/icodestylepreferences.h>
62
#include <texteditor/texteditorsettings.h>
63
#include <texteditor/indenter.h>
Leandro Melo's avatar
Leandro Melo committed
64 65 66 67 68 69
#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
70

71
#include <find/findplugin.h>
72
#include <find/textfindconstants.h>
73
#include <find/ifindsupport.h>
74

75
#include <utils/hostosinfo.h>
hjk's avatar
hjk committed
76
#include <utils/qtcassert.h>
hjk's avatar
hjk committed
77
#include <utils/savedaction.h>
78
#include <utils/treewidgetcolumnstretcher.h>
79
#include <utils/stylehelper.h>
hjk's avatar
hjk committed
80

81 82
#include <cpptools/cpptoolsconstants.h>

Lukas Holecek's avatar
Lukas Holecek committed
83 84
#include <extensionsystem/pluginmanager.h>

85 86 87
#include <QAbstractTableModel>
#include <QDebug>
#include <QFile>
88
#include <QFileDialog>
89 90 91
#include <QtPlugin>
#include <QObject>
#include <QSettings>
92
#include <QStackedWidget>
93
#include <QTextStream>
hjk's avatar
hjk committed
94

95 96 97 98 99 100 101 102 103
#include <QDesktopServices>
#include <QItemDelegate>
#include <QMessageBox>
#include <QPlainTextEdit>
#include <QShortcut>
#include <QTextBlock>
#include <QTextCursor>
#include <QTextEdit>
#include <QTreeWidgetItem>
hjk's avatar
hjk committed
104 105 106 107 108

using namespace TextEditor;
using namespace Core;

namespace FakeVim {
hjk's avatar
hjk committed
109
namespace Internal {
hjk's avatar
hjk committed
110

hjk's avatar
hjk committed
111 112 113 114 115 116
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";
117
typedef QLatin1String _;
hjk's avatar
hjk committed
118

119
class MiniBuffer : public QStackedWidget
120 121 122 123
{
    Q_OBJECT

public:
124
    MiniBuffer() : m_label(new QLabel(this)), m_edit(new QLineEdit(this)), m_eventFilter(0)
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()));
129
        connect(m_edit, SIGNAL(selectionChanged()), SLOT(changed()));
130 131 132 133
        m_label->setTextInteractionFlags(Qt::TextSelectableByMouse);

        addWidget(m_label);
        addWidget(m_edit);
134 135
    }

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

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

170
            if (m_edit->hasFocus())
171
                emit edited(QString(), -1, -1);
172 173 174 175 176 177 178

            setCurrentWidget(m_label);
        }

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

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

signals:
198
    void edited(const QString &text, int cursorPos, int anchorPos);
199 200 201

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

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

private:
    QLabel *m_label;
    QLineEdit *m_edit;
    QObject *m_eventFilter;
226 227
};

hjk's avatar
hjk committed
228 229 230 231 232 233
///////////////////////////////////////////////////////////////////////
//
// FakeVimOptionPage
//
///////////////////////////////////////////////////////////////////////

234 235
typedef QMap<QString, QRegExp> ExCommandMap;
typedef QMap<int, QString> UserCommandMap;
236

hjk's avatar
hjk committed
237
class FakeVimOptionPage : public IOptionsPage
hjk's avatar
hjk committed
238 239 240 241
{
    Q_OBJECT

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

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

private slots:
    void copyTextEditorSettings();
    void setQtStyle();
    void setPlainStyle();
260 261
    void openVimRc();
    void updateVimRcWidgets();
hjk's avatar
hjk committed
262 263 264 265

private:
    friend class DebuggerPlugin;
    Ui::FakeVimOptionPage m_ui;
266
    QString m_searchKeywords;
267
    Utils::SavedActionSet m_group;
hjk's avatar
hjk committed
268 269 270 271 272 273 274 275
};

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

    m_group.clear();
276
    m_group.insert(theFakeVimSetting(ConfigUseFakeVim),
277
        m_ui.checkBoxUseFakeVim);
278 279
    m_group.insert(theFakeVimSetting(ConfigReadVimRc),
        m_ui.checkBoxReadVimRc);
280 281
    m_group.insert(theFakeVimSetting(ConfigVimRcPath),
        m_ui.lineEditVimRcPath);
hjk's avatar
hjk committed
282

283
    m_group.insert(theFakeVimSetting(ConfigExpandTab),
hjk's avatar
hjk committed
284
        m_ui.checkBoxExpandTab);
285
    m_group.insert(theFakeVimSetting(ConfigHlSearch),
hjk's avatar
hjk committed
286
        m_ui.checkBoxHlSearch);
287
    m_group.insert(theFakeVimSetting(ConfigShiftWidth),
288
        m_ui.spinBoxShiftWidth);
289 290
    m_group.insert(theFakeVimSetting(ConfigShowMarks),
        m_ui.checkBoxShowMarks);
hjk's avatar
hjk committed
291

292
    m_group.insert(theFakeVimSetting(ConfigSmartTab),
hjk's avatar
hjk committed
293
        m_ui.checkBoxSmartTab);
294
    m_group.insert(theFakeVimSetting(ConfigStartOfLine),
hjk's avatar
hjk committed
295
        m_ui.checkBoxStartOfLine);
296
    m_group.insert(theFakeVimSetting(ConfigTabStop),
297
        m_ui.spinBoxTabStop);
298
    m_group.insert(theFakeVimSetting(ConfigBackspace),
hjk's avatar
hjk committed
299
        m_ui.lineEditBackspace);
300 301
    m_group.insert(theFakeVimSetting(ConfigIsKeyword),
        m_ui.lineEditIsKeyword);
hjk's avatar
hjk committed
302

303 304
    m_group.insert(theFakeVimSetting(ConfigPassControlKey),
        m_ui.checkBoxPassControlKey);
305
    m_group.insert(theFakeVimSetting(ConfigAutoIndent),
hjk's avatar
hjk committed
306
        m_ui.checkBoxAutoIndent);
307
    m_group.insert(theFakeVimSetting(ConfigSmartIndent),
308
        m_ui.checkBoxSmartIndent);
Lukas Holecek's avatar
Lukas Holecek committed
309

310
    m_group.insert(theFakeVimSetting(ConfigIncSearch),
311
        m_ui.checkBoxIncSearch);
312 313
    m_group.insert(theFakeVimSetting(ConfigUseCoreSearch),
        m_ui.checkBoxUseCoreSearch);
314 315
    m_group.insert(theFakeVimSetting(ConfigSmartCase),
        m_ui.checkBoxSmartCase);
316 317
    m_group.insert(theFakeVimSetting(ConfigIgnoreCase),
        m_ui.checkBoxIgnoreCase);
Lukas Holecek's avatar
Lukas Holecek committed
318 319
    m_group.insert(theFakeVimSetting(ConfigWrapScan),
        m_ui.checkBoxWrapScan);
hjk's avatar
hjk committed
320

321 322 323
    m_group.insert(theFakeVimSetting(ConfigShowCmd),
        m_ui.checkBoxShowCmd);

hjk's avatar
hjk committed
324
    connect(m_ui.pushButtonCopyTextEditorSettings, SIGNAL(clicked()),
325
        SLOT(copyTextEditorSettings()));
hjk's avatar
hjk committed
326
    connect(m_ui.pushButtonSetQtStyle, SIGNAL(clicked()),
327
        SLOT(setQtStyle()));
hjk's avatar
hjk committed
328
    connect(m_ui.pushButtonSetPlainStyle, SIGNAL(clicked()),
329
        SLOT(setPlainStyle()));
330 331 332 333 334
    connect(m_ui.pushButtonVimRcPath, SIGNAL(clicked()),
        SLOT(openVimRc()));
    connect(m_ui.checkBoxReadVimRc, SIGNAL(stateChanged(int)),
        SLOT(updateVimRcWidgets()));
    updateVimRcWidgets();
335

336
    if (m_searchKeywords.isEmpty()) {
337
        QLatin1Char sep(' ');
338
        QTextStream(&m_searchKeywords)
339 340 341 342 343 344 345 346 347 348
                << 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()
349
                << sep << m_ui.checkBoxSmartCase->text()
350 351 352 353 354 355
                << 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();
356 357
        m_searchKeywords.remove(QLatin1Char('&'));
    }
hjk's avatar
hjk committed
358 359 360 361 362
    return w;
}

void FakeVimOptionPage::copyTextEditorSettings()
{
Jarek Kobus's avatar
Jarek Kobus committed
363 364 365
    TabSettings ts = TextEditorSettings::instance()->codeStyle()->tabSettings();
    TypingSettings tps = TextEditorSettings::instance()->typingSettings();
    m_ui.checkBoxExpandTab->setChecked(ts.m_tabPolicy != TabSettings::TabsOnlyTabPolicy);
366 367
    m_ui.spinBoxTabStop->setValue(ts.m_tabSize);
    m_ui.spinBoxShiftWidth->setValue(ts.m_indentSize);
368
    m_ui.checkBoxSmartTab->setChecked(
Jarek Kobus's avatar
Jarek Kobus committed
369
        tps.m_smartBackspaceBehavior == TypingSettings::BackspaceFollowsPreviousIndents);
370
    m_ui.checkBoxAutoIndent->setChecked(true);
Jarek Kobus's avatar
Jarek Kobus committed
371
    m_ui.checkBoxSmartIndent->setChecked(tps.m_autoIndent);
372
    m_ui.checkBoxIncSearch->setChecked(true);
hjk's avatar
hjk committed
373 374 375 376 377
}

void FakeVimOptionPage::setQtStyle()
{
    m_ui.checkBoxExpandTab->setChecked(true);
378 379
    m_ui.spinBoxTabStop->setValue(4);
    m_ui.spinBoxShiftWidth->setValue(4);
hjk's avatar
hjk committed
380 381
    m_ui.checkBoxSmartTab->setChecked(true);
    m_ui.checkBoxAutoIndent->setChecked(true);
382
    m_ui.checkBoxSmartIndent->setChecked(true);
383
    m_ui.checkBoxIncSearch->setChecked(true);
hjk's avatar
hjk committed
384
    m_ui.lineEditBackspace->setText(_("indent,eol,start"));
hjk's avatar
hjk committed
385 386 387 388 389
}

void FakeVimOptionPage::setPlainStyle()
{
    m_ui.checkBoxExpandTab->setChecked(false);
390 391
    m_ui.spinBoxTabStop->setValue(8);
    m_ui.spinBoxShiftWidth->setValue(8);
hjk's avatar
hjk committed
392 393
    m_ui.checkBoxSmartTab->setChecked(false);
    m_ui.checkBoxAutoIndent->setChecked(false);
394
    m_ui.checkBoxSmartIndent->setChecked(false);
395
    m_ui.checkBoxIncSearch->setChecked(false);
hjk's avatar
hjk committed
396
    m_ui.lineEditBackspace->setText(QString());
hjk's avatar
hjk committed
397 398
}

399 400 401 402 403 404 405 406 407 408 409 410 411 412 413
void FakeVimOptionPage::openVimRc()
{
    const QString fileName = QFileDialog::getOpenFileName();
    if (!fileName.isNull())
        m_ui.lineEditVimRcPath->setText(fileName);
}

void FakeVimOptionPage::updateVimRcWidgets()
{
    bool enabled = m_ui.checkBoxReadVimRc->isChecked();
    m_ui.labelVimRcPath->setEnabled(enabled);
    m_ui.lineEditVimRcPath->setEnabled(enabled);
    m_ui.pushButtonVimRcPath->setEnabled(enabled);
}

414 415 416 417 418
bool FakeVimOptionPage::matches(const QString &s) const
{
    return m_searchKeywords.contains(s, Qt::CaseInsensitive);
}

419
//const char *FAKEVIM_CONTEXT = "FakeVim";
hjk's avatar
hjk committed
420

421 422 423 424 425 426
///////////////////////////////////////////////////////////////////////
//
// FakeVimExCommandsPage
//
///////////////////////////////////////////////////////////////////////

427
enum { CommandRole = Qt::UserRole };
428

hjk's avatar
hjk committed
429
class FakeVimExCommandsPage : public CommandMappings
430 431 432 433
{
    Q_OBJECT

public:
434 435 436
    FakeVimExCommandsPage(FakeVimPluginPrivate *q)
        : m_q(q)
    {
hjk's avatar
hjk committed
437
        setId(SETTINGS_EX_CMDS_ID);
438
        setDisplayName(tr("Ex Command Mapping"));
hjk's avatar
hjk committed
439
        setCategory(SETTINGS_CATEGORY);
440
        setDisplayCategory(tr("FakeVim"));
hjk's avatar
hjk committed
441
        setCategoryIcon(_(SETTINGS_CATEGORY_FAKEVIM_ICON));
442
    }
443 444 445

    QWidget *createPage(QWidget *parent);
    void initialize();
446 447
    ExCommandMap &exCommandMap();
    ExCommandMap &defaultExCommandMap();
448 449 450

public slots:
    void commandChanged(QTreeWidgetItem *current);
451 452 453
    void targetIdentifierChanged();
    void resetTargetIdentifier();
    void removeTargetIdentifier();
454 455 456
    void defaultAction();

private:
457
    FakeVimPluginPrivate *m_q;
458 459 460 461
};

QWidget *FakeVimExCommandsPage::createPage(QWidget *parent)
{
462 463 464
    QWidget *w = CommandMappings::createPage(parent);
    setPageTitle(tr("Ex Command Mapping"));
    setTargetHeader(tr("Ex Trigger Expression"));
465
    setTargetLabelText(tr("Regular expression:"));
466 467
    setTargetEditTitle(tr("Ex Command"));
    setImportExportEnabled(false);
468 469 470 471 472
    return w;
}

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

hjk's avatar
hjk committed
475
    foreach (Command *c, ActionManager::commands()) {
476 477 478
        if (c->action() && c->action()->isSeparator())
            continue;

479
        QTreeWidgetItem *item = new QTreeWidgetItem;
480
        const QString name = c->id().toString();
481 482
        const int pos = name.indexOf(QLatin1Char('.'));
        const QString section = name.left(pos);
483
        const QString subId = name.mid(pos + 1);
484
        item->setData(0, CommandRole, name);
485 486

        if (!sections.contains(section)) {
487 488
            QTreeWidgetItem *categoryItem =
                new QTreeWidgetItem(commandList(), QStringList() << section);
489 490 491 492
            QFont f = categoryItem->font(0);
            f.setBold(true);
            categoryItem->setFont(0, f);
            sections.insert(section, categoryItem);
493
            commandList()->expandItem(categoryItem);
494 495 496 497
        }
        sections[section]->addChild(item);

        item->setText(0, subId);
498
        item->setText(1, c->description());
499

500 501 502 503
        QString regex;
        if (exCommandMap().contains(name))
            regex = exCommandMap()[name].pattern();
        item->setText(2, regex);
504

505
        if (regex != defaultExCommandMap()[name].pattern())
506
            setModified(item, true);
507 508 509 510 511 512 513
    }

    commandChanged(0);
}

void FakeVimExCommandsPage::commandChanged(QTreeWidgetItem *current)
{
514
    CommandMappings::commandChanged(current);
515 516
    if (current)
        targetEdit()->setText(current->text(2));
517 518
}

519
void FakeVimExCommandsPage::targetIdentifierChanged()
520
{
521
    QTreeWidgetItem *current = commandList()->currentItem();
522 523 524
    if (!current)
        return;

525
    const QString name =  current->data(0, CommandRole).toString();
526
    const QString regex = targetEdit()->text();
527 528

    if (current->data(0, Qt::UserRole).isValid()) {
529 530
        current->setText(2, regex);
        exCommandMap()[name] = QRegExp(regex);
531
    }
532

533
    setModified(current, regex != defaultExCommandMap()[name].pattern());
534 535
}

536
void FakeVimExCommandsPage::resetTargetIdentifier()
537
{
538
    QTreeWidgetItem *current = commandList()->currentItem();
539 540
    if (!current)
        return;
541
    const QString name = current->data(0, CommandRole).toString();
542 543 544 545
    QString regex;
    if (defaultExCommandMap().contains(name))
        regex = defaultExCommandMap()[name].pattern();
    targetEdit()->setText(regex);
546 547
}

548
void FakeVimExCommandsPage::removeTargetIdentifier()
549
{
550
    targetEdit()->clear();
551 552 553 554
}

void FakeVimExCommandsPage::defaultAction()
{
hjk's avatar
hjk committed
555 556 557 558 559 560
    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);
561
            const QString name = item->data(0, CommandRole).toString();
hjk's avatar
hjk committed
562 563 564 565 566 567 568 569
            QString regex;
            if (defaultExCommandMap().contains(name))
                regex = defaultExCommandMap()[name].pattern();
            setModified(item, false);
            item->setText(2, regex);
            if (item == commandList()->currentItem())
                commandChanged(item);
        }
570 571 572
    }
}

573 574 575 576 577 578 579 580
///////////////////////////////////////////////////////////////////////
//
// FakeVimUserCommandsPage
//
///////////////////////////////////////////////////////////////////////

class FakeVimUserCommandsModel : public QAbstractTableModel
{
Friedemann Kleint's avatar
Friedemann Kleint committed
581
    Q_OBJECT
582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 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
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
651
class FakeVimUserCommandsPage : public IOptionsPage
652 653 654 655
{
    Q_OBJECT

public:
656 657 658
    FakeVimUserCommandsPage(FakeVimPluginPrivate *q)
        : m_q(q)
    {
hjk's avatar
hjk committed
659
        setId(SETTINGS_USER_CMDS_ID);
660
        setDisplayName(tr("User Command Mapping"));
hjk's avatar
hjk committed
661
        setCategory(SETTINGS_CATEGORY);
662
        setDisplayCategory(tr("FakeVim"));
hjk's avatar
hjk committed
663
        setCategoryIcon(_(SETTINGS_CATEGORY_FAKEVIM_ICON));
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
    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
702 703 704 705 706 707 708

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

hjk's avatar
hjk committed
709
class FakeVimCompletionAssistProvider : public CompletionAssistProvider
hjk's avatar
hjk committed
710 711
{
public:
hjk's avatar
hjk committed
712
    bool supportsEditor(const Id &) const
hjk's avatar
hjk committed
713
    {
Leandro Melo's avatar
Leandro Melo committed
714
        return false;
hjk's avatar
hjk committed
715 716
    }

hjk's avatar
hjk committed
717
    IAssistProcessor *createProcessor() const;
Leandro Melo's avatar
Leandro Melo committed
718 719

    void setActive(const QString &needle, bool forward, FakeVimHandler *handler)
hjk's avatar
hjk committed
720
    {
Leandro Melo's avatar
Leandro Melo committed
721 722 723 724 725 726 727 728 729 730 731 732
        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
733 734
    }

Leandro Melo's avatar
Leandro Melo committed
735
    void setInactive()
hjk's avatar
hjk committed
736
    {
Leandro Melo's avatar
Leandro Melo committed
737 738
        m_needle.clear();
        m_handler = 0;
hjk's avatar
hjk committed
739 740
    }

Leandro Melo's avatar
Leandro Melo committed
741
    const QString &needle() const
742
    {
Leandro Melo's avatar
Leandro Melo committed
743
        return m_needle;
744
    }
hjk's avatar
hjk committed
745

Leandro Melo's avatar
Leandro Melo committed
746
    void appendNeedle(const QChar &c)
747
    {
Leandro Melo's avatar
Leandro Melo committed
748
        m_needle.append(c);
749 750
    }

Leandro Melo's avatar
Leandro Melo committed
751
    FakeVimHandler *handler() const
752
    {
Leandro Melo's avatar
Leandro Melo committed
753
        return m_handler;
754
    }
hjk's avatar
hjk committed
755

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

Leandro Melo's avatar
Leandro Melo committed
773
    virtual bool prematurelyApplies(const QChar &c) const
hjk's avatar
hjk committed
774
    {
Leandro Melo's avatar
Leandro Melo committed
775 776
        m_provider->appendNeedle(c);
        return text() == m_provider->needle();
hjk's avatar
hjk committed
777 778
    }

Leandro Melo's avatar
Leandro Melo committed
779
    virtual void applyContextualContent(BaseTextEditor *, int) const
hjk's avatar
hjk committed
780
    {
Leandro Melo's avatar
Leandro Melo committed
781 782 783
        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
784 785
    }

Leandro Melo's avatar
Leandro Melo committed
786 787 788 789 790 791 792 793 794 795 796 797 798
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
799
    {
Leandro Melo's avatar
Leandro Melo committed
800
        return false;
hjk's avatar
hjk committed
801
    }
Leandro Melo's avatar
Leandro Melo committed
802
};
hjk's avatar
hjk committed
803

Leandro Melo's avatar
Leandro Melo committed
804 805 806
class FakeVimCompletionAssistProcessor : public IAssistProcessor
{
public:
hjk's avatar
hjk committed
807
    FakeVimCompletionAssistProcessor(const IAssistProvider *provider)
Leandro Melo's avatar
Leandro Melo committed
808 809 810
        : m_provider(static_cast<const FakeVimCompletionAssistProvider *>(provider))
    {}

hjk's avatar
hjk committed
811
    IAssistProposal *perform(const IAssistInterface *interface)
hjk's avatar
hjk committed
812
    {
Leandro Melo's avatar
Leandro Melo committed
813 814 815 816
        const QString &needle = m_provider->needle();

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

817
        QTextCursor tc(interface->textDocument());
Leandro Melo's avatar
Leandro Melo committed
818
        tc.setPosition(interface->position());
hjk's avatar
hjk committed
819 820
        tc.movePosition(QTextCursor::Start, QTextCursor::MoveAnchor);

Leandro Melo's avatar
Leandro Melo committed
821
        QList<BasicProposalItem *> items;
hjk's avatar
hjk committed
822 823 824
        QSet<QString> seen;
        QTextDocument::FindFlags flags = QTextDocument::FindCaseSensitively;
        while (1) {
Leandro Melo's avatar
Leandro Melo committed
825
            tc = tc.document()->find(needle, tc.position(), flags);
hjk's avatar
hjk committed
826 827 828 829 830 831
            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
832
            if (found.startsWith(needle)
hjk's avatar
hjk committed
833
                    && !seen.contains(found)
Leandro Melo's avatar
Leandro Melo committed
834
                    && sel.anchor() != basePosition) {
hjk's avatar
hjk committed
835
                seen.insert(found);
Leandro Melo's avatar
Leandro Melo committed
836 837 838
                BasicProposalItem *item = new FakeVimAssistProposalItem(m_provider);
                item->setText(found);
                items.append(item);
hjk's avatar
hjk committed
839 840 841 842 843
            }
            tc.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor);
        }
        //qDebug() << "COMPLETIONS" << completions->size();

Leandro Melo's avatar
Leandro Melo committed
844 845
        delete interface;
        return new GenericProposal(basePosition, new FakeVimAssistProposalModel(items));
hjk's avatar
hjk committed
846 847 848
    }

private:
Leandro Melo's avatar
Leandro Melo committed
849
    const FakeVimCompletionAssistProvider *m_provider;
hjk's avatar
hjk committed
850
};
851

Leandro Melo's avatar
Leandro Melo committed
852 853 854 855 856
IAssistProcessor *FakeVimCompletionAssistProvider::createProcessor() const
{
    return new FakeVimCompletionAssistProcessor(this);
}

857

hjk's avatar
hjk committed
858 859
///////////////////////////////////////////////////////////////////////
//
860
// FakeVimPluginPrivate
hjk's avatar
hjk committed
861 862 863
//
///////////////////////////////////////////////////////////////////////

864
class FakeVimPluginPrivate : public QObject
hjk's avatar
hjk committed
865
{
866 867 868 869 870 871
    Q_OBJECT

public:
    FakeVimPlu