basetexteditor.cpp 210 KB
Newer Older
1
/**************************************************************************
con's avatar
con committed
2
3
4
**
** This file is part of Qt Creator
**
con's avatar
con committed
5
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
con's avatar
con committed
6
**
hjk's avatar
hjk committed
7
** Contact: Nokia Corporation (info@qt.nokia.com)
con's avatar
con committed
8
**
9
**
10
** GNU Lesser General Public License Usage
11
**
hjk's avatar
hjk committed
12
13
14
15
16
17
** 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.
18
**
con's avatar
con committed
19
** In addition, as a special exception, Nokia gives you certain additional
hjk's avatar
hjk committed
20
** rights. These rights are described in the Nokia Qt LGPL Exception
con's avatar
con committed
21
22
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
23
24
25
26
27
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
con's avatar
con committed
28
** If you have questions regarding the use of this file, please contact
Tobias Hunger's avatar
Tobias Hunger committed
29
** Nokia at info@qt.nokia.com.
con's avatar
con committed
30
**
31
**************************************************************************/
hjk's avatar
hjk committed
32

con's avatar
con committed
33
#include "texteditor_global.h"
hjk's avatar
hjk committed
34

con's avatar
con committed
35
#include "basetextdocument.h"
36
#include "basetextdocumentlayout.h"
con's avatar
con committed
37
#include "basetexteditor_p.h"
38
#include "behaviorsettings.h"
con's avatar
con committed
39
#include "codecselector.h"
40
#include "completionsettings.h"
41
#include "tabsettings.h"
Jarek Kobus's avatar
Jarek Kobus committed
42
43
#include "typingsettings.h"
#include "icodestylepreferences.h"
44
45
#include "texteditorconstants.h"
#include "texteditorplugin.h"
46
#include "syntaxhighlighter.h"
47
48
#include "tooltip.h"
#include "tipcontents.h"
49
#include "indenter.h"
50
#include "autocompleter.h"
51
#include "snippet.h"
Leandro Melo's avatar
Leandro Melo committed
52
53
54
#include "codeassistant.h"
#include "defaultassistinterface.h"
#include "convenience.h"
55
#include "texteditorsettings.h"
56
#include "texteditoroverlay.h"
con's avatar
con committed
57

58
#include <aggregation/aggregate.h>
59
#include <coreplugin/actionmanager/actionmanager.h>
60
61
#include <coreplugin/actionmanager/actioncontainer.h>
#include <coreplugin/actionmanager/command.h>
con's avatar
con committed
62
63
#include <coreplugin/coreconstants.h>
#include <coreplugin/editormanager/editormanager.h>
64
#include <coreplugin/icore.h>
65
#include <coreplugin/infobar.h>
66
#include <coreplugin/manhattanstyle.h>
67
#include <coreplugin/id.h>
68
#include <extensionsystem/pluginmanager.h>
con's avatar
con committed
69
70
#include <find/basetextfind.h>
#include <utils/linecolumnlabel.h>
hjk's avatar
hjk committed
71
#include <utils/qtcassert.h>
72
#include <utils/stylehelper.h>
con's avatar
con committed
73
74
75
76
77
78

#include <QtCore/QCoreApplication>
#include <QtCore/QTextCodec>
#include <QtCore/QFile>
#include <QtCore/QDebug>
#include <QtCore/QTimer>
79
#include <QtCore/QTimeLine>
80
#include <QtCore/QTime>
con's avatar
con committed
81
82
83
84
85
86
87
88
89
90
91
92
93
#include <QtGui/QAbstractTextDocumentLayout>
#include <QtGui/QApplication>
#include <QtGui/QKeyEvent>
#include <QtGui/QLabel>
#include <QtGui/QLayout>
#include <QtGui/QPainter>
#include <QtGui/QPrinter>
#include <QtGui/QPrintDialog>
#include <QtGui/QScrollBar>
#include <QtGui/QShortcut>
#include <QtGui/QStyle>
#include <QtGui/QSyntaxHighlighter>
#include <QtGui/QTextCursor>
94
#include <QtGui/QTextDocumentFragment>
con's avatar
con committed
95
96
97
98
#include <QtGui/QTextBlock>
#include <QtGui/QTextLayout>
#include <QtGui/QToolBar>
#include <QtGui/QInputDialog>
99
#include <QtGui/QMenu>
100
#include <QtGui/QMessageBox>
con's avatar
con committed
101

102
103
//#define DO_FOO

Leandro Melo's avatar
Leandro Melo committed
104
105
106
107
108
109
110
111
112
113
114
115
/*!
    \namespace TextEditor
    \brief The TextEditor namespace contains the base text editor and several classes which
    provide supporting functionality like snippets, highlighting, \l{CodeAssist}{code assist},
    indentation and style, and others.
*/

/*!
    \namespace TextEditor::Internal
    \internal
*/

con's avatar
con committed
116
117
118
119
using namespace TextEditor;
using namespace TextEditor::Internal;

namespace TextEditor {
120
namespace Internal {
con's avatar
con committed
121
122

class TextEditExtraArea : public QWidget {
123
    BaseTextEditorWidget *textEdit;
con's avatar
con committed
124
public:
125
    TextEditExtraArea(BaseTextEditorWidget *edit):QWidget(edit) {
con's avatar
con committed
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
        textEdit = edit;
        setAutoFillBackground(true);
    }
public:

    QSize sizeHint() const {
        return QSize(textEdit->extraAreaWidth(), 0);
    }
protected:
    void paintEvent(QPaintEvent *event){
        textEdit->extraAreaPaintEvent(event);
    }
    void mousePressEvent(QMouseEvent *event){
        textEdit->extraAreaMouseEvent(event);
    }
    void mouseMoveEvent(QMouseEvent *event){
        textEdit->extraAreaMouseEvent(event);
    }
    void mouseReleaseEvent(QMouseEvent *event){
        textEdit->extraAreaMouseEvent(event);
    }
    void leaveEvent(QEvent *event){
        textEdit->extraAreaLeaveEvent(event);
    }
150
151
152
    void contextMenuEvent(QContextMenuEvent *event) {
        textEdit->extraAreaContextMenuEvent(event);
    }
con's avatar
con committed
153
154
155
156
157
158

