texteditor.cpp 295 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
con's avatar
con committed
2
**
3 4
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
con's avatar
con committed
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
con's avatar
con committed
7
**
hjk's avatar
hjk committed
8 9 10 11
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
12 13 14
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
15
**
16 17 18 19 20 21 22
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
con's avatar
con committed
23
**
hjk's avatar
hjk committed
24
****************************************************************************/
hjk's avatar
hjk committed
25

26 27
#include "texteditor.h"
#include "texteditor_p.h"
28 29 30 31
#include "displaysettings.h"
#include "marginsettings.h"
#include "fontsettings.h"
#include "texteditoractionhandler.h"
hjk's avatar
hjk committed
32

33
#include "autocompleter.h"
34
#include "basehoverhandler.h"
35
#include "behaviorsettings.h"
36 37
#include "circularclipboard.h"
#include "circularclipboardassist.h"
con's avatar
con committed
38
#include "codecselector.h"
39
#include "completionsettings.h"
40
#include "highlighterutils.h"
Jarek Kobus's avatar
Jarek Kobus committed
41
#include "icodestylepreferences.h"
42
#include "indenter.h"
43 44 45 46 47
#include "snippets/snippet.h"
#include "syntaxhighlighter.h"
#include "tabsettings.h"
#include "textdocument.h"
#include "textdocumentlayout.h"
48
#include "texteditorconstants.h"
49
#include "texteditoroverlay.h"
50
#include "refactoroverlay.h"
51 52
#include "texteditorsettings.h"
#include "typingsettings.h"
53 54
#include "extraencodingsettings.h"
#include "storagesettings.h"
55

56
#include <texteditor/codeassist/assistinterface.h>
57 58
#include <texteditor/codeassist/codeassistant.h>
#include <texteditor/codeassist/completionassistprovider.h>
59
#include <texteditor/codeassist/keywordscompletionassist.h>
60 61 62 63 64
#include <texteditor/generichighlighter/context.h>
#include <texteditor/generichighlighter/highlightdefinition.h>
#include <texteditor/generichighlighter/highlighter.h>
#include <texteditor/generichighlighter/highlightersettings.h>
#include <texteditor/generichighlighter/manager.h>
con's avatar
con committed
65

66
#include <coreplugin/icore.h>
67
#include <aggregation/aggregate.h>
68
#include <coreplugin/actionmanager/actionmanager.h>
69
#include <coreplugin/actionmanager/actioncontainer.h>
70
#include <coreplugin/actionmanager/command.h>
con's avatar
con committed
71
#include <coreplugin/coreconstants.h>
72
#include <coreplugin/editormanager/editormanager.h>
73
#include <coreplugin/infobar.h>
74
#include <coreplugin/manhattanstyle.h>
75
#include <coreplugin/find/basetextfind.h>
76
#include <coreplugin/find/highlightscrollbar.h>
77
#include <utils/algorithm.h>
78
#include <utils/asconst.h>
79
#include <utils/textutils.h>
con's avatar
con committed
80
#include <utils/linecolumnlabel.h>
81
#include <utils/fileutils.h>
82
#include <utils/dropsupport.h>
83
#include <utils/fadingindicator.h>
84
#include <utils/filesearch.h>
85
#include <utils/hostosinfo.h>
Eike Ziller's avatar
Eike Ziller committed
86
#include <utils/mimetypes/mimedatabase.h>
hjk's avatar
hjk committed
87
#include <utils/qtcassert.h>
88
#include <utils/stylehelper.h>
89
#include <utils/tooltip/tooltip.h>
90
#include <utils/uncommentselection.h>
91
#include <utils/theme/theme.h>
con's avatar
con committed
92

93 94
#include <QAbstractTextDocumentLayout>
#include <QApplication>
95 96 97
#include <QClipboard>
#include <QCoreApplication>
#include <QDebug>
98
#include <QGridLayout>
99
#include <QKeyEvent>
100
#include <QMap>
101 102 103
#include <QMenu>
#include <QMessageBox>
#include <QMimeData>
104 105
#include <QPainter>
#include <QPrintDialog>
106
#include <QPrinter>
107
#include <QPropertyAnimation>
108
#include <QDrag>
109
#include <QSequentialAnimationGroup>
110 111 112
#include <QScrollBar>
#include <QShortcut>
#include <QStyle>
113 114
#include <QTextBlock>
#include <QTextCodec>
115 116 117
#include <QTextCursor>
#include <QTextDocumentFragment>
#include <QTextLayout>
118 119 120
#include <QTime>
#include <QTimeLine>
#include <QTimer>
121
#include <QToolBar>
con's avatar
con committed
122

123 124 125 126 127 128 129 130 131 132 133 134
/*!
    \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
*/

135 136
/*!
    \class TextEditor::BaseTextEditor
137 138 139 140 141 142
    \brief The BaseTextEditor class is base implementation for QPlainTextEdit-based
    text editors. It can use the Kate text highlighting definitions, and some basic
    auto indentation.

    The corresponding document base class is BaseTextDocument, the corresponding
    widget base class is BaseTextEditorWidget.
143

144 145
    It is the default editor for text files used by \QC, if no other editor
    implementation matches the MIME type.
146 147
*/

hjk's avatar
hjk committed
148

149
using namespace Core;
150
using namespace Utils;
con's avatar
con committed
151 152

