fakevimplugin.cpp 69.3 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/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(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);
315 316
    m_group.insert(theFakeVimSetting(ConfigIgnoreCase),
        m_ui.checkBoxIgnoreCase);
Lukas Holecek's avatar
Lukas Holecek committed
317 318
    m_group.insert(theFakeVimSetting(ConfigWrapScan),
        m_ui.checkBoxWrapScan);
hjk's avatar
hjk committed
319

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

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

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

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

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

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

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

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

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

420 421 422 423 424 425
///////////////////////////////////////////////////////////////////////
//
// FakeVimExCommandsPage
//
///////////////////////////////////////////////////////////////////////

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    commandChanged(0);
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

856

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

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

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