| @@ -43805,26 +43805,20 @@ class CodeDocumentLine | |||
| public: | |||
| CodeDocumentLine (const String& line_, | |||
| const int lineLength_, | |||
| const int lineStartInFile_) throw() | |||
| const int numNewLineChars, | |||
| const int lineStartInFile_) | |||
| : line (line_), | |||
| lineStartInFile (lineStartInFile_), | |||
| lineLength (lineLength_) | |||
| lineLength (lineLength_), | |||
| lineLengthWithoutNewLines (lineLength_ - numNewLineChars) | |||
| { | |||
| lineLengthWithoutNewLines = lineLength; | |||
| while (lineLengthWithoutNewLines > 0 | |||
| && (line [lineLengthWithoutNewLines - 1] == '\n' | |||
| || line [lineLengthWithoutNewLines - 1] == '\r')) | |||
| { | |||
| --lineLengthWithoutNewLines; | |||
| } | |||
| } | |||
| ~CodeDocumentLine() throw() | |||
| { | |||
| } | |||
| static void createLines (Array <CodeDocumentLine*>& newLines, const String& text) throw() | |||
| static void createLines (Array <CodeDocumentLine*>& newLines, const String& text) | |||
| { | |||
| const tchar* const t = (const tchar*) text; | |||
| int pos = 0; | |||
| @@ -43832,20 +43826,27 @@ public: | |||
| while (t [pos] != 0) | |||
| { | |||
| const int startOfLine = pos; | |||
| int numNewLineChars = 0; | |||
| while (t[pos] != 0) | |||
| { | |||
| if (t[pos] == T('\r')) | |||
| { | |||
| ++numNewLineChars; | |||
| ++pos; | |||
| if (t[pos] == T('\n')) | |||
| { | |||
| ++numNewLineChars; | |||
| ++pos; | |||
| } | |||
| break; | |||
| } | |||
| if (t[pos] == T('\n')) | |||
| { | |||
| ++numNewLineChars; | |||
| ++pos; | |||
| break; | |||
| } | |||
| @@ -43854,7 +43855,7 @@ public: | |||
| } | |||
| newLines.add (new CodeDocumentLine (String (t + startOfLine, pos - startOfLine), | |||
| pos - startOfLine, | |||
| pos - startOfLine, numNewLineChars, | |||
| startOfLine)); | |||
| } | |||
| @@ -43882,17 +43883,19 @@ public: | |||
| int lineStartInFile, lineLength, lineLengthWithoutNewLines; | |||
| }; | |||
| CodeDocument::Iterator::Iterator (CodeDocument* const document_) throw() | |||
| CodeDocument::Iterator::Iterator (CodeDocument* const document_) | |||
| : document (document_), | |||
| line (0), | |||
| position (0) | |||
| position (0), | |||
| currentLine (document_->lines[0]) | |||
| { | |||
| } | |||
| CodeDocument::Iterator::Iterator (const CodeDocument::Iterator& other) | |||
| : document (other.document), | |||
| line (other.line), | |||
| position (other.position) | |||
| position (other.position), | |||
| currentLine (other.currentLine) | |||
| { | |||
| } | |||
| @@ -43901,6 +43904,7 @@ const CodeDocument::Iterator& CodeDocument::Iterator::operator= (const CodeDocum | |||
| document = other.document; | |||
| line = other.line; | |||
| position = other.position; | |||
| currentLine = other.currentLine; | |||
| return *this; | |||
| } | |||
| @@ -43909,73 +43913,63 @@ CodeDocument::Iterator::~Iterator() throw() | |||
| { | |||
| } | |||
| juce_wchar CodeDocument::Iterator::nextChar() throw() | |||
| juce_wchar CodeDocument::Iterator::nextChar() | |||
| { | |||
| if (line >= document->lines.size()) | |||
| if (currentLine == 0) | |||
| return 0; | |||
| const CodeDocumentLine* const currentLine = document->lines.getUnchecked (line); | |||
| jassert (currentLine == document->lines.getUnchecked (line)); | |||
| const juce_wchar result = currentLine->line [position - currentLine->lineStartInFile]; | |||
| if (++position >= currentLine->lineStartInFile + currentLine->lineLength) | |||
| ++line; | |||
| skip(); | |||
| return result; | |||
| } | |||
| void CodeDocument::Iterator::skip() throw() | |||
| void CodeDocument::Iterator::skip() | |||
| { | |||
| if (line < document->lines.size()) | |||
| if (currentLine != 0) | |||
| { | |||
| const CodeDocumentLine* const currentLine = document->lines.getUnchecked (line); | |||
| jassert (currentLine == document->lines.getUnchecked (line)); | |||
| if (++position >= currentLine->lineStartInFile + currentLine->lineLength) | |||
| { | |||
| ++line; | |||
| currentLine = document->lines [line]; | |||
| } | |||
| } | |||
| } | |||
| juce_wchar CodeDocument::Iterator::peekNextChar() const throw() | |||
| void CodeDocument::Iterator::skipToEndOfLine() | |||
| { | |||
| if (line >= document->lines.size()) | |||
| return 0; | |||
| if (currentLine != 0) | |||
| { | |||
| jassert (currentLine == document->lines.getUnchecked (line)); | |||
| const CodeDocumentLine* const currentLine = document->lines.getUnchecked (line); | |||
| return currentLine->line [position - currentLine->lineStartInFile]; | |||
| ++line; | |||
| currentLine = document->lines [line]; | |||
| if (currentLine != 0) | |||
| position = currentLine->lineStartInFile; | |||
| } | |||
| } | |||
| void CodeDocument::Iterator::skipWhitespace() | |||
| juce_wchar CodeDocument::Iterator::peekNextChar() const | |||
| { | |||
| while (line < document->lines.size()) | |||
| { | |||
| const CodeDocumentLine* const currentLine = document->lines.getUnchecked (line); | |||
| for (;;) | |||
| { | |||
| if (! CharacterFunctions::isWhitespace (currentLine->line [position - currentLine->lineStartInFile])) | |||
| return; | |||
| if (currentLine == 0) | |||
| return 0; | |||
| if (++position >= currentLine->lineStartInFile + currentLine->lineLength) | |||
| { | |||
| ++line; | |||
| break; | |||
| } | |||
| } | |||
| } | |||
| jassert (currentLine == document->lines.getUnchecked (line)); | |||
| return currentLine->line [position - currentLine->lineStartInFile]; | |||
| } | |||
| void CodeDocument::Iterator::skipToEndOfLine() | |||
| void CodeDocument::Iterator::skipWhitespace() | |||
| { | |||
| if (line < document->lines.size()) | |||
| { | |||
| const CodeDocumentLine* const currentLine = document->lines.getUnchecked (line); | |||
| position = currentLine->lineStartInFile + currentLine->lineLength; | |||
| ++line; | |||
| } | |||
| while (CharacterFunctions::isWhitespace (peekNextChar())) | |||
| skip(); | |||
| } | |||
| bool CodeDocument::Iterator::isEOF() const throw() | |||
| { | |||
| return position >= document->getNumCharacters(); | |||
| return currentLine == 0; | |||
| } | |||
| CodeDocument::Position::Position() throw() | |||
| @@ -44426,6 +44420,25 @@ const CodeDocument::Position CodeDocument::findWordBreakBefore (const Position& | |||
| return p; | |||
| } | |||
| void CodeDocument::checkLastLineStatus() | |||
| { | |||
| while (lines.size() > 0 | |||
| && lines.getLast()->lineLength == 0 | |||
| && (lines.size() == 1 || ! lines.getUnchecked (lines.size() - 2)->endsWithLineBreak())) | |||
| { | |||
| // remove any empty lines at the end if the preceding line doesn't end in a newline. | |||
| lines.removeLast(); | |||
| } | |||
| const CodeDocumentLine* const lastLine = lines.getLast(); | |||
| if (lastLine != 0 && lastLine->endsWithLineBreak()) | |||
| { | |||
| // check that there's an empty line at the end if the preceding one ends in a newline.. | |||
| lines.add (new CodeDocumentLine (String::empty, 0, 0, lastLine->lineStartInFile + lastLine->lineLength)); | |||
| } | |||
| } | |||
| void CodeDocument::addListener (CodeDocument::Listener* const listener) throw() | |||
| { | |||
| listeners.addIfNotAlreadyThere (listener); | |||
| @@ -44540,6 +44553,8 @@ void CodeDocument::insert (const String& text, const int insertPos, const bool u | |||
| lineStart += l->lineLength; | |||
| } | |||
| checkLastLineStatus(); | |||
| const int newTextLength = text.length(); | |||
| for (i = 0; i < positionsToMaintain.size(); ++i) | |||
| { | |||
| @@ -44640,6 +44655,8 @@ void CodeDocument::remove (const int startPos, const int endPos, const bool undo | |||
| l->lineStartInFile = previousLine->lineStartInFile + previousLine->lineLength; | |||
| } | |||
| checkLastLineStatus(); | |||
| const int totalChars = getNumCharacters(); | |||
| for (i = 0; i < positionsToMaintain.size(); ++i) | |||
| @@ -45746,12 +45763,12 @@ const Colour CodeEditorComponent::getColourForTokenType (const int tokenType) co | |||
| void CodeEditorComponent::clearCachedIterators (const int firstLineToBeInvalid) throw() | |||
| { | |||
| for (int i = cachedIterators.size(); --i >= 0;) | |||
| if (cachedIterators.getUnchecked (i)->getLine() >= firstLineToBeInvalid) | |||
| cachedIterators.remove (i); | |||
| int i; | |||
| for (i = cachedIterators.size(); --i >= 0;) | |||
| if (cachedIterators.getUnchecked (i)->getLine() < firstLineToBeInvalid) | |||
| break; | |||
| // need to also clear the one before the invalid line | |||
| cachedIterators.removeLast(); | |||
| cachedIterators.removeRange (jmax (0, i - 1), cachedIterators.size()); | |||
| } | |||
| void CodeEditorComponent::updateCachedIterators (int maxLineNum) | |||
| @@ -20101,16 +20101,16 @@ public: | |||
| class Iterator | |||
| { | |||
| public: | |||
| Iterator (CodeDocument* const document) throw(); | |||
| Iterator (CodeDocument* const document); | |||
| Iterator (const Iterator& other); | |||
| const Iterator& operator= (const Iterator& other) throw(); | |||
| ~Iterator() throw(); | |||
| juce_wchar nextChar() throw(); | |||
| juce_wchar nextChar(); | |||
| juce_wchar peekNextChar() const throw(); | |||
| juce_wchar peekNextChar() const; | |||
| void skip() throw(); | |||
| void skip(); | |||
| int getPosition() const throw() { return position; } | |||
| @@ -20124,6 +20124,7 @@ public: | |||
| private: | |||
| CodeDocument* document; | |||
| CodeDocumentLine* currentLine; | |||
| int line, position; | |||
| }; | |||
| @@ -20147,6 +20148,7 @@ private: | |||
| void insert (const String& text, const int insertPos, const bool undoable); | |||
| void remove (const int startPos, const int endPos, const bool undoable); | |||
| void checkLastLineStatus(); | |||
| CodeDocument (const CodeDocument&); | |||
| const CodeDocument& operator= (const CodeDocument&); | |||
| @@ -36,26 +36,20 @@ class CodeDocumentLine | |||
| public: | |||
| CodeDocumentLine (const String& line_, | |||
| const int lineLength_, | |||
| const int lineStartInFile_) throw() | |||
| const int numNewLineChars, | |||
| const int lineStartInFile_) | |||
| : line (line_), | |||
| lineStartInFile (lineStartInFile_), | |||
| lineLength (lineLength_) | |||
| lineLength (lineLength_), | |||
| lineLengthWithoutNewLines (lineLength_ - numNewLineChars) | |||
| { | |||
| lineLengthWithoutNewLines = lineLength; | |||
| while (lineLengthWithoutNewLines > 0 | |||
| && (line [lineLengthWithoutNewLines - 1] == '\n' | |||
| || line [lineLengthWithoutNewLines - 1] == '\r')) | |||
| { | |||
| --lineLengthWithoutNewLines; | |||
| } | |||
| } | |||
| ~CodeDocumentLine() throw() | |||
| { | |||
| } | |||
| static void createLines (Array <CodeDocumentLine*>& newLines, const String& text) throw() | |||
| static void createLines (Array <CodeDocumentLine*>& newLines, const String& text) | |||
| { | |||
| const tchar* const t = (const tchar*) text; | |||
| int pos = 0; | |||
| @@ -63,20 +57,27 @@ public: | |||
| while (t [pos] != 0) | |||
| { | |||
| const int startOfLine = pos; | |||
| int numNewLineChars = 0; | |||
| while (t[pos] != 0) | |||
| { | |||
| if (t[pos] == T('\r')) | |||
| { | |||
| ++numNewLineChars; | |||
| ++pos; | |||
| if (t[pos] == T('\n')) | |||
| { | |||
| ++numNewLineChars; | |||
| ++pos; | |||
| } | |||
| break; | |||
| } | |||
| if (t[pos] == T('\n')) | |||
| { | |||
| ++numNewLineChars; | |||
| ++pos; | |||
| break; | |||
| } | |||
| @@ -85,7 +86,7 @@ public: | |||
| } | |||
| newLines.add (new CodeDocumentLine (String (t + startOfLine, pos - startOfLine), | |||
| pos - startOfLine, | |||
| pos - startOfLine, numNewLineChars, | |||
| startOfLine)); | |||
| } | |||
| @@ -114,17 +115,19 @@ public: | |||
| }; | |||
| //============================================================================== | |||
| CodeDocument::Iterator::Iterator (CodeDocument* const document_) throw() | |||
| CodeDocument::Iterator::Iterator (CodeDocument* const document_) | |||
| : document (document_), | |||
| line (0), | |||
| position (0) | |||
| position (0), | |||
| currentLine (document_->lines[0]) | |||
| { | |||
| } | |||
| CodeDocument::Iterator::Iterator (const CodeDocument::Iterator& other) | |||
| : document (other.document), | |||
| line (other.line), | |||
| position (other.position) | |||
| position (other.position), | |||
| currentLine (other.currentLine) | |||
| { | |||
| } | |||
| @@ -133,6 +136,7 @@ const CodeDocument::Iterator& CodeDocument::Iterator::operator= (const CodeDocum | |||
| document = other.document; | |||
| line = other.line; | |||
| position = other.position; | |||
| currentLine = other.currentLine; | |||
| return *this; | |||
| } | |||
| @@ -141,73 +145,63 @@ CodeDocument::Iterator::~Iterator() throw() | |||
| { | |||
| } | |||
| juce_wchar CodeDocument::Iterator::nextChar() throw() | |||
| juce_wchar CodeDocument::Iterator::nextChar() | |||
| { | |||
| if (line >= document->lines.size()) | |||
| if (currentLine == 0) | |||
| return 0; | |||
| const CodeDocumentLine* const currentLine = document->lines.getUnchecked (line); | |||
| jassert (currentLine == document->lines.getUnchecked (line)); | |||
| const juce_wchar result = currentLine->line [position - currentLine->lineStartInFile]; | |||
| if (++position >= currentLine->lineStartInFile + currentLine->lineLength) | |||
| ++line; | |||
| skip(); | |||
| return result; | |||
| } | |||
| void CodeDocument::Iterator::skip() throw() | |||
| void CodeDocument::Iterator::skip() | |||
| { | |||
| if (line < document->lines.size()) | |||
| if (currentLine != 0) | |||
| { | |||
| const CodeDocumentLine* const currentLine = document->lines.getUnchecked (line); | |||
| jassert (currentLine == document->lines.getUnchecked (line)); | |||
| if (++position >= currentLine->lineStartInFile + currentLine->lineLength) | |||
| { | |||
| ++line; | |||
| currentLine = document->lines [line]; | |||
| } | |||
| } | |||
| } | |||
| juce_wchar CodeDocument::Iterator::peekNextChar() const throw() | |||
| void CodeDocument::Iterator::skipToEndOfLine() | |||
| { | |||
| if (line >= document->lines.size()) | |||
| return 0; | |||
| if (currentLine != 0) | |||
| { | |||
| jassert (currentLine == document->lines.getUnchecked (line)); | |||
| const CodeDocumentLine* const currentLine = document->lines.getUnchecked (line); | |||
| return currentLine->line [position - currentLine->lineStartInFile]; | |||
| ++line; | |||
| currentLine = document->lines [line]; | |||
| if (currentLine != 0) | |||
| position = currentLine->lineStartInFile; | |||
| } | |||
| } | |||
| void CodeDocument::Iterator::skipWhitespace() | |||
| juce_wchar CodeDocument::Iterator::peekNextChar() const | |||
| { | |||
| while (line < document->lines.size()) | |||
| { | |||
| const CodeDocumentLine* const currentLine = document->lines.getUnchecked (line); | |||
| for (;;) | |||
| { | |||
| if (! CharacterFunctions::isWhitespace (currentLine->line [position - currentLine->lineStartInFile])) | |||
| return; | |||
| if (currentLine == 0) | |||
| return 0; | |||
| if (++position >= currentLine->lineStartInFile + currentLine->lineLength) | |||
| { | |||
| ++line; | |||
| break; | |||
| } | |||
| } | |||
| } | |||
| jassert (currentLine == document->lines.getUnchecked (line)); | |||
| return currentLine->line [position - currentLine->lineStartInFile]; | |||
| } | |||
| void CodeDocument::Iterator::skipToEndOfLine() | |||
| void CodeDocument::Iterator::skipWhitespace() | |||
| { | |||
| if (line < document->lines.size()) | |||
| { | |||
| const CodeDocumentLine* const currentLine = document->lines.getUnchecked (line); | |||
| position = currentLine->lineStartInFile + currentLine->lineLength; | |||
| ++line; | |||
| } | |||
| while (CharacterFunctions::isWhitespace (peekNextChar())) | |||
| skip(); | |||
| } | |||
| bool CodeDocument::Iterator::isEOF() const throw() | |||
| { | |||
| return position >= document->getNumCharacters(); | |||
| return currentLine == 0; | |||
| } | |||
| //============================================================================== | |||
| @@ -661,6 +655,24 @@ const CodeDocument::Position CodeDocument::findWordBreakBefore (const Position& | |||
| return p; | |||
| } | |||
| void CodeDocument::checkLastLineStatus() | |||
| { | |||
| while (lines.size() > 0 | |||
| && lines.getLast()->lineLength == 0 | |||
| && (lines.size() == 1 || ! lines.getUnchecked (lines.size() - 2)->endsWithLineBreak())) | |||
| { | |||
| // remove any empty lines at the end if the preceding line doesn't end in a newline. | |||
| lines.removeLast(); | |||
| } | |||
| const CodeDocumentLine* const lastLine = lines.getLast(); | |||
| if (lastLine != 0 && lastLine->endsWithLineBreak()) | |||
| { | |||
| // check that there's an empty line at the end if the preceding one ends in a newline.. | |||
| lines.add (new CodeDocumentLine (String::empty, 0, 0, lastLine->lineStartInFile + lastLine->lineLength)); | |||
| } | |||
| } | |||
| //============================================================================== | |||
| void CodeDocument::addListener (CodeDocument::Listener* const listener) throw() | |||
| @@ -778,6 +790,8 @@ void CodeDocument::insert (const String& text, const int insertPos, const bool u | |||
| lineStart += l->lineLength; | |||
| } | |||
| checkLastLineStatus(); | |||
| const int newTextLength = text.length(); | |||
| for (i = 0; i < positionsToMaintain.size(); ++i) | |||
| { | |||
| @@ -879,6 +893,8 @@ void CodeDocument::remove (const int startPos, const int endPos, const bool undo | |||
| l->lineStartInFile = previousLine->lineStartInFile + previousLine->lineLength; | |||
| } | |||
| checkLastLineStatus(); | |||
| const int totalChars = getNumCharacters(); | |||
| for (i = 0; i < positionsToMaintain.size(); ++i) | |||
| @@ -329,7 +329,7 @@ public: | |||
| class Iterator | |||
| { | |||
| public: | |||
| Iterator (CodeDocument* const document) throw(); | |||
| Iterator (CodeDocument* const document); | |||
| Iterator (const Iterator& other); | |||
| const Iterator& operator= (const Iterator& other) throw(); | |||
| ~Iterator() throw(); | |||
| @@ -337,13 +337,13 @@ public: | |||
| /** Reads the next character and returns it. | |||
| @see peekNextChar | |||
| */ | |||
| juce_wchar nextChar() throw(); | |||
| juce_wchar nextChar(); | |||
| /** Reads the next character without advancing the current position. */ | |||
| juce_wchar peekNextChar() const throw(); | |||
| juce_wchar peekNextChar() const; | |||
| /** Advances the position by one character. */ | |||
| void skip() throw(); | |||
| void skip(); | |||
| /** Returns the position of the next character as its position within the | |||
| whole document. | |||
| @@ -364,6 +364,7 @@ public: | |||
| private: | |||
| CodeDocument* document; | |||
| CodeDocumentLine* currentLine; | |||
| int line, position; | |||
| }; | |||
| @@ -388,6 +389,7 @@ private: | |||
| void insert (const String& text, const int insertPos, const bool undoable); | |||
| void remove (const int startPos, const int endPos, const bool undoable); | |||
| void checkLastLineStatus(); | |||
| CodeDocument (const CodeDocument&); | |||
| const CodeDocument& operator= (const CodeDocument&); | |||
| @@ -1132,12 +1132,12 @@ const Colour CodeEditorComponent::getColourForTokenType (const int tokenType) co | |||
| void CodeEditorComponent::clearCachedIterators (const int firstLineToBeInvalid) throw() | |||
| { | |||
| for (int i = cachedIterators.size(); --i >= 0;) | |||
| if (cachedIterators.getUnchecked (i)->getLine() >= firstLineToBeInvalid) | |||
| cachedIterators.remove (i); | |||
| int i; | |||
| for (i = cachedIterators.size(); --i >= 0;) | |||
| if (cachedIterators.getUnchecked (i)->getLine() < firstLineToBeInvalid) | |||
| break; | |||
| // need to also clear the one before the invalid line | |||
| cachedIterators.removeLast(); | |||
| cachedIterators.removeRange (jmax (0, i - 1), cachedIterators.size()); | |||
| } | |||
| void CodeEditorComponent::updateCachedIterators (int maxLineNum) | |||