File: juce_CodeEditorComponent.h

package info (click to toggle)
juce 8.0.10%2Bds-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 78,768 kB
  • sloc: cpp: 526,464; ansic: 159,952; java: 3,038; javascript: 847; xml: 269; python: 224; sh: 167; makefile: 84
file content (486 lines) | stat: -rw-r--r-- 20,921 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
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
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
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
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
483
484
485
486
/*
  ==============================================================================

   This file is part of the JUCE framework.
   Copyright (c) Raw Material Software Limited

   JUCE is an open source framework subject to commercial or open source
   licensing.

   By downloading, installing, or using the JUCE framework, or combining the
   JUCE framework with any other source code, object code, content or any other
   copyrightable work, you agree to the terms of the JUCE End User Licence
   Agreement, and all incorporated terms including the JUCE Privacy Policy and
   the JUCE Website Terms of Service, as applicable, which will bind you. If you
   do not agree to the terms of these agreements, we will not license the JUCE
   framework to you, and you must discontinue the installation or download
   process and cease use of the JUCE framework.

   JUCE End User Licence Agreement: https://juce.com/legal/juce-8-licence/
   JUCE Privacy Policy: https://juce.com/juce-privacy-policy
   JUCE Website Terms of Service: https://juce.com/juce-website-terms-of-service/

   Or:

   You may also use this code under the terms of the AGPLv3:
   https://www.gnu.org/licenses/agpl-3.0.en.html

   THE JUCE FRAMEWORK IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL
   WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING WARRANTY OF
   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, ARE DISCLAIMED.

  ==============================================================================
*/