namespace TextEditor {
153
namespace Internal {
con's avatar
con committed
154

155 156
enum { NExtraSelectionKinds = 12 };

157
typedef QString (TransformationMethod)(const QString &);
158
typedef void (ListTransformationMethod)(QStringList &);
159 160 161 162 163 164 165 166 167 168

static QString QString_toUpper(const QString &str)
{
    return str.toUpper();
}

static QString QString_toLower(const QString &str)
{
    return str.toLower();
}
169

170
class TextEditorAnimator : public QObject
171 172 173 174
{
    Q_OBJECT

public:
175
    TextEditorAnimator(QObject *parent);
176

177 178
    void init(const QTextCursor &cursor, const QFont &f, const QPalette &pal);
    inline QTextCursor cursor() const { return m_cursor; }
179 180 181 182 183 184 185 186 187 188 189 190

    void draw(QPainter *p, const QPointF &pos);
    QRectF rect() const;

    inline qreal value() const { return m_value; }
    inline QPointF lastDrawPos() const { return m_lastDrawPos; }

    void finish();

    bool isRunning() const;

signals:
191
    void updateRequest(const QTextCursor &cursor, QPointF lastPos, QRectF rect);
192

193
private:
194 195
    void step(qreal v);

196
    QTimeLine m_timeline;
197
    qreal m_value;
198
    QTextCursor m_cursor;
199 200 201 202 203 204 205
    QPointF m_lastDrawPos;
    QFont m_font;
    QPalette m_palette;
    QString m_text;
    QSizeF m_size;
};

206 207 208
class TextEditExtraArea : public QWidget
{
public:
209
    TextEditExtraArea(TextEditorWidget *edit)
210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243
        : QWidget(edit)
    {
        textEdit = edit;
        setAutoFillBackground(true);
    }

protected:
    QSize sizeHint() const {
        return QSize(textEdit->extraAreaWidth(), 0);
    }
    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);
    }
    void contextMenuEvent(QContextMenuEvent *event) {
        textEdit->extraAreaContextMenuEvent(event);
    }

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

private:
244
    TextEditorWidget *textEdit;
245 246
};

hjk's avatar
hjk committed
247 248 249
class BaseTextEditorPrivate
{
public:
250
    BaseTextEditorPrivate() {}
251

252
    TextEditorFactoryPrivate *m_origin = nullptr;
hjk's avatar
hjk committed
253 254
};

255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270
class HoverHandlerRunner
{
public:
    HoverHandlerRunner(TextEditorWidget *widget, QList<BaseHoverHandler *> &handlers)
        : m_widget(widget)
        , m_handlers(handlers)
    {
    }

    void startChecking(const QTextCursor &textCursor, const QPoint &point)
    {
        if (m_handlers.empty())
            return;

        // Does the last handler still applies?
        const int documentRevision = textCursor.document()->revision();
271
        const int position = Text::wordStartCursor(textCursor).position();
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 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 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
        if (m_lastHandlerInfo.applies(documentRevision, position)) {
            m_lastHandlerInfo.handler->showToolTip(m_widget, point, /*decorate=*/ false);
            return;
        }

        // Cancel currently running checks
        for (BaseHoverHandler *handler : m_handlers) {
            if (handler->isAsyncHandler())
                handler->cancelAsyncCheck();
        }

        // Update invocation data
        m_documentRevision = documentRevision;
        m_position = position;
        m_point = point;

        // Re-initialize process data
        m_currentHandlerIndex = 0;
        m_bestHandler = nullptr;
        m_highestHandlerPriority = -1;

        // Start checking
        checkNext();
    }

    void checkNext()
    {
        QTC_ASSERT(m_currentHandlerIndex < m_handlers.size(), return);
        BaseHoverHandler *currentHandler = m_handlers[m_currentHandlerIndex];

        currentHandler->checkPriority(m_widget, m_position, [this](int priority) {
            onHandlerFinished(m_documentRevision, m_position, priority);
        });
    }

    void onHandlerFinished(int documentRevision, int position, int priority)
    {
        QTC_ASSERT(m_currentHandlerIndex < m_handlers.size(), return);
        QTC_ASSERT(documentRevision == m_documentRevision, return);
        QTC_ASSERT(position == m_position, return);

        BaseHoverHandler *currentHandler = m_handlers[m_currentHandlerIndex];
        if (priority > m_highestHandlerPriority) {
            m_highestHandlerPriority = priority;
            m_bestHandler = currentHandler;
        }

        // There are more, check next
        ++m_currentHandlerIndex;
        if (m_currentHandlerIndex < m_handlers.size()) {
            checkNext();
            return;
        }

        // All were queried, run the best
        if (m_bestHandler) {
            m_lastHandlerInfo = LastHandlerInfo(m_bestHandler, m_documentRevision, m_position);
            m_bestHandler->showToolTip(m_widget, m_point);
        }
    }

private:
    TextEditorWidget *m_widget = nullptr;
    const QList<BaseHoverHandler *> &m_handlers;

    struct LastHandlerInfo {
        LastHandlerInfo() = default;
        LastHandlerInfo(BaseHoverHandler *handler, int documentRevision, int cursorPosition)
            : handler(handler)
            , documentRevision(documentRevision)
            , cursorPosition(cursorPosition)
        {}

        bool applies(int documentRevision, int cursorPosition) const
        {
            return handler
                && documentRevision == this->documentRevision
                && cursorPosition == this->cursorPosition;
        }

        BaseHoverHandler *handler = nullptr;
        int documentRevision = -1;
        int cursorPosition = -1;
    } m_lastHandlerInfo;

    // invocation data
    QPoint m_point;
    int m_position = -1;
    int m_documentRevision = -1;

