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
342
    Core::EditorManager::instance()->hideEditorInfoBar(
        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)