fakevimplugin.cpp 70.8 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/documentmodel.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
};

QWidget *FakeVimOptionPage::createPage(QWidget *parent)
{
    QWidget *w = new QWidget(parent);
    m_ui.setupUi(w);
Friedemann Kleint's avatar
Friedemann Kleint committed
273 274 275
    const QString vimrcDefault = Utils::HostOsInfo::isAnyUnixHost() ?
        QLatin1String("$HOME/.vimrc") : QLatin1String("%USERPROFILE%\\_vimrc");
    m_ui.lineEditVimRcPath->setPlaceholderText(tr("Default: %1").arg(vimrcDefault));
hjk's avatar
hjk committed
276 277

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

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

294
    m_group.insert(theFakeVimSetting(ConfigSmartTab),
hjk's avatar
hjk committed
295
        m_ui.checkBoxSmartTab);
296
    m_group.insert(theFakeVimSetting(ConfigStartOfLine),
hjk's avatar
hjk committed
297
        m_ui.checkBoxStartOfLine);
298 299
    m_group.insert(theFakeVimSetting(ConfigPassKeys),
        m_ui.checkBoxPassKeys);
300
    m_group.insert(theFakeVimSetting(ConfigTabStop),
301
        m_ui.spinBoxTabStop);
302 303
    m_group.insert(theFakeVimSetting(ConfigScrollOff),
        m_ui.spinBoxScrollOff);
304
    m_group.insert(theFakeVimSetting(ConfigBackspace),
hjk's avatar
hjk committed
305
        m_ui.lineEditBackspace);
306 307
    m_group.insert(theFakeVimSetting(ConfigIsKeyword),
        m_ui.lineEditIsKeyword);
hjk's avatar
hjk committed
308

309 310
    m_group.insert(theFakeVimSetting(ConfigPassControlKey),
        m_ui.checkBoxPassControlKey);
311
    m_group.insert(theFakeVimSetting(ConfigAutoIndent),
hjk's avatar
hjk committed
312
        m_ui.checkBoxAutoIndent);
313
    m_group.insert(theFakeVimSetting(ConfigSmartIndent),
314
        m_ui.checkBoxSmartIndent);
Lukas Holecek's avatar
Lukas Holecek committed
315

316
    m_group.insert(theFakeVimSetting(ConfigIncSearch),
317
        m_ui.checkBoxIncSearch);
318 319
    m_group.insert(theFakeVimSetting(ConfigUseCoreSearch),
        m_ui.checkBoxUseCoreSearch);
320 321
    m_group.insert(theFakeVimSetting(ConfigSmartCase),
        m_ui.checkBoxSmartCase);
322 323
    m_group.insert(theFakeVimSetting(ConfigIgnoreCase),
        m_ui.checkBoxIgnoreCase);
Lukas Holecek's avatar
Lukas Holecek committed
324 325
    m_group.insert(theFakeVimSetting(ConfigWrapScan),
        m_ui.checkBoxWrapScan);
hjk's avatar
hjk committed
326

327 328 329
    m_group.insert(theFakeVimSetting(ConfigShowCmd),
        m_ui.checkBoxShowCmd);

hjk's avatar
hjk committed
330
    connect(m_ui.pushButtonCopyTextEditorSettings, SIGNAL(clicked()),
331
        SLOT(copyTextEditorSettings()));
hjk's avatar
hjk committed
332
    connect(m_ui.pushButtonSetQtStyle, SIGNAL(clicked()),
333
        SLOT(setQtStyle()));
hjk's avatar
hjk committed
334
    connect(m_ui.pushButtonSetPlainStyle, SIGNAL(clicked()),
335
        SLOT(setPlainStyle()));
336 337 338 339 340
    connect(m_ui.pushButtonVimRcPath, SIGNAL(clicked()),
        SLOT(openVimRc()));
    connect(m_ui.checkBoxReadVimRc, SIGNAL(stateChanged(int)),
        SLOT(updateVimRcWidgets()));
    updateVimRcWidgets();
341

342
    if (m_searchKeywords.isEmpty()) {
343
        QLatin1Char sep(' ');
344
        QTextStream(&m_searchKeywords)
345 346 347 348 349 350 351 352 353 354
                << 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()
355
                << sep << m_ui.checkBoxSmartCase->text()
356 357
                << sep << m_ui.checkBoxShowMarks->text()
                << sep << m_ui.checkBoxPassControlKey->text()
358
                << sep << m_ui.checkBoxPassKeys->text()
359 360 361
                << sep << m_ui.checkBoxIgnoreCase->text()
                << sep << m_ui.checkBoxWrapScan->text()
                << sep << m_ui.checkBoxShowCmd->text()
362 363 364
                << sep << m_ui.labelShiftWidth->text()
                << sep << m_ui.labelTabulator->text()
                << sep << m_ui.labelBackspace->text()
365 366 367
                << sep << m_ui.labelIsKeyword->text()
                << sep << m_ui.labelScrollOff->text()
                << sep << m_ui.lineEditVimRcPath->text();
368 369
        m_searchKeywords.remove(QLatin1Char('&'));
    }
hjk's avatar
hjk committed
370 371 372 373 374
    return w;
}