    // processing data
    int m_currentHandlerIndex = -1;
    int m_highestHandlerPriority = -1;
    BaseHoverHandler *m_bestHandler = nullptr;
};

368 369 370 371 372 373 374 375 376 377 378 379 380 381
struct PaintEventData
{
    PaintEventData(TextEditorWidget *editor, QPaintEvent *event, QPointF offset)
        : offset(offset)
        , viewportRect(editor->viewport()->rect())
        , eventRect(event->rect())
        , doc(editor->document())
        , documentLayout(qobject_cast<TextDocumentLayout*>(doc->documentLayout()))
        , documentWidth(int(doc->size().width()))
        , textCursor(editor->textCursor())
        , textCursorBlock(textCursor.block())
        , isEditable(!editor->isReadOnly())
        , fontSettings(editor->textDocument()->fontSettings())
        , searchScopeFormat(fontSettings.toTextCharFormat(C_SEARCH_SCOPE))
382 383
        , searchResultFormat(fontSettings.toTextCharFormat(C_SEARCH_RESULT))
        , visualWhitespaceFormat(fontSettings.toTextCharFormat(C_VISUAL_WHITESPACE))
384 385 386 387 388 389
        , ifdefedOutFormat(fontSettings.toTextCharFormat(C_DISABLED_CODE))
        , suppressSyntaxInIfdefedOutBlock(ifdefedOutFormat.foreground() != editor->palette().foreground())
    { }
    QPointF offset;
    const QRect viewportRect;
    const QRect eventRect;
390
    qreal rightMargin = -1;
391 392 393 394 395 396 397 398
    const QTextDocument *doc;
    const TextDocumentLayout *documentLayout;
    const int documentWidth;
    const QTextCursor textCursor;
    const QTextBlock textCursorBlock;
    const bool isEditable;
    const FontSettings fontSettings;
    const QTextCharFormat searchScopeFormat;
399 400
    const QTextCharFormat searchResultFormat;
    const QTextCharFormat visualWhitespaceFormat;
401 402 403 404 405 406 407 408 409 410
    const QTextCharFormat ifdefedOutFormat;
    const bool suppressSyntaxInIfdefedOutBlock;
    QAbstractTextDocumentLayout::PaintContext context;
    QTextBlock visibleCollapsedBlock;
    QPointF visibleCollapsedBlockOffset;
    QTextBlock block;
    QTextLayout *cursorLayout = 0;
    QPointF cursorOffset;
    int cursorPos = 0;
    QPen cursorPen;
411 412 413 414 415 416 417 418 419 420 421 422
    int blockSelectionIndex = -1;
};

struct PaintEventBlockData
{
    QRectF boundingRect;
    QVector<QTextLayout::FormatRange> selections;
    QVector<QTextLayout::FormatRange> prioritySelections;
    QRectF blockSelectionCursorRect;
    QTextLayout *layout = nullptr;
    int position = 0;
    int length = 0;
423 424
};

