fakevimplugin.cpp 37.3 KB
Newer Older
1
/**************************************************************************
hjk's avatar
hjk committed
2 3 4
**
** This file is part of Qt Creator
**
hjk's avatar
hjk committed
5
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
hjk's avatar
hjk committed
6
**
7
** Contact: Nokia Corporation (qt-info@nokia.com)
hjk's avatar
hjk committed
8
**
9
** Commercial Usage
hjk's avatar
hjk committed
10
**
11 12 13 14
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
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
** 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.
hjk's avatar
hjk committed
24
**
25
** If you are unsure which license is appropriate for your use, please
hjk's avatar
hjk committed
26
** contact the sales department at http://qt.nokia.com/contact.
hjk's avatar
hjk committed
27
**
28
**************************************************************************/
hjk's avatar
hjk committed
29 30 31

#include "fakevimplugin.h"

32
#include "fakevimhandler.h"
hjk's avatar
hjk committed
33 34
#include "ui_fakevimoptions.h"

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

#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/session.h>

#include <texteditor/basetexteditor.h>
#include <texteditor/basetextmark.h>
hjk's avatar
hjk committed
55
#include <texteditor/completionsupport.h>
hjk's avatar
hjk committed
56 57
#include <texteditor/itexteditor.h>
#include <texteditor/texteditorconstants.h>
58 59
#include <texteditor/tabsettings.h>
#include <texteditor/texteditorsettings.h>
60
#include <texteditor/textblockiterator.h>
hjk's avatar
hjk committed
61

62
#include <find/findplugin.h>
63 64
#include <find/textfindconstants.h>

hjk's avatar
hjk committed
65
#include <utils/qtcassert.h>
hjk's avatar
hjk committed
66
#include <utils/savedaction.h>
67
#include <utils/treewidgetcolumnstretcher.h>
hjk's avatar
hjk committed
68

69 70
#include <cppeditor/cppeditorconstants.h>

71 72
#include <cpptools/cpptoolsconstants.h>

73 74
#include <indenter.h>

hjk's avatar
hjk committed
75
#include <QtCore/QDebug>
76
#include <QtCore/QtPlugin>
hjk's avatar
hjk committed
77 78 79
#include <QtCore/QObject>
#include <QtCore/QPoint>
#include <QtCore/QSettings>
80
#include <QtCore/QTextStream>
hjk's avatar
hjk committed
81

82
#include <QtGui/QMessageBox>
hjk's avatar
hjk committed
83
#include <QtGui/QPlainTextEdit>
84
#include <QtGui/QShortcut>
hjk's avatar
hjk committed
85 86
#include <QtGui/QTextBlock>
#include <QtGui/QTextCursor>
87
#include <QtGui/QTextEdit>
88
#include <QtGui/QTreeWidgetItem>
hjk's avatar
hjk committed
89 90 91 92 93 94 95 96 97 98

using namespace FakeVim::Internal;
using namespace TextEditor;
using namespace Core;
using namespace ProjectExplorer;


namespace FakeVim {
namespace Constants {

99 100
const char * const INSTALL_HANDLER        = "TextEditor.FakeVimHandler";
const char * const MINI_BUFFER            = "TextEditor.FakeVimMiniBuffer";
hjk's avatar
hjk committed
101
const char * const INSTALL_KEY            = "Alt+V,Alt+V";
102
const char * const SETTINGS_CATEGORY      = "D.FakeVim";
103 104
const char * const SETTINGS_ID            = "A.General";
const char * const SETTINGS_EX_CMDS_ID    = "B.ExCommands";
105 106
const char * const CMD_FILE_NEXT          = "FakeVim.SwitchFileNext";
const char * const CMD_FILE_PREV          = "FakeVim.SwitchFilePrev";
hjk's avatar
hjk committed
107 108 109 110 111

} // namespace Constants
} // namespace FakeVim


hjk's avatar
hjk committed
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
///////////////////////////////////////////////////////////////////////
//
// FakeVimOptionPage
//
///////////////////////////////////////////////////////////////////////

namespace FakeVim {
namespace Internal {

class FakeVimOptionPage : public Core::IOptionsPage
{
    Q_OBJECT

public:
    FakeVimOptionPage() {}

    // IOptionsPage
129
    QString id() const { return QLatin1String(Constants::SETTINGS_ID); }
130
    QString displayName() const { return tr("General"); }
131
    QString category() const { return QLatin1String(Constants::SETTINGS_CATEGORY); }
132
    QString displayCategory() const { return tr("FakeVim"); }
133
    QIcon categoryIcon() const { return QIcon(); } // TODO: Add an icon or move into another category
hjk's avatar
hjk committed
134 135 136 137

    QWidget *createPage(QWidget *parent);
    void apply() { m_group.apply(ICore::instance()->settings()); }
    void finish() { m_group.finish(); }
138
    virtual bool matches(const QString &) const;
hjk's avatar
hjk committed
139 140 141 142 143 144 145 146 147

private slots:
    void copyTextEditorSettings();
    void setQtStyle();
    void setPlainStyle();

private:
    friend class DebuggerPlugin;
    Ui::FakeVimOptionPage m_ui;
148
    QString m_searchKeywords;
149
    Utils::SavedActionSet m_group;
hjk's avatar
hjk committed
150 151 152 153 154 155 156 157
};

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

