fakevimplugin.cpp 14.1 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 8
**
** Contact:  Qt Software Information (qt-info@nokia.com)
**
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 26
** If you are unsure which license is appropriate for your use, please
** contact the sales department at qt-sales@nokia.com.
hjk's avatar
hjk committed
27
**
28
**************************************************************************/
hjk's avatar
hjk committed
29 30 31

#include "fakevimplugin.h"

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

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

#include <utils/qtcassert.h>

60 61
#include <indenter.h>

hjk's avatar
hjk committed
62
#include <QtCore/QDebug>
63
#include <QtCore/QtPlugin>
hjk's avatar
hjk committed
64 65 66 67
#include <QtCore/QObject>
#include <QtCore/QPoint>
#include <QtCore/QSettings>

68
#include <QtGui/QMessageBox>
hjk's avatar
hjk committed
69 70 71
#include <QtGui/QPlainTextEdit>
#include <QtGui/QTextBlock>
#include <QtGui/QTextCursor>
72
#include <QtGui/QTextEdit>
hjk's avatar
hjk committed
73 74 75 76 77 78 79 80 81 82 83


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


namespace FakeVim {
namespace Constants {

84 85
const char * const INSTALL_HANDLER        = "TextEditor.FakeVimHandler";
const char * const MINI_BUFFER            = "TextEditor.FakeVimMiniBuffer";
hjk's avatar
hjk committed
86 87 88 89 90 91 92 93
const char * const INSTALL_KEY            = "Alt+V,Alt+V";

} // namespace Constants
} // namespace FakeVim


///////////////////////////////////////////////////////////////////////
//
94
// FakeVimPluginPrivate
hjk's avatar
hjk committed
95 96 97
//
///////////////////////////////////////////////////////////////////////

98 99 100 101
namespace FakeVim {
namespace Internal {

class FakeVimPluginPrivate : public QObject
hjk's avatar
hjk committed
102
{
103 104 105 106 107 108 109
    Q_OBJECT

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

110
    bool initialize();
111 112 113 114 115
    void shutdown();

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

hjk's avatar
hjk committed
117
    void installHandlerOnCurrentEditor();
118 119
    void installHandler(Core::IEditor *editor);
    void removeHandler();
120
    void triggerCompletions();
121 122 123 124 125

    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);
126
    void moveToMatchingParenthesis(bool *moved, bool *forward, QTextCursor *cursor);
hjk's avatar
hjk committed
127
    void indentRegion(int *amount, int beginLine, int endLine,  QChar typedChar);
128 129 130 131 132 133 134 135 136 137 138 139 140

private:
    FakeVimPlugin *q;
    QAction *m_installHandlerAction; 
};

} // namespace Internal
} // namespace FakeVim

FakeVimPluginPrivate::FakeVimPluginPrivate(FakeVimPlugin *plugin)
{       
    q = plugin;
    m_installHandlerAction = 0;
hjk's avatar
hjk committed
141 142
}

143 144 145
FakeVimPluginPrivate::~FakeVimPluginPrivate()
{
}
hjk's avatar
hjk committed
146

147
void FakeVimPluginPrivate::shutdown()
hjk's avatar
hjk committed
148 149 150
{
}

151
bool FakeVimPluginPrivate::initialize()
hjk's avatar
hjk committed
152
{
hjk's avatar
hjk committed
153
    Core::ActionManager *actionManager = Core::ICore::instance()->actionManager();
hjk's avatar
hjk committed
154 155 156 157 158 159 160 161
    QTC_ASSERT(actionManager, return false);

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

    m_installHandlerAction = new QAction(this);
    m_installHandlerAction->setText(tr("Set vi-Style Keyboard Action Handler"));
    
con's avatar
con committed
162
    Core::Command *cmd = 0;
hjk's avatar
hjk committed
163 164 165 166
    cmd = actionManager->registerAction(m_installHandlerAction,
        Constants::INSTALL_HANDLER, globalcontext);
    cmd->setDefaultKeySequence(QKeySequence(Constants::INSTALL_KEY));

167
    ActionContainer *advancedMenu =
hjk's avatar
hjk committed
168
        actionManager->actionContainer(Core::Constants::M_EDIT_ADVANCED);
169
    advancedMenu->addAction(cmd, Core::Constants::G_EDIT_EDITOR);
hjk's avatar
hjk committed
170 171

    connect(m_installHandlerAction, SIGNAL(triggered()),
hjk's avatar
hjk committed
172
        this, SLOT(installHandlerOnCurrentEditor()));
173 174

    // EditorManager
hjk's avatar
hjk committed
175
    QObject *editorManager = Core::ICore::instance()->editorManager();
176 177 178 179 180
    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
181 182 183
    return true;
}