    void wheelEvent(QWheelEvent *event) {
        QCoreApplication::sendEvent(textEdit->viewport(), event);
    }
};

159
160
} // namespace Internal
} // namespace TextEditor
con's avatar
con committed
161

162
Core::IEditor *BaseTextEditorWidget::openEditorAt(const QString &fileName, int line, int column,
163
164
165
                                 const QString &editorKind,
                                 Core::EditorManager::OpenEditorFlags flags,
                                 bool *newEditor)
con's avatar
con committed
166
{
167
    Core::EditorManager *editorManager = Core::EditorManager::instance();
168
    editorManager->cutForwardNavigationHistory();
169
    editorManager->addCurrentPositionToNavigationHistory();
170
    Core::IEditor *editor = editorManager->openEditor(fileName, editorKind,
171
            flags, newEditor);
con's avatar
con committed
172
    TextEditor::ITextEditor *texteditor = qobject_cast<TextEditor::ITextEditor *>(editor);
173
    if (texteditor && line != -1) {
con's avatar
con committed
174
175
176
        texteditor->gotoLine(line, column);
        return texteditor;
    }
177

178
    return editor;
con's avatar
con committed
179
180
}

181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
static void convertToPlainText(QString &txt)
{
    QChar *uc = txt.data();
    QChar *e = uc + txt.size();

    for (; uc != e; ++uc) {
        switch (uc->unicode()) {
        case 0xfdd0: // QTextBeginningOfFrame
        case 0xfdd1: // QTextEndOfFrame
        case QChar::ParagraphSeparator:
        case QChar::LineSeparator:
            *uc = QLatin1Char('\n');
            break;
        case QChar::Nbsp:
            *uc = QLatin1Char(' ');
            break;
        default:
            ;
        }
    }
}

203
BaseTextEditorWidget::BaseTextEditorWidget(QWidget *parent)
con's avatar
con committed
204
205
    : QPlainTextEdit(parent)
{
206
    d = new BaseTextEditorPrivate;
con's avatar
con committed
207
208
209
210
211
    d->q = this;
    d->m_extraArea = new TextEditExtraArea(this);
    d->m_extraArea->setMouseTracking(true);
    setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);

212
    d->m_overlay = new TextEditorOverlay(this);
213
    d->m_snippetOverlay = new TextEditorOverlay(this);
214
    d->m_searchResultOverlay = new TextEditorOverlay(this);
215
    d->m_refactorOverlay = new RefactorOverlay(this);
216

con's avatar
con committed
217
218
219
220
221
222
223
224
225
226
    d->setupDocumentSignals(d->m_document);

    d->m_lastScrollPos = -1;

    // from RESEARCH

    setLayoutDirection(Qt::LeftToRight);
    viewport()->setMouseTracking(true);
    d->extraAreaSelectionAnchorBlockNumber
        = d->extraAreaToggleMarkBlockNumber
mae's avatar
mae committed
227
        = d->extraAreaHighlightFoldedBlockNumber
con's avatar
con committed
228
229
        = -1;

mae's avatar
mae committed
230
    d->visibleFoldedBlockNumber = d->suggestedVisibleFoldedBlockNumber = -1;
con's avatar
con committed
231
232
233
234
235
236
237
238
239
240
241

    connect(this, SIGNAL(blockCountChanged(int)), this, SLOT(slotUpdateExtraAreaWidth()));
    connect(this, SIGNAL(modificationChanged(bool)), this, SLOT(slotModificationChanged(bool)));
    connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(slotCursorPositionChanged()));
    connect(this, SIGNAL(updateRequest(QRect, int)), this, SLOT(slotUpdateRequest(QRect, int)));
    connect(this, SIGNAL(selectionChanged()), this, SLOT(slotSelectionChanged()));

//     (void) new QShortcut(tr("CTRL+L"), this, SLOT(centerCursor()), 0, Qt::WidgetShortcut);
//     (void) new QShortcut(tr("F9"), this, SLOT(slotToggleMark()), 0, Qt::WidgetShortcut);
//     (void) new QShortcut(tr("F11"), this, SLOT(slotToggleBlockVisible()));

242
243
244
245
#ifdef DO_FOO
    (void) new QShortcut(tr("CTRL+D"), this, SLOT(doFoo()));
#endif

con's avatar
con committed
246
247
248
249
250
251
252
253
254
255

    // parentheses matcher
    d->m_formatRange = true;
    d->m_matchFormat.setForeground(Qt::red);
    d->m_rangeFormat.setBackground(QColor(0xb4, 0xee, 0xb4));
    d->m_mismatchFormat.setBackground(Qt::magenta);
    d->m_parenthesesMatchingTimer = new QTimer(this);
    d->m_parenthesesMatchingTimer->setSingleShot(true);
    connect(d->m_parenthesesMatchingTimer, SIGNAL(timeout()), this, SLOT(_q_matchParentheses()));

256
257
258
259
    d->m_highlightBlocksTimer = new QTimer(this);
    d->m_highlightBlocksTimer->setSingleShot(true);
    connect(d->m_highlightBlocksTimer, SIGNAL(timeout()), this, SLOT(_q_highlightBlocks()));

260
    d->m_animator = 0;
con's avatar
con committed
261
262
263
264

    d->m_searchResultFormat.setBackground(QColor(0xffef0b));

    slotUpdateExtraAreaWidth();
con's avatar
con committed
265
    updateHighlights();
con's avatar
con committed
266
267
    setFrameStyle(QFrame::NoFrame);

