fakevimplugin.cpp 69.4 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 "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

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

80 81
#include <cpptools/cpptoolsconstants.h>

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

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

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

using namespace TextEditor;
using namespace Core;

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

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

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

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

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

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

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

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

            setCurrentWidget(m_label);
        }

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

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

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

private slots:
    void changed()
201
    {
202 203 204 205 206
        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);
207
    }
208 209

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

318 319 320
    m_group.insert(theFakeVimSetting(ConfigShowCmd),
        m_ui.checkBoxShowCmd);

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

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

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

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

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

396 397 398 399 400 401 402 403 404 405 406 407 408 409 410
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);
}

411 412 413 414 415
bool FakeVimOptionPage::matches(const QString &s) const
{
    return m_searchKeywords.contains(s, Qt::CaseInsensitive);
}

416
//const char *FAKEVIM_CONTEXT = "FakeVim";
hjk's avatar
hjk committed
417

418 419 420 421 422 423
///////////////////////////////////////////////////////////////////////
//
// FakeVimExCommandsPage
//
///////////////////////////////////////////////////////////////////////

424
enum { CommandRole = Qt::UserRole };
425

hjk's avatar
hjk committed
426
class FakeVimExCommandsPage : public CommandMappings
427 428 429 430
{
    Q_OBJECT

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

    QWidget *createPage(QWidget *parent);
    void initialize();
443 444
    ExCommandMap &exCommandMap();
    ExCommandMap &defaultExCommandMap();
445 446 447

public slots:
    void commandChanged(QTreeWidgetItem *current);
448 449 450
    void targetIdentifierChanged();
    void resetTargetIdentifier();
    void removeTargetIdentifier();
451 452 453
    void defaultAction();

private:
454
    FakeVimPluginPrivate *m_q;
455 456 457 458
};

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

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

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

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

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

        item->setText(0, subId);
495
        item->setText(1, c->description());
496

497 498 499 500
        QString regex;
        if (exCommandMap().contains(name))
            regex = exCommandMap()[name].pattern();
        item->setText(2, regex);
501

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

    commandChanged(0);
}

void FakeVimExCommandsPage::commandChanged(QTreeWidgetItem *current)
{
511
    CommandMappings::commandChanged(current);
512 513
    if (current)
        targetEdit()->setText(current->text(2));
514 515
}

516
void FakeVimExCommandsPage::targetIdentifierChanged()
517
{
518
    QTreeWidgetItem *current = commandList()->currentItem();
519 520 521
    if (!current)
        return;

522
    const QString name =  current->data(0, CommandRole).toString();
523
    const QString regex = targetEdit()->text();
524 525

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

530
    setModified(current, regex != defaultExCommandMap()[name].pattern());
531 532
}

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

545
void FakeVimExCommandsPage::removeTargetIdentifier()
546
{
547
    targetEdit()->clear();
548 549 550 551
}

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

570 571 572 573 574 575 576 577
///////////////////////////////////////////////////////////////////////
//
// FakeVimUserCommandsPage
//
///////////////////////////////////////////////////////////////////////

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

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

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

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

hjk's avatar
hjk committed
714
    IAssistProcessor *createProcessor() const;
Leandro Melo's avatar
Leandro Melo committed
715 716

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

Leandro Melo's avatar
Leandro Melo committed
732
    void setInactive()
hjk's avatar
hjk committed
733
    {
Leandro Melo's avatar
Leandro Melo committed
734 735
        m_needle.clear();
        m_handler = 0;
hjk's avatar
hjk committed
736 737
    }

Leandro Melo's avatar
Leandro Melo committed
738
    const QString &needle() const
739
    {
Leandro Melo's avatar
Leandro Melo committed
740
        return m_needle;
741
    }
hjk's avatar
hjk committed
742

Leandro Melo's avatar
Leandro Melo committed
743
    void appendNeedle(const QChar &c)
744
    {
Leandro Melo's avatar
Leandro Melo committed
745
        m_needle.append(c);
746 747
    }

Leandro Melo's avatar
Leandro Melo committed
748
    FakeVimHandler *handler() const
749
    {
Leandro Melo's avatar
Leandro Melo committed
750
        return m_handler;
751
    }
hjk's avatar
hjk committed
752

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

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

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

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

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

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

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

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

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

Leandro Melo's avatar
Leandro Melo committed
841 842
        delete interface;
        return new GenericProposal(basePosition, new FakeVimAssistProposalModel(items));
hjk's avatar
hjk committed
843 844 845
    }

private:
Leandro Melo's avatar
Leandro Melo committed
846
    const FakeVimCompletionAssistProvider *m_provider;
hjk's avatar
hjk committed
847
};
848

Leandro Melo's avatar
Leandro Melo committed
849 850 851 852 853
IAssistProcessor *FakeVimCompletionAssistProvider::createProcessor() const
{
    return new FakeVimCompletionAssistProcessor(this);
}

854

hjk's avatar
hjk committed
855 856
///////////////////////////////////////////////////////////////////////
//
857
// FakeVimPluginPrivate
hjk's avatar
hjk committed
858 859 860
//
///////////////////////////////////////////////////////////////////////

861
class FakeVimPluginPrivate : public QObject
hjk's avatar
hjk committed
862
{
863 864 865 866 867 868
    Q_OBJECT

public:
    FakeVimPluginPrivate(FakeVimPlugin *);
    ~FakeVimPluginPrivate();
    friend class FakeVimPlugin;
869
    friend class FakeVimExCommandsPage;
870