    m_group.clear();
158
    m_group.insert(theFakeVimSetting(ConfigUseFakeVim),
159
        m_ui.checkBoxUseFakeVim);
hjk's avatar
hjk committed
160

161
    m_group.insert(theFakeVimSetting(ConfigExpandTab),
hjk's avatar
hjk committed
162
        m_ui.checkBoxExpandTab);
163
    m_group.insert(theFakeVimSetting(ConfigHlSearch),
hjk's avatar
hjk committed
164
        m_ui.checkBoxHlSearch);
165
    m_group.insert(theFakeVimSetting(ConfigShiftWidth),
hjk's avatar
hjk committed
166 167
        m_ui.lineEditShiftWidth);

168
    m_group.insert(theFakeVimSetting(ConfigSmartTab),
hjk's avatar
hjk committed
169
        m_ui.checkBoxSmartTab);
170
    m_group.insert(theFakeVimSetting(ConfigStartOfLine),
hjk's avatar
hjk committed
171
        m_ui.checkBoxStartOfLine);
172
    m_group.insert(theFakeVimSetting(ConfigTabStop),
hjk's avatar
hjk committed
173
        m_ui.lineEditTabStop);
174
    m_group.insert(theFakeVimSetting(ConfigBackspace),
hjk's avatar
hjk committed
175 176
        m_ui.lineEditBackspace);

177
    m_group.insert(theFakeVimSetting(ConfigAutoIndent),
hjk's avatar
hjk committed
178
        m_ui.checkBoxAutoIndent);
179
    m_group.insert(theFakeVimSetting(ConfigSmartIndent),
180
        m_ui.checkBoxSmartIndent);
181
    m_group.insert(theFakeVimSetting(ConfigIncSearch),
182
        m_ui.checkBoxIncSearch);
183 184
    m_group.insert(theFakeVimSetting(ConfigUseCoreSearch),
        m_ui.checkBoxUseCoreSearch);
hjk's avatar
hjk committed
185 186

    connect(m_ui.pushButtonCopyTextEditorSettings, SIGNAL(clicked()),
hjk's avatar
hjk committed
187
        this, SLOT(copyTextEditorSettings()));
hjk's avatar
hjk committed
188 189 190 191
    connect(m_ui.pushButtonSetQtStyle, SIGNAL(clicked()),
        this, SLOT(setQtStyle()));
    connect(m_ui.pushButtonSetPlainStyle, SIGNAL(clicked()),
        this, SLOT(setPlainStyle()));
192 193
    if (m_searchKeywords.isEmpty()) {
        QTextStream(&m_searchKeywords)
hjk's avatar
hjk committed
194 195 196 197 198 199 200 201 202 203 204
            << ' ' << m_ui.labelAutoIndent->text()
            << ' ' << m_ui.labelExpandTab->text()
            << ' ' << m_ui.labelSmartIndent->text()
            << ' ' << m_ui.labelExpandTab->text()
            << ' ' << m_ui.labelHlSearch->text()
            << ' ' << m_ui.labelIncSearch->text()
            << ' ' << m_ui.labelShiftWidth->text()
            << ' ' << m_ui.labelSmartTab->text()
            << ' ' << m_ui.labelStartOfLine->text()
            << ' ' << m_ui.tabulatorLabel->text()
            << ' ' << m_ui.labelBackspace->text();
205 206
        m_searchKeywords.remove(QLatin1Char('&'));
    }
hjk's avatar
hjk committed
207 208 209 210 211
    return w;
}

void FakeVimOptionPage::copyTextEditorSettings()
{
212
    TextEditor::TabSettings ts =
hjk's avatar
hjk committed
213
        TextEditor::TextEditorSettings::instance()->tabSettings();
214

hjk's avatar
hjk committed
215 216 217 218
    m_ui.checkBoxExpandTab->setChecked(ts.m_spacesForTabs);
    m_ui.lineEditTabStop->setText(QString::number(ts.m_tabSize));
    m_ui.lineEditShiftWidth->setText(QString::number(ts.m_indentSize));
    m_ui.checkBoxSmartTab->setChecked(ts.m_smartBackspace);
219 220
    m_ui.checkBoxAutoIndent->setChecked(true);
    m_ui.checkBoxSmartIndent->setChecked(ts.m_autoIndent);
221 222
    // FIXME: Not present in core
    //m_ui.checkBoxIncSearch->setChecked(ts.m_incSearch);
hjk's avatar
hjk committed
223 224 225 226 227
}

void FakeVimOptionPage::setQtStyle()
{
    m_ui.checkBoxExpandTab->setChecked(true);
228 229 230
    const QString four = QString(QLatin1Char('4'));
    m_ui.lineEditTabStop->setText(four);
    m_ui.lineEditShiftWidth->setText(four);
hjk's avatar
hjk committed
231 232
    m_ui.checkBoxSmartTab->setChecked(true);
    m_ui.checkBoxAutoIndent->setChecked(true);
233
    m_ui.checkBoxSmartIndent->setChecked(true);
234
    m_ui.checkBoxIncSearch->setChecked(true);
235
    m_ui.lineEditBackspace->setText(QLatin1String("indent,eol,start"));
hjk's avatar
hjk committed
236 237 238 239 240
}

void FakeVimOptionPage::setPlainStyle()
{
    m_ui.checkBoxExpandTab->setChecked(false);
241 242 243
    const QString eight = QString(QLatin1Char('4'));
    m_ui.lineEditTabStop->setText(eight);
    m_ui.lineEditShiftWidth->setText(eight);
hjk's avatar
hjk committed
244 245
    m_ui.checkBoxSmartTab->setChecked(false);
    m_ui.checkBoxAutoIndent->setChecked(false);
246
    m_ui.checkBoxSmartIndent->setChecked(false);
247
    m_ui.checkBoxIncSearch->setChecked(false);
hjk's avatar
hjk committed
248
    m_ui.lineEditBackspace->setText(QString());
hjk's avatar
hjk committed
249 250
}

251 252 253 254 255
bool FakeVimOptionPage::matches(const QString &s) const
{
    return m_searchKeywords.contains(s, Qt::CaseInsensitive);
}

hjk's avatar
hjk committed
256 257 258 259
} // namespace Internal
} // namespace FakeVim