425
class TextEditorWidgetPrivate : public QObject
426 427
{
public:
428
    TextEditorWidgetPrivate(TextEditorWidget *parent);
429
    ~TextEditorWidgetPrivate();
430 431 432 433 434 435

    void setupDocumentSignals();
    void updateLineSelectionColor();

    void print(QPrinter *printer);

436
    void maybeSelectLine();
437 438
    void duplicateSelection(bool comment);
    void duplicateBlockSelection(bool comment);
439 440 441
    void updateCannotDecodeInfo();
    void collectToCircularClipboard();

442
    void ctor(const QSharedPointer<TextDocument> &doc);
443 444 445 446 447 448
    void handleHomeKey(bool anchor);
    void handleBackspaceKey();
    void moveLineUpDown(bool up);
    void copyLineUpDown(bool up);
    void saveCurrentCursorPositionForNavigation();
    void updateHighlights();
449
    void updateCurrentLineInScrollbar();
450 451 452 453 454 455 456
    void updateCurrentLineHighlight();

    void drawFoldingMarker(QPainter *painter, const QPalette &pal,
                           const QRect &rect,
                           bool expanded,
                           bool active,
                           bool hovered) const;
457
    void updateLineAnnotation(const PaintEventData &data, QPainter &painter);
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
    void paintRightMarginArea(PaintEventData &data, QPainter &painter) const;
    void paintRightMarginLine(const PaintEventData &data, QPainter &painter) const;
    void paintBlockHighlight(const PaintEventData &data, QPainter &painter) const;
    void paintSearchResultOverlay(const PaintEventData &data, QPainter &painter) const;
    void paintIfDefedOutBlocks(const PaintEventData &data, QPainter &painter) const;
    void paintFindScope(const PaintEventData &data, QPainter &painter) const;
    void paintCurrentLineHighlight(const PaintEventData &data, QPainter &painter) const;
    void paintBlockSelection(const PaintEventData &data, QPainter &painter,
                             PaintEventBlockData &blockData) const;
    void paintCursorAsBlock(const PaintEventData &data, QPainter &painter,
                            PaintEventBlockData &blockData) const;
    void paintAdditionalVisualWhitespaces(PaintEventData &data, QPainter &painter, qreal top) const;
    void paintReplacement(PaintEventData &data, QPainter &painter, qreal top) const;
    void paintWidgetBackground(const PaintEventData &data, QPainter &painter) const;
    void paintOverlays(const PaintEventData &data, QPainter &painter) const;
    void paintCursor(const PaintEventData &data, QPainter &painter) const;

    void clearSelectionBackground(PaintEventData &data) const;
    void setupBlockLayout(const PaintEventData &data, QPainter &painter,
                          PaintEventBlockData &blockData) const;
    void setupSelections(const PaintEventData &data, PaintEventBlockData &blockData) const;
    void setupCursorPosition(PaintEventData &data, QPainter &painter,
                             PaintEventBlockData &blockData) const;
    QTextBlock nextVisibleBlock(const QTextBlock &block) const;
    void cleanupAnnotationCache();
483 484 485 486 487 488

    void toggleBlockVisible(const QTextBlock &block);
    QRect foldBox();

    QTextBlock foldedBlockAt(const QPoint &pos, QRect *box = 0) const;

489 490
    void requestUpdateLink(QMouseEvent *e, bool immediate = false);
    void updateLink();
491
    void showLink(const TextEditorWidget::Link &);
492 493 494 495 496 497 498 499 500
    void clearLink();

    void universalHelper(); // test function for development

    bool cursorMoveKeyEvent(QKeyEvent *e);
    bool camelCaseRight(QTextCursor &cursor, QTextCursor::MoveMode mode);
    bool camelCaseLeft(QTextCursor &cursor, QTextCursor::MoveMode mode);

    void processTooltipRequest(const QTextCursor &c);
501
    bool processAnnotaionTooltipRequest(const QTextBlock &block, const QPoint &pos) const;
502

503 504
    void transformSelection(TransformationMethod method);
    void transformBlockSelection(TransformationMethod method);
505

506 507
    void transformSelectedLines(ListTransformationMethod method);

508 509 510 511 512
    void slotUpdateExtraAreaWidth();
    void slotUpdateRequest(const QRect &r, int dy);
    void slotUpdateBlockNotify(const QTextBlock &);
    void updateTabStops();
    void applyFontSettingsDelayed();
513
    void markRemoved(TextMark *mark);
514 515 516 517

    void editorContentsChange(int position, int charsRemoved, int charsAdded);
    void documentAboutToBeReloaded();
    void documentReloadFinished(bool success);
518
    void highlightSearchResultsSlot(const QString &txt, FindFlags findFlags);
519 520 521 522 523 524 525 526 527 528 529 530 531
    void searchResultsReady(int beginIndex, int endIndex);
    void searchFinished();
    void setupScrollBar();
    void highlightSearchResultsInScrollBar();
    void scheduleUpdateHighlightScrollBar();
    void updateHighlightScrollBarNow();
    struct SearchResult {
        int start;
        int length;
    };
    void addSearchResultsToScrollBar(QVector<SearchResult> results);
    void adjustScrollBarRanges();

532 533
    void setFindScope(const QTextCursor &start, const QTextCursor &end, int, int);

534 535
    void updateCursorPosition();

536 537 538
    // parentheses matcher
    void _q_matchParentheses();
    void _q_highlightBlocks();
539
    void autocompleterHighlight(const QTextCursor &cursor = QTextCursor());
540 541
    void updateAnimator(QPointer<TextEditorAnimator> animator, QPainter &painter);
    void cancelCurrentAnimations();
542
    void slotSelectionChanged();
543
    void _q_animateUpdate(const QTextCursor &cursor, QPointF lastPos, QRectF rect);
544 545
    void updateCodeFoldingVisible();

546 547
    void reconfigure();

548
public:
549
    TextEditorWidget *q;
550 551 552 553 554 555
    QToolBar *m_toolBar = nullptr;
    QWidget *m_stretchWidget = nullptr;
    LineColumnLabel *m_cursorPositionLabel = nullptr;
    LineColumnLabel *m_fileEncodingLabel = nullptr;
    QAction *m_cursorPositionLabelAction = nullptr;
    QAction *m_fileEncodingLabelAction = nullptr;
556

557 558
    bool m_contentsChanged = false;
    bool m_lastCursorChangeWasInteresting = false;
559

560
    QSharedPointer<TextDocument> m_document;
561 562 563
    QByteArray m_tempState;
    QByteArray m_tempNavigationState;

564
    bool m_parenthesesMatchingEnabled = false;
565 566

    // parentheses matcher
567
    bool m_formatRange = false;
568 569 570
    QTimer m_parenthesesMatchingTimer;
    // end parentheses matcher

571
    QWidget *m_extraArea = nullptr;
572

573
    Id m_tabSettingsId;
574
    ICodeStylePreferences *m_codeStylePreferences = nullptr;
575
    DisplaySettings m_displaySettings;
576
    bool m_annotationsrRight = true;
577
    MarginSettings m_marginSettings;
578 579
    // apply when making visible the first time, for the split case
    bool m_fontSettingsNeedsApply = true;
580 581
    BehaviorSettings m_behaviorSettings;

582 583 584 585
    int extraAreaSelectionAnchorBlockNumber = -1;
    int extraAreaToggleMarkBlockNumber = -1;
    int extraAreaHighlightFoldedBlockNumber = -1;
    int extraAreaPreviousMarkTooltipRequestedLine = -1;
586

587 588 589
    TextEditorOverlay *m_overlay = nullptr;
    TextEditorOverlay *m_snippetOverlay = nullptr;
    TextEditorOverlay *m_searchResultOverlay = nullptr;
590 591 592
    bool snippetCheckCursor(const QTextCursor &cursor);
    void snippetTabOrBacktab(bool forward);

593 594 595 596 597 598
    struct AnnotationRect
    {
        QRectF rect;
        const TextMark *mark;
    };
    QMap<int, QList<AnnotationRect>> m_annotationRects;
599
    QRectF getLastLineLineRect(const QTextBlock &block);
600

601
    RefactorOverlay *m_refactorOverlay = nullptr;
602
    QString m_contextHelpId;
603 604

    QBasicTimer foldedBlockTimer;
605 606
    int visibleFoldedBlockNumber = -1;
    int suggestedVisibleFoldedBlockNumber = -1;
607
    void clearVisibleFoldedBlock();
608
    bool m_mouseOnFoldedMarker = false;
609 610 611 612 613 614 615 616 617 618 619 620
    void foldLicenseHeader();

    QBasicTimer autoScrollTimer;
    uint m_marksVisible : 1;
    uint m_codeFoldingVisible : 1;
    uint m_codeFoldingSupported : 1;
    uint m_revisionsVisible : 1;
    uint m_lineNumbersVisible : 1;
    uint m_highlightCurrentLine : 1;
    uint m_requestMarkEnabled : 1;
    uint m_lineSeparatorsAllowed : 1;
    uint m_maybeFakeTooltipEvent : 1;
621
    int m_visibleWrapColumn = 0;
622

623
    TextEditorWidget::Link m_currentLink;
624
    bool m_linkPressed = false;
625 626
    QTextCursor m_pendingLinkUpdate;
    QTextCursor m_lastLinkUpdate;
627 628

    QRegExp m_searchExpr;
629
    FindFlags m_findFlags;
630
    void highlightSearchResults(const QTextBlock &block, TextEditorOverlay *overlay) const;
631 632
    QTimer m_delayedUpdateTimer;

633 634
    void setExtraSelections(Core::Id kind, const QList<QTextEdit::ExtraSelection> &selections);
    QHash<Core::Id, QList<QTextEdit::ExtraSelection>> m_extraSelections;
635 636

    // block selection mode
637
    bool m_inBlockSelectionMode = false;
638 639 640 641 642 643 644 645
    QString copyBlockSelection();
    void insertIntoBlockSelection(const QString &text = QString());
    void setCursorToColumn(QTextCursor &cursor, int column,
                          QTextCursor::MoveMode moveMode = QTextCursor::MoveAnchor);
    void removeBlockSelection();
    void enableBlockSelection(const QTextCursor &cursor);
    void enableBlockSelection(int positionBlock, int positionColumn,
                              int anchorBlock, int anchorColumn);
646 647 648 649 650 651 652

    enum BlockSelectionUpdateKind {
        NoCursorUpdate,
        CursorUpdateKeepSelection,
        CursorUpdateClearSelection,
    };
    void disableBlockSelection(BlockSelectionUpdateKind kind);
653 654
    void resetCursorFlashTimer();
    QBasicTimer m_cursorFlashTimer;
655
    bool m_cursorVisible = true;
656
    bool m_moveLineUndoHack = false;
657 658 659

    QTextCursor m_findScopeStart;
    QTextCursor m_findScopeEnd;
660 661
    int m_findScopeVerticalBlockSelectionFirstColumn = -1;
    int m_findScopeVerticalBlockSelectionLastColumn = -1;
662 663 664

    QTextCursor m_selectBlockAnchor;

665
    TextBlockSelection m_blockSelection;
666 667 668 669

    void moveCursorVisible(bool ensureVisible = true);

    int visualIndent(const QTextBlock &block) const;
670
    TextEditorPrivateHighlightBlocks m_highlightBlocksInfo;
671 672
    QTimer m_highlightBlocksTimer;

673
    CodeAssistant m_codeAssistant;
674
    bool m_assistRelevantContentAdded = false;
675

676
    QList<BaseHoverHandler *> m_hoverHandlers; // Not owned
677
    HoverHandlerRunner m_hoverHandlerRunner;
678

679 680
    QPointer<QSequentialAnimationGroup> m_navigationAnimation;

681
    QPointer<TextEditorAnimator> m_bracketsAnimator;
682 683

    // Animation and highlighting of auto completed text
684 685
    QPointer<TextEditorAnimator> m_autocompleteAnimator;
    bool m_animateAutoComplete = true;
686
    bool m_highlightAutoComplete = true;
687
    bool m_skipAutoCompletedText = true;
688
    bool m_removeAutoCompletedText = true;
689
    bool m_keepAutoCompletionHighlight = false;
690 691
    QList<QTextCursor> m_autoCompleteHighlightPos;
    void updateAutoCompleteHighlight();
692

693 694
    int m_cursorBlockNumber = -1;
    int m_blockCount = 0;
695 696

    QPoint m_markDragStart;
697
    bool m_markDragging = false;
698

699
    QScopedPointer<ClipboardAssistProvider> m_clipboardAssistProvider;
700

701
    bool m_isMissingSyntaxDefinition = false;
702 703

    QScopedPointer<AutoCompleter> m_autoCompleter;
704
    CommentDefinition m_commentDefinition;
705

706
    QFutureWatcher<FileSearchResultList> *m_searchWatcher = nullptr;
707 708
    QVector<SearchResult> m_searchResults;
    QTimer m_scrollBarUpdateTimer;
709 710
    HighlightScrollBar *m_highlightScrollBar = nullptr;
    bool m_scrollBarUpdateScheduled = false;
711 712
};

