From f7636fe1a3e279c432a3175557a0e21f5dabdd69 Mon Sep 17 00:00:00 2001 From: Julian Storer Date: Thu, 4 Feb 2010 12:46:39 +0000 Subject: [PATCH] Fix for CodeDocument when opening files with a blank line at the end. --- juce_amalgamated.cpp | 141 ++++++++++-------- juce_amalgamated.h | 10 +- .../code_editor/juce_CodeDocument.cpp | 130 +++++++++------- .../code_editor/juce_CodeDocument.h | 10 +- .../code_editor/juce_CodeEditorComponent.cpp | 10 +- 5 files changed, 169 insertions(+), 132 deletions(-) diff --git a/juce_amalgamated.cpp b/juce_amalgamated.cpp index 68f9d71cac..be8b6faaa8 100644 --- a/juce_amalgamated.cpp +++ b/juce_amalgamated.cpp @@ -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 & newLines, const String& text) throw() + static void createLines (Array & 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; - } -} - -juce_wchar CodeDocument::Iterator::peekNextChar() const throw() -{ - if (line >= document->lines.size()) - return 0; - - const CodeDocumentLine* const currentLine = document->lines.getUnchecked (line); - return currentLine->line [position - currentLine->lineStartInFile]; -} - -void CodeDocument::Iterator::skipWhitespace() -{ - while (line < document->lines.size()) - { - const CodeDocumentLine* const currentLine = document->lines.getUnchecked (line); - - for (;;) { - if (! CharacterFunctions::isWhitespace (currentLine->line [position - currentLine->lineStartInFile])) - return; - - if (++position >= currentLine->lineStartInFile + currentLine->lineLength) - { - ++line; - break; - } + ++line; + currentLine = document->lines [line]; } } } void CodeDocument::Iterator::skipToEndOfLine() { - if (line < document->lines.size()) + if (currentLine != 0) { - const CodeDocumentLine* const currentLine = document->lines.getUnchecked (line); - position = currentLine->lineStartInFile + currentLine->lineLength; + jassert (currentLine == document->lines.getUnchecked (line)); + ++line; + currentLine = document->lines [line]; + + if (currentLine != 0) + position = currentLine->lineStartInFile; } } +juce_wchar CodeDocument::Iterator::peekNextChar() const +{ + if (currentLine == 0) + return 0; + + jassert (currentLine == document->lines.getUnchecked (line)); + return currentLine->line [position - currentLine->lineStartInFile]; +} + +void CodeDocument::Iterator::skipWhitespace() +{ + 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) diff --git a/juce_amalgamated.h b/juce_amalgamated.h index fe3ba7c4e1..7f506d0e8c 100644 --- a/juce_amalgamated.h +++ b/juce_amalgamated.h @@ -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&); diff --git a/src/gui/components/code_editor/juce_CodeDocument.cpp b/src/gui/components/code_editor/juce_CodeDocument.cpp index 0d6a50b310..7579cf061a 100644 --- a/src/gui/components/code_editor/juce_CodeDocument.cpp +++ b/src/gui/components/code_editor/juce_CodeDocument.cpp @@ -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 & newLines, const String& text) throw() + static void createLines (Array & 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; - } -} - -juce_wchar CodeDocument::Iterator::peekNextChar() const throw() -{ - if (line >= document->lines.size()) - return 0; - - const CodeDocumentLine* const currentLine = document->lines.getUnchecked (line); - return currentLine->line [position - currentLine->lineStartInFile]; -} - -void CodeDocument::Iterator::skipWhitespace() -{ - while (line < document->lines.size()) - { - const CodeDocumentLine* const currentLine = document->lines.getUnchecked (line); - - for (;;) { - if (! CharacterFunctions::isWhitespace (currentLine->line [position - currentLine->lineStartInFile])) - return; - - if (++position >= currentLine->lineStartInFile + currentLine->lineLength) - { - ++line; - break; - } + ++line; + currentLine = document->lines [line]; } } } void CodeDocument::Iterator::skipToEndOfLine() { - if (line < document->lines.size()) + if (currentLine != 0) { - const CodeDocumentLine* const currentLine = document->lines.getUnchecked (line); - position = currentLine->lineStartInFile + currentLine->lineLength; + jassert (currentLine == document->lines.getUnchecked (line)); + ++line; + currentLine = document->lines [line]; + + if (currentLine != 0) + position = currentLine->lineStartInFile; } } +juce_wchar CodeDocument::Iterator::peekNextChar() const +{ + if (currentLine == 0) + return 0; + + jassert (currentLine == document->lines.getUnchecked (line)); + return currentLine->line [position - currentLine->lineStartInFile]; +} + +void CodeDocument::Iterator::skipWhitespace() +{ + 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) diff --git a/src/gui/components/code_editor/juce_CodeDocument.h b/src/gui/components/code_editor/juce_CodeDocument.h index 1ed189354d..eedfe47574 100644 --- a/src/gui/components/code_editor/juce_CodeDocument.h +++ b/src/gui/components/code_editor/juce_CodeDocument.h @@ -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&); diff --git a/src/gui/components/code_editor/juce_CodeEditorComponent.cpp b/src/gui/components/code_editor/juce_CodeEditorComponent.cpp index 24086f54dc..780c39438b 100644 --- a/src/gui/components/code_editor/juce_CodeEditorComponent.cpp +++ b/src/gui/components/code_editor/juce_CodeEditorComponent.cpp @@ -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)