260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281
///////////////////////////////////////////////////////////////////////
//
// FakeVimExCommandsPage
//
///////////////////////////////////////////////////////////////////////

struct CommandItem
{
    Command *m_cmd;
    QString m_regex;
    QTreeWidgetItem *m_item;
};

Q_DECLARE_METATYPE(CommandItem*);

namespace FakeVim {
namespace Internal {

static QMap<QString, QRegExp> s_exCommandMap;
static QMap<QString, QRegExp> s_defaultExCommandMap;


282
class FakeVimExCommandsPage : public Core::CommandMappings
283 284 285 286 287 288 289 290 291 292 293
{
    Q_OBJECT

public:
    FakeVimExCommandsPage() {}

    // IOptionsPage
    QString id() const { return QLatin1String(Constants::SETTINGS_EX_CMDS_ID); }
    QString displayName() const { return tr("Ex Command Mapping"); }
    QString category() const { return QLatin1String(Constants::SETTINGS_CATEGORY); }
    QString displayCategory() const { return tr("FakeVim"); }
294
    QIcon categoryIcon() const { return QIcon(); } // TODO: Icon for FakeVim
295 296 297 298 299 300

    QWidget *createPage(QWidget *parent);
    void initialize();

public slots:
    void commandChanged(QTreeWidgetItem *current);
301 302 303
    void targetIdentifierChanged();
    void resetTargetIdentifier();
    void removeTargetIdentifier();
304 305 306 307 308 309 310 311 312
    void defaultAction();

private:
    void setRegex(const QString &regex);
    QList<CommandItem *> m_citems;
};

QWidget *FakeVimExCommandsPage::createPage(QWidget *parent)
{
313 314 315 316 317
    QWidget *w = CommandMappings::createPage(parent);
    setPageTitle(tr("Ex Command Mapping"));
    setTargetHeader(tr("Ex Trigger Expression"));
    setTargetLabelText(tr("Regular Expression:"));
    setTargetEditTitle(tr("Ex Command"));
318

319
    setImportExportEnabled(false);
320 321 322 323 324 325 326 327 328 329 330

    return w;
}

void FakeVimExCommandsPage::initialize()
{
    Core::ActionManager *am = Core::ICore::instance()->actionManager();
    QTC_ASSERT(am, return);
    UniqueIDManager *uidm = UniqueIDManager::instance();
    QTC_ASSERT(uidm, return);

331 332
    QMap<QString, QTreeWidgetItem *> sections;

333 334 335 336 337
    foreach (Command *c, am->commands()) {
        if (c->action() && c->action()->isSeparator())
            continue;

        CommandItem *ci = new CommandItem;
338
        QTreeWidgetItem *item = new QTreeWidgetItem;
339 340 341 342 343
        ci->m_cmd = c;
        ci->m_item = item;
        m_citems << ci;

        const QString name = uidm->stringForUniqueIdentifier(c->id());
344 345 346 347 348
        const int pos = name.indexOf(QLatin1Char('.'));
        const QString section = name.left(pos);
        const QString subId = name.mid(pos+1);

        if (!sections.contains(section)) {
349
            QTreeWidgetItem *categoryItem = new QTreeWidgetItem(commandList(), QStringList() << section);
350 351 352 353
            QFont f = categoryItem->font(0);
            f.setBold(true);
            categoryItem->setFont(0, f);
            sections.insert(section, categoryItem);
354
            commandList()->expandItem(categoryItem);
355 356 357 358
        }
        sections[section]->addChild(item);

        item->setText(0, subId);
359 360 361 362 363 364 365 366 367 368 369

        if (c->action()) {
            QString text = c->hasAttribute(Command::CA_UpdateText) && !c->defaultText().isNull() ? c->defaultText() : c->action()->text();
            text.remove(QRegExp("&(?!&)"));
            item->setText(1, text);
        } else {
            item->setText(1, c->shortcut()->whatsThis());
        }
        if (s_exCommandMap.contains(name)) {
            ci->m_regex = s_exCommandMap[name].pattern();
        } else {
hjk's avatar
hjk committed
370
            ci->m_regex.clear();
371 372 373 374
        }

        item->setText(2, ci->m_regex);
        item->setData(0, Qt::UserRole, qVariantFromValue(ci));
375

376 377
        if (ci->m_regex != s_defaultExCommandMap[name].pattern())
            setModified(item, true);
378 379 380 381 382 383 384
    }

    commandChanged(0);
}

void FakeVimExCommandsPage::commandChanged(QTreeWidgetItem *current)
{
385 386 387
    CommandMappings::commandChanged(current);

    if (!current || !current->data(0, Qt::UserRole).isValid())
388
        return;
389

390
    CommandItem *citem = qVariantValue<CommandItem *>(current->data(0, Qt::UserRole));
391
    targetEdit()->setText(citem->m_regex);
392 393
}

394
void FakeVimExCommandsPage::targetIdentifierChanged()
395
{
396
    QTreeWidgetItem *current = commandList()->currentItem();
397 398 399 400 401 402 403 404
    if (!current)
        return;

    UniqueIDManager *uidm = UniqueIDManager::instance();
    CommandItem *citem = qVariantValue<CommandItem *>(current->data(0, Qt::UserRole));
    const QString name = uidm->stringForUniqueIdentifier(citem->m_cmd->id());

    if (current->data(0, Qt::UserRole).isValid()) {
405
        citem->m_regex = targetEdit()->text();
406
        current->setText(2, citem->m_regex);
407
        s_exCommandMap[name] = QRegExp(citem->m_regex);
408
    }
409

410 411 412 413
    if (citem->m_regex != s_defaultExCommandMap[name].pattern())
        setModified(current, true);
    else
        setModified(current, false);
414

415 416 417 418
}

void FakeVimExCommandsPage::setRegex(const QString &regex)
{
419
    targetEdit()->setText(regex);
420 421
}

422
void FakeVimExCommandsPage::resetTargetIdentifier()
423 424
{
    UniqueIDManager *uidm = UniqueIDManager::instance();
425
    QTreeWidgetItem *current = commandList()->currentItem();
426 427 428 429 430 431
    if (current && current->data(0, Qt::UserRole).isValid()) {
        CommandItem *citem = qVariantValue<CommandItem *>(current->data(0, Qt::UserRole));
        const QString &name = uidm->stringForUniqueIdentifier(citem->m_cmd->id());
        if (s_defaultExCommandMap.contains(name))
            setRegex(s_defaultExCommandMap[name].pattern());
        else
hjk's avatar
hjk committed
432
            setRegex(QString());
433 434 435
    }
}

436
void FakeVimExCommandsPage::removeTargetIdentifier()
437
{
438
    targetEdit()->clear();
439 440 441 442 443 444 445 446 447 448
}

void FakeVimExCommandsPage::defaultAction()
{
    UniqueIDManager *uidm = UniqueIDManager::instance();
    foreach (CommandItem *item, m_citems) {
        const QString &name = uidm->stringForUniqueIdentifier(item->m_cmd->id());
        if (s_defaultExCommandMap.contains(name)) {
            item->m_regex = s_defaultExCommandMap[name].pattern();
        } else {
hjk's avatar
hjk committed
449
            item->m_regex.clear();
450
        }
451
        setModified(item->m_item, false);
452
        item->m_item->setText(2, item->m_regex);
453
        if (item->m_item == commandList()->currentItem())
454 455 456 457 458 459 460 461
            commandChanged(item->m_item);
    }
}

} // namespace Internal
} // namespace FakeVim