268
269
270
271
    d->m_delayedUpdateTimer = new QTimer(this);
    d->m_delayedUpdateTimer->setSingleShot(true);
    connect(d->m_delayedUpdateTimer, SIGNAL(timeout()), viewport(), SLOT(update()));

mae's avatar
mae committed
272
    d->m_moveLineUndoHack = false;
con's avatar
con committed
273
274
}

275
BaseTextEditorWidget::~BaseTextEditorWidget()
con's avatar
con committed
276
277
278
279
280
{
    delete d;
    d = 0;
}

281
QString BaseTextEditorWidget::mimeType() const
con's avatar
con committed
282
283
284
285
{
    return d->m_document->mimeType();
}

286
void BaseTextEditorWidget::setMimeType(const QString &mt)
con's avatar
con committed
287
288
289
290
{
    d->m_document->setMimeType(mt);
}

291
void BaseTextEditorWidget::print(QPrinter *printer)
con's avatar
con committed
292
293
294
295
296
297
298
299
300
301
302
303
{
    const bool oldFullPage =  printer->fullPage();
    printer->setFullPage(true);
    QPrintDialog *dlg = new QPrintDialog(printer, this);
    dlg->setWindowTitle(tr("Print Document"));
    if (dlg->exec() == QDialog::Accepted) {
        d->print(printer);
    }
    printer->setFullPage(oldFullPage);
    delete dlg;
}

mae's avatar
mae committed
304
static int foldBoxWidth(const QFontMetrics &fm)
305
306
307
308
309
{
    const int lineSpacing = fm.lineSpacing();
    return lineSpacing + lineSpacing%2 + 1;
}

con's avatar
con committed
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
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
static void printPage(int index, QPainter *painter, const QTextDocument *doc,
                      const QRectF &body, const QRectF &titleBox,
                      const QString &title)
{
    painter->save();

    painter->translate(body.left(), body.top() - (index - 1) * body.height());
    QRectF view(0, (index - 1) * body.height(), body.width(), body.height());

    QAbstractTextDocumentLayout *layout = doc->documentLayout();
    QAbstractTextDocumentLayout::PaintContext ctx;

    painter->setFont(QFont(doc->defaultFont()));
    QRectF box = titleBox.translated(0, view.top());
    int dpix = painter->device()->logicalDpiX();
    int dpiy = painter->device()->logicalDpiY();
    int mx = 5 * dpix / 72.0;
    int my = 2 * dpiy / 72.0;
    painter->fillRect(box.adjusted(-mx, -my, mx, my), QColor(210, 210, 210));
    if (!title.isEmpty())
        painter->drawText(box, Qt::AlignCenter, title);
    const QString pageString = QString::number(index);
    painter->drawText(box, Qt::AlignRight, pageString);

    painter->setClipRect(view);
    ctx.clip = view;
    // don't use the system palette text as default text color, on HP/UX
    // for example that's white, and white text on white paper doesn't
    // look that nice
    ctx.palette.setColor(QPalette::Text, Qt::black);

    layout->draw(painter, ctx);

    painter->restore();
}

void BaseTextEditorPrivate::print(QPrinter *printer)
{
    QTextDocument *doc = q->document();

    QString title = q->displayName();
    if (title.isEmpty())
        printer->setDocName(title);


    QPainter p(printer);

    // Check that there is a valid device to print to.
    if (!p.isActive())
        return;

    doc = doc->clone(doc);

    QTextOption opt = doc->defaultTextOption();
    opt.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
    doc->setDefaultTextOption(opt);

    (void)doc->documentLayout(); // make sure that there is a layout


    QColor background = q->palette().color(QPalette::Base);
    bool backgroundIsDark = background.value() < 128;

    for (QTextBlock srcBlock = q->document()->firstBlock(), dstBlock = doc->firstBlock();
         srcBlock.isValid() && dstBlock.isValid();
         srcBlock = srcBlock.next(), dstBlock = dstBlock.next()) {


        QList<QTextLayout::FormatRange> formatList = srcBlock.layout()->additionalFormats();
        if (backgroundIsDark) {
            // adjust syntax highlighting colors for better contrast
            for (int i = formatList.count() - 1; i >=0; --i) {
                QTextCharFormat &format = formatList[i].format;
                if (format.background().color() == background) {
                    QBrush brush = format.foreground();
                    QColor color = brush.color();
                    int h,s,v,a;
                    color.getHsv(&h, &s, &v, &a);
                    color.setHsv(h, s, qMin(128, v), a);
                    brush.setColor(color);
                    format.setForeground(brush);
                }
                format.setBackground(Qt::white);
            }
        }

        dstBlock.layout()->setAdditionalFormats(formatList);
    }

    QAbstractTextDocumentLayout *layout = doc->documentLayout();
    layout->setPaintDevice(p.device());

    int dpiy = p.device()->logicalDpiY();
    int margin = (int) ((2/2.54)*dpiy); // 2 cm margins

    QTextFrameFormat fmt = doc->rootFrame()->frameFormat();
    fmt.setMargin(margin);
    doc->rootFrame()->setFrameFormat(fmt);

    QRectF pageRect(printer->pageRect());
    QRectF body = QRectF(0, 0, pageRect.width(), pageRect.height());
    QFontMetrics fontMetrics(doc->defaultFont(), p.device());

    QRectF titleBox(margin,
                    body.top() + margin
                    - fontMetrics.height()
                    - 6 * dpiy / 72.0,
                    body.width() - 2*margin,
                    fontMetrics.height());
    doc->setPageSize(body.size());

    int docCopies;
    int pageCopies;
    if (printer->collateCopies() == true){
        docCopies = 1;
        pageCopies = printer->numCopies();
    } else {
        docCopies = printer->numCopies();
        pageCopies = 1;
    }

    int fromPage = printer->fromPage();
    int toPage = printer->toPage();
    bool ascending = true;

    if (fromPage == 0 && toPage == 0) {
        fromPage = 1;
        toPage = doc->pageCount();
    }
    // paranoia check
    fromPage = qMax(1, fromPage);
    toPage = qMin(doc->pageCount(), toPage);

    if (printer->pageOrder() == QPrinter::LastPageFirst) {
        int tmp = fromPage;
        fromPage = toPage;
        toPage = tmp;
        ascending = false;
    }

    for (int i = 0; i < docCopies; ++i) {

        int page = fromPage;
        while (true) {
            for (int j = 0; j < pageCopies; ++j) {
                if (printer->printerState() == QPrinter::Aborted
                    || printer->printerState() == QPrinter::Error)
                    goto UserCanceled;
                printPage(page, &p, doc, body, titleBox, title);
                if (j < pageCopies - 1)
                    printer->newPage();
            }

            if (page == toPage)
                break;

            if (ascending)
                ++page;
            else
                --page;

            printer->newPage();
        }

        if ( i < docCopies - 1)
            printer->newPage();
    }

UserCanceled:
    delete doc;
}