713
TextEditorWidgetPrivate::TextEditorWidgetPrivate(TextEditorWidget *parent)
714 715 716 717 718 719 720 721 722 723
  : q(parent),
    m_marksVisible(false),
    m_codeFoldingVisible(false),
    m_codeFoldingSupported(false),
    m_revisionsVisible(false),
    m_lineNumbersVisible(true),
    m_highlightCurrentLine(true),
    m_requestMarkEnabled(true),
    m_lineSeparatorsAllowed(false),
    m_maybeFakeTooltipEvent(false),
724
    m_hoverHandlerRunner(parent, m_hoverHandlers),
725
    m_clipboardAssistProvider(new ClipboardAssistProvider),
726
    m_autoCompleter(new AutoCompleter)
727 728 729
{
    Aggregation::Aggregate *aggregate = new Aggregation::Aggregate;
    BaseTextFind *baseTextFind = new BaseTextFind(q);
hjk's avatar
hjk committed
730
    connect(baseTextFind, &BaseTextFind::highlightAllRequested,
731
            this, &TextEditorWidgetPrivate::highlightSearchResultsSlot);
732
    connect(baseTextFind, &BaseTextFind::findScopeChanged,
733
            this, &TextEditorWidgetPrivate::setFindScope);
734 735
    aggregate->add(baseTextFind);
    aggregate->add(q);
736

737 738 739
    m_extraArea = new TextEditExtraArea(q);
    m_extraArea->setMouseTracking(true);

740 741 742 743 744 745
    m_stretchWidget = new QWidget;
    m_stretchWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
    m_toolBar = new QToolBar;
    m_toolBar->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum);
    m_toolBar->addWidget(m_stretchWidget);