hjk's avatar
hjk committed
462 463
///////////////////////////////////////////////////////////////////////
//
464
// FakeVimPluginPrivate
hjk's avatar
hjk committed
465 466 467
//
///////////////////////////////////////////////////////////////////////

468 469 470 471
namespace FakeVim {
namespace Internal {

class FakeVimPluginPrivate : public QObject
hjk's avatar
hjk committed
472
{
473 474 475 476 477 478 479
    Q_OBJECT

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

480
    bool initialize();
481 482 483 484 485
    void shutdown();

private slots:
    void editorOpened(Core::IEditor *);
    void editorAboutToClose(Core::IEditor *);
486

487
    void setUseFakeVim(const QVariant &value);
488
    void quitFakeVim();
489
    void triggerCompletions();
hjk's avatar
hjk committed
490
    void windowCommand(int key);
491
    void find(bool reverse);
492
    void findNext(bool reverse);
hjk's avatar
hjk committed
493
    void showSettingsDialog();
494 495 496 497 498

    void showCommandBuffer(const QString &contents);
    void showExtraInformation(const QString &msg);
    void changeSelection(const QList<QTextEdit::ExtraSelection> &selections);
    void writeFile(bool *handled, const QString &fileName, const QString &contents);
499
    void moveToMatchingParenthesis(bool *moved, bool *forward, QTextCursor *cursor);
500
    void checkForElectricCharacter(bool *result, QChar c);
hjk's avatar
hjk committed
501
    void indentRegion(int *amount, int beginLine, int endLine,  QChar typedChar);
502
    void handleExCommand(const QString &cmd);
503
    void handleSetCommand(bool *handled, QString cmd);
504 505 506 507

    void handleDelayedQuitAll(bool forced);
    void handleDelayedQuit(bool forced, Core::IEditor *editor);

508 509 510 511
    void switchFile(bool previous);
    void switchFileNext();
    void switchFilePrev();

512 513 514
signals:
    void delayedQuitRequested(bool forced, Core::IEditor *editor);
    void delayedQuitAllRequested(bool forced);
515 516 517

private:
    FakeVimPlugin *q;
hjk's avatar
hjk committed
518
    FakeVimOptionPage *m_fakeVimOptionsPage;
519
    FakeVimExCommandsPage *m_fakeVimExCommandsPage;
520
    QHash<Core::IEditor *, FakeVimHandler *> m_editorToHandler;
521 522

    void triggerAction(const QString& code);
523
    void setActionChecked(const QString& code, bool check);
524 525 526

    void readSettings(QSettings *settings);
    void writeSettings(QSettings *settings);
527 528 529 530 531 532
};

} // namespace Internal
} // namespace FakeVim