483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
int BaseTextEditorPrivate::visualIndent(const QTextBlock &block) const
{
    if (!block.isValid())
        return 0;
    const QTextDocument *document = block.document();
    int i = 0;
    while (i < block.length()) {
        if (!document->characterAt(block.position() + i).isSpace()) {
            QTextCursor cursor(block);
            cursor.setPosition(block.position() + i);
            return q->cursorRect(cursor).x();
        }
        ++i;
    }

    return 0;
}
con's avatar
con committed
500

501
ITextMarkable *BaseTextEditorWidget::markableInterface() const
con's avatar
con committed
502
503
504
505
{
    return baseTextDocument()->documentMarker();
}

506
BaseTextEditor *BaseTextEditorWidget::editor() const
con's avatar
con committed
507
{
508
    if (!d->m_editor) {
Leandro Melo's avatar
Leandro Melo committed
509
510
        d->m_editor = const_cast<BaseTextEditorWidget *>(this)->createEditor();
        d->m_codeAssistant->configure(d->m_editor);
con's avatar
con committed
511
        connect(this, SIGNAL(textChanged()),
512
                d->m_editor, SIGNAL(contentsChanged()));
con's avatar
con committed
513
        connect(this, SIGNAL(changed()),
514
                d->m_editor, SIGNAL(changed()));
con's avatar
con committed
515
    }
516
    return d->m_editor;
con's avatar
con committed
517
518
519
}


520
void BaseTextEditorWidget::selectEncoding()
con's avatar
con committed
521
522
523
524
525
{
    BaseTextDocument *doc = d->m_document;
    CodecSelector codecSelector(this, doc);

    switch (codecSelector.exec()) {
526
527
528
529
530
531
532
    case CodecSelector::Reload: {
        QString errorString;
        if (!doc->reload(&errorString, codecSelector.selectedCodec())) {
            QMessageBox::critical(this, tr("File Error"), errorString);
            break;
        }
        break; }
con's avatar
con committed
533
534
    case CodecSelector::Save:
        doc->setCodec(codecSelector.selectedCodec());
535
        Core::EditorManager::instance()->saveEditor(editor());
con's avatar
con committed
536
537
538
539
540
541
        break;
    case CodecSelector::Cancel:
        break;
    }
}

542
QString BaseTextEditorWidget::msgTextTooLarge(quint64 size)
543
544
545
546
547
{
    return tr("The text is too large to be displayed (%1 MB).").
           arg(size >> 20);
}

548
bool BaseTextEditorWidget::createNew(const QString &contents)
con's avatar
con committed
549
{
550
551
552
553
554
    if (contents.size() > Core::EditorManager::maxTextFileSize()) {
        setPlainText(msgTextTooLarge(contents.size()));
        document()->setModified(false);
        return false;
    }
con's avatar
con committed
555
556
557
558
559
    setPlainText(contents);
    document()->setModified(false);
    return true;
}

560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
void BaseTextEditorWidget::updateCannotDecodeInfo()
{
    setReadOnly(d->m_document->hasDecodingError());
    if (d->m_document->hasDecodingError()) {
        Core::InfoBarEntry info(
            QLatin1String(Constants::SELECT_ENCODING),
            tr("<b>Error:</b> Could not decode \"%1\" with \"%2\"-encoding. Editing not possible.")
            .arg(displayName()).arg(QString::fromLatin1(d->m_document->codec()->name())));
        info.setCustomButtonInfo(tr("Select Encoding"), this, SLOT(selectEncoding()));
        d->m_document->infoBar()->addInfo(info);
    } else {
        d->m_document->infoBar()->removeInfo(QLatin1String(Constants::SELECT_ENCODING));
    }
}

575
bool BaseTextEditorWidget::open(QString *errorString, const QString &fileName, const QString &realFileName)
con's avatar
con committed
576
{
577
    if (d->m_document->open(errorString, fileName, realFileName)) {
con's avatar
con committed
578
        moveCursor(QTextCursor::Start);
579
        updateCannotDecodeInfo();
con's avatar
con committed
580
581
582
583
584
        return true;
    }
    return false;
}

585
586
587
/*
  Collapses the first comment in a file, if there is only whitespace above
  */
mae's avatar
mae committed
588
void BaseTextEditorPrivate::foldLicenseHeader()
589
590
{
    QTextDocument *doc = q->document();
591
    BaseTextDocumentLayout *documentLayout = qobject_cast<BaseTextDocumentLayout*>(doc->documentLayout());
592
593
594
    QTC_ASSERT(documentLayout, return);
    QTextBlock block = doc->firstBlock();
    const TabSettings &ts = m_document->tabSettings();
595
    while (block.isValid() && block.isVisible()) {
596
        QString text = block.text();
mae's avatar
mae committed
597
598
599
600
601
602
603
604
605
        if (BaseTextDocumentLayout::canFold(block) && block.next().isVisible()) {
            if (text.trimmed().startsWith(QLatin1String("/*"))) {
                BaseTextDocumentLayout::doFoldOrUnfold(block, false);
                moveCursorVisible();
                documentLayout->requestUpdate();
                documentLayout->emitDocumentSizeChanged();
                break;
            }
        }
606
607
608
609
610
611
        if (ts.firstNonSpace(text) < text.size())
            break;
        block = block.next();
    }
}