184
void FakeVimPluginPrivate::installHandler(Core::IEditor *editor)
185
{
186 187 188
    if (!editor)
        return;

189
    QWidget *widget = editor->widget();
190 191
    if (!widget)
        return;
192 193 194 195

    // we can only handle QTextEdit and QPlainTextEdit
    if (!qobject_cast<QTextEdit *>(widget) && !qobject_cast<QPlainTextEdit *>(widget))
        return;
196
    
197
    FakeVimHandler *handler = new FakeVimHandler(widget, widget);
198 199 200 201

    connect(handler, SIGNAL(extraInformationChanged(QString)),
        this, SLOT(showExtraInformation(QString)));
    connect(handler, SIGNAL(commandBufferChanged(QString)),
hjk's avatar
hjk committed
202
        this, SLOT(showCommandBuffer(QString)));
203 204 205 206 207 208
    connect(handler, SIGNAL(quitRequested()),
        this, SLOT(removeHandler()), Qt::QueuedConnection);
    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>)));
209 210
    connect(handler, SIGNAL(moveToMatchingParenthesis(bool*,bool*,QTextCursor*)),
        this, SLOT(moveToMatchingParenthesis(bool*,bool*,QTextCursor*)));
hjk's avatar
hjk committed
211 212
    connect(handler, SIGNAL(indentRegion(int*,int,int,QChar)),
        this, SLOT(indentRegion(int*,int,int,QChar)));
213 214
    connect(handler, SIGNAL(completionRequested()),
        this, SLOT(triggerCompletions()));
215 216 217

    handler->setupWidget();
    handler->setExtraData(editor);
218

219
    if (BaseTextEditor *bt = qobject_cast<BaseTextEditor *>(widget)) {
220 221
        using namespace TextEditor;
        using namespace FakeVim::Constants;
222
        handler->setCurrentFileName(editor->file()->fileName());
223
        TabSettings settings = bt->tabSettings();
224
        handler->setConfigValue(ConfigTabStop,
225
            QString::number(settings.m_tabSize));
226
        handler->setConfigValue(ConfigShiftWidth,
227
            QString::number(settings.m_indentSize));
228
        handler->setConfigValue(ConfigExpandTab,
229
            settings.m_spacesForTabs ? ConfigOn : ConfigOff);
230
        handler->setConfigValue(ConfigSmartTab,
231
            settings.m_smartBackspace ? ConfigOn : ConfigOff); 
232
        handler->setConfigValue(ConfigAutoIndent,
233
            settings.m_autoIndent ? ConfigOn : ConfigOff); 
234
    }
hjk's avatar
hjk committed
235 236
}

hjk's avatar
hjk committed
237 238 239 240 241
void FakeVimPluginPrivate::installHandlerOnCurrentEditor()
{
    installHandler(EditorManager::instance()->currentEditor());
}

242 243 244 245 246 247
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
248 249 250
        TextEditor::Internal::CompletionSupport::instance()->
            autoComplete(bt->editableInterface(), false);
   //     bt->triggerCompletions();
251 252
}

253 254
void FakeVimPluginPrivate::writeFile(bool *handled,
    const QString &fileName, const QString &contents)
255
{
256 257
    Q_UNUSED(contents);

258 259 260 261 262 263
    FakeVimHandler *handler = qobject_cast<FakeVimHandler *>(sender());
    if (!handler)
        return;

    Core::IEditor *editor = qobject_cast<Core::IEditor *>(handler->extraData());
    if (editor && editor->file()->fileName() == fileName) {
hjk's avatar
hjk committed
264
        // Handle that as a special case for nicer interaction with core
265
        Core::IFile *file = editor->file();
hjk's avatar
hjk committed
266
        Core::ICore::instance()->fileManager()->blockFileChange(file);
267
        file->save(fileName);
hjk's avatar
hjk committed
268
        Core::ICore::instance()->fileManager()->unblockFileChange(file);
269 270
        *handled = true;
    } 
271 272
}