void FakeVimOptionPage::copyTextEditorSettings()
{
Jarek Kobus's avatar
Jarek Kobus committed
375 376 377
    TabSettings ts = TextEditorSettings::instance()->codeStyle()->tabSettings();
    TypingSettings tps = TextEditorSettings::instance()->typingSettings();
    m_ui.checkBoxExpandTab->setChecked(ts.m_tabPolicy != TabSettings::TabsOnlyTabPolicy);
378 379
    m_ui.spinBoxTabStop->setValue(ts.m_tabSize);
    m_ui.spinBoxShiftWidth->setValue(ts.m_indentSize);
380
    m_ui.checkBoxSmartTab->setChecked(
Jarek Kobus's avatar
Jarek Kobus committed
381
        tps.m_smartBackspaceBehavior == TypingSettings::BackspaceFollowsPreviousIndents);
382
    m_ui.checkBoxAutoIndent->setChecked(true);
Jarek Kobus's avatar
Jarek Kobus committed
383
    m_ui.checkBoxSmartIndent->setChecked(tps.m_autoIndent);
384
    m_ui.checkBoxIncSearch->setChecked(true);
hjk's avatar
hjk committed
385 386 387 388 389
}

void FakeVimOptionPage::setQtStyle()
{
    m_ui.checkBoxExpandTab->setChecked(true);
390 391
    m_ui.spinBoxTabStop->setValue(4);
    m_ui.spinBoxShiftWidth->setValue(4);
hjk's avatar
hjk committed
392 393
    m_ui.checkBoxSmartTab->setChecked(true);
    m_ui.checkBoxAutoIndent->setChecked(true);
394
    m_ui.checkBoxSmartIndent->setChecked(true);
395
    m_ui.checkBoxIncSearch->setChecked(true);
hjk's avatar
hjk committed
396
    m_ui.lineEditBackspace->setText(_("indent,eol,start"));
397
    m_ui.checkBoxPassKeys->setChecked(true);
hjk's avatar
hjk committed
398 399 400 401 402
}

void FakeVimOptionPage::setPlainStyle()
{
    m_ui.checkBoxExpandTab->setChecked(false);
403 404
    m_ui.spinBoxTabStop->setValue(8);
    m_ui.spinBoxShiftWidth->setValue(8);
hjk's avatar
hjk committed
405 406
    m_ui.checkBoxSmartTab->setChecked(false);
    m_ui.checkBoxAutoIndent->setChecked(false);
407
    m_ui.checkBoxSmartIndent->setChecked(false);
408
    m_ui.checkBoxIncSearch->setChecked(false);
hjk's avatar
hjk committed
409
    m_ui.lineEditBackspace->setText(QString());
410
    m_ui.checkBoxPassKeys->setChecked(false);
hjk's avatar
hjk committed
411 412
}

413 414 415 416 417 418 419 420 421 422 423 424 425 426
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.lineEditVimRcPath->setEnabled(enabled);
    m_ui.pushButtonVimRcPath->setEnabled(enabled);
}

427 428 429 430 431
bool FakeVimOptionPage::matches(const QString &s) const
{
    return m_searchKeywords.contains(s, Qt::CaseInsensitive);
}

432
//const char *FAKEVIM_CONTEXT = "FakeVim";
hjk's avatar
hjk committed
433

434 435 436 437 438 439
///////////////////////////////////////////////////////////////////////
//
// FakeVimExCommandsPage
//
///////////////////////////////////////////////////////////////////////