612
const Utils::ChangeSet &BaseTextEditorWidget::changeSet() const
613
614
615
616
{
    return d->m_changeSet;
}

617
void BaseTextEditorWidget::setChangeSet(const Utils::ChangeSet &changeSet)
618
{
Roberto Raggi's avatar
Roberto Raggi committed
619
620
    using namespace Utils;

621
    d->m_changeSet = changeSet;
Roberto Raggi's avatar
Roberto Raggi committed
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648

    foreach (const ChangeSet::EditOp &op, changeSet.operationList()) {
        // ### TODO: process the edit operation

        switch (op.type) {
        case ChangeSet::EditOp::Replace:
            break;

        case ChangeSet::EditOp::Move:
            break;

        case ChangeSet::EditOp::Insert:
            break;

        case ChangeSet::EditOp::Remove:
            break;

        case ChangeSet::EditOp::Flip:
            break;

        case ChangeSet::EditOp::Copy:
            break;

        default:
            break;
        } // switch
    }
649
650
}

651
Core::IFile *BaseTextEditorWidget::file()
con's avatar
con committed
652
653
654
655
{
    return d->m_document;
}

656
void BaseTextEditorWidget::editorContentsChange(int position, int charsRemoved, int charsAdded)
con's avatar
con committed
657
{
658
659
660
    if (d->m_animator)
        d->m_animator->finish();

con's avatar
con committed
661
    d->m_contentsChanged = true;
662
    QTextDocument *doc = document();
con's avatar
con committed
663
664
665
666
667
668

    // Keep the line numbers and the block information for the text marks updated
    if (charsRemoved != 0) {
        d->updateMarksLineNumber();
        d->updateMarksBlock(document()->findBlock(position));
    } else {
669
670
        const QTextBlock posBlock = doc->findBlock(position);
        const QTextBlock nextBlock = doc->findBlock(position + charsAdded);
con's avatar
con committed
671
672
673
674
675
676
677
678
        if (posBlock != nextBlock) {
            d->updateMarksLineNumber();
            d->updateMarksBlock(posBlock);
            d->updateMarksBlock(nextBlock);
        } else {
            d->updateMarksBlock(posBlock);
        }
    }
679

680
681
682
    if (d->m_snippetOverlay->isVisible()) {
        QTextCursor cursor = textCursor();
        cursor.setPosition(position);
683
        d->snippetCheckCursor(cursor);
684
685
    }

686
    if (doc->isRedoAvailable())
687
        emit editor()->contentsChangedBecauseOfUndo();
Leandro Melo's avatar
Leandro Melo committed
688
689
690

    if (charsAdded != 0 && characterAt(position + charsAdded - 1).isPrint())
        d->m_assistRelevantContentAdded = true;
con's avatar
con committed
691
692
}

693
void BaseTextEditorWidget::slotSelectionChanged()
con's avatar
con committed
694
{
mae's avatar
mae committed
695
696
697
    if (d->m_inBlockSelectionMode && !textCursor().hasSelection()) {
        d->m_inBlockSelectionMode = false;
        d->m_blockSelection.clear();
con's avatar
con committed
698
        viewport()->update();
mae's avatar
mae committed
699
700
    }

701
702
    if (!d->m_selectBlockAnchor.isNull() && !textCursor().hasSelection())
        d->m_selectBlockAnchor = QTextCursor();
703
704
705

    // Clear any link which might be showing when the selection changes
    clearLink();
con's avatar
con committed
706
707
}

708
void BaseTextEditorWidget::gotoBlockStart()
709
710
{
    QTextCursor cursor = textCursor();
711
    if (TextBlockUserData::findPreviousOpenParenthesis(&cursor, false)) {
712
        setTextCursor(cursor);
713
714
        _q_matchParentheses();
    }
715
716
}

717
void BaseTextEditorWidget::gotoBlockEnd()
718
719
{
    QTextCursor cursor = textCursor();
720
    if (TextBlockUserData::findNextClosingParenthesis(&cursor, false)) {
721
        setTextCursor(cursor);
722
723
        _q_matchParentheses();
    }
724
725
}

726
void BaseTextEditorWidget::gotoBlockStartWithSelection()
727
728
{
    QTextCursor cursor = textCursor();
729
    if (TextBlockUserData::findPreviousOpenParenthesis(&cursor, true)) {
730
        setTextCursor(cursor);
731
732
        _q_matchParentheses();
    }
733
734
}

735
void BaseTextEditorWidget::gotoBlockEndWithSelection()
736
737
{
    QTextCursor cursor = textCursor();
738
    if (TextBlockUserData::findNextClosingParenthesis(&cursor, true)) {
739
        setTextCursor(cursor);
740
741
742
743
        _q_matchParentheses();
    }
}

mae's avatar
mae committed
744

745
void BaseTextEditorWidget::gotoLineStart()
mae's avatar
mae committed
746
747
748
749
{
    handleHomeKey(false);
}

750
void BaseTextEditorWidget::gotoLineStartWithSelection()
mae's avatar
mae committed
751
752
753
754
{
    handleHomeKey(true);
}

755
void BaseTextEditorWidget::gotoLineEnd()
mae's avatar
mae committed
756
757
758
759
{
    moveCursor(QTextCursor::EndOfLine);
}

760
void BaseTextEditorWidget::gotoLineEndWithSelection()
mae's avatar
mae committed
761
762
763
764
{
    moveCursor(QTextCursor::EndOfLine, QTextCursor::KeepAnchor);
}

