fakevimplugin.cpp 13.5 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 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>
#include <texteditor/itexteditor.h>
#include <texteditor/texteditorconstants.h>
52
#include <texteditor/interactionsettings.h>
53 54
#include <texteditor/tabsettings.h>
#include <texteditor/texteditorsettings.h>
55
#include <texteditor/textblockiterator.h>
hjk's avatar
hjk committed
56 57 58

#include <utils/qtcassert.h>

59 60
#include <indenter.h>

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

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


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


namespace FakeVim {
namespace Constants {

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

} // namespace Constants
} // namespace FakeVim


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

97 98 99 100
namespace FakeVim {
namespace Internal {

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

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

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

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

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

    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);
124
    void moveToMatchingParenthesis(bool *moved, bool *forward, QTextCursor *cursor);
125
    void indentRegion(int *amount, QTextBlock begin, QTextBlock end,  QChar typedChar);
126 127 128 129 130 131 132 133 134 135 136 137 138

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

} // namespace Internal
} // namespace FakeVim

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

141 142 143
FakeVimPluginPrivate::~FakeVimPluginPrivate()
{
}
hjk's avatar
hjk committed
144

145
void FakeVimPluginPrivate::shutdown()
hjk's avatar
hjk committed
146 147 148
{
}

149
bool FakeVimPluginPrivate::initialize()
hjk's avatar
hjk committed
150
{
hjk's avatar
hjk committed
151
    Core::ActionManager *actionManager = Core::ICore::instance()->actionManager();
hjk's avatar
hjk committed
152 153 154 155 156 157 158 159
    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
160
    Core::Command *cmd = 0;
hjk's avatar
hjk committed
161 162 163 164
    cmd = actionManager->registerAction(m_installHandlerAction,
        Constants::INSTALL_HANDLER, globalcontext);
    cmd->setDefaultKeySequence(QKeySequence(Constants::INSTALL_KEY));

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

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

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

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

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

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

    connect(handler, SIGNAL(extraInformationChanged(QString)),
        this, SLOT(showExtraInformation(QString)));
    connect(handler, SIGNAL(commandBufferChanged(QString)),
hjk's avatar
hjk committed
200
        this, SLOT(showCommandBuffer(QString)));
201 202 203 204 205 206
    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>)));
207 208
    connect(handler, SIGNAL(moveToMatchingParenthesis(bool*,bool*,QTextCursor*)),
        this, SLOT(moveToMatchingParenthesis(bool*,bool*,QTextCursor*)));
209 210
    connect(handler, SIGNAL(indentRegion(int*,QTextBlock,QTextBlock,QChar)),
        this, SLOT(indentRegion(int*,QTextBlock,QTextBlock,QChar)));
211 212 213

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

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

hjk's avatar
hjk committed
233 234 235 236 237
void FakeVimPluginPrivate::installHandlerOnCurrentEditor()
{
    installHandler(EditorManager::instance()->currentEditor());
}

238 239
void FakeVimPluginPrivate::writeFile(bool *handled,
    const QString &fileName, const QString &contents)
240
{
241 242
    Q_UNUSED(contents);

243 244 245 246 247 248
    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
249
        // Handle that as a special case for nicer interaction with core
250
        Core::IFile *file = editor->file();
hjk's avatar
hjk committed
251
        Core::ICore::instance()->fileManager()->blockFileChange(file);
252
        file->save(fileName);
hjk's avatar
hjk committed
253
        Core::ICore::instance()->fileManager()->unblockFileChange(file);
254 255
        *handled = true;
    } 
256 257
}

258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293
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);
            }
        }
    }
}

294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332
void FakeVimPluginPrivate::indentRegion(int *amount, QTextBlock begin, QTextBlock end,
      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);

    const QTextDocument *doc = begin.document();
    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);
}

333
void FakeVimPluginPrivate::removeHandler()
hjk's avatar
hjk committed
334
{
335 336 337 338
    if (FakeVimHandler *handler = qobject_cast<FakeVimHandler *>(sender())) {
        handler->restoreWidget();
        handler->deleteLater();
    }
hjk's avatar
hjk committed
339 340
    Core::EditorManager::instance()->hideEditorInfoBar(
        QLatin1String(Constants::MINI_BUFFER));
hjk's avatar
hjk committed
341 342
}

343
void FakeVimPluginPrivate::editorOpened(Core::IEditor *editor)
344 345
{
    //qDebug() << "OPENING: " << editor << editor->widget();
346 347 348 349
    QSettings *s = ICore::instance()->settings();
    bool automatic = s->value("textInteractionSettings/UseVim").toBool();
    if (automatic)
       installHandler(editor);
350 351
}

352
void FakeVimPluginPrivate::editorAboutToClose(Core::IEditor *editor)
353
{
hjk's avatar
hjk committed
354
    //qDebug() << "CLOSING: " << editor << editor->widget();
355
    Q_UNUSED(editor);
356 357
}

358
void FakeVimPluginPrivate::showCommandBuffer(const QString &contents)
hjk's avatar
hjk committed
359
{
360 361
    FakeVimHandler *handler = qobject_cast<FakeVimHandler *>(sender());
    if (handler) {
362 363
        //qDebug() << "SHOW COMMAND BUFFER" << contents;
        Core::EditorManager::instance()->showEditorStatusBar( 
364 365
            QLatin1String(Constants::MINI_BUFFER), contents,
            tr("Quit FakeVim"), handler, SLOT(quit()));
366 367
    } else {
        qDebug() << "\nNO HANDLER\n";
368
    }
hjk's avatar
hjk committed
369 370
}

371
void FakeVimPluginPrivate::showExtraInformation(const QString &text)
372
{
373 374 375
    FakeVimHandler *handler = qobject_cast<FakeVimHandler *>(sender());
    if (handler)
        QMessageBox::information(handler->widget(), tr("FakeVim Information"), text);
376 377
}

378 379
void FakeVimPluginPrivate::changeSelection
    (const QList<QTextEdit::ExtraSelection> &selection)
380
{
381 382 383
    if (FakeVimHandler *handler = qobject_cast<FakeVimHandler *>(sender()))
        if (BaseTextEditor *bt = qobject_cast<BaseTextEditor *>(handler->widget()))
            bt->setExtraSelections(BaseTextEditor::FakeVimSelection, selection);
384 385
}

386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401

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

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

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

402
bool FakeVimPlugin::initialize(const QStringList &arguments, QString *errorMessage)
403
{
404 405 406
    Q_UNUSED(arguments);
    Q_UNUSED(errorMessage);
    return d->initialize();
407 408 409 410 411 412 413 414 415 416 417 418
}

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

void FakeVimPlugin::extensionsInitialized()
{
}

#include "fakevimplugin.moc"
hjk's avatar
hjk committed
419 420

Q_EXPORT_PLUGIN(FakeVimPlugin)