namespace juce
{

class CodeTokeniser;


//==============================================================================
/**
    A text editor component designed specifically for source code.

    This is designed to handle syntax highlighting and fast editing of very large
    files.

    @tags{GUI}
*/
class JUCE_API  CodeEditorComponent   : public TextInputTarget,
                                        public Component,
                                        public ApplicationCommandTarget
{
public:
    //==============================================================================
    /** Creates an editor for a document.

        The tokeniser object is optional - pass nullptr to disable syntax highlighting.
        The object that you pass in is not owned or deleted by the editor - you must
        make sure that it doesn't get deleted while this component is still using it.

        @see CodeDocument
    */
    CodeEditorComponent (CodeDocument& document,
                         CodeTokeniser* codeTokeniser);

    /** Destructor. */
    ~CodeEditorComponent() override;

    //==============================================================================
    /** Returns the code document that this component is editing. */
    CodeDocument& getDocument() const noexcept          { return document; }

    /** Loads the given content into the document.
        This will completely reset the CodeDocument object, clear its undo history,
        and fill it with this text.
    */
    void loadContent (const String& newContent);

    //==============================================================================
    /** Returns the standard character width. */
    float getCharWidth() const noexcept                         { return charWidth; }

    /** Returns the height of a line of text, in pixels. */
    int getLineHeight() const noexcept                          { return lineHeight; }

    /** Returns the number of whole lines visible on the screen,
        This doesn't include a cut-off line that might be visible at the bottom if the
        component's height isn't an exact multiple of the line-height.
    */
    int getNumLinesOnScreen() const noexcept                    { return linesOnScreen; }

    /** Returns the index of the first line that's visible at the top of the editor. */
    int getFirstLineOnScreen() const noexcept                   { return firstLineOnScreen; }

    /** Returns the number of whole columns visible on the screen.
        This doesn't include any cut-off columns at the right-hand edge.
    */
    int getNumColumnsOnScreen() const noexcept                  { return columnsOnScreen; }

    /** Returns the current caret position. */
    CodeDocument::Position getCaretPos() const                  { return caretPos; }

    /** Returns the total number of codepoints in the string. */
    int getTotalNumChars() const override                       { return document.getNumCharacters(); }

    /** Moves the caret.
        If selecting is true, the section of the document between the current
        caret position and the new one will become selected. If false, any currently
        selected region will be deselected.
    */
    void moveCaretTo (const CodeDocument::Position& newPos, bool selecting);

    /** Returns the on-screen position of a character in the document.
        The rectangle returned is relative to this component's top-left origin.
    */
    Rectangle<int> getCharacterBounds (const CodeDocument::Position& pos) const;

    /** Finds the character at a given on-screen position.
        The coordinates are relative to this component's top-left origin.
    */
    CodeDocument::Position getPositionAt (int x, int y) const;

    /** Returns the start of the selection as a position. */
    CodeDocument::Position getSelectionStart() const            { return selectionStart; }

    /** Returns the end of the selection as a position. */
    CodeDocument::Position getSelectionEnd() const              { return selectionEnd; }

    /** Enables or disables the line-number display in the gutter. */
    void setLineNumbersShown (bool shouldBeShown);

    /** Returns the number of characters from the beginning of the document to the caret. */
    int getCaretPosition() const override       { return getCaretPos().getPosition(); }

    /** @see getPositionAt */
    int getCharIndexForPoint (Point<int> point) const override;

    /** Returns the bounds of the caret at a particular location in the text. */
    Rectangle<int> getCaretRectangleForCharIndex (int index) const override
    {
        return getCharacterBounds ({ document, index });
    }

    /** Returns the bounding box for a range of text in the editor. As the range may span
        multiple lines, this method returns a RectangleList.

        The bounds are relative to the component's top-left and may extend beyond the bounds
        of the component if the text is long and word wrapping is disabled.
    */
    RectangleList<int> getTextBounds (Range<int> textRange) const override;

    //==============================================================================
    bool moveCaretLeft (bool moveInWholeWordSteps, bool selecting);
    bool moveCaretRight (bool moveInWholeWordSteps, bool selecting);
    bool moveCaretUp (bool selecting);
    bool moveCaretDown (bool selecting);
    bool scrollDown();
    bool scrollUp();
    bool pageUp (bool selecting);
    bool pageDown (bool selecting);
    bool moveCaretToTop (bool selecting);
    bool moveCaretToStartOfLine (bool selecting);
    bool moveCaretToEnd (bool selecting);
    bool moveCaretToEndOfLine (bool selecting);
    bool deleteBackwards (bool moveInWholeWordSteps);
    bool deleteForwards (bool moveInWholeWordSteps);
    bool deleteWhitespaceBackwardsToTabStop();
    virtual bool copyToClipboard();
    virtual bool cutToClipboard();
    virtual bool pasteFromClipboard();
    bool undo();
    bool redo();

    void selectRegion (const CodeDocument::Position& start, const CodeDocument::Position& end);
    bool selectAll();
    void deselectAll();

    void scrollToLine (int newFirstLineOnScreen);
    void scrollBy (int deltaLines);
    void scrollToColumn (int newFirstColumnOnScreen);
    void scrollToKeepCaretOnScreen();
    void scrollToKeepLinesOnScreen (Range<int> linesToShow);

    void insertTextAtCaret (const String& textToInsert) override;
    void insertTabAtCaret();

    void indentSelection();
    void unindentSelection();

    //==============================================================================
    Range<int> getHighlightedRegion() const override;
    bool isHighlightActive() const noexcept;
    void setHighlightedRegion (const Range<int>& newRange) override;
    String getTextInRange (const Range<int>& range) const override;

    //==============================================================================
    /** Can be used to save and restore the editor's caret position, selection state, etc. */
    struct JUCE_API State
    {
        /** Creates an object containing the state of the given editor. */
        State (const CodeEditorComponent&);
        /** Creates a state object from a string that was previously created with toString(). */
        State (const String& stringifiedVersion);
        State (const State&) noexcept;

        /** Updates the given editor with this saved state. */
        void restoreState (CodeEditorComponent&) const;

        /** Returns a stringified version of this state that can be used to recreate it later. */
        String toString() const;

    private:
        int lastTopLine, lastCaretPos, lastSelectionEnd;
    };

    //==============================================================================
    /** Changes the current tab settings.
        This lets you change the tab size and whether pressing the tab key inserts a
        tab character, or its equivalent number of spaces.
    */
    void setTabSize (int numSpacesPerTab, bool insertSpacesInsteadOfTabCharacters);

    /** Returns the current number of spaces per tab.
        @see setTabSize
    */
    int getTabSize() const noexcept                     { return spacesPerTab; }

    /** Returns true if the tab key will insert spaces instead of actual tab characters.
        @see setTabSize
    */
    bool areSpacesInsertedForTabs() const               { return useSpacesForTabs; }

    /** Returns a string containing spaces or tab characters to generate the given number of spaces. */
    String getTabString (int numSpaces) const;

    /** Changes the font.
        Make sure you only use a fixed-width font, or this component will look pretty nasty!
    */
    void setFont (const Font& newFont);

    /** Returns the font that the editor is using. */
    const Font& getFont() const noexcept                { return font; }

    /** Makes the editor read-only. */
    void setReadOnly (bool shouldBeReadOnly) noexcept;

    /** Returns true if the editor is set to be read-only. */
    bool isReadOnly() const noexcept                    { return readOnly; }

    //==============================================================================
    /** Defines a syntax highlighting colour scheme */
    struct JUCE_API  ColourScheme
    {
        /** Defines a colour for a token type */
        struct JUCE_API TokenType
        {
            String name;
            Colour colour;
        };

        Array<TokenType> types;

        void set (const String& name, Colour colour);
    };

    /** Changes the syntax highlighting scheme.
        The token type values are dependent on the tokeniser being used - use
        CodeTokeniser::getTokenTypes() to get a list of the token types.
        @see getColourForTokenType
    */
    void setColourScheme (const ColourScheme& scheme);

    /** Returns the current syntax highlighting colour scheme. */
    const ColourScheme& getColourScheme() const noexcept    { return colourScheme; }

    /** Returns one the syntax highlighting colour for the given token.
        The token type values are dependent on the tokeniser being used.
        @see setColourScheme
    */
    Colour getColourForTokenType (int tokenType) const;

    /** Rebuilds the syntax highlighting for a section of text.

        This happens automatically any time the CodeDocument is edited, but this
        method lets you change text colours even when the CodeDocument hasn't changed.

        For example, you could use this to highlight tokens as the cursor moves.
        To do so you'll need to tell your custom CodeTokeniser where the token you
        want to highlight is, and make it return a special type of token. Then you
        should call this method supplying the range of the highlighted text.
        @see CodeTokeniser
     */
    void retokenise (int startIndex, int endIndex);

    //==============================================================================
    /** A set of colour IDs to use to change the colour of various aspects of the editor.

        These constants can be used either via the Component::setColour(), or LookAndFeel::setColour()
        methods.

        @see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour
    */
    enum ColourIds
    {
        backgroundColourId          = 0x1004500,  /**< A colour to use to fill the editor's background. */
        highlightColourId           = 0x1004502,  /**< The colour to use for the highlighted background under selected text. */
        defaultTextColourId         = 0x1004503,  /**< The colour to use for text when no syntax colouring is enabled. */
        lineNumberBackgroundId      = 0x1004504,  /**< The colour to use for filling the background of the line-number gutter. */
        lineNumberTextId            = 0x1004505,  /**< The colour to use for drawing the line numbers. */
    };

    //==============================================================================
    /** Changes the size of the scrollbars. */
    void setScrollbarThickness (int thickness);

    /** Returns the thickness of the scrollbars. */
    int getScrollbarThickness() const noexcept          { return scrollbarThickness; }

    //==============================================================================
    /** Called when the return key is pressed - this can be overridden for custom behaviour. */
    virtual void handleReturnKey();
    /** Called when the tab key is pressed - this can be overridden for custom behaviour. */
    virtual void handleTabKey();
    /** Called when the escape key is pressed - this can be overridden for custom behaviour. */
    virtual void handleEscapeKey();

    /** Called when the view position is scrolled horizontally or vertically. */
    virtual void editorViewportPositionChanged();

    /** Called when the caret position moves. */
    virtual void caretPositionMoved();

    //==============================================================================
    /** This adds the items to the popup menu.

        By default it adds the cut/copy/paste items, but you can override this if
        you need to replace these with your own items.

        If you want to add your own items to the existing ones, you can override this,
        call the base class's addPopupMenuItems() method, then append your own items.

        When the menu has been shown, performPopupMenuAction() will be called to
        perform the item that the user has chosen.

        If this was triggered by a mouse-click, the mouseClickEvent parameter will be
        a pointer to the info about it, or may be null if the menu is being triggered
        by some other means.

        @see performPopupMenuAction, setPopupMenuEnabled, isPopupMenuEnabled
    */
    virtual void addPopupMenuItems (PopupMenu& menuToAddTo,
                                    const MouseEvent* mouseClickEvent);

    /** This is called to perform one of the items that was shown on the popup menu.

        If you've overridden addPopupMenuItems(), you should also override this
        to perform the actions that you've added.

        If you've overridden addPopupMenuItems() but have still left the default items
        on the menu, remember to call the superclass's performPopupMenuAction()
        so that it can perform the default actions if that's what the user clicked on.

        @see addPopupMenuItems, setPopupMenuEnabled, isPopupMenuEnabled
    */
    virtual void performPopupMenuAction (int menuItemID);

    /** Specifies a command-manager which the editor will notify whenever the state
        of any of its commands changes.
        If you're making use of the editor's ApplicationCommandTarget interface, then
        you should also use this to tell it which command manager it should use. Make
        sure that the manager does not go out of scope while the editor is using it. You
        can pass a nullptr here to disable this.
    */
    void setCommandManager (ApplicationCommandManager* newManager) noexcept;

    //==============================================================================
    /** @internal */
    void paint (Graphics&) override;
    /** @internal */
    void resized() override;
    /** @internal */
    bool keyPressed (const KeyPress&) override;
    /** @internal */
    void mouseDown (const MouseEvent&) override;
    /** @internal */
    void mouseDrag (const MouseEvent&) override;
    /** @internal */
    void mouseUp (const MouseEvent&) override;
    /** @internal */
    void mouseDoubleClick (const MouseEvent&) override;
    /** @internal */
    void mouseWheelMove (const MouseEvent&, const MouseWheelDetails&) override;
    /** @internal */
    void focusGained (FocusChangeType) override;
    /** @internal */
    void focusLost (FocusChangeType) override;
    /** @internal */
    bool isTextInputActive() const override;
    /** @internal */
    void setTemporaryUnderlining (const Array<Range<int>>&) override;
    /** @internal */
    ApplicationCommandTarget* getNextCommandTarget() override;
    /** @internal */
    void getAllCommands (Array<CommandID>&) override;
    /** @internal */
    void getCommandInfo (CommandID, ApplicationCommandInfo&) override;
    /** @internal */
    bool perform (const InvocationInfo&) override;
    /** @internal */
    void lookAndFeelChanged() override;
    /** @internal */
    std::unique_ptr<AccessibilityHandler> createAccessibilityHandler() override;

private:
    //==============================================================================
    CodeDocument& document;

    Font font { withDefaultMetrics (FontOptions{}) };
    int firstLineOnScreen = 0, spacesPerTab = 4;
    float charWidth = 0;
    int lineHeight = 0, linesOnScreen = 0, columnsOnScreen = 0;
    int scrollbarThickness = 16, columnToTryToMaintain = -1;
    bool readOnly = false, useSpacesForTabs = true, showLineNumbers = false, shouldFollowDocumentChanges = false;
    double xOffset = 0;
    CodeDocument::Position caretPos, selectionStart, selectionEnd;

    std::unique_ptr<CaretComponent> caret;
    ScrollBar verticalScrollBar { true }, horizontalScrollBar { false };
    ApplicationCommandManager* appCommandManager = nullptr;

    class Pimpl;
    std::unique_ptr<Pimpl> pimpl;

    class GutterComponent;
    std::unique_ptr<GutterComponent> gutter;

    class CodeEditorAccessibilityHandler;

    enum DragType
    {
        notDragging,
        draggingSelectionStart,
        draggingSelectionEnd
    };

    DragType dragType = notDragging;

    //==============================================================================
    CodeTokeniser* codeTokeniser;
    ColourScheme colourScheme;

    class CodeEditorLine;
    OwnedArray<CodeEditorLine> lines;
    void rebuildLineTokens();
    void rebuildLineTokensAsync();
    void codeDocumentChanged (int start, int end);

    Array<CodeDocument::Iterator> cachedIterators;
    void clearCachedIterators (int firstLineToBeInvalid);
    void updateCachedIterators (int maxLineNum);
    void getIteratorForPosition (int position, CodeDocument::Iterator&);

    void moveLineDelta (int delta, bool selecting);
    int getGutterSize() const noexcept;

    //==============================================================================
    void insertText (const String&);
    virtual void updateCaretPosition();
    void updateScrollBars();
    void scrollToLineInternal (int line);
    void scrollToColumnInternal (double column);
    void newTransaction();
    void cut();
    void indentSelectedLines (int spacesToAdd);
    bool skipBackwardsToPreviousTab();
    bool performCommand (CommandID);
    void setSelection (CodeDocument::Position, CodeDocument::Position);

    int indexToColumn (int line, int index) const noexcept;
    int columnToIndex (int line, int column) const noexcept;

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CodeEditorComponent)
};

} // namespace juce