421 lines
16 KiB
C++
421 lines
16 KiB
C++
/*
|
|
==============================================================================
|
|
|
|
This file is part of the JUCE library.
|
|
Copyright (c) 2017 - ROLI Ltd.
|
|
|
|
JUCE is an open source library subject to commercial or open-source
|
|
licensing.
|
|
|
|
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
|
|
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
|
|
27th April 2017).
|
|
|
|
End User License Agreement: www.juce.com/juce-5-licence
|
|
Privacy Policy: www.juce.com/juce-5-privacy-policy
|
|
|
|
Or: You may also use this code under the terms of the GPL v3 (see
|
|
www.gnu.org/licenses).
|
|
|
|
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
|
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
|
DISCLAIMED.
|
|
|
|
==============================================================================
|
|
*/
|
|
|
|
namespace juce
|
|
{
|
|
|
|
class CodeDocumentLine;
|
|
|
|
|
|
//==============================================================================
|
|
/**
|
|
A class for storing and manipulating a source code file.
|
|
|
|
When using a CodeEditorComponent, it takes one of these as its source object.
|
|
|
|
The CodeDocument stores its content as an array of lines, which makes it
|
|
quick to insert and delete.
|
|
|
|
@see CodeEditorComponent
|
|
|
|
@tags{GUI}
|
|
*/
|
|
class JUCE_API CodeDocument
|
|
{
|
|
public:
|
|
/** Creates a new, empty document. */
|
|
CodeDocument();
|
|
|
|
/** Destructor. */
|
|
~CodeDocument();
|
|
|
|
//==============================================================================
|
|
/** A position in a code document.
|
|
|
|
Using this class you can find a position in a code document and quickly get its
|
|
character position, line, and index. By calling setPositionMaintained (true), the
|
|
position is automatically updated when text is inserted or deleted in the document,
|
|
so that it maintains its original place in the text.
|
|
*/
|
|
class JUCE_API Position
|
|
{
|
|
public:
|
|
/** Creates an uninitialised position.
|
|
Don't attempt to call any methods on this until you've given it an owner document
|
|
to refer to!
|
|
*/
|
|
Position() noexcept;
|
|
|
|
/** Creates a position based on a line and index in a document.
|
|
|
|
Note that this index is NOT the column number, it's the number of characters from the
|
|
start of the line. The "column" number isn't quite the same, because if the line
|
|
contains any tab characters, the relationship of the index to its visual column depends on
|
|
the number of spaces per tab being used!
|
|
|
|
Lines are numbered from zero, and if the line or index are beyond the bounds of the document,
|
|
they will be adjusted to keep them within its limits.
|
|
*/
|
|
Position (const CodeDocument& ownerDocument,
|
|
int line, int indexInLine) noexcept;
|
|
|
|
/** Creates a position based on a character index in a document.
|
|
This position is placed at the specified number of characters from the start of the
|
|
document. The line and column are auto-calculated.
|
|
|
|
If the position is beyond the range of the document, it'll be adjusted to keep it
|
|
inside.
|
|
*/
|
|
Position (const CodeDocument& ownerDocument,
|
|
int charactersFromStartOfDocument) noexcept;
|
|
|
|
/** Creates a copy of another position.
|
|
|
|
This will copy the position, but the new object will not be set to maintain its position,
|
|
even if the source object was set to do so.
|
|
*/
|
|
Position (const Position&) noexcept;
|
|
|
|
/** Destructor. */
|
|
~Position();
|
|
|
|
Position& operator= (const Position&);
|
|
|
|
bool operator== (const Position&) const noexcept;
|
|
bool operator!= (const Position&) const noexcept;
|
|
|
|
/** Points this object at a new position within the document.
|
|
|
|
If the position is beyond the range of the document, it'll be adjusted to keep it
|
|
inside.
|
|
@see getPosition, setLineAndIndex
|
|
*/
|
|
void setPosition (int charactersFromStartOfDocument);
|
|
|
|
/** Returns the position as the number of characters from the start of the document.
|
|
@see setPosition, getLineNumber, getIndexInLine
|
|
*/
|
|
int getPosition() const noexcept { return characterPos; }
|
|
|
|
/** Moves the position to a new line and index within the line.
|
|
|
|
Note that the index is NOT the column at which the position appears in an editor.
|
|
If the line contains any tab characters, the relationship of the index to its
|
|
visual position depends on the number of spaces per tab being used!
|
|
|
|
Lines are numbered from zero, and if the line or index are beyond the bounds of the document,
|
|
they will be adjusted to keep them within its limits.
|
|
*/
|
|
void setLineAndIndex (int newLineNumber, int newIndexInLine);
|
|
|
|
/** Returns the line number of this position.
|
|
The first line in the document is numbered zero, not one!
|
|
*/
|
|
int getLineNumber() const noexcept { return line; }
|
|
|
|
/** Returns the number of characters from the start of the line.
|
|
|
|
Note that this value is NOT the column at which the position appears in an editor.
|
|
If the line contains any tab characters, the relationship of the index to its
|
|
visual position depends on the number of spaces per tab being used!
|
|
*/
|
|
int getIndexInLine() const noexcept { return indexInLine; }
|
|
|
|
/** Allows the position to be automatically updated when the document changes.
|
|
|
|
If this is set to true, the position will register with its document so that
|
|
when the document has text inserted or deleted, this position will be automatically
|
|
moved to keep it at the same position in the text.
|
|
*/
|
|
void setPositionMaintained (bool isMaintained);
|
|
|
|
//==============================================================================
|
|
/** Moves the position forwards or backwards by the specified number of characters.
|
|
@see movedBy
|
|
*/
|
|
void moveBy (int characterDelta);
|
|
|
|
/** Returns a position which is the same as this one, moved by the specified number of
|
|
characters.
|
|
@see moveBy
|
|
*/
|
|
Position movedBy (int characterDelta) const;
|
|
|
|
/** Returns a position which is the same as this one, moved up or down by the specified
|
|
number of lines.
|
|
@see movedBy
|
|
*/
|
|
Position movedByLines (int deltaLines) const;
|
|
|
|
/** Returns the character in the document at this position.
|
|
@see getLineText
|
|
*/
|
|
juce_wchar getCharacter() const;
|
|
|
|
/** Returns the line from the document that this position is within.
|
|
@see getCharacter, getLineNumber
|
|
*/
|
|
String getLineText() const;
|
|
|
|
private:
|
|
CodeDocument* owner = nullptr;
|
|
int characterPos = 0, line = 0, indexInLine = 0;
|
|
bool positionMaintained = false;
|
|
};
|
|
|
|
//==============================================================================
|
|
/** Returns the full text of the document. */
|
|
String getAllContent() const;
|
|
|
|
/** Returns a section of the document's text. */
|
|
String getTextBetween (const Position& start, const Position& end) const;
|
|
|
|
/** Returns a line from the document. */
|
|
String getLine (int lineIndex) const noexcept;
|
|
|
|
/** Returns the number of characters in the document. */
|
|
int getNumCharacters() const noexcept;
|
|
|
|
/** Returns the number of lines in the document. */
|
|
int getNumLines() const noexcept { return lines.size(); }
|
|
|
|
/** Returns the number of characters in the longest line of the document. */
|
|
int getMaximumLineLength() noexcept;
|
|
|
|
/** Deletes a section of the text.
|
|
This operation is undoable.
|
|
*/
|
|
void deleteSection (const Position& startPosition, const Position& endPosition);
|
|
|
|
/** Deletes a section of the text.
|
|
This operation is undoable.
|
|
*/
|
|
void deleteSection (int startIndex, int endIndex);
|
|
|
|
/** Inserts some text into the document at a given position.
|
|
This operation is undoable.
|
|
*/
|
|
void insertText (const Position& position, const String& text);
|
|
|
|
/** Inserts some text into the document at a given position.
|
|
This operation is undoable.
|
|
*/
|
|
void insertText (int insertIndex, const String& text);
|
|
|
|
/** Replaces a section of the text with a new string.
|
|
This operation is undoable.
|
|
*/
|
|
void replaceSection (int startIndex, int endIndex, const String& newText);
|
|
|
|
/** Clears the document and replaces it with some new text.
|
|
|
|
This operation is undoable - if you're trying to completely reset the document, you
|
|
might want to also call clearUndoHistory() and setSavePoint() after using this method.
|
|
*/
|
|
void replaceAllContent (const String& newContent);
|
|
|
|
/** Analyses the changes between the current content and some new text, and applies
|
|
those changes.
|
|
*/
|
|
void applyChanges (const String& newContent);
|
|
|
|
/** Replaces the editor's contents with the contents of a stream.
|
|
This will also reset the undo history and save point marker.
|
|
*/
|
|
bool loadFromStream (InputStream& stream);
|
|
|
|
/** Writes the editor's current contents to a stream. */
|
|
bool writeToStream (OutputStream& stream);
|
|
|
|
//==============================================================================
|
|
/** Returns the preferred new-line characters for the document.
|
|
This will be either "\\n", "\\r\\n", or (rarely) "\\r".
|
|
@see setNewLineCharacters
|
|
*/
|
|
String getNewLineCharacters() const noexcept { return newLineChars; }
|
|
|
|
/** Sets the new-line characters that the document should use.
|
|
The string must be either "\\n", "\\r\\n", or (rarely) "\\r".
|
|
@see getNewLineCharacters
|
|
*/
|
|
void setNewLineCharacters (const String& newLineCharacters) noexcept;
|
|
|
|
//==============================================================================
|
|
/** Begins a new undo transaction.
|
|
|
|
The document itself will not call this internally, so relies on whatever is using the
|
|
document to periodically call this to break up the undo sequence into sensible chunks.
|
|
@see UndoManager::beginNewTransaction
|
|
*/
|
|
void newTransaction();
|
|
|
|
/** Undo the last operation.
|
|
@see UndoManager::undo
|
|
*/
|
|
void undo();
|
|
|
|
/** Redo the last operation.
|
|
@see UndoManager::redo
|
|
*/
|
|
void redo();
|
|
|
|
/** Clears the undo history.
|
|
@see UndoManager::clearUndoHistory
|
|
*/
|
|
void clearUndoHistory();
|
|
|
|
/** Returns the document's UndoManager */
|
|
UndoManager& getUndoManager() noexcept { return undoManager; }
|
|
|
|
//==============================================================================
|
|
/** Makes a note that the document's current state matches the one that is saved.
|
|
|
|
After this has been called, hasChangedSinceSavePoint() will return false until
|
|
the document has been altered, and then it'll start returning true. If the document is
|
|
altered, but then undone until it gets back to this state, hasChangedSinceSavePoint()
|
|
will again return false.
|
|
|
|
@see hasChangedSinceSavePoint
|
|
*/
|
|
void setSavePoint() noexcept;
|
|
|
|
/** Returns true if the state of the document differs from the state it was in when
|
|
setSavePoint() was last called.
|
|
|
|
@see setSavePoint
|
|
*/
|
|
bool hasChangedSinceSavePoint() const noexcept;
|
|
|
|
//==============================================================================
|
|
/** Searches for a word-break. */
|
|
Position findWordBreakAfter (const Position& position) const noexcept;
|
|
/** Searches for a word-break. */
|
|
Position findWordBreakBefore (const Position& position) const noexcept;
|
|
/** Finds the token that contains the given position. */
|
|
void findTokenContaining (const Position& pos, Position& start, Position& end) const noexcept;
|
|
/** Finds the line that contains the given position. */
|
|
void findLineContaining (const Position& pos, Position& start, Position& end) const noexcept;
|
|
|
|
//==============================================================================
|
|
/** An object that receives callbacks from the CodeDocument when its text changes.
|
|
@see CodeDocument::addListener, CodeDocument::removeListener
|
|
*/
|
|
class JUCE_API Listener
|
|
{
|
|
public:
|
|
Listener() = default;
|
|
virtual ~Listener() = default;
|
|
|
|
/** Called by a CodeDocument when text is added. */
|
|
virtual void codeDocumentTextInserted (const String& newText, int insertIndex) = 0;
|
|
|
|
/** Called by a CodeDocument when text is deleted. */
|
|
virtual void codeDocumentTextDeleted (int startIndex, int endIndex) = 0;
|
|
};
|
|
|
|
/** Registers a listener object to receive callbacks when the document changes.
|
|
If the listener is already registered, this method has no effect.
|
|
@see removeListener
|
|
*/
|
|
void addListener (Listener* listener);
|
|
|
|
/** Deregisters a listener.
|
|
@see addListener
|
|
*/
|
|
void removeListener (Listener* listener);
|
|
|
|
//==============================================================================
|
|
/** Iterates the text in a CodeDocument.
|
|
|
|
This class lets you read characters from a CodeDocument. It's designed to be used
|
|
by a CodeTokeniser object.
|
|
|
|
@see CodeDocument
|
|
*/
|
|
class JUCE_API Iterator
|
|
{
|
|
public:
|
|
Iterator (const CodeDocument& document) noexcept;
|
|
Iterator (const Iterator&) = default;
|
|
Iterator& operator= (const Iterator&) = default;
|
|
~Iterator() noexcept;
|
|
|
|
/** Reads the next character and returns it.
|
|
@see peekNextChar
|
|
*/
|
|
juce_wchar nextChar() noexcept;
|
|
|
|
/** Reads the next character without advancing the current position. */
|
|
juce_wchar peekNextChar() const noexcept;
|
|
|
|
/** Advances the position by one character. */
|
|
void skip() noexcept;
|
|
|
|
/** Returns the position as the number of characters from the start of the document. */
|
|
int getPosition() const noexcept { return position; }
|
|
|
|
/** Skips over any whitespace characters until the next character is non-whitespace. */
|
|
void skipWhitespace() noexcept;
|
|
|
|
/** Skips forward until the next character will be the first character on the next line */
|
|
void skipToEndOfLine() noexcept;
|
|
|
|
/** Returns the line number of the next character. */
|
|
int getLine() const noexcept { return line; }
|
|
|
|
/** Returns true if the iterator has reached the end of the document. */
|
|
bool isEOF() const noexcept;
|
|
|
|
private:
|
|
const CodeDocument* document;
|
|
mutable String::CharPointerType charPointer { nullptr };
|
|
int line = 0, position = 0;
|
|
};
|
|
|
|
private:
|
|
//==============================================================================
|
|
struct InsertAction;
|
|
struct DeleteAction;
|
|
friend class Iterator;
|
|
friend class Position;
|
|
|
|
OwnedArray<CodeDocumentLine> lines;
|
|
Array<Position*> positionsToMaintain;
|
|
UndoManager undoManager;
|
|
int currentActionIndex = 0, indexOfSavedState = -1;
|
|
int maximumLineLength = -1;
|
|
ListenerList<Listener> listeners;
|
|
String newLineChars { "\r\n" };
|
|
|
|
void insert (const String& text, int insertPos, bool undoable);
|
|
void remove (int startPos, int endPos, bool undoable);
|
|
void checkLastLineStatus();
|
|
|
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CodeDocument)
|
|
};
|
|
|
|
} // namespace juce
|