/** * Actually paints the text area. Only lines that have been damaged are repainted. * * @param g The graphics context with which to paint. * @param a The allocated region in which to render. */ @Override public void paint(Graphics g, Shape a) { RSyntaxDocument document = (RSyntaxDocument) getDocument(); Rectangle alloc = a.getBounds(); tabBase = alloc.x; host = (RSyntaxTextArea) getContainer(); Rectangle clip = g.getClipBounds(); // An attempt to speed things up for files with long lines. Note that // this will actually slow things down a bit for the common case of // regular-length lines, but it doesn't make a perceivable difference. clipStart = clip.x; clipEnd = clipStart + clip.width; lineHeight = host.getLineHeight(); ascent = host.getMaxAscent(); // metrics.getAscent(); int heightAbove = clip.y - alloc.y; int linesAbove = Math.max(0, heightAbove / lineHeight); FoldManager fm = host.getFoldManager(); linesAbove += fm.getHiddenLineCountAbove(linesAbove, true); Rectangle lineArea = lineToRect(a, linesAbove); int y = lineArea.y + ascent; int x = lineArea.x; Element map = getElement(); int lineCount = map.getElementCount(); // Whether token styles should always be painted, even in selections int selStart = host.getSelectionStart(); int selEnd = host.getSelectionEnd(); RSyntaxTextAreaHighlighter h = (RSyntaxTextAreaHighlighter) host.getHighlighter(); Graphics2D g2d = (Graphics2D) g; Token token; // System.err.println("Painting lines: " + linesAbove + " to " + (endLine-1)); TokenPainter painter = host.getTokenPainter(); int line = linesAbove; // int count = 0; while (y < clip.y + clip.height + ascent && line < lineCount) { Fold fold = fm.getFoldForLine(line); Element lineElement = map.getElement(line); int startOffset = lineElement.getStartOffset(); // int endOffset = (line==lineCount ? lineElement.getEndOffset()-1 : // lineElement.getEndOffset()-1); int endOffset = lineElement.getEndOffset() - 1; // Why always "-1"? h.paintLayeredHighlights(g2d, startOffset, endOffset, a, host, this); // Paint a line of text. token = document.getTokenListForLine(line); if (selStart == selEnd || startOffset >= selEnd || endOffset < selStart) { drawLine(painter, token, g2d, x, y, line); } else { // System.out.println("Drawing line with selection: " + line); drawLineWithSelection(painter, token, g2d, x, y, selStart, selEnd); } if (fold != null && fold.isCollapsed()) { // Visible indicator of collapsed lines Color c = RSyntaxUtilities.getFoldedLineBottomColor(host); if (c != null) { g.setColor(c); g.drawLine(x, y + lineHeight - ascent - 1, host.getWidth(), y + lineHeight - ascent - 1); } // Skip to next line to paint, taking extra care for lines with // block ends and begins together, e.g. "} else {" do { int hiddenLineCount = fold.getLineCount(); if (hiddenLineCount == 0) { // Fold parser identified a zero-line fold region. // This is really a bug, but we'll be graceful here // and avoid an infinite loop. break; } line += hiddenLineCount; fold = fm.getFoldForLine(line); } while (fold != null && fold.isCollapsed()); } y += lineHeight; line++; // count++; } // System.out.println("SyntaxView: lines painted=" + count); }
/** {@inheritDoc} */ public void markOccurrences( RSyntaxDocument doc, Token t, RSyntaxTextAreaHighlighter h, MarkOccurrencesHighlightPainter p) { char[] lexeme = t.getLexeme().toCharArray(); int tokenOffs = t.getOffset(); Element root = doc.getDefaultRootElement(); int lineCount = root.getElementCount(); int curLine = root.getElementIndex(t.getOffset()); // For now, we only check for tags on the current line, for // simplicity. Tags spanning multiple lines aren't common anyway. boolean found = false; boolean forward = true; t = doc.getTokenListForLine(curLine); while (t != null && t.isPaintable()) { if (t.getType() == Token.MARKUP_TAG_DELIMITER) { if (t.isSingleChar('<') && t.getOffset() + 1 == tokenOffs) { found = true; break; } else if (t.is(CLOSE_TAG_START) && t.getOffset() + 2 == tokenOffs) { found = true; forward = false; break; } } t = t.getNextToken(); } if (!found) { return; } if (forward) { int depth = 0; t = t.getNextToken().getNextToken(); do { while (t != null && t.isPaintable()) { if (t.getType() == Token.MARKUP_TAG_DELIMITER) { if (t.isSingleChar('<')) { depth++; } else if (t.is(TAG_SELF_CLOSE)) { if (depth > 0) { depth--; } else { return; // No match for this tag } } else if (t.is(CLOSE_TAG_START)) { if (depth > 0) { depth--; } else { Token match = t.getNextToken(); if (match != null && match.is(lexeme)) { try { int end = match.getOffset() + match.length(); h.addMarkedOccurrenceHighlight(match.getOffset(), end, p); end = tokenOffs + match.length(); h.addMarkedOccurrenceHighlight(tokenOffs, end, p); } catch (BadLocationException ble) { ble.printStackTrace(); // Never happens } } return; // We're done! } } } t = t.getNextToken(); } if (++curLine < lineCount) { t = doc.getTokenListForLine(curLine); } } while (curLine < lineCount); } else { // !forward Stack<Token> matches = new Stack<Token>(); boolean inPossibleMatch = false; t = doc.getTokenListForLine(curLine); final int endBefore = tokenOffs - 2; // Stop before "</". do { while (t != null && t.getOffset() < endBefore && t.isPaintable()) { if (t.getType() == Token.MARKUP_TAG_DELIMITER) { if (t.isSingleChar('<')) { Token next = t.getNextToken(); if (next != null) { if (next.is(lexeme)) { matches.push(next); inPossibleMatch = true; } else { inPossibleMatch = false; } t = next; } } else if (t.isSingleChar('>')) { inPossibleMatch = false; } else if (inPossibleMatch && t.is(TAG_SELF_CLOSE)) { matches.pop(); } else if (t.is(CLOSE_TAG_START)) { Token next = t.getNextToken(); if (next != null) { // Invalid XML might not have a match if (next.is(lexeme) && !matches.isEmpty()) { matches.pop(); } t = next; } } } t = t.getNextToken(); } if (!matches.isEmpty()) { try { Token match = matches.pop(); int end = match.getOffset() + match.length(); h.addMarkedOccurrenceHighlight(match.getOffset(), end, p); end = tokenOffs + match.length(); h.addMarkedOccurrenceHighlight(tokenOffs, end, p); } catch (BadLocationException ble) { ble.printStackTrace(); // Never happens } return; } if (--curLine >= 0) { t = doc.getTokenListForLine(curLine); } } while (curLine >= 0); } }