FakeVimPluginPrivate::FakeVimPluginPrivate(FakeVimPlugin *plugin)
533
{
534
    q = plugin;
hjk's avatar
hjk committed
535
    m_fakeVimOptionsPage = 0;
536 537
    m_fakeVimExCommandsPage = 0;

538 539
    s_defaultExCommandMap[Constants::CMD_FILE_NEXT] = QRegExp("^n(ext)?!?( (.*))?$");
    s_defaultExCommandMap[Constants::CMD_FILE_PREV] = QRegExp("^(N(ext)?|prev(ious)?)!?( (.*))?$");
540 541 542 543
    s_defaultExCommandMap[CppTools::Constants::SWITCH_HEADER_SOURCE] = QRegExp("^A$");
    s_defaultExCommandMap[ProjectExplorer::Constants::BUILD] = QRegExp("^make$");
    s_defaultExCommandMap["Coreplugin.OutputPane.previtem"] = QRegExp("^(cN(ext)?|cp(revious)?)!?( (.*))?$");
    s_defaultExCommandMap["Coreplugin.OutputPane.nextitem"] = QRegExp("^cn(ext)?!?( (.*))?$");
544 545
    s_defaultExCommandMap[CppEditor::Constants::JUMP_TO_DEFINITION] = QRegExp("^tag?$");
    s_defaultExCommandMap[Core::Constants::GO_BACK] = QRegExp("^pop?$");
hjk's avatar
hjk committed
546 547
}

548 549 550
FakeVimPluginPrivate::~FakeVimPluginPrivate()
{
}
hjk's avatar
hjk committed
551

552
void FakeVimPluginPrivate::shutdown()
hjk's avatar
hjk committed
553
{
hjk's avatar
hjk committed
554 555 556 557
    q->removeObject(m_fakeVimOptionsPage);
    delete m_fakeVimOptionsPage;
    m_fakeVimOptionsPage = 0;
    theFakeVimSettings()->writeSettings(Core::ICore::instance()->settings());
dt's avatar
dt committed
558
    delete theFakeVimSettings();
559 560 561 562 563

    q->removeObject(m_fakeVimExCommandsPage);
    delete m_fakeVimExCommandsPage;
    m_fakeVimExCommandsPage = 0;
    writeSettings(Core::ICore::instance()->settings());
hjk's avatar
hjk committed
564 565
}

