fakevimplugin.cpp 13.5 KB
Newer Older
hjk's avatar
hjk committed
1 2 3 4
/***************************************************************************
**
** This file is part of Qt Creator
**
5
** Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies).
hjk's avatar
hjk committed
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
**
** Contact:  Qt Software Information (qt-info@nokia.com)
**
**
** Non-Open Source Usage
**
** Licensees may use this file in accordance with the Qt Beta Version
** License Agreement, Agreement version 2.2 provided with the Software or,
** alternatively, in accordance with the terms contained in a written
** agreement between you and Nokia.
**
** GNU General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU General
** Public License versions 2.0 or 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the packaging
** of this file.  Please review the following information to ensure GNU
** General Public Licensing requirements will be met:
**
** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
** http://www.gnu.org/copyleft/gpl.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt GPL Exception
** version 1.3, included in the file GPL_EXCEPTION.txt in this package.
**
***************************************************************************/

#include "fakevimplugin.h"

36 37
#include "fakevimconstants.h"
#include "fakevimhandler.h"
hjk's avatar
hjk committed
38

39
#include <coreplugin/actionmanager/actionmanager.h>
hjk's avatar
hjk committed
40 41
#include <coreplugin/coreconstants.h>
#include <coreplugin/editormanager/editormanager.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 46 47 48 49 50 51 52 53 54 55
#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>
56
#include <texteditor/interactionsettings.h>
57 58
#include <texteditor/tabsettings.h>
#include <texteditor/texteditorsettings.h>
59
#include <texteditor/textblockiterator.h>
hjk's avatar
hjk committed
60 61 62

#include <utils/qtcassert.h>

63 64
#include <indenter.h>

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

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


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


namespace FakeVim {
namespace Constants {

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

} // namespace Constants
} // namespace FakeVim


///////////////////////////////////////////////////////////////////////
//
97
// FakeVimPluginPrivate
hjk's avatar
hjk committed
98 99 100
//
///////////////////////////////////////////////////////////////////////

101 102 103 104
namespace FakeVim {
namespace Internal {

class FakeVimPluginPrivate : public QObject
hjk's avatar
hjk committed
105
{
106 107 108 109 110 111 112
    Q_OBJECT

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

113
    bool initialize();
114 115 116 117 118
    void shutdown();

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

hjk's avatar
hjk committed
120
    void installHandlerOnCurrentEditor();
121 122 123 124 125 126 127
    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);
128
    void moveToMatchingParenthesis(bool *moved, bool *forward, QTextCursor *cursor);
129
    void indentRegion(int *amount, QTextBlock begin, QTextBlock end,  QChar typedChar);
130 131 132 133 134 135 136 137 138 139 140 141 142

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

} // namespace Internal
} // namespace FakeVim

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

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

149
void FakeVimPluginPrivate::shutdown()
hjk's avatar
hjk committed
150 151 152
{
}

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

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

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

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

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

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

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

    connect(handler, SIGNAL(extraInformationChanged(QString)),
        this, SLOT(showExtraInformation(QString)));
    connect(handler, SIGNAL(commandBufferChanged(QString)),
hjk's avatar
hjk committed
204
        this, SLOT(showCommandBuffer(QString)));
205 206 207 208 209 210
    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>)));
211 212
    connect(handler, SIGNAL(moveToMatchingParenthesis(bool*,bool*,QTextCursor*)),
        this, SLOT(moveToMatchingParenthesis(bool*,bool*,QTextCursor*)));
213 214
    connect(handler, SIGNAL(indentRegion(int*,QTextBlock,QTextBlock,QChar)),
        this, SLOT(indentRegion(int*,QTextBlock,QTextBlock,QChar)));
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
void FakeVimPluginPrivate::writeFile(bool *handled,
    const QString &fileName, const QString &contents)
244
{
245 246
    Q_UNUSED(contents);

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

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 333 334 335 336
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);
}

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

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

356
void FakeVimPluginPrivate::editorAboutToClose(Core::IEditor *editor)
357
{
hjk's avatar
hjk committed
358
    //qDebug() << "CLOSING: " << editor << editor->widget();
359
    Q_UNUSED(editor);
360 361
}

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

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

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

388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403

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

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

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

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

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

void FakeVimPlugin::extensionsInitialized()
{
}

#include "fakevimplugin.moc"
hjk's avatar
hjk committed
421 422

Q_EXPORT_PLUGIN(FakeVimPlugin)