746
    m_cursorPositionLabel = new LineColumnLabel;
747 748 749
    const int spacing = q->style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing) / 2;
    m_cursorPositionLabel->setContentsMargins(spacing, 0, spacing, 0);

750
    m_fileEncodingLabel = new LineColumnLabel;
751 752 753 754
    m_fileEncodingLabel->setContentsMargins(spacing, 0, spacing, 0);

    m_cursorPositionLabelAction = m_toolBar->addWidget(m_cursorPositionLabel);
    m_fileEncodingLabelAction = m_toolBar->addWidget(m_fileEncodingLabel);
755
    m_extraSelections.reserve(NExtraSelectionKinds);
756
}
757

758 759
TextEditorWidgetPrivate::~TextEditorWidgetPrivate()
{
760 761
    QObject::disconnect(m_document.data(), &TextDocument::markRemoved,
                        this, &TextEditorWidgetPrivate::markRemoved);
762 763 764 765
    q->disconnect(this);
    delete m_toolBar;
}

766
} // namespace Internal
hjk's avatar
hjk committed
767 768

using namespace Internal;
con's avatar
con committed
769

770 771 772 773
/*!
 * Test if syntax highlighter is available (or unneeded) for \a widget.
 * If not found, show a warning with a link to the relevant settings page.
 */
774
static void updateEditorInfoBar(TextEditorWidget *widget)
775 776 777 778 779 780 781 782 783 784
{
    Id infoSyntaxDefinition(Constants::INFO_SYNTAX_DEFINITION);
    InfoBar *infoBar = widget->textDocument()->infoBar();
    if (!widget->isMissingSyntaxDefinition()) {
        infoBar->removeInfo(infoSyntaxDefinition);
    } else if (infoBar->canInfoBeAdded(infoSyntaxDefinition)) {
        InfoBarEntry info(infoSyntaxDefinition,
                          BaseTextEditor::tr("A highlight definition was not found for this file. "
                                             "Would you like to try to find one?"),
                          InfoBarEntry::GlobalSuppressionEnabled);
785
        info.setCustomButtonInfo(BaseTextEditor::tr("Show Highlighter Options..."), [widget]() {
786
            ICore::showOptionsDialog(Constants::TEXT_EDITOR_HIGHLIGHTER_SETTINGS, widget);
787 788
        });

789 790 791 792
        infoBar->addInfo(info);
    }
}

793
QString TextEditorWidget::plainTextFromSelection(const QTextCursor &cursor) const
794
{
795 796 797 798 799
    // Copy the selected text as plain text
    QString text = cursor.selectedText();
    return convertToPlainText(text);
}

800
QString TextEditorWidget::convertToPlainText(const QString &txt)
801 802 803 804
{
    QString ret = txt;
    QChar *uc = ret.data();
    QChar *e = uc + ret.size();
805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820

    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:
            ;
        }
    }
821
    return ret;
822 823
}

Orgad Shaneh's avatar
Orgad Shaneh committed
824
static const char kTextBlockMimeType[] = "application/vnd.qtcreator.blocktext";
825

826 827 828
Id TextEditorWidget::SnippetPlaceholderSelection("TextEdit.SnippetPlaceHolderSelection");
Id TextEditorWidget::CurrentLineSelection("TextEdit.CurrentLineSelection");
Id TextEditorWidget::ParenthesesMatchingSelection("TextEdit.ParenthesesMatchingSelection");
829
Id TextEditorWidget::AutoCompleteSelection("TextEdit.AutoCompleteSelection");
830 831 832 833 834 835 836 837 838
Id TextEditorWidget::CodeWarningsSelection("TextEdit.CodeWarningsSelection");
Id TextEditorWidget::CodeSemanticsSelection("TextEdit.CodeSemanticsSelection");
Id TextEditorWidget::UndefinedSymbolSelection("TextEdit.UndefinedSymbolSelection");
Id TextEditorWidget::UnusedSymbolSelection("TextEdit.UnusedSymbolSelection");
Id TextEditorWidget::OtherSelection("TextEdit.OtherSelection");
Id TextEditorWidget::ObjCSelection("TextEdit.ObjCSelection");
Id TextEditorWidget::DebuggerExceptionSelection("TextEdit.DebuggerExceptionSelection");
Id TextEditorWidget::FakeVimSelection("TextEdit.FakeVimSelection");