566
bool FakeVimPluginPrivate::initialize()
hjk's avatar
hjk committed
567
{
hjk's avatar
hjk committed
568
    Core::ActionManager *actionManager = Core::ICore::instance()->actionManager();
hjk's avatar
hjk committed
569 570 571 572 573
    QTC_ASSERT(actionManager, return false);

    QList<int> globalcontext;
    globalcontext << Core::Constants::C_GLOBAL_ID;

hjk's avatar
hjk committed
574 575 576
    m_fakeVimOptionsPage = new FakeVimOptionPage;
    q->addObject(m_fakeVimOptionsPage);
    theFakeVimSettings()->readSettings(Core::ICore::instance()->settings());
577 578 579 580

    m_fakeVimExCommandsPage = new FakeVimExCommandsPage;
    q->addObject(m_fakeVimExCommandsPage);
    readSettings(Core::ICore::instance()->settings());
581

con's avatar
con committed
582
    Core::Command *cmd = 0;
583
    cmd = actionManager->registerAction(theFakeVimSetting(ConfigUseFakeVim),
hjk's avatar
hjk committed
584 585 586
        Constants::INSTALL_HANDLER, globalcontext);
    cmd->setDefaultKeySequence(QKeySequence(Constants::INSTALL_KEY));

587
    ActionContainer *advancedMenu =
hjk's avatar
hjk committed
588
        actionManager->actionContainer(Core::Constants::M_EDIT_ADVANCED);
589
    advancedMenu->addAction(cmd, Core::Constants::G_EDIT_EDITOR);
hjk's avatar
hjk committed
590

591
    // EditorManager
hjk's avatar
hjk committed
592
    QObject *editorManager = Core::ICore::instance()->editorManager();
593 594 595 596 597
    connect(editorManager, SIGNAL(editorAboutToClose(Core::IEditor*)),
        this, SLOT(editorAboutToClose(Core::IEditor*)));
    connect(editorManager, SIGNAL(editorOpened(Core::IEditor*)),
        this, SLOT(editorOpened(Core::IEditor*)));

hjk's avatar
hjk committed
598 599
    connect(theFakeVimSetting(SettingsDialog), SIGNAL(triggered()),
        this, SLOT(showSettingsDialog()));
600 601
    connect(theFakeVimSetting(ConfigUseFakeVim), SIGNAL(valueChanged(QVariant)),
        this, SLOT(setUseFakeVim(QVariant)));
hjk's avatar
hjk committed
602

603 604 605 606 607 608 609 610 611 612
    QAction *switchFileNextAction = new QAction(tr("Switch to next file"), this);
    cmd = actionManager->registerAction(switchFileNextAction, Constants::CMD_FILE_NEXT, globalcontext);
    cmd->setAttribute(Command::CA_Hide);
    connect(switchFileNextAction, SIGNAL(triggered()), this, SLOT(switchFileNext()));

    QAction *switchFilePrevAction = new QAction(tr("Switch to previous file"), this);
    cmd = actionManager->registerAction(switchFilePrevAction, Constants::CMD_FILE_PREV, globalcontext);
    cmd->setAttribute(Command::CA_Hide);
    connect(switchFilePrevAction, SIGNAL(triggered()), this, SLOT(switchFilePrev()));

613 614 615 616 617 618
    // Delayed operatiosn
    connect(this, SIGNAL(delayedQuitRequested(bool,Core::IEditor*)),
        this, SLOT(handleDelayedQuit(bool,Core::IEditor*)), Qt::QueuedConnection);
    connect(this, SIGNAL(delayedQuitAllRequested(bool)),
        this, SLOT(handleDelayedQuitAll(bool)), Qt::QueuedConnection);

hjk's avatar
hjk committed
619 620 621
    return true;
}

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
static const char *exCommandMapGroup = "FakeVimExCommand";
static const char *reKey = "RegEx";
static const char *idKey = "Command";

void FakeVimPluginPrivate::writeSettings(QSettings *settings)
{
    settings->beginWriteArray(QLatin1String(exCommandMapGroup));

    int count = 0;
    const QMap<QString, QRegExp>::const_iterator end = s_exCommandMap.constEnd();
    for (QMap<QString, QRegExp>::const_iterator it = s_exCommandMap.constBegin(); it != end; ++it) {
        const QString &id = it.key();
        const QRegExp &re = it.value();

        if ((s_defaultExCommandMap.contains(id) && s_defaultExCommandMap[id] != re)
            || (!s_defaultExCommandMap.contains(id) && !re.pattern().isEmpty())) {
            settings->setArrayIndex(count);
            settings->setValue(QLatin1String(idKey), id);
            settings->setValue(QLatin1String(reKey), re.pattern());
            ++count;
        }
    }

    settings->endArray();
}

void FakeVimPluginPrivate::readSettings(QSettings *settings)
{
    s_exCommandMap = s_defaultExCommandMap;

    int size = settings->beginReadArray(QLatin1String(exCommandMapGroup));
    for (int i=0; i<size; ++i) {
        settings->setArrayIndex(i);
        const QString id = settings->value(QLatin1String(idKey)).toString();
        const QString re = settings->value(QLatin1String(reKey)).toString();
        s_exCommandMap[id] = QRegExp(re);
    }
    settings->endArray();
}

hjk's avatar
hjk committed
662 663
void FakeVimPluginPrivate::showSettingsDialog()
{
664 665
    Core::ICore::instance()->showOptionsDialog(QLatin1String(Constants::SETTINGS_CATEGORY),
                                               QLatin1String(Constants::SETTINGS_ID));
hjk's avatar
hjk committed
666 667
}

668 669 670 671 672 673 674 675 676 677 678
void FakeVimPluginPrivate::triggerAction(const QString& code)
{
    Core::ActionManager *am = Core::ICore::instance()->actionManager();
    QTC_ASSERT(am, return);
    Core::Command *cmd = am->command(code);
    QTC_ASSERT(cmd, return);
    QAction *action = cmd->action();
    QTC_ASSERT(action, return);
    action->trigger();
}

679 680 681 682 683 684 685 686 687 688 689 690 691
void FakeVimPluginPrivate::setActionChecked(const QString& code, bool check)
{
    Core::ActionManager *am = Core::ICore::instance()->actionManager();
    QTC_ASSERT(am, return);
    Core::Command *cmd = am->command(code);
    QTC_ASSERT(cmd, return);
    QAction *action = cmd->action();
    QTC_ASSERT(action, return);
    QTC_ASSERT(action->isCheckable(), return);
    action->setChecked(check);
    action->trigger();
}