440
enum { CommandRole = Qt::UserRole };
441

hjk's avatar
hjk committed
442
class FakeVimExCommandsPage : public CommandMappings
443 444 445 446
{
    Q_OBJECT

public:
447 448 449
    FakeVimExCommandsPage(FakeVimPluginPrivate *q)
        : m_q(q)
    {
hjk's avatar
hjk committed
450
        setId(SETTINGS_EX_CMDS_ID);
451
        setDisplayName(tr("Ex Command Mapping"));
hjk's avatar
hjk committed
452
        setCategory(SETTINGS_CATEGORY);
453
        setDisplayCategory(tr("FakeVim"));
hjk's avatar
hjk committed
454
        setCategoryIcon(_(SETTINGS_CATEGORY_FAKEVIM_ICON));
455
    }
456 457 458

    QWidget *createPage(QWidget *parent);
    void initialize();
459 460
    ExCommandMap &exCommandMap();
    ExCommandMap &defaultExCommandMap();
461 462 463

public slots:
    void commandChanged(QTreeWidgetItem *current);
464 465 466
    void targetIdentifierChanged();
    void resetTargetIdentifier();
    void removeTargetIdentifier();
467 468 469
    void defaultAction();

private:
470
    FakeVimPluginPrivate *m_q;
471 472 473 474
};

QWidget *FakeVimExCommandsPage::createPage(QWidget *parent)
{
475 476 477
    QWidget *w = CommandMappings::createPage(parent);
    setPageTitle(tr("Ex Command Mapping"));
    setTargetHeader(tr("Ex Trigger Expression"));
478
    setTargetLabelText(tr("Regular expression:"));
479 480
    setTargetEditTitle(tr("Ex Command"));
    setImportExportEnabled(false);
481 482 483 484 485
    return w;
}

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

hjk's avatar
hjk committed
488
    foreach (Command *c, ActionManager::commands()) {
489 490 491
        if (c->action() && c->action()->isSeparator())
            continue;

492
        QTreeWidgetItem *item = new QTreeWidgetItem;
493
        const QString name = c->id().toString();
494 495
        const int pos = name.indexOf(QLatin1Char('.'));
        const QString section = name.left(pos);
496
        const QString subId = name.mid(pos + 1);
497
        item->setData(0, CommandRole, name);
498 499

        if (!sections.contains(section)) {
500 501
            QTreeWidgetItem *categoryItem =
                new QTreeWidgetItem(commandList(), QStringList() << section);
502 503 504 505
            QFont f = categoryItem->font(0);
            f.setBold(true);
            categoryItem->setFont(0, f);
            sections.insert(section, categoryItem);
506
            commandList()->expandItem(categoryItem);
507 508 509 510
        }
        sections[section]->addChild(item);

        item->setText(0, subId);
511
        item->setText(1, c->description());
512

513 514 515 516
        QString regex;
        if (exCommandMap().contains(name))
            regex = exCommandMap()[name].pattern();
        item->setText(2, regex);
517

518
        if (regex != defaultExCommandMap()[name].pattern())
519
            setModified(item, true);
520 521 522 523 524 525 526
    }

    commandChanged(0);
}

void FakeVimExCommandsPage::commandChanged(QTreeWidgetItem *current)
{
527
    CommandMappings::commandChanged(current);
528 529
    if (current)
        targetEdit()->setText(current->text(2));
530 531
}

532
void FakeVimExCommandsPage::targetIdentifierChanged()
533
{
534
    QTreeWidgetItem *current = commandList()->currentItem();
535 536 537
    if (!current)
        return;

538
    const QString name =  current->data(0, CommandRole).toString();
539
    const QString regex = targetEdit()->text();
540 541

    if (current->data(0, Qt::UserRole).isValid()) {
542 543
        current->setText(2, regex);
        exCommandMap()[name] = QRegExp(regex);
544
    }
545

546
    setModified(current, regex != defaultExCommandMap()[name].pattern());
547 548
}

549
void FakeVimExCommandsPage::resetTargetIdentifier()
550
{
551
    QTreeWidgetItem *current = commandList()->currentItem();
552 553
    if (!current)
        return;
554
    const QString name = current->data(0, CommandRole).toString();
555 556 557 558
    QString regex;
    if (defaultExCommandMap().contains(name))
        regex = defaultExCommandMap()[name].pattern();
    targetEdit()->setText(regex);
559 560
}

