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);
hjk's avatar
hjk committed
125
    void indentRegion(int *amount, int beginLine, int endLine,  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*)));
hjk's avatar
hjk committed
209 210
    connect(handler, SIGNAL(indentRegion(int*,int,int,QChar)),
        this, SLOT(indentRegion(int*,int,int,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);
            }
        }
    }
}

hjk's avatar
hjk committed
294
void FakeVimPluginPrivate::indentRegion(int *amount, int beginLine, int endLine,
295 296 297 298 299 300 301 302 303 304 305 306 307 308 309
      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
310 311 312
    const QTextDocument *doc = bt->document();
    QTextBlock begin = doc->findBlockByNumber(beginLine);
    QTextBlock end = doc->findBlockByNumber(endLine);
313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334
    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);
}

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

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

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

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