765
void BaseTextEditorWidget::gotoNextLine()
mae's avatar
mae committed
766
{
767
    moveCursor(QTextCursor::Down);
mae's avatar
mae committed
768
769
}

770
void BaseTextEditorWidget::gotoNextLineWithSelection()
mae's avatar
mae committed
771
{
772
    moveCursor(QTextCursor::Down, QTextCursor::KeepAnchor);
mae's avatar
mae committed
773
774
}

775
void BaseTextEditorWidget::gotoPreviousLine()
mae's avatar
mae committed
776
{
777
    moveCursor(QTextCursor::Up);
mae's avatar
mae committed
778
779
}

780
void BaseTextEditorWidget::gotoPreviousLineWithSelection()
mae's avatar
mae committed
781
{
782
    moveCursor(QTextCursor::Up, QTextCursor::KeepAnchor);
mae's avatar
mae committed
783
784
}

785
void BaseTextEditorWidget::gotoPreviousCharacter()
mae's avatar
mae committed
786
787
788
789
{
    moveCursor(QTextCursor::PreviousCharacter);
}

790
void BaseTextEditorWidget::gotoPreviousCharacterWithSelection()
mae's avatar
mae committed
791
792
793
794
{
    moveCursor(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor);
}

795
void BaseTextEditorWidget::gotoNextCharacter()
mae's avatar
mae committed
796
797
798
799
{
    moveCursor(QTextCursor::NextCharacter);
}

800
void BaseTextEditorWidget::gotoNextCharacterWithSelection()
mae's avatar
mae committed
801
802
803
804
{
    moveCursor(QTextCursor::NextCharacter, QTextCursor::KeepAnchor);
}

805
void BaseTextEditorWidget::gotoPreviousWord()
mae's avatar
mae committed
806
807
{
    moveCursor(QTextCursor::PreviousWord);
808
    setTextCursor(textCursor());
mae's avatar
mae committed
809
810
}

811
void BaseTextEditorWidget::gotoPreviousWordWithSelection()
mae's avatar
mae committed
812
813
{
    moveCursor(QTextCursor::PreviousWord, QTextCursor::KeepAnchor);
814
    setTextCursor(textCursor());
mae's avatar
mae committed
815
816
}

817
void BaseTextEditorWidget::gotoNextWord()
mae's avatar
mae committed
818
819
{
    moveCursor(QTextCursor::NextWord);
820
    setTextCursor(textCursor());
mae's avatar
mae committed
821
822
}

823
void BaseTextEditorWidget::gotoNextWordWithSelection()
mae's avatar
mae committed
824
825
{
    moveCursor(QTextCursor::NextWord, QTextCursor::KeepAnchor);
826
    setTextCursor(textCursor());
mae's avatar
mae committed
827
828
}

829
void BaseTextEditorWidget::gotoPreviousWordCamelCase()
mae's avatar
mae committed
830
831
832
833
834
835
{
    QTextCursor c = textCursor();
    camelCaseLeft(c, QTextCursor::MoveAnchor);
    setTextCursor(c);
}

836
void BaseTextEditorWidget::gotoPreviousWordCamelCaseWithSelection()
mae's avatar
mae committed
837
838
839
840
841
842
{
    QTextCursor c = textCursor();
    camelCaseLeft(c, QTextCursor::KeepAnchor);
    setTextCursor(c);
}

843
void BaseTextEditorWidget::gotoNextWordCamelCase()
mae's avatar
mae committed
844
845
846
847
848
849
850
{
    qDebug() << Q_FUNC_INFO;
    QTextCursor c = textCursor();
    camelCaseRight(c, QTextCursor::MoveAnchor);
    setTextCursor(c);
}

851
void BaseTextEditorWidget::gotoNextWordCamelCaseWithSelection()
mae's avatar
mae committed
852
853
854
855
856
857
{
    QTextCursor c = textCursor();
    camelCaseRight(c, QTextCursor::KeepAnchor);
    setTextCursor(c);
}

mae's avatar
mae committed
858
859


860
861
static QTextCursor flippedCursor(const QTextCursor &cursor)
{
862
863
864
865
    QTextCursor flipped = cursor;
    flipped.clearSelection();
    flipped.setPosition(cursor.anchor(), QTextCursor::KeepAnchor);
    return flipped;
866
867
}

868
void BaseTextEditorWidget::selectBlockUp()
869
870
871
872
873
874
875
876
877
878
879
880
{
    QTextCursor cursor = textCursor();
    if (!cursor.hasSelection())
        d->m_selectBlockAnchor = cursor;
    else
        cursor.setPosition(cursor.selectionStart());


    if (!TextBlockUserData::findPreviousOpenParenthesis(&cursor, false))
        return;
    if (!TextBlockUserData::findNextClosingParenthesis(&cursor, true))
        return;
881
882
    setTextCursor(flippedCursor(cursor));
    _q_matchParentheses();
883
884
}

885
void BaseTextEditorWidget::selectBlockDown()
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
{
    QTextCursor tc = textCursor();
    QTextCursor cursor = d->m_selectBlockAnchor;

    if (!tc.hasSelection() || cursor.isNull())
        return;
    tc.setPosition(tc.selectionStart());

    forever {
        QTextCursor ahead = cursor;
        if (!TextBlockUserData::findPreviousOpenParenthesis(&ahead, false))
            break;
        if (ahead.position() <= tc.position())
            break;
        cursor = ahead;
    }
    if ( cursor != d->m_selectBlockAnchor)
        TextBlockUserData::findNextClosingParenthesis(&cursor, true);

905
906
    setTextCursor(flippedCursor(cursor));
    _q_matchParentheses();
907
908
}

909
void BaseTextEditorWidget::copyLineUp()
910
911
912
913
{
    copyLineUpDown(true);
}

914
void BaseTextEditorWidget::copyLineDown()
915
916
917
918
{
    copyLineUpDown(false);
}