839
TextEditorWidget::TextEditorWidget(QWidget *parent)
840 841
    : QPlainTextEdit(parent)
{
842 843 844
    // "Needed", as the creation below triggers ChildEvents that are
    // passed to this object's event() which uses 'd'.
    d = 0;
845
    d = new TextEditorWidgetPrivate(this);
846 847
}

848
void TextEditorWidget::setTextDocument(const QSharedPointer<TextDocument> &doc)
849
{
850
    d->ctor(doc);
851 852
}

853 854 855 856 857 858 859 860 861 862 863 864 865 866 867
void TextEditorWidgetPrivate::setupScrollBar()
{
    if (m_displaySettings.m_scrollBarHighlights) {
        if (m_highlightScrollBar)
            return;
        m_highlightScrollBar = new HighlightScrollBar(Qt::Vertical, q);
        q->setVerticalScrollBar(m_highlightScrollBar);
        highlightSearchResultsInScrollBar();
        scheduleUpdateHighlightScrollBar();
    } else if (m_highlightScrollBar) {
        q->setVerticalScrollBar(new QScrollBar(Qt::Vertical, q));
        m_highlightScrollBar = 0;
    }
}

868
void TextEditorWidgetPrivate::ctor(const QSharedPointer<TextDocument> &doc)
con's avatar
con committed
869
{
870
    q->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
con's avatar
con committed
871

872 873 874 875
    m_overlay = new TextEditorOverlay(q);
    m_snippetOverlay = new TextEditorOverlay(q);
    m_searchResultOverlay = new TextEditorOverlay(q);
    m_refactorOverlay = new RefactorOverlay(q);
876

877 878
    m_document = doc;
    setupDocumentSignals();
con's avatar
con committed
879 880 881

    // from RESEARCH

882 883
    q->setLayoutDirection(Qt::LeftToRight);
    q->viewport()->setMouseTracking(true);
con's avatar
con committed
884

885 886 887 888 889
    extraAreaSelectionAnchorBlockNumber = -1;
    extraAreaToggleMarkBlockNumber = -1;
    extraAreaHighlightFoldedBlockNumber = -1;
    visibleFoldedBlockNumber = -1;
    suggestedVisibleFoldedBlockNumber = -1;
con's avatar
con committed
890

891
    QObject::connect(&m_codeAssistant, &CodeAssistant::finished,
892
                     q, &TextEditorWidget::assistFinished);
893

894
    QObject::connect(q, &QPlainTextEdit::blockCountChanged,
895
                     this, &TextEditorWidgetPrivate::slotUpdateExtraAreaWidth);
896

897
    QObject::connect(q, &QPlainTextEdit::modificationChanged, m_extraArea,
898 899 900
                     static_cast<void (QWidget::*)()>(&QWidget::update));

    QObject::connect(q, &QPlainTextEdit::cursorPositionChanged,
901
                     q, &TextEditorWidget::slotCursorPositionChanged);
902

903
    QObject::connect(q, &QPlainTextEdit::cursorPositionChanged,
904
                     this, &TextEditorWidgetPrivate::updateCursorPosition);
905

906
    QObject::connect(q, &QPlainTextEdit::updateRequest,
907
                     this, &TextEditorWidgetPrivate::slotUpdateRequest);
908 909

    QObject::connect(q, &QPlainTextEdit::selectionChanged,
910
                     this, &TextEditorWidgetPrivate::slotSelectionChanged);
con's avatar
con committed
911 912

    // parentheses matcher
913 914
    m_formatRange = true;
    m_parenthesesMatchingTimer.setSingleShot(true);
915
    QObject::connect(&m_parenthesesMatchingTimer, &QTimer::timeout,
916
                     this, &TextEditorWidgetPrivate::_q_matchParentheses);
con's avatar
con committed
917

918
    m_highlightBlocksTimer.setSingleShot(true);
919
    QObject::connect(&m_highlightBlocksTimer, &QTimer::timeout,
920
                     this, &TextEditorWidgetPrivate::_q_highlightBlocks);
921

922 923 924 925
    m_scrollBarUpdateTimer.setSingleShot(true);
    QObject::connect(&m_scrollBarUpdateTimer, &QTimer::timeout,
                     this, &TextEditorWidgetPrivate::highlightSearchResultsInScrollBar);

926 927
    m_bracketsAnimator = 0;
    m_autocompleteAnimator = 0;
con's avatar
con committed
928

929
    slotUpdateExtraAreaWidth();
con's avatar
con committed
930
    updateHighlights();
931
    q->setFrameStyle(QFrame::NoFrame);
con's avatar
con committed
932

933
    m_delayedUpdateTimer.setSingleShot(true);
934 935
    QObject::connect(&m_delayedUpdateTimer, &QTimer::timeout, q->viewport(),
                     static_cast<void (QWidget::*)()>(&QWidget::update));
936

937
    m_moveLineUndoHack = false;
938 939 940

    updateCannotDecodeInfo();

941 942 943 944
    QObject::connect(m_document.data(), &TextDocument::aboutToOpen,
                     q, &TextEditorWidget::aboutToOpen);
    QObject::connect(m_document.data(), &TextDocument::openFinishedSuccessfully,
                     q, &TextEditorWidget::openFinishedSuccessfully);
945 946 947 948 949
    connect(m_fileEncodingLabel, &LineColumnLabel::clicked,
            q, &TextEditorWidget::selectEncoding);
    connect(m_document->document(), &QTextDocument::modificationChanged,
            q, &TextEditorWidget::updateTextCodecLabel);
    q->updateTextCodecLabel();
con's avatar
con committed
950 951
}