hjk's avatar
hjk committed
692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717
void FakeVimPluginPrivate::windowCommand(int key)
{
    #define control(n) (256 + n)
    QString code;
    switch (key) {
        case 'c': case 'C': case control('c'):
            code = Core::Constants::CLOSE;
            break;
        case 'n': case 'N': case control('n'):
            code = Core::Constants::GOTONEXT;
            break;
        case 'o': case 'O': case control('o'):
            code = Core::Constants::REMOVE_ALL_SPLITS;
            code = Core::Constants::REMOVE_CURRENT_SPLIT;
            break;
        case 'p': case 'P': case control('p'):
            code = Core::Constants::GOTOPREV;
            break;
        case 's': case 'S': case control('s'):
            code = Core::Constants::SPLIT;
            break;
        case 'w': case 'W': case control('w'):
            code = Core::Constants::GOTO_OTHER_SPLIT;
            break;
    }
    #undef control
hjk's avatar
hjk committed
718
    //qDebug() << "RUNNING WINDOW COMMAND: " << key << code;
hjk's avatar
hjk committed
719
    if (code.isEmpty()) {
hjk's avatar
hjk committed
720
        //qDebug() << "UNKNOWN WINDOWS COMMAND: " << key;
hjk's avatar
hjk committed
721 722
        return;
    }
723 724 725 726 727
    triggerAction(code);
}

void FakeVimPluginPrivate::find(bool reverse)
{
728
    if (Find::FindPlugin *plugin = Find::FindPlugin::instance()) {
729 730
        plugin->setUseFakeVim(true);
        plugin->openFindToolBar(reverse
731 732
                ? Find::FindPlugin::FindBackward
                : Find::FindPlugin::FindForward);
733
    }
hjk's avatar
hjk committed
734 735
}

736 737 738 739 740 741 742 743
void FakeVimPluginPrivate::findNext(bool reverse)
{
    if (reverse)
        triggerAction(Find::Constants::FIND_PREVIOUS);
    else
        triggerAction(Find::Constants::FIND_NEXT);
}

744
// This class defers deletion of a child FakeVimHandler using deleteLater().
745 746 747 748 749 750 751 752
class DeferredDeleter : public QObject
{
    Q_OBJECT

    FakeVimHandler *m_handler;

public:
    DeferredDeleter(QObject *parent, FakeVimHandler *handler)
753
        : QObject(parent), m_handler(handler)
754 755 756 757 758 759 760 761 762 763 764 765
    {}

    virtual ~DeferredDeleter()
    {
        if (m_handler) {
            m_handler->disconnectFromEditor();
            m_handler->deleteLater();
            m_handler = 0;
        }
    }
};

766
void FakeVimPluginPrivate::editorOpened(Core::IEditor *editor)
767
{
768 769 770
    if (!editor)
        return;

771
    QWidget *widget = editor->widget();
772 773
    if (!widget)
        return;
774 775 776 777

    // we can only handle QTextEdit and QPlainTextEdit
    if (!qobject_cast<QTextEdit *>(widget) && !qobject_cast<QPlainTextEdit *>(widget))
        return;
778

779 780 781
    //qDebug() << "OPENING: " << editor << editor->widget()
    //    << "MODE: " << theFakeVimSetting(ConfigUseFakeVim)->value();

782 783 784 785
    FakeVimHandler *handler = new FakeVimHandler(widget, 0);
    // the handler might have triggered the deletion of the editor:
    // make sure that it can return before being deleted itself
    new DeferredDeleter(widget, handler);
786
    m_editorToHandler[editor] = handler;
787 788 789 790

    connect(handler, SIGNAL(extraInformationChanged(QString)),
        this, SLOT(showExtraInformation(QString)));
    connect(handler, SIGNAL(commandBufferChanged(QString)),
hjk's avatar
hjk committed
791
        this, SLOT(showCommandBuffer(QString)));
792 793 794 795
    connect(handler, SIGNAL(writeFileRequested(bool*,QString,QString)),
        this, SLOT(writeFile(bool*,QString,QString)));
    connect(handler, SIGNAL(selectionChanged(QList<QTextEdit::ExtraSelection>)),
        this, SLOT(changeSelection(QList<QTextEdit::ExtraSelection>)));
796 797
    connect(handler, SIGNAL(moveToMatchingParenthesis(bool*,bool*,QTextCursor*)),
        this, SLOT(moveToMatchingParenthesis(bool*,bool*,QTextCursor*)));
hjk's avatar
hjk committed
798 799
    connect(handler, SIGNAL(indentRegion(int*,int,int,QChar)),
        this, SLOT(indentRegion(int*,int,int,QChar)));
800 801
    connect(handler, SIGNAL(checkForElectricCharacter(bool*,QChar)),
        this, SLOT(checkForElectricCharacter(bool*,QChar)));
802 803
    connect(handler, SIGNAL(completionRequested()),
        this, SLOT(triggerCompletions()));
hjk's avatar
hjk committed
804 805
    connect(handler, SIGNAL(windowCommandRequested(int)),
        this, SLOT(windowCommand(int)));
806 807
    connect(handler, SIGNAL(findRequested(bool)),
        this, SLOT(find(bool)));
808 809
    connect(handler, SIGNAL(findNextRequested(bool)),
        this, SLOT(findNext(bool)));
810

811 812
    connect(handler, SIGNAL(handleExCommandRequested(QString)),
        this, SLOT(handleExCommand(QString)));
813 814
    connect(handler, SIGNAL(handleSetCommandRequested(bool *,QString)),
        this, SLOT(handleSetCommand(bool *,QString)));
815

hjk's avatar
hjk committed
816
    handler->setCurrentFileName(editor->file()->fileName());
817
    handler->installEventFilter();
818

819
    // pop up the bar
820
    if (theFakeVimSetting(ConfigUseFakeVim)->value().toBool()) {
hjk's avatar
hjk committed
821
       showCommandBuffer(QString());
822 823
       handler->setupWidget();
    }
hjk's avatar
hjk committed
824 825
}

826
void FakeVimPluginPrivate::editorAboutToClose(Core::IEditor *editor)
hjk's avatar
hjk committed
827
{
828 829 830 831 832 833
    //qDebug() << "CLOSING: " << editor << editor->widget();
    m_editorToHandler.remove(editor);
}

void FakeVimPluginPrivate::setUseFakeVim(const QVariant &value)
{
834
    //qDebug() << "SET USE FAKEVIM" << value;
835
    bool on = value.toBool();
836 837
    if (Find::FindPlugin::instance())
        Find::FindPlugin::instance()->setUseFakeVim(on);
838
    if (on) {
839 840
        Core::EditorManager::instance()->showEditorStatusBar(
            QLatin1String(Constants::MINI_BUFFER),
841
            "vi emulation mode. Type :q to leave. Use , Ctrl-R to trigger run.",
842
            tr("Quit FakeVim"), this, SLOT(quitFakeVim()));
843 844 845 846 847
        foreach (Core::IEditor *editor, m_editorToHandler.keys())
            m_editorToHandler[editor]->setupWidget();
    } else {
        Core::EditorManager::instance()->hideEditorStatusBar(
            QLatin1String(Constants::MINI_BUFFER));
848 849
        TextEditor::TabSettings ts =
            TextEditor::TextEditorSettings::instance()->tabSettings();
850
        foreach (Core::IEditor *editor, m_editorToHandler.keys())
851
            m_editorToHandler[editor]->restoreWidget(ts.m_tabSize);
852
    }
hjk's avatar
hjk committed
853 854
}

855 856 857 858 859 860
void FakeVimPluginPrivate::triggerCompletions()
{
    FakeVimHandler *handler = qobject_cast<FakeVimHandler *>(sender());
    if (!handler)
        return;
    if (BaseTextEditor *bt = qobject_cast<BaseTextEditor *>(handler->widget()))
hjk's avatar
hjk committed
861 862 863
        TextEditor::Internal::CompletionSupport::instance()->
            autoComplete(bt->editableInterface(), false);
   //     bt->triggerCompletions();
864 865
}

866 867 868 869 870 871 872 873 874
void FakeVimPluginPrivate::checkForElectricCharacter(bool *result, QChar c)
{
    FakeVimHandler *handler = qobject_cast<FakeVimHandler *>(sender());
    if (!handler)
        return;
    if (BaseTextEditor *bt = qobject_cast<BaseTextEditor *>(handler->widget()))
        *result = bt->isElectricCharacter(c);
}

875 876
void FakeVimPluginPrivate::writeFile(bool *handled,
    const QString &fileName, const QString &contents)
877
{
878
    Q_UNUSED(contents)
879

880 881 882 883
    FakeVimHandler *handler = qobject_cast<FakeVimHandler *>(sender());
    if (!handler)
        return;

884
    Core::IEditor *editor = m_editorToHandler.key(handler);
885
    if (editor && editor->file()->fileName() == fileName) {
hjk's avatar
hjk committed
886
        // Handle that as a special case for nicer interaction with core
887
        Core::IFile *file = editor->file();
hjk's avatar
hjk committed
888
        Core::ICore::instance()->fileManager()->blockFileChange(file);
889
        file->save(fileName);
hjk's avatar
hjk committed
890
        Core::ICore::instance()->fileManager()->unblockFileChange(file);
891
        *handled = true;
892
    }
893 894
}

895 896 897 898 899 900 901 902 903 904 905 906 907 908 909
void FakeVimPluginPrivate::handleExCommand(const QString &cmd)
{
    static QRegExp reWriteAll("^wa(ll)?!?$");
    static QRegExp reQuit("^q!?$");
    static QRegExp reQuitAll("^qa!?$");

    using namespace Core;

    FakeVimHandler *handler = qobject_cast<FakeVimHandler *>(sender());
    if (!handler)
        return;

    EditorManager *editorManager = EditorManager::instance();
    QTC_ASSERT(editorManager, return);

910
    if (reWriteAll.indexIn(cmd) != -1) {
911 912 913 914