919
// @todo: Potential reuse of some code around the following functions...
920
void BaseTextEditorWidget::copyLineUpDown(bool up)
921
922
923
924
925
926
927
928
929
930
931
{
    QTextCursor cursor = textCursor();
    QTextCursor move = cursor;
    move.beginEditBlock();

    bool hasSelection = cursor.hasSelection();

    if (hasSelection) {
        move.setPosition(cursor.selectionStart());
        move.movePosition(QTextCursor::StartOfBlock);
        move.setPosition(cursor.selectionEnd(), QTextCursor::KeepAnchor);
932
933
        move.movePosition(move.atBlockStart() ? QTextCursor::Left: QTextCursor::EndOfBlock,
                          QTextCursor::KeepAnchor);
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
    } else {
        move.movePosition(QTextCursor::StartOfBlock);
        move.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
    }

    QString text = move.selectedText();

    if (up) {
        move.setPosition(cursor.selectionStart());
        move.movePosition(QTextCursor::StartOfBlock);
        move.insertBlock();
        move.movePosition(QTextCursor::Left);
    } else {
        move.movePosition(QTextCursor::EndOfBlock);
        if (move.atBlockStart()) {
            move.movePosition(QTextCursor::NextBlock);
            move.insertBlock();
            move.movePosition(QTextCursor::Left);
        } else {
            move.insertBlock();
        }
    }

    int start = move.position();
    move.clearSelection();
    move.insertText(text);
    int end = move.position();

    move.setPosition(start);
    move.setPosition(end, QTextCursor::KeepAnchor);

    indent(document(), move, QChar::Null);
    move.endEditBlock();

    setTextCursor(move);
}

971
void BaseTextEditorWidget::joinLines()
972
{
973
974
975
    QTextCursor cursor = textCursor();
    QTextCursor start = cursor;
    QTextCursor end = cursor;
976

977
978
    start.setPosition(cursor.selectionStart());
    end.setPosition(cursor.selectionEnd() - 1);
979

980
    int lineCount = qMax(1, end.blockNumber() - start.blockNumber());
981

982
983
984
985
986
987
988
    cursor.beginEditBlock();
    cursor.setPosition(cursor.selectionStart());
    while (lineCount--) {
        cursor.movePosition(QTextCursor::NextBlock);
        cursor.movePosition(QTextCursor::StartOfBlock);
        cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
        QString cutLine = cursor.selectedText();
989

990
991
992
993
994
995
996
997
998
999
1000
1001
1002
        // Collapse leading whitespaces to one or insert whitespace
        cutLine.replace(QRegExp("^\\s*"), " ");
        cursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor);
        cursor.removeSelectedText();

        cursor.movePosition(QTextCursor::PreviousBlock);
        cursor.movePosition(QTextCursor::EndOfBlock);

        cursor.insertText(cutLine);
    }
    cursor.endEditBlock();

    setTextCursor(cursor);
1003
1004
}

1005
void BaseTextEditorWidget::insertLineAbove()
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
{
    QTextCursor cursor = textCursor();
    cursor.beginEditBlock();
    cursor.movePosition(QTextCursor::PreviousBlock, QTextCursor::MoveAnchor);
    cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::MoveAnchor);
    cursor.insertBlock();
    indent(document(), cursor, QChar::Null);
    cursor.endEditBlock();
    setTextCursor(cursor);
}

1017
void BaseTextEditorWidget::insertLineBelow()
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
{
    QTextCursor cursor = textCursor();
    cursor.beginEditBlock();
    cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::MoveAnchor);
    cursor.insertBlock();
    indent(document(), cursor, QChar::Null);
    cursor.endEditBlock();
    setTextCursor(cursor);
}

1028
void BaseTextEditorWidget::moveLineUp()
1029
1030
1031
1032
{
    moveLineUpDown(true);
}

1033
void BaseTextEditorWidget::moveLineDown()
1034
1035
1036
1037
{
    moveLineUpDown(false);
}

1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
void BaseTextEditorWidget::uppercaseSelection()
{
    transformSelection(&QString::toUpper);
}


void BaseTextEditorWidget::lowercaseSelection()
{
    transformSelection(&QString::toLower);
}

1049
void BaseTextEditorWidget::moveLineUpDown(bool up)
1050
1051
1052
{
    QTextCursor cursor = textCursor();
    QTextCursor move = cursor;
1053

mae's avatar
mae committed
1054
    move.setVisualNavigation(false); // this opens folded items instead of destroying them
1055

1056
1057
1058
1059
    if (d->m_moveLineUndoHack)
        move.joinPreviousEditBlock();
    else
        move.beginEditBlock();
1060
1061
1062
1063
1064
1065
1066

    bool hasSelection = cursor.hasSelection();

    if (cursor.hasSelection()) {
        move.setPosition(cursor.selectionStart());
        move.movePosition(QTextCursor::StartOfBlock);
        move.setPosition(cursor.selectionEnd(), QTextCursor::KeepAnchor);
1067
1068
        move.movePosition(move.atBlockStart() ? QTextCursor::Left: QTextCursor::EndOfBlock,
                          QTextCursor::KeepAnchor);
1069
1070
1071
1072
1073
    } else {
        move.movePosition(QTextCursor::StartOfBlock);
        move.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
    }
    QString text = move.selectedText();
1074
1075
1076
1077
1078
1079
1080

    RefactorMarkers affectedMarkers;
    RefactorMarkers nonAffectedMarkers;
    QList<int> markerOffsets;

    foreach (const RefactorMarker &marker, d->m_refactorOverlay->markers()) {
        //test if marker is part of the selection to be moved
1081
1082
        if ((move.selectionStart() <= marker.cursor.position())
                && (move.selectionEnd() >= marker.cursor.position())) {
1083
1084
1085
1086
1087
1088
1089
1090
1091
            affectedMarkers.append(marker);
            //remember the offset of markers in text
            int offset = marker.cursor.position() - move.selectionStart();
            markerOffsets.append(offset);
        } else {
            nonAffectedMarkers.append(marker);
        }
    }

1092
1093
    move.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor);
    move.removeSelectedText();