952
TextEditorWidget::~TextEditorWidget()
con's avatar
con committed
953 954 955 956 957
{
    delete d;
    d = 0;
}

958
void TextEditorWidget::print(QPrinter *printer)
con's avatar
con committed
959
{
hjk's avatar
hjk committed
960
    const bool oldFullPage = printer->fullPage();
con's avatar
con committed
961 962 963
    printer->setFullPage(true);
    QPrintDialog *dlg = new QPrintDialog(printer, this);
    dlg->setWindowTitle(tr("Print Document"));
964
    if (dlg->exec() == QDialog::Accepted)
con's avatar
con committed
965 966 967 968 969
        d->print(printer);
    printer->setFullPage(oldFullPage);
    delete dlg;
}

mae's avatar
mae committed
970
static int foldBoxWidth(const QFontMetrics &fm)
971 972
{
    const int lineSpacing = fm.lineSpacing();
hjk's avatar
hjk committed
973
    return lineSpacing + lineSpacing % 2 + 1;
974 975
}

con's avatar
con committed
976 977 978 979 980 981 982
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());
983
    const QRectF view(0, (index - 1) * body.height(), body.width(), body.height());
con's avatar
con committed
984 985 986 987 988

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

    painter->setFont(QFont(doc->defaultFont()));
989 990 991 992 993
    const QRectF box = titleBox.translated(0, view.top());
    const int dpix = painter->device()->logicalDpiX();
    const int dpiy = painter->device()->logicalDpiY();
    const int mx = 5 * dpix / 72.0;
    const int my = 2 * dpiy / 72.0;
con's avatar
con committed
994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011
    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();
}

1012
void TextEditorWidgetPrivate::print(QPrinter *printer)
con's avatar
con committed
1013 1014 1015
{
    QTextDocument *doc = q->document();

1016
    QString title = m_document->displayName();
1017
    if (!title.isEmpty())
con's avatar
con committed
1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043
        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()) {


1044
        QVector<QTextLayout::FormatRange> formatList = srcBlock.layout()->formats();
con's avatar
con committed
1045 1046
        if (backgroundIsDark) {
            // adjust syntax highlighting colors for better contrast
hjk's avatar
hjk committed
1047
            for (int i = formatList.count() - 1; i >= 0; --i) {
con's avatar
con committed
1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061
                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);
            }
        }

1062
        dstBlock.layout()->setFormats(formatList);
con's avatar
con committed
1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088
    }

    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;
hjk's avatar
hjk committed
1089
    if (printer->collateCopies() == true) {
con's avatar
con committed
1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148
        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;
}


1149
int TextEditorWidgetPrivate::visualIndent(const QTextBlock &block) const
1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165
{
    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
1166

1167 1168 1169
void TextEditorWidgetPrivate::updateAutoCompleteHighlight()
{
    const QTextCharFormat &matchFormat
1170
            = q->textDocument()->fontSettings().toTextCharFormat(C_AUTOCOMPLETE);
1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181

    QList<QTextEdit::ExtraSelection> extraSelections;
    for (QTextCursor cursor : Utils::asConst(m_autoCompleteHighlightPos)) {
        QTextEdit::ExtraSelection sel;
        sel.cursor = cursor;
        sel.format.setBackground(matchFormat.background());
        extraSelections.append(sel);
    }
    q->setExtraSelections(TextEditorWidget::AutoCompleteSelection, extraSelections);
}

1182
void TextEditorWidget::selectEncoding()
con's avatar
con committed
1183
{
1184
    TextDocument *doc = d->m_document.data();
con's avatar
con committed
1185 1186 1187
    CodecSelector codecSelector(this, doc);

    switch (codecSelector.exec()) {
1188 1189 1190 1191 1192 1193 1194
    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
1195 1196
    case CodecSelector::Save:
        doc->setCodec(codecSelector.selectedCodec());
1197
        EditorManager::saveDocument(textDocument());
1198
        updateTextCodecLabel();
con's avatar
con committed
1199 1200 1201 1202 1203 1204
        break;
    case CodecSelector::Cancel:
        break;
    }
}

1205
void TextEditorWidget::updateTextCodecLabel()
1206
{
1207 1208
    QString text = QString::fromLatin1(d->m_document->codec()->name());
    d->m_fileEncodingLabel->setText(text, text);
1209 1210
}

1211
QString TextEditorWidget::msgTextTooLarge(quint64 size)
1212 1213 1214 1215 1216
{
    return tr("The text is too large to be displayed (%1 MB).").
           arg(size >> 20);
}

1217
void TextEditorWidget::insertPlainText(const QString &text)
1218 1219 1220 1221 1222 1223 1224
{
    if (d->m_inBlockSelectionMode)
        d->insertIntoBlockSelection(text);
    else
        QPlainTextEdit::insertPlainText(text);
}

1225
QString TextEditorWidget::selectedText() const
1226 1227 1228 1229 1230 1231 1232
{
    if (d->m_inBlockSelectionMode)
        return d->copyBlockSelection();
    else
        return textCursor().selectedText();
}

1233
void TextEditorWidgetPrivate::updateCannotDecodeInfo()