561
void FakeVimExCommandsPage::removeTargetIdentifier()
562
{
563
    targetEdit()->clear();
564 565 566 567
}

void FakeVimExCommandsPage::defaultAction()
{
hjk's avatar
hjk committed
568 569 570 571 572 573
    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);
574
            const QString name = item->data(0, CommandRole).toString();
hjk's avatar
hjk committed
575 576 577 578 579 580 581 582
            QString regex;
            if (defaultExCommandMap().contains(name))
                regex = defaultExCommandMap()[name].pattern();
            setModified(item, false);
            item->setText(2, regex);
            if (item == commandList()->currentItem())
                commandChanged(item);
        }
583 584 585
    }
}

586 587 588 589 590 591 592 593
///////////////////////////////////////////////////////////////////////
//
// FakeVimUserCommandsPage
//
///////////////////////////////////////////////////////////////////////

class FakeVimUserCommandsModel : public QAbstractTableModel
{
Friedemann Kleint's avatar
Friedemann Kleint committed
594
    Q_OBJECT
595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663
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
664
class FakeVimUserCommandsPage : public IOptionsPage
665 666 667 668
{
    Q_OBJECT

public:
669 670 671
    FakeVimUserCommandsPage(FakeVimPluginPrivate *q)
        : m_q(q)
    {
hjk's avatar
hjk committed
672
        setId(SETTINGS_USER_CMDS_ID);
673
        setDisplayName(tr("User Command Mapping"));
hjk's avatar
hjk committed
674
        setCategory(SETTINGS_CATEGORY);
675
        setDisplayCategory(tr("FakeVim"));
hjk's avatar
hjk committed
676
        setCategoryIcon(_(SETTINGS_CATEGORY_FAKEVIM_ICON));
677 678
    }

679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714
    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
715 716 717 718 719 720 721

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

hjk's avatar
hjk committed
722
class FakeVimCompletionAssistProvider : public CompletionAssistProvider
hjk's avatar
hjk committed
723 724
{
public:
hjk's avatar
hjk committed
725
    bool supportsEditor(const Id &) const
hjk's avatar
hjk committed
726
    {
Leandro Melo's avatar
Leandro Melo committed
727
        return false;
hjk's avatar
hjk committed
728 729
    }

hjk's avatar
hjk committed
730
    IAssistProcessor *createProcessor() const;
Leandro Melo's avatar
Leandro Melo committed
731 732

    void setActive(const QString &needle, bool forward, FakeVimHandler *handler)
hjk's avatar
hjk committed
733
    {
Leandro Melo's avatar
Leandro Melo committed
734 735 736 737 738 739 740 741 742 743 744 745
        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
746 747
    }

Leandro Melo's avatar
Leandro Melo committed
748
    void setInactive()
hjk's avatar
hjk committed
749
    {
Leandro Melo's avatar
Leandro Melo committed
750 751
        m_needle.clear();
        m_handler = 0;
hjk's avatar
hjk committed
752 753
    }

Leandro Melo's avatar
Leandro Melo committed
754
    const QString &needle() const
755
    {
Leandro Melo's avatar
Leandro Melo committed
756
        return m_needle;
757
    }
hjk's avatar
hjk committed
758

Leandro Melo's avatar
Leandro Melo committed
759
    void appendNeedle(const QChar &c)
760
    {
Leandro Melo's avatar
Leandro Melo committed
761
        m_needle.append(c);
762 763
    }

Leandro Melo's avatar
Leandro Melo committed
764
    FakeVimHandler *handler() const
765
    {
Leandro Melo's avatar
Leandro Melo committed
766
        return m_handler;
767
    }
hjk's avatar
hjk committed
768

Leandro Melo's avatar
Leandro Melo committed
769 770 771 772 773 774 775 776 777 778 779 780 781
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
782
    {
Leandro Melo's avatar
Leandro Melo committed
783
        return false;
hjk's avatar
hjk committed
784 785
    }

Leandro Melo's avatar
Leandro Melo committed
786
    virtual bool prematurelyApplies(const QChar &c) const
hjk's avatar
hjk committed
787
    {
Leandro Melo's avatar
Leandro Melo committed
788 789
        m_provider->appendNeedle(c);
        return text() == m_provider->needle();
hjk's avatar
hjk committed
790 791
    }

Leandro Melo's avatar
Leandro Melo committed
792
    virtual void applyContextualContent(BaseTextEditor *, int) const
hjk's avatar
hjk committed
793
    {
Leandro Melo's avatar
Leandro Melo committed
794 795 796
        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
797 798
    }

Leandro Melo's avatar
Leandro Melo committed
799 800 801 802 803 804 805 806 807 808 809 810 811
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
812
    {
Leandro Melo's avatar
Leandro Melo committed
813
        return false;
hjk's avatar
hjk committed
814
    }
Leandro Melo's avatar
Leandro Melo committed
815
};
hjk's avatar
hjk committed
816

Leandro Melo's avatar
Leandro Melo committed
817 818 819
class FakeVimCompletionAssistProcessor : public IAssistProcessor
{
public:
hjk's avatar
hjk committed
820
    FakeVimCompletionAssistProcessor(const IAssistProvider *provider)
Leandro Melo's avatar
Leandro Melo committed
821 822 823
        : m_provider(static_cast<const FakeVimCompletionAssistProvider *>(provider))
    {}

hjk's avatar
hjk committed
824
    IAssistProposal *perform(const IAssistInterface *interface)
hjk's avatar
hjk committed
825
    {
Leandro Melo's avatar
Leandro Melo committed
826 827 828 829
        const QString &needle = m_provider->needle();

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

830
        QTextCursor tc(interface->textDocument());
Leandro Melo's avatar
Leandro Melo committed
831
        tc.setPosition(interface->position());
hjk's avatar
hjk committed
832 833
        tc.movePosition(QTextCursor::Start, QTextCursor::MoveAnchor);

Leandro Melo's avatar
Leandro Melo committed
834
        QList<BasicProposalItem *> items;
hjk's avatar
hjk committed
835 836 837
        QSet<QString> seen;
        QTextDocument::FindFlags flags = QTextDocument::FindCaseSensitively;
        while (1) {
Leandro Melo's avatar
Leandro Melo committed
838
            tc = tc.document()->find(needle, tc.position(), flags);
hjk's avatar
hjk committed
839 840 841 842 843 844
            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
845
            if (found.startsWith(needle)
hjk's avatar
hjk committed
846
                    && !seen.contains(found)
Leandro Melo's avatar
Leandro Melo committed
847
                    && sel.anchor() != basePosition) {
hjk's avatar
hjk committed
848
                seen.insert(found);
Leandro Melo's avatar
Leandro Melo committed
849 850 851
                BasicProposalItem *item = new FakeVimAssistProposalItem(m_provider);
                item->setText(found);
                items.append(item);
hjk's avatar
hjk committed
852 853 854 855 856
            }
            tc.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor);
        }
        //qDebug() << "COMPLETIONS" << completions->size();

Leandro Melo's avatar
Leandro Melo committed
857 858
        delete interface;
        return new GenericProposal(basePosition, new FakeVimAssistProposalModel(items));
hjk's avatar
hjk committed
859 860 861
    }

private:
Leandro Melo's avatar
Leandro Melo committed
862
    const FakeVimCompletionAssistProvider *m_provider;
hjk's avatar
hjk committed
863
};
864

Leandro Melo's avatar
Leandro Melo committed
865 866 867 868 869
IAssistProcessor *FakeVimCompletionAssistProvider::createProcessor() const
{
    return new FakeVimCompletionAssistProcessor(this);
}

870

hjk's avatar
hjk committed
871 872
///////////////////////////////////////////////////////////////////////
//
873
// FakeVimPluginPrivate
hjk's avatar
hjk committed
874 875 876
//
///////////////////////////////////////////////////////////////////////

877
class FakeVimPluginPrivate : public QObject
hjk's avatar
hjk committed
878
{
879 880 881 882 883 884
    Q_OBJECT

public:
    FakeVimPluginPrivate(FakeVimPlugin *);
    ~FakeVimPluginPrivate();
    friend class FakeVimPlugin;
885
    friend class FakeVimExCommandsPage;
886 887
    friend class FakeVimUserCommandsPage;
    friend class FakeVimUserCommandsModel;
888

889
    bool initialize();