1094

1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
    if (up) {
        move.movePosition(QTextCursor::PreviousBlock);
        move.insertBlock();
        move.movePosition(QTextCursor::Left);
    } else {
        move.movePosition(QTextCursor::EndOfBlock);
        if (move.atBlockStart()) { // empty block
            move.movePosition(QTextCursor::NextBlock);
            move.insertBlock();
            move.movePosition(QTextCursor::Left);
        } else {
            move.insertBlock();
        }
    }
1109

1110
1111
1112
1113
    int start = move.position();
    move.clearSelection();
    move.insertText(text);
    int end = move.position();
1114

1115
1116
1117
1118
1119
    if (hasSelection) {
        move.setPosition(start);
        move.setPosition(end, QTextCursor::KeepAnchor);
    }

1120
1121
1122
1123
1124
1125
1126
    //update positions of affectedMarkers
    for (int i=0;i < affectedMarkers.count(); i++) {
        int newPosition = start + markerOffsets.at(i);
        affectedMarkers[i].cursor.setPosition(newPosition);
    }
    d->m_refactorOverlay->setMarkers(nonAffectedMarkers + affectedMarkers);

mae's avatar
mae committed
1127
    reindent(document(), move);
1128
1129
    move.endEditBlock();

1130
    setTextCursor(move);
1131
    d->m_moveLineUndoHack = true;
1132
}
1133

1134
void BaseTextEditorWidget::cleanWhitespace()
1135
{
1136
    d->m_document->cleanWhitespace(textCursor());
1137
}
1138

1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154

// could go into QTextCursor...
static QTextLine currentTextLine(const QTextCursor &cursor)
{
    const QTextBlock block = cursor.block();
    if (!block.isValid())
        return QTextLine();

    const QTextLayout *layout = block.layout();
    if (!layout)
        return QTextLine();

    const int relativePos = cursor.position() - block.position();
    return layout->lineForTextPosition(relativePos);
}

1155
bool BaseTextEditorWidget::camelCaseLeft(QTextCursor &cursor, QTextCursor::MoveMode mode)
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
{
    int state = 0;
    enum Input {
        Input_U,
        Input_l,
        Input_underscore,
        Input_space,
        Input_other
    };

    if (!cursor.movePosition(QTextCursor::Left, mode))
        return false;

    forever {
        QChar c = characterAt(cursor.position());
        Input input = Input_other;
        if (c.isUpper())
            input = Input_U;
        else if (c.isLower() || c.isDigit())
            input = Input_l;
        else if (c == QLatin1Char('_'))
            input = Input_underscore;
        else if (c.isSpace() && c != QChar::ParagraphSeparator)
            input = Input_space;
        else
            input = Input_other;

        switch (state) {
        case 0:
            switch (input) {
            case Input_U:
                state = 1;
                break;
            case Input_l:
                state = 2;
                break;
            case Input_underscore:
                state = 3;
                break;
            case Input_space:
                state = 4;
                break;
            default:
                cursor.movePosition(QTextCursor::Right, mode);
                return cursor.movePosition(QTextCursor::WordLeft, mode);
            }
            break;
        case 1:
            switch (input) {
            case Input_U:
                break;
            default:
mae's avatar
mae committed
1208
                cursor.movePosition(QTextCursor::Right, mode);
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
                return true;
            }
            break;

        case 2:
            switch (input) {
            case Input_U:
                return true;
            case Input_l:
                break;
            default:
                cursor.movePosition(QTextCursor::Right, mode);
                return true;
            }
            break;
        case 3:
            switch (input) {
            case Input_underscore:
                break;
            case Input_U:
                state = 1;
                break;
            case Input_l:
                state = 2;
                break;
            default:
                cursor.movePosition(QTextCursor::Right, mode);
                return true;
            }
            break;
        case 4:
            switch (input) {
            case Input_space:
                break;
            case Input_U:
                state = 1;
                break;
            case Input_l:
                state = 2;
                break;
            case Input_underscore:
                state = 3;
                break;
            default:
                cursor.movePosition(QTextCursor::Right, mode);
                if (cursor.positionInBlock() == 0)
                    return true;
                return cursor.movePosition(QTextCursor::WordLeft, mode);
            }
        }

        if (!cursor.movePosition(QTextCursor::Left, mode))
            return true;
    }
}

1265
bool BaseTextEditorWidget::camelCaseRight(QTextCursor &cursor, QTextCursor::MoveMode mode)
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
{
    int state = 0;
    enum Input {
        Input_U,
        Input_l,
        Input_underscore,
        Input_space,
        Input_other
    };

    forever {
        QChar c = characterAt(cursor.position());
        Input input = Input_other;
        if (c.isUpper())
            input = Input_U;
        else if (c.isLower() || c.isDigit())
            input = Input_l;
        else if (c == QLatin1Char('_'))
            input = Input_underscore;
        else if (c.isSpace() && c != QChar::ParagraphSeparator)
            input = Input_space;
        else
            input = Input_other;

        switch (state) {
        case 0:
            switch (input) {
            case Input_U:
                state = 4;
                break;
            case Input_l:
                state = 1;
                break;
            case Input_underscore:
                state = 6;
                break;
            default:
                return cursor.movePosition(QTextCursor::WordRight, mode);
            }
            break;
        case 1:
            switch (input) {
            case Input_U:
                return true;
            case Input_l:
                break;
            case Input_underscore:
                state = 6;
                break;
            case Input_space:
                state = 7;
                break;
            default:
                return true;
            }
            break;
        case 2:
            switch (input) {
            case Input_U:
                break;
            case Input_l:
                cursor.movePosition(QTextCursor::Left, mode);
                return true;
mae's avatar
mae committed
1329
1330
1331
            case Input_underscore:
                state = 6;
                break;
mae's avatar