273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308
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;
   } else {
        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
309
void FakeVimPluginPrivate::indentRegion(int *amount, int beginLine, int endLine,
310 311 312 313 314 315 316 317 318 319 320 321 322 323 324
      QChar typedChar)
{
    FakeVimHandler *handler = qobject_cast<FakeVimHandler *>(sender());
    if (!handler)
        return;

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

    typedef SharedTools::Indenter<TextEditor::TextBlockIterator> Indenter;
    Indenter &indenter = Indenter::instance();
    indenter.setIndentSize(bt->tabSettings().m_indentSize);
    indenter.setTabSize(bt->tabSettings().m_tabSize);

hjk's avatar
hjk committed
325 326 327
    const QTextDocument *doc = bt->document();
    QTextBlock begin = doc->findBlockByNumber(beginLine);
    QTextBlock end = doc->findBlockByNumber(endLine);
328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349
    const TextEditor::TextBlockIterator docStart(doc->begin());
    QTextBlock cur = begin;
    do {
        if (typedChar == 0 && cur.text().simplified().isEmpty()) {
            *amount = 0;
            if (cur != end) {
                QTextCursor cursor(cur);
                while (!cursor.atBlockEnd())
                    cursor.deleteChar();
            }
        } else {
            const TextEditor::TextBlockIterator current(cur);
            const TextEditor::TextBlockIterator next(cur.next());
            *amount = indenter.indentForBottomLine(current, docStart, next, typedChar);
            if (cur != end)
                bt->tabSettings().indentLine(cur, *amount);
        }
        if (cur != end)
           cur = cur.next();
    } while (cur != end);
}

350
void FakeVimPluginPrivate::removeHandler()
hjk's avatar
hjk committed
351
{
352 353 354 355
    if (FakeVimHandler *handler = qobject_cast<FakeVimHandler *>(sender())) {
        handler->restoreWidget();
        handler->deleteLater();
    }
hjk's avatar
hjk committed
356
    Core::EditorManager::instance()->hideEditorStatusBar(
hjk's avatar
hjk committed
357
        QLatin1String(Constants::MINI_BUFFER));
hjk's avatar
hjk committed
358 359
}

360
void FakeVimPluginPrivate::editorOpened(Core::IEditor *editor)
361 362
{
    //qDebug() << "OPENING: " << editor << editor->widget();
363 364 365 366
    QSettings *s = ICore::instance()->settings();
    bool automatic = s->value("textInteractionSettings/UseVim").toBool();
    if (automatic)
       installHandler(editor);
367 368
}

369
void FakeVimPluginPrivate::editorAboutToClose(Core::IEditor *editor)
370
{
hjk's avatar
hjk committed
371
    //qDebug() << "CLOSING: " << editor << editor->widget();
372
    Q_UNUSED(editor);
373 374
}

375
void FakeVimPluginPrivate::showCommandBuffer(const QString &contents)
hjk's avatar
hjk committed
376
{
377 378
    FakeVimHandler *handler = qobject_cast<FakeVimHandler *>(sender());
    if (handler) {
379 380
        //qDebug() << "SHOW COMMAND BUFFER" << contents;
        Core::EditorManager::instance()->showEditorStatusBar( 
381 382
            QLatin1String(Constants::MINI_BUFFER), contents,
            tr("Quit FakeVim"), handler, SLOT(quit()));
383 384
    } else {
        qDebug() << "\nNO HANDLER\n";
385
    }
hjk's avatar
hjk committed
386 387
}

388
void FakeVimPluginPrivate::showExtraInformation(const QString &text)
389
{
390 391 392
    FakeVimHandler *handler = qobject_cast<FakeVimHandler *>(sender());
    if (handler)
        QMessageBox::information(handler->widget(), tr("FakeVim Information"), text);
393 394
}

395 396
void FakeVimPluginPrivate::changeSelection
    (const QList<QTextEdit::ExtraSelection> &selection)
397
{
398 399 400
    if (FakeVimHandler *handler = qobject_cast<FakeVimHandler *>(sender()))
        if (BaseTextEditor *bt = qobject_cast<BaseTextEditor *>(handler->widget()))
            bt->setExtraSelections(BaseTextEditor::FakeVimSelection, selection);
401 402
}

403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418

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

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

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

419
bool FakeVimPlugin::initialize(const QStringList &arguments, QString *errorMessage)
420
{
421 422 423
    Q_UNUSED(arguments);
    Q_UNUSED(errorMessage);
    return d->initialize();
424 425 426 427 428 429 430 431 432 433 434 435
}

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

void FakeVimPlugin::extensionsInitialized()
{
}

#include "fakevimplugin.moc"
hjk's avatar
hjk committed
436 437

Q_EXPORT_PLUGIN(FakeVimPlugin)