fakevimplugin.cpp 24 KB
Newer Older
1
/**************************************************************************
hjk's avatar
hjk committed
2 3 4
**
** This file is part of Qt Creator
**
5
** Copyright (c) 2009 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"

hjk's avatar
hjk committed
35

36
#include <coreplugin/actionmanager/actionmanager.h>
hjk's avatar
hjk committed
37 38
#include <coreplugin/coreconstants.h>
#include <coreplugin/editormanager/editormanager.h>
39
#include <coreplugin/filemanager.h>
hjk's avatar
hjk committed
40
#include <coreplugin/icore.h>
41
#include <coreplugin/ifile.h>
hjk's avatar
hjk committed
42
#include <coreplugin/dialogs/ioptionspage.h>
hjk's avatar
hjk committed
43 44 45 46 47 48 49 50 51
#include <coreplugin/messagemanager.h>
#include <coreplugin/modemanager.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
52
#include <texteditor/completionsupport.h>
hjk's avatar
hjk committed
53 54
#include <texteditor/itexteditor.h>
#include <texteditor/texteditorconstants.h>
55 56
#include <texteditor/tabsettings.h>
#include <texteditor/texteditorsettings.h>
57
#include <texteditor/textblockiterator.h>
hjk's avatar
hjk committed
58

59 60
#include <find/textfindconstants.h>

hjk's avatar
hjk committed
61
#include <utils/qtcassert.h>
hjk's avatar
hjk committed
62
#include <utils/savedaction.h>
hjk's avatar
hjk committed
63

64 65
#include <indenter.h>

hjk's avatar
hjk committed
66
#include <QtCore/QDebug>
67
#include <QtCore/QtPlugin>
hjk's avatar
hjk committed
68 69 70
#include <QtCore/QObject>
#include <QtCore/QPoint>
#include <QtCore/QSettings>
71
#include <QtCore/QTextStream>
hjk's avatar
hjk committed
72

73
#include <QtGui/QMessageBox>
hjk's avatar
hjk committed
74 75 76
#include <QtGui/QPlainTextEdit>
#include <QtGui/QTextBlock>
#include <QtGui/QTextCursor>
77
#include <QtGui/QTextEdit>
hjk's avatar
hjk committed
78 79 80 81 82 83 84 85 86 87 88


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


namespace FakeVim {
namespace Constants {

89 90
const char * const INSTALL_HANDLER        = "TextEditor.FakeVimHandler";
const char * const MINI_BUFFER            = "TextEditor.FakeVimMiniBuffer";
hjk's avatar
hjk committed
91
const char * const INSTALL_KEY            = "Alt+V,Alt+V";
92 93
const char * const SETTINGS_CATEGORY      = "D.FakeVim";
const char * const SETTINGS_ID            = "General";
hjk's avatar
hjk committed
94 95 96 97 98

} // namespace Constants
} // namespace FakeVim


hjk's avatar
hjk committed
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
///////////////////////////////////////////////////////////////////////
//
// FakeVimOptionPage
//
///////////////////////////////////////////////////////////////////////

namespace FakeVim {
namespace Internal {

class FakeVimOptionPage : public Core::IOptionsPage
{
    Q_OBJECT

public:
    FakeVimOptionPage() {}

    // IOptionsPage
116
    QString id() const { return QLatin1String(Constants::SETTINGS_ID); }
hjk's avatar
hjk committed
117
    QString trName() const { return tr("General"); }
118
    QString category() const { return QLatin1String(Constants::SETTINGS_CATEGORY); }
hjk's avatar
hjk committed
119 120 121 122 123
    QString trCategory() const { return tr("FakeVim"); }

    QWidget *createPage(QWidget *parent);
    void apply() { m_group.apply(ICore::instance()->settings()); }
    void finish() { m_group.finish(); }
124
    virtual bool matches(const QString &) const;
hjk's avatar
hjk committed
125 126 127 128 129 130 131 132 133

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

private:
    friend class DebuggerPlugin;
    Ui::FakeVimOptionPage m_ui;
134
    QString m_searchKeywords;
135
    Utils::SavedActionSet m_group;
hjk's avatar
hjk committed
136 137 138 139 140 141 142 143 144
};

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

    m_group.clear();
    m_group.insert(theFakeVimSetting(ConfigUseFakeVim), 
145
        m_ui.groupBox);
hjk's avatar
hjk committed
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164

    m_group.insert(theFakeVimSetting(ConfigExpandTab), 
        m_ui.checkBoxExpandTab);
    m_group.insert(theFakeVimSetting(ConfigHlSearch), 
        m_ui.checkBoxHlSearch);
    m_group.insert(theFakeVimSetting(ConfigShiftWidth), 
        m_ui.lineEditShiftWidth);

    m_group.insert(theFakeVimSetting(ConfigSmartTab), 
        m_ui.checkBoxSmartTab);
    m_group.insert(theFakeVimSetting(ConfigStartOfLine), 
        m_ui.checkBoxStartOfLine);
    m_group.insert(theFakeVimSetting(ConfigTabStop), 
        m_ui.lineEditTabStop);
    m_group.insert(theFakeVimSetting(ConfigBackspace), 
        m_ui.lineEditBackspace);

    m_group.insert(theFakeVimSetting(ConfigAutoIndent), 
        m_ui.checkBoxAutoIndent);
165 166
    m_group.insert(theFakeVimSetting(ConfigIncSearch), 
        m_ui.checkBoxIncSearch);
hjk's avatar
hjk committed
167 168

    connect(m_ui.pushButtonCopyTextEditorSettings, SIGNAL(clicked()),
hjk's avatar
hjk committed
169
        this, SLOT(copyTextEditorSettings()));
hjk's avatar
hjk committed
170 171 172 173
    connect(m_ui.pushButtonSetQtStyle, SIGNAL(clicked()),
        this, SLOT(setQtStyle()));
    connect(m_ui.pushButtonSetPlainStyle, SIGNAL(clicked()),
        this, SLOT(setPlainStyle()));
174 175 176 177 178 179 180 181 182
    if (m_searchKeywords.isEmpty()) {
        QTextStream(&m_searchKeywords)
           << ' ' << m_ui.labelAutoIndent->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();
        m_searchKeywords.remove(QLatin1Char('&'));
    }
hjk's avatar
hjk committed
183 184 185 186 187 188 189 190 191 192 193 194 195
    return w;
}

void FakeVimOptionPage::copyTextEditorSettings()
{
    TextEditor::TabSettings ts = 
        TextEditor::TextEditorSettings::instance()->tabSettings();
    
    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);
    m_ui.checkBoxAutoIndent->setChecked(ts.m_autoIndent);
196 197
    // FIXME: Not present in core
    //m_ui.checkBoxIncSearch->setChecked(ts.m_incSearch);
hjk's avatar
hjk committed
198 199 200 201 202
}

void FakeVimOptionPage::setQtStyle()
{
    m_ui.checkBoxExpandTab->setChecked(true);
203 204 205
    const QString four = QString(QLatin1Char('4'));
    m_ui.lineEditTabStop->setText(four);
    m_ui.lineEditShiftWidth->setText(four);
hjk's avatar
hjk committed
206 207
    m_ui.checkBoxSmartTab->setChecked(true);
    m_ui.checkBoxAutoIndent->setChecked(true);
208
    m_ui.checkBoxIncSearch->setChecked(true);
209
    m_ui.lineEditBackspace->setText(QLatin1String("indent,eol,start"));
hjk's avatar
hjk committed
210 211 212 213 214
}

void FakeVimOptionPage::setPlainStyle()
{
    m_ui.checkBoxExpandTab->setChecked(false);
215 216 217
    const QString eight = QString(QLatin1Char('4'));
    m_ui.lineEditTabStop->setText(eight);
    m_ui.lineEditShiftWidth->setText(eight);
hjk's avatar
hjk committed
218 219
    m_ui.checkBoxSmartTab->setChecked(false);
    m_ui.checkBoxAutoIndent->setChecked(false);
220
    m_ui.checkBoxIncSearch->setChecked(false);
hjk's avatar
hjk committed
221
    m_ui.lineEditBackspace->setText(QString());
hjk's avatar
hjk committed
222 223
}

224 225 226 227 228
bool FakeVimOptionPage::matches(const QString &s) const
{
    return m_searchKeywords.contains(s, Qt::CaseInsensitive);
}

hjk's avatar
hjk committed
229 230 231 232
} // namespace Internal
} // namespace FakeVim


hjk's avatar
hjk committed
233 234
///////////////////////////////////////////////////////////////////////
//
235
// FakeVimPluginPrivate
hjk's avatar
hjk committed
236 237 238
//
///////////////////////////////////////////////////////////////////////

239 240 241 242
namespace FakeVim {
namespace Internal {

class FakeVimPluginPrivate : public QObject
hjk's avatar
hjk committed
243
{
244 245 246 247 248 249 250
    Q_OBJECT

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

251
    bool initialize();
252 253 254 255 256
    void shutdown();

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

258
    void setUseFakeVim(const QVariant &value);
259
    void quitFakeVim();
260
    void triggerCompletions();
hjk's avatar
hjk committed
261
    void windowCommand(int key);
262
    void find(bool reverse);
263
    void findNext(bool reverse);
hjk's avatar
hjk committed
264
    void showSettingsDialog();
265 266 267 268 269

    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);
270
    void moveToMatchingParenthesis(bool *moved, bool *forward, QTextCursor *cursor);
271
    void checkForElectricCharacter(bool *result, QChar c);
hjk's avatar
hjk committed
272
    void indentRegion(int *amount, int beginLine, int endLine,  QChar typedChar);
273 274 275 276 277 278 279 280
    void handleExCommand(const QString &cmd);

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

signals:
    void delayedQuitRequested(bool forced, Core::IEditor *editor);
    void delayedQuitAllRequested(bool forced);
281 282 283

private:
    FakeVimPlugin *q;
hjk's avatar
hjk committed
284
    FakeVimOptionPage *m_fakeVimOptionsPage;
285
    QHash<Core::IEditor *, FakeVimHandler *> m_editorToHandler;
286 287

    void triggerAction(const QString& code);
288 289 290 291 292 293 294 295
};

} // namespace Internal
} // namespace FakeVim

FakeVimPluginPrivate::FakeVimPluginPrivate(FakeVimPlugin *plugin)
{       
    q = plugin;
hjk's avatar
hjk committed
296
    m_fakeVimOptionsPage = 0;
hjk's avatar
hjk committed
297 298
}

299 300 301
FakeVimPluginPrivate::~FakeVimPluginPrivate()
{
}
hjk's avatar
hjk committed
302

303
void FakeVimPluginPrivate::shutdown()
hjk's avatar
hjk committed
304
{
hjk's avatar
hjk committed
305 306 307 308
    q->removeObject(m_fakeVimOptionsPage);
    delete m_fakeVimOptionsPage;
    m_fakeVimOptionsPage = 0;
    theFakeVimSettings()->writeSettings(Core::ICore::instance()->settings());
dt's avatar
dt committed
309
    delete theFakeVimSettings();
hjk's avatar
hjk committed
310 311
}

312
bool FakeVimPluginPrivate::initialize()
hjk's avatar
hjk committed
313
{
hjk's avatar
hjk committed
314
    Core::ActionManager *actionManager = Core::ICore::instance()->actionManager();
hjk's avatar
hjk committed
315 316 317 318 319
    QTC_ASSERT(actionManager, return false);

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

hjk's avatar
hjk committed
320 321 322
    m_fakeVimOptionsPage = new FakeVimOptionPage;
    q->addObject(m_fakeVimOptionsPage);
    theFakeVimSettings()->readSettings(Core::ICore::instance()->settings());
hjk's avatar
hjk committed
323
    
con's avatar
con committed
324
    Core::Command *cmd = 0;
325
    cmd = actionManager->registerAction(theFakeVimSetting(ConfigUseFakeVim),
hjk's avatar
hjk committed
326 327 328
        Constants::INSTALL_HANDLER, globalcontext);
    cmd->setDefaultKeySequence(QKeySequence(Constants::INSTALL_KEY));

329
    ActionContainer *advancedMenu =
hjk's avatar
hjk committed
330
        actionManager->actionContainer(Core::Constants::M_EDIT_ADVANCED);
331
    advancedMenu->addAction(cmd, Core::Constants::G_EDIT_EDITOR);
hjk's avatar
hjk committed
332

333
    // EditorManager
hjk's avatar
hjk committed
334
    QObject *editorManager = Core::ICore::instance()->editorManager();
335 336 337 338 339
    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
340 341
    connect(theFakeVimSetting(SettingsDialog), SIGNAL(triggered()),
        this, SLOT(showSettingsDialog()));
342 343
    connect(theFakeVimSetting(ConfigUseFakeVim), SIGNAL(valueChanged(QVariant)),
        this, SLOT(setUseFakeVim(QVariant)));
hjk's avatar
hjk committed
344

345 346 347 348 349 350
    // 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
351 352 353
    return true;
}

hjk's avatar
hjk committed
354 355
void FakeVimPluginPrivate::showSettingsDialog()
{
356 357
    Core::ICore::instance()->showOptionsDialog(QLatin1String(Constants::SETTINGS_CATEGORY),
                                               QLatin1String(Constants::SETTINGS_ID));
hjk's avatar
hjk committed
358 359
}

360 361 362 363 364 365 366 367 368 369 370
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();
}

hjk's avatar
hjk committed
371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401
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
    qDebug() << "RUNNING WINDOW COMMAND: " << key << code;
    if (code.isEmpty()) {
        qDebug() << "UNKNOWN WINDOWS COMMAND: " << key;
        return;
    }
402 403 404 405 406
    triggerAction(code);
}

void FakeVimPluginPrivate::find(bool reverse)
{
407
    Q_UNUSED(reverse)  // TODO: Creator needs an action for find in reverse.
408
    triggerAction(Find::Constants::FIND_IN_DOCUMENT);
hjk's avatar
hjk committed
409 410
}

411 412 413 414 415 416 417 418
void FakeVimPluginPrivate::findNext(bool reverse)
{
    if (reverse)
        triggerAction(Find::Constants::FIND_PREVIOUS);
    else
        triggerAction(Find::Constants::FIND_NEXT);
}

419
void FakeVimPluginPrivate::editorOpened(Core::IEditor *editor)
420
{
421 422 423
    if (!editor)
        return;

424
    QWidget *widget = editor->widget();
425 426
    if (!widget)
        return;
427 428 429 430

    // we can only handle QTextEdit and QPlainTextEdit
    if (!qobject_cast<QTextEdit *>(widget) && !qobject_cast<QPlainTextEdit *>(widget))
        return;
431
    
432 433 434
    //qDebug() << "OPENING: " << editor << editor->widget()
    //    << "MODE: " << theFakeVimSetting(ConfigUseFakeVim)->value();

435
    FakeVimHandler *handler = new FakeVimHandler(widget, widget);
436
    m_editorToHandler[editor] = handler;
437 438 439 440

    connect(handler, SIGNAL(extraInformationChanged(QString)),
        this, SLOT(showExtraInformation(QString)));
    connect(handler, SIGNAL(commandBufferChanged(QString)),
hjk's avatar
hjk committed
441
        this, SLOT(showCommandBuffer(QString)));
442 443 444 445
    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>)));
446 447
    connect(handler, SIGNAL(moveToMatchingParenthesis(bool*,bool*,QTextCursor*)),
        this, SLOT(moveToMatchingParenthesis(bool*,bool*,QTextCursor*)));
hjk's avatar
hjk committed
448 449
    connect(handler, SIGNAL(indentRegion(int*,int,int,QChar)),
        this, SLOT(indentRegion(int*,int,int,QChar)));
450 451
    connect(handler, SIGNAL(checkForElectricCharacter(bool*,QChar)),
        this, SLOT(checkForElectricCharacter(bool*,QChar)));
452 453
    connect(handler, SIGNAL(completionRequested()),
        this, SLOT(triggerCompletions()));
hjk's avatar
hjk committed
454 455
    connect(handler, SIGNAL(windowCommandRequested(int)),
        this, SLOT(windowCommand(int)));
456 457
    connect(handler, SIGNAL(findRequested(bool)),
        this, SLOT(find(bool)));
458 459
    connect(handler, SIGNAL(findNextRequested(bool)),
        this, SLOT(findNext(bool)));
460

461 462 463
    connect(handler, SIGNAL(handleExCommandRequested(QString)),
        this, SLOT(handleExCommand(QString)));

hjk's avatar
hjk committed
464
    handler->setCurrentFileName(editor->file()->fileName());
465
    handler->installEventFilter();
466 467 468 469
    
    // pop up the bar
    if (theFakeVimSetting(ConfigUseFakeVim)->value().toBool())
       showCommandBuffer("");
hjk's avatar
hjk committed
470 471
}

472
void FakeVimPluginPrivate::editorAboutToClose(Core::IEditor *editor)
hjk's avatar
hjk committed
473
{
474 475 476 477 478 479
    //qDebug() << "CLOSING: " << editor << editor->widget();
    m_editorToHandler.remove(editor);
}

void FakeVimPluginPrivate::setUseFakeVim(const QVariant &value)
{
480
    //qDebug() << "SET USE FAKEVIM" << value;
481 482 483 484 485
    bool on = value.toBool();
    if (on) {
        Core::EditorManager::instance()->showEditorStatusBar( 
            QLatin1String(Constants::MINI_BUFFER), 
            "vi emulation mode. Type :q to leave. Use , Ctrl-R to trigger run.",
486
            tr("Quit FakeVim"), this, SLOT(quitFakeVim()));
487 488 489 490 491 492 493 494
        foreach (Core::IEditor *editor, m_editorToHandler.keys())
            m_editorToHandler[editor]->setupWidget();
    } else {
        Core::EditorManager::instance()->hideEditorStatusBar(
            QLatin1String(Constants::MINI_BUFFER));
        foreach (Core::IEditor *editor, m_editorToHandler.keys())
            m_editorToHandler[editor]->restoreWidget();
    }
hjk's avatar
hjk committed
495 496
}

497 498 499 500 501 502
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
503 504 505
        TextEditor::Internal::CompletionSupport::instance()->
            autoComplete(bt->editableInterface(), false);
   //     bt->triggerCompletions();
506 507
}

508 509 510 511 512 513 514 515 516
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);
}

517 518
void FakeVimPluginPrivate::writeFile(bool *handled,
    const QString &fileName, const QString &contents)
519
{
520
    Q_UNUSED(contents)
521

522 523 524 525
    FakeVimHandler *handler = qobject_cast<FakeVimHandler *>(sender());
    if (!handler)
        return;

526
    Core::IEditor *editor = m_editorToHandler.key(handler);
527
    if (editor && editor->file()->fileName() == fileName) {
hjk's avatar
hjk committed
528
        // Handle that as a special case for nicer interaction with core
529
        Core::IFile *file = editor->file();
hjk's avatar
hjk committed
530
        Core::ICore::instance()->fileManager()->blockFileChange(file);
531
        file->save(fileName);
hjk's avatar
hjk committed
532
        Core::ICore::instance()->fileManager()->unblockFileChange(file);
533 534
        *handled = true;
    } 
535 536
}

537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567
void FakeVimPluginPrivate::handleExCommand(const QString &cmd)
{
    static QRegExp reNextFile("^n(ext)?!?( (.*))?$");
    static QRegExp rePreviousFile("^(N(ext)?|prev(ious)?)!?( (.*))?$");
    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);

    if (reNextFile.indexIn(cmd) != -1) {
        // :n
        editorManager->goForwardInNavigationHistory();
    } else if (rePreviousFile.indexIn(cmd) != -1) {
        // :N, :prev
        editorManager->goBackInNavigationHistory();
    } else if (reWriteAll.indexIn(cmd) != -1) {
        // :wa
        FileManager *fm = ICore::instance()->fileManager();
        QList<IFile *> toSave = fm->modifiedFiles();
        QList<IFile *> failed = fm->saveModifiedFilesSilently(toSave);
        if (failed.isEmpty())
            handler->showBlackMessage(tr("Saving succeeded"));
        else
568
            handler->showRedMessage(tr("%n files not saved", 0, failed.size()));
569 570 571 572 573 574 575 576 577
    } else if (reQuit.indexIn(cmd) != -1) {
        // :q
        bool forced = cmd.contains(QChar('!'));
        emit delayedQuitRequested(forced, m_editorToHandler.key(handler));
    } else if (reQuitAll.indexIn(cmd) != -1) {
        // :qa
        bool forced = cmd.contains(QChar('!'));
        emit delayedQuitAllRequested(forced);
    } else {
578
        handler->showRedMessage(tr("Not an editor command: %1").arg(cmd));
579 580 581 582 583 584 585 586 587 588 589 590 591 592 593
    }
}

void FakeVimPluginPrivate::handleDelayedQuit(bool forced, Core::IEditor *editor)
{
    QList<Core::IEditor *> editors;
    editors.append(editor);
    Core::EditorManager::instance()->closeEditors(editors, !forced);
}

void FakeVimPluginPrivate::handleDelayedQuitAll(bool forced)
{
    Core::EditorManager::instance()->closeAllEditors(!forced);
}

594 595 596 597 598 599 600 601 602 603 604 605 606 607 608
void FakeVimPluginPrivate::moveToMatchingParenthesis(bool *moved, bool *forward,
        QTextCursor *cursor)
{
    *moved = false;

    bool undoFakeEOL = false;
    if (cursor->atBlockEnd() && cursor->block().length() > 1) {
        cursor->movePosition(QTextCursor::Left, QTextCursor::KeepAnchor, 1);
        undoFakeEOL = true;
    }
    TextEditor::TextBlockUserData::MatchType match
        = TextEditor::TextBlockUserData::matchCursorForward(cursor);
    if (match == TextEditor::TextBlockUserData::Match) {
        *moved = true;
        *forward = true;
609
    } else {
610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629
        if (undoFakeEOL)
            cursor->movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, 1);
        if (match == TextEditor::TextBlockUserData::NoMatch) {
            // backward matching is according to the character before the cursor
            bool undoMove = false;
            if (!cursor->atBlockEnd()) {
                cursor->movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, 1);
                undoMove = true;
            }
            match = TextEditor::TextBlockUserData::matchCursorBackward(cursor);
            if (match == TextEditor::TextBlockUserData::Match) {
                *moved = true;
                *forward = false;
            } else if (undoMove) {
                cursor->movePosition(QTextCursor::Left, QTextCursor::KeepAnchor, 1);
            }
        }
    }
}

hjk's avatar
hjk committed
630
void FakeVimPluginPrivate::indentRegion(int *amount, int beginLine, int endLine,
631 632 633 634 635 636 637 638 639 640
      QChar typedChar)
{
    FakeVimHandler *handler = qobject_cast<FakeVimHandler *>(sender());
    if (!handler)
        return;

    BaseTextEditor *bt = qobject_cast<BaseTextEditor *>(handler->widget());
    if (!bt)
        return;

hjk's avatar
hjk committed
641 642
    TextEditor::TabSettings tabSettings = 
        TextEditor::TextEditorSettings::instance()->tabSettings();
643 644
    typedef SharedTools::Indenter<TextEditor::TextBlockIterator> Indenter;
    Indenter &indenter = Indenter::instance();
hjk's avatar
hjk committed
645 646
    indenter.setIndentSize(tabSettings.m_indentSize);
    indenter.setTabSize(tabSettings.m_tabSize);
647

hjk's avatar
hjk committed
648
    const QTextDocument *doc = bt->document();
649
    const TextEditor::TextBlockIterator docStart(doc->begin());
Martin Aumüller's avatar
Martin Aumüller committed
650 651 652
    QTextBlock cur = doc->findBlockByNumber(beginLine);
    for(int i = beginLine; i<= endLine; ++i)
    {
653
        if (typedChar == 0 && cur.text().simplified().isEmpty()) {
Martin Aumüller's avatar
Martin Aumüller committed
654
            // clear empty lines
655
            *amount = 0;
Martin Aumüller's avatar
Martin Aumüller committed
656 657 658
            QTextCursor cursor(cur);
            while (!cursor.atBlockEnd())
                cursor.deleteChar();
659 660 661 662
        } else {
            const TextEditor::TextBlockIterator current(cur);
            const TextEditor::TextBlockIterator next(cur.next());
            *amount = indenter.indentForBottomLine(current, docStart, next, typedChar);
Martin Aumüller's avatar
Martin Aumüller committed
663
            tabSettings.indentLine(cur, *amount);
664
        }
Martin Aumüller's avatar
Martin Aumüller committed
665 666
        cur = cur.next();
    }
667 668
}

669
void FakeVimPluginPrivate::quitFakeVim()
670
{
hjk's avatar
hjk committed
671
    theFakeVimSetting(ConfigUseFakeVim)->setValue(false);
672 673
}

674
void FakeVimPluginPrivate::showCommandBuffer(const QString &contents)
hjk's avatar
hjk committed
675
{
676 677 678
    //qDebug() << "SHOW COMMAND BUFFER" << contents;
    Core::EditorManager::instance()->showEditorStatusBar( 
        QLatin1String(Constants::MINI_BUFFER), contents,
679
        tr("Quit FakeVim"), this, SLOT(quitFakeVim()));
hjk's avatar
hjk committed
680 681
}

682
void FakeVimPluginPrivate::showExtraInformation(const QString &text)
683
{
684 685 686
    FakeVimHandler *handler = qobject_cast<FakeVimHandler *>(sender());
    if (handler)
        QMessageBox::information(handler->widget(), tr("FakeVim Information"), text);
687 688
}

689 690
void FakeVimPluginPrivate::changeSelection
    (const QList<QTextEdit::ExtraSelection> &selection)
691
{
692 693 694
    if (FakeVimHandler *handler = qobject_cast<FakeVimHandler *>(sender()))
        if (BaseTextEditor *bt = qobject_cast<BaseTextEditor *>(handler->widget()))
            bt->setExtraSelections(BaseTextEditor::FakeVimSelection, selection);
695 696
}

697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712

///////////////////////////////////////////////////////////////////////
//
// FakeVimPlugin
//
///////////////////////////////////////////////////////////////////////

FakeVimPlugin::FakeVimPlugin()
    : d(new FakeVimPluginPrivate(this))
{}

FakeVimPlugin::~FakeVimPlugin()
{
    delete d;
}

713
bool FakeVimPlugin::initialize(const QStringList &arguments, QString *errorMessage)
714
{
715 716
    Q_UNUSED(arguments)
    Q_UNUSED(errorMessage)
717
    return d->initialize();
718 719 720 721 722 723 724 725 726 727 728 729
}

void FakeVimPlugin::shutdown()
{
    d->shutdown();
}

void FakeVimPlugin::extensionsInitialized()
{
}

#include "fakevimplugin.moc"
hjk's avatar
hjk committed
730 731

Q_EXPORT_PLUGIN(FakeVimPlugin)