| @@ -41,7 +41,8 @@ public: | |||
| if (tokeniser == nullptr) | |||
| { | |||
| newTokens.add (SyntaxToken (document.getLine (lineNum), -1)); | |||
| const String line (document.getLine (lineNum)); | |||
| addToken (newTokens, line, line.length(), -1); | |||
| } | |||
| else if (lineNum < document.getNumLines()) | |||
| { | |||
| @@ -81,22 +82,29 @@ public: | |||
| } | |||
| void draw (CodeEditorComponent& owner, Graphics& g, const Font& font, | |||
| float x, const float rightEdge, const int y, const int baselineOffset, const int lineHeight, | |||
| const float leftClip, const float rightClip, | |||
| const float xOffset, const int y, const int baselineOffset, | |||
| const int lineHeight, const float charWidth, | |||
| const Colour& highlightColour) const | |||
| { | |||
| if (highlightColumnStart < highlightColumnEnd) | |||
| { | |||
| g.setColour (highlightColour); | |||
| g.fillRect (roundToInt (x + highlightColumnStart * owner.getCharWidth()), y, | |||
| g.fillRect (roundToInt (xOffset + highlightColumnStart * owner.getCharWidth()), y, | |||
| roundToInt ((highlightColumnEnd - highlightColumnStart) * owner.getCharWidth()), lineHeight); | |||
| } | |||
| const float baselineY = (float) (y + baselineOffset); | |||
| Colour lastColour (0x00000001); | |||
| GlyphArrangement ga; | |||
| int column = 0; | |||
| for (int i = 0; i < tokens.size(); ++i) | |||
| { | |||
| const float tokenX = xOffset + column * charWidth; | |||
| if (tokenX > rightClip) | |||
| break; | |||
| SyntaxToken& token = tokens.getReference(i); | |||
| const Colour newColour (owner.getColourForTokenType (token.tokenType)); | |||
| @@ -109,18 +117,11 @@ public: | |||
| g.setColour (newColour); | |||
| } | |||
| ga.addCurtailedLineOfText (font, token.text, x, baselineY, rightEdge - x, false); | |||
| if (i < tokens.size() - 1) | |||
| { | |||
| if (token.width < 0) | |||
| token.width = font.getStringWidthFloat (token.text); | |||
| x += token.width; | |||
| column += token.length; | |||
| if (x > rightEdge) | |||
| break; | |||
| } | |||
| if (xOffset + column * charWidth >= leftClip) | |||
| ga.addCurtailedLineOfText (font, token.text, tokenX, baselineY, | |||
| (rightClip - tokenX) + charWidth, false); | |||
| } | |||
| ga.draw (g); | |||
| @@ -129,19 +130,21 @@ public: | |||
| private: | |||
| struct SyntaxToken | |||
| { | |||
| SyntaxToken (const String& t, const int type) noexcept | |||
| : text (t), tokenType (type), width (-1.0f) | |||
| SyntaxToken (const String& t, const int len, const int type) noexcept | |||
| : text (t), length (len), tokenType (type) | |||
| { | |||
| } | |||
| bool operator== (const SyntaxToken& other) const noexcept | |||
| { | |||
| return tokenType == other.tokenType && text == other.text; | |||
| return tokenType == other.tokenType | |||
| && length == other.length | |||
| && text == other.text; | |||
| } | |||
| String text; | |||
| int length; | |||
| int tokenType; | |||
| float width; | |||
| }; | |||
| Array <SyntaxToken> tokens; | |||
| @@ -169,8 +172,9 @@ private: | |||
| if (tokenEnd > 0) | |||
| { | |||
| tokenStart -= startPosition; | |||
| newTokens.add (SyntaxToken (lineText.substring (jmax (0, tokenStart), tokenEnd), | |||
| tokenType)); | |||
| const int start = jmax (0, tokenStart); | |||
| addToken (newTokens, lineText.substring (start, tokenEnd), | |||
| tokenEnd - start, tokenType); | |||
| if (tokenEnd >= lineLength) | |||
| break; | |||
| @@ -219,6 +223,21 @@ private: | |||
| return col; | |||
| } | |||
| static void addToken (Array<SyntaxToken>& dest, const String& text, | |||
| const int length, const int type) | |||
| { | |||
| if (length > 1000) | |||
| { | |||
| // subdivide very long tokens to avoid unwieldy glyph sequences | |||
| addToken (dest, text.substring (0, length / 2), length / 2, type); | |||
| addToken (dest, text.substring (length / 2), length - length / 2, type); | |||
| } | |||
| else | |||
| { | |||
| dest.add (SyntaxToken (text, length, type)); | |||
| } | |||
| } | |||
| }; | |||
| namespace CodeEditorHelpers | |||
| @@ -451,11 +470,13 @@ void CodeEditorComponent::paint (Graphics& g) | |||
| const int firstLineToDraw = jmax (0, clip.getY() / lineHeight); | |||
| const int lastLineToDraw = jmin (lines.size(), clip.getBottom() / lineHeight + 1); | |||
| const float x = (float) (gutter - xOffset * charWidth); | |||
| const float rightEdge = (float) getWidth(); | |||
| const float leftClip = (float) clip.getX(); | |||
| const float rightClip = (float) clip.getRight(); | |||
| for (int i = firstLineToDraw; i < lastLineToDraw; ++i) | |||
| lines.getUnchecked(i)->draw (*this, g, font, x, rightEdge, lineHeight * i, | |||
| baselineOffset, lineHeight, highlightColour); | |||
| lines.getUnchecked(i)->draw (*this, g, font, leftClip, rightClip, | |||
| x, lineHeight * i, baselineOffset, | |||
| lineHeight, charWidth, highlightColour); | |||
| } | |||
| void CodeEditorComponent::setScrollbarThickness (const int thickness) | |||