| @@ -41,7 +41,8 @@ public: | |||||
| if (tokeniser == nullptr) | 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()) | else if (lineNum < document.getNumLines()) | ||||
| { | { | ||||
| @@ -81,22 +82,29 @@ public: | |||||
| } | } | ||||
| void draw (CodeEditorComponent& owner, Graphics& g, const Font& font, | 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 | const Colour& highlightColour) const | ||||
| { | { | ||||
| if (highlightColumnStart < highlightColumnEnd) | if (highlightColumnStart < highlightColumnEnd) | ||||
| { | { | ||||
| g.setColour (highlightColour); | 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); | roundToInt ((highlightColumnEnd - highlightColumnStart) * owner.getCharWidth()), lineHeight); | ||||
| } | } | ||||
| const float baselineY = (float) (y + baselineOffset); | const float baselineY = (float) (y + baselineOffset); | ||||
| Colour lastColour (0x00000001); | Colour lastColour (0x00000001); | ||||
| GlyphArrangement ga; | GlyphArrangement ga; | ||||
| int column = 0; | |||||
| for (int i = 0; i < tokens.size(); ++i) | for (int i = 0; i < tokens.size(); ++i) | ||||
| { | { | ||||
| const float tokenX = xOffset + column * charWidth; | |||||
| if (tokenX > rightClip) | |||||
| break; | |||||
| SyntaxToken& token = tokens.getReference(i); | SyntaxToken& token = tokens.getReference(i); | ||||
| const Colour newColour (owner.getColourForTokenType (token.tokenType)); | const Colour newColour (owner.getColourForTokenType (token.tokenType)); | ||||
| @@ -109,18 +117,11 @@ public: | |||||
| g.setColour (newColour); | 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); | ga.draw (g); | ||||
| @@ -129,19 +130,21 @@ public: | |||||
| private: | private: | ||||
| struct SyntaxToken | 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 | 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; | String text; | ||||
| int length; | |||||
| int tokenType; | int tokenType; | ||||
| float width; | |||||
| }; | }; | ||||
| Array <SyntaxToken> tokens; | Array <SyntaxToken> tokens; | ||||
| @@ -169,8 +172,9 @@ private: | |||||
| if (tokenEnd > 0) | if (tokenEnd > 0) | ||||
| { | { | ||||
| tokenStart -= startPosition; | 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) | if (tokenEnd >= lineLength) | ||||
| break; | break; | ||||
| @@ -219,6 +223,21 @@ private: | |||||
| return col; | 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 | namespace CodeEditorHelpers | ||||
| @@ -451,11 +470,13 @@ void CodeEditorComponent::paint (Graphics& g) | |||||
| const int firstLineToDraw = jmax (0, clip.getY() / lineHeight); | const int firstLineToDraw = jmax (0, clip.getY() / lineHeight); | ||||
| const int lastLineToDraw = jmin (lines.size(), clip.getBottom() / lineHeight + 1); | const int lastLineToDraw = jmin (lines.size(), clip.getBottom() / lineHeight + 1); | ||||
| const float x = (float) (gutter - xOffset * charWidth); | 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) | 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) | void CodeEditorComponent::setScrollbarThickness (const int thickness) | ||||