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 "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);
Lukas Holecek's avatar
Lukas Holecek committed
316 317
    m_group.insert(theFakeVimSetting(ConfigWrapScan),
        m_ui.checkBoxWrapScan);
hjk's avatar
hjk committed
318

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

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

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

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

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

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

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

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

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

419 420 421 422 423 424
///////////////////////////////////////////////////////////////////////
//
// FakeVimExCommandsPage
//
///////////////////////////////////////////////////////////////////////

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    commandChanged(0);
}

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

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

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

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

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

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

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

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

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

class FakeVimUserCommandsModel : public QAbstractTableModel
{
Friedemann Kleint's avatar
Friedemann Kleint committed
579
    Q_OBJECT
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 648
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
649
class FakeVimUserCommandsPage : public IOptionsPage
650 651 652 653
{
    Q_OBJECT

public:
654 655 656
    FakeVimUserCommandsPage(FakeVimPluginPrivate *q)
        : m_q(q)
    {
hjk's avatar
hjk committed
657
        setId(SETTINGS_USER_CMDS_ID);
658
        setDisplayName(tr("User Command Mapping"));
hjk's avatar
hjk committed
659
        setCategory(SETTINGS_CATEGORY);
660
        setDisplayCategory(tr("FakeVim"));
hjk's avatar
hjk committed
661
        setCategoryIcon(_(SETTINGS_CATEGORY_FAKEVIM_ICON));
662 663
    }

664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699
    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
700 701 702 703 704 705 706

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

855

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

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

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