public Token consumeType(Token.Type type) throws IOException, ParseException { Token result = nextToken(); if (result.getType() != type) throw new ParseException( "Expected token `" + type + "', found `" + result.getType() + "'", result.getOffset()); return result; }
/** * Makes one token point to the same text segment, and have the same value as another token. * * @param t2 The token from which to copy. */ public void copyFrom(Token t2) { text = t2.getTextArray(); textOffset = t2.getTextOffset(); textCount = t2.length(); setOffset(t2.getOffset()); setType(t2.getType()); hyperlink = t2.isHyperlink(); languageIndex = t2.getLanguageIndex(); nextToken = t2.getNextToken(); }
/** * Modifies the passed-in token list to start at the specified offset. For example, if the token * list covered positions 20-60 in the document (inclusive) like so: * * <pre> * [token1] -> [token2] -> [token3] -> [token4] * 20 30 31 40 41 50 51 60 * </pre> * * and you used this method to make the token list start at position 44, then the token list would * be modified to be the following: * * <pre> * [part-of-old-token3] -> [token4] * 44 50 51 60 * </pre> * * Tokens that come before the specified position are forever lost, and the token containing that * position is made to begin at that position if necessary. All token types remain the same as * they were originally. * * <p>This method can be useful if you are only interested in part of a token list (i.e., the line * it represents), but you don't want to modify the token list yourself. * * @param tokenList The list to make start at the specified position. This parameter is modified. * @param pos The position at which the new token list is to start. If this position is not in the * passed-in token list, returned token list will either be <code>null</code> or the * unpaintable token(s) at the end of the passed-in token list. * @param e How to expand tabs. * @param textArea The text area from which the token list came. * @param x0 The initial x-pixel position of the old token list. * @param tempToken A temporary token to use when creating the token list result. This may be * <code>null</code> but callers can pass in a "buffer" token for performance if desired. * @return Information about the "sub" token list. This will be <code>null</code> if <code>pos * </code> was not a valid offset into the token list. * @see #getSubTokenList(Token, int, TabExpander, RSyntaxTextArea, float) */ public static TokenSubList getSubTokenList( Token tokenList, int pos, TabExpander e, final RSyntaxTextArea textArea, float x0, TokenImpl tempToken) { if (tempToken == null) { tempToken = new TokenImpl(); } Token t = tokenList; // Loop through the token list until you find the one that contains // pos. Remember the cumulative width of all of these tokens. while (t != null && t.isPaintable() && !t.containsPosition(pos)) { x0 += t.getWidth(textArea, e, x0); t = t.getNextToken(); } // Make the token that contains pos start at pos. if (t != null && t.isPaintable()) { if (t.getOffset() != pos) { // Number of chars between p0 and token start. int difference = pos - t.getOffset(); x0 += t.getWidthUpTo(t.length() - difference + 1, textArea, e, x0); tempToken.copyFrom(t); tempToken.makeStartAt(pos); return new TokenSubList(tempToken, x0); } else { // t.getOffset()==pos return new TokenSubList(t, x0); } } // This could be a null token, so we need to just return it. return new TokenSubList(tokenList, x0); // return null; }
/** * Draws a single view (i.e., a line of text for a wrapped view), wrapping the text onto multiple * lines if necessary. Any selected text is rendered with the editor's "selected text" color. * * @param painter The painter to use to render tokens. * @param g The graphics context in which to paint. * @param r The rectangle in which to paint. * @param view The <code>View</code> to paint. * @param fontHeight The height of the font being used. * @param y The y-coordinate at which to begin painting. * @param selStart The start of the selection. * @param selEnd The end of the selection. */ protected void drawViewWithSelection( TokenPainter painter, Graphics2D g, Rectangle r, View view, int fontHeight, int y, int selStart, int selEnd) { float x = r.x; LayeredHighlighter h = (LayeredHighlighter) host.getHighlighter(); RSyntaxDocument document = (RSyntaxDocument) getDocument(); Element map = getElement(); int p0 = view.getStartOffset(); int lineNumber = map.getElementIndex(p0); int p1 = view.getEndOffset(); // - 1; setSegment(p0, p1 - 1, document, drawSeg); // System.err.println("drawSeg=='" + drawSeg + "' (p0/p1==" + p0 + "/" + p1 + ")"); int start = p0 - drawSeg.offset; Token token = document.getTokenListForLine(lineNumber); // If this line is an empty line, then the token list is simply a // null token. In this case, the line highlight will be skipped in // the loop below, so unfortunately we must manually do it here. if (token != null && token.getType() == Token.NULL) { h.paintLayeredHighlights(g, p0, p1, r, host, this); return; } // Loop through all tokens in this view and paint them! while (token != null && token.isPaintable()) { int p = calculateBreakPosition(p0, token, x); x = r.x; h.paintLayeredHighlights(g, p0, p, r, host, this); while (token != null && token.isPaintable() && token.getEndOffset() - 1 < p) { // <=p) { // Selection starts in this token if (token.containsPosition(selStart)) { if (selStart > token.getOffset()) { tempToken.copyFrom(token); tempToken.textCount = selStart - tempToken.getOffset(); x = painter.paint(tempToken, g, x, y, host, this); tempToken.textCount = token.length(); tempToken.makeStartAt(selStart); // Clone required since token and tempToken must be // different tokens for else statement below token = new TokenImpl(tempToken); } int selCount = Math.min(token.length(), selEnd - token.getOffset()); if (selCount == token.length()) { x = painter.paintSelected(token, g, x, y, host, this); } else { tempToken.copyFrom(token); tempToken.textCount = selCount; x = painter.paintSelected(tempToken, g, x, y, host, this); tempToken.textCount = token.length(); tempToken.makeStartAt(token.getOffset() + selCount); token = tempToken; x = painter.paint(token, g, x, y, host, this); } } // Selection ends in this token else if (token.containsPosition(selEnd)) { tempToken.copyFrom(token); tempToken.textCount = selEnd - tempToken.getOffset(); x = painter.paintSelected(tempToken, g, x, y, host, this); tempToken.textCount = token.length(); tempToken.makeStartAt(selEnd); token = tempToken; x = painter.paint(token, g, x, y, host, this); } // This token is entirely selected else if (token.getOffset() >= selStart && token.getEndOffset() <= selEnd) { x = painter.paintSelected(token, g, x, y, host, this); } // This token is entirely unselected else { x = painter.paint(token, g, x, y, host, this); } token = token.getNextToken(); } // If there's a token that's going to be split onto the next line if (token != null && token.isPaintable() && token.getOffset() < p) { int tokenOffset = token.getOffset(); Token orig = token; token = new TokenImpl( drawSeg, tokenOffset - start, p - 1 - start, tokenOffset, token.getType()); // Selection starts in this token if (token.containsPosition(selStart)) { if (selStart > token.getOffset()) { tempToken.copyFrom(token); tempToken.textCount = selStart - tempToken.getOffset(); x = painter.paint(tempToken, g, x, y, host, this); tempToken.textCount = token.length(); tempToken.makeStartAt(selStart); // Clone required since token and tempToken must be // different tokens for else statement below token = new TokenImpl(tempToken); } int selCount = Math.min(token.length(), selEnd - token.getOffset()); if (selCount == token.length()) { x = painter.paintSelected(token, g, x, y, host, this); } else { tempToken.copyFrom(token); tempToken.textCount = selCount; x = painter.paintSelected(tempToken, g, x, y, host, this); tempToken.textCount = token.length(); tempToken.makeStartAt(token.getOffset() + selCount); token = tempToken; x = painter.paint(token, g, x, y, host, this); } } // Selection ends in this token else if (token.containsPosition(selEnd)) { tempToken.copyFrom(token); tempToken.textCount = selEnd - tempToken.getOffset(); x = painter.paintSelected(tempToken, g, x, y, host, this); tempToken.textCount = token.length(); tempToken.makeStartAt(selEnd); token = tempToken; x = painter.paint(token, g, x, y, host, this); } // This token is entirely selected else if (token.getOffset() >= selStart && token.getEndOffset() <= selEnd) { x = painter.paintSelected(token, g, x, y, host, this); } // This token is entirely unselected else { x = painter.paint(token, g, x, y, host, this); } token = new TokenImpl(orig); ((TokenImpl) token).makeStartAt(p); } p0 = (p == p0) ? p1 : p; y += fontHeight; } // End of while (token!=null && token.isPaintable()). // NOTE: We should re-use code from Token (paintBackground()) here, // but don't because I'm just too lazy. if (host.getEOLMarkersVisible()) { g.setColor(host.getForegroundForTokenType(Token.WHITESPACE)); g.setFont(host.getFontForTokenType(Token.WHITESPACE)); g.drawString("\u00B6", x, y - fontHeight); } }
/** * Draws a single view (i.e., a line of text for a wrapped view), wrapping the text onto multiple * lines if necessary. * * @param painter The painter to use to render tokens. * @param g The graphics context in which to paint. * @param r The rectangle in which to paint. * @param view The <code>View</code> to paint. * @param fontHeight The height of the font being used. * @param y The y-coordinate at which to begin painting. */ protected void drawView( TokenPainter painter, Graphics2D g, Rectangle r, View view, int fontHeight, int y) { float x = r.x; LayeredHighlighter h = (LayeredHighlighter) host.getHighlighter(); RSyntaxDocument document = (RSyntaxDocument) getDocument(); Element map = getElement(); int p0 = view.getStartOffset(); int lineNumber = map.getElementIndex(p0); int p1 = view.getEndOffset(); // - 1; setSegment(p0, p1 - 1, document, drawSeg); // System.err.println("drawSeg=='" + drawSeg + "' (p0/p1==" + p0 + "/" + p1 + ")"); int start = p0 - drawSeg.offset; Token token = document.getTokenListForLine(lineNumber); // If this line is an empty line, then the token list is simply a // null token. In this case, the line highlight will be skipped in // the loop below, so unfortunately we must manually do it here. if (token != null && token.getType() == Token.NULL) { h.paintLayeredHighlights(g, p0, p1, r, host, this); return; } // Loop through all tokens in this view and paint them! while (token != null && token.isPaintable()) { int p = calculateBreakPosition(p0, token, x); x = r.x; h.paintLayeredHighlights(g, p0, p, r, host, this); while (token != null && token.isPaintable() && token.getEndOffset() - 1 < p) { // <=p) { x = painter.paint(token, g, x, y, host, this); token = token.getNextToken(); } if (token != null && token.isPaintable() && token.getOffset() < p) { int tokenOffset = token.getOffset(); tempToken.set( drawSeg.array, tokenOffset - start, p - 1 - start, tokenOffset, token.getType()); painter.paint(tempToken, g, x, y, host, this); tempToken.copyFrom(token); tempToken.makeStartAt(p); token = new TokenImpl(tempToken); } p0 = (p == p0) ? p1 : p; y += fontHeight; } // End of while (token!=null && token.isPaintable()). // NOTE: We should re-use code from Token (paintBackground()) here, // but don't because I'm just too lazy. if (host.getEOLMarkersVisible()) { g.setColor(host.getForegroundForTokenType(Token.WHITESPACE)); g.setFont(host.getFontForTokenType(Token.WHITESPACE)); g.drawString("\u00B6", x, y - fontHeight); } }
/** * Draws the passed-in text using syntax highlighting for the current language. Tokens are checked * for being in a selected region, and are rendered appropriately if they are. * * @param painter The painter to render the tokens. * @param token The list of tokens to draw. * @param g The graphics context in which to draw. * @param x The x-coordinate at which to draw. * @param y The y-coordinate at which to draw. * @param selStart The start of the selection. * @param selEnd The end of the selection. * @return The x-coordinate representing the end of the painted text. */ private float drawLineWithSelection( TokenPainter painter, Token token, Graphics2D g, float x, float y, int selStart, int selEnd) { float nextX = x; // The x-value at the end of our text. boolean useSTC = host.getUseSelectedTextColor(); while (token != null && token.isPaintable() && nextX < clipEnd) { // Selection starts in this token if (token.containsPosition(selStart)) { if (selStart > token.getOffset()) { tempToken.copyFrom(token); tempToken.textCount = selStart - tempToken.getOffset(); nextX = painter.paint(tempToken, g, nextX, y, host, this, clipStart); tempToken.textCount = token.length(); tempToken.makeStartAt(selStart); // Clone required since token and tempToken must be // different tokens for else statement below token = new TokenImpl(tempToken); } int tokenLen = token.length(); int selCount = Math.min(tokenLen, selEnd - token.getOffset()); if (selCount == tokenLen) { nextX = painter.paintSelected(token, g, nextX, y, host, this, clipStart, useSTC); } else { tempToken.copyFrom(token); tempToken.textCount = selCount; nextX = painter.paintSelected(tempToken, g, nextX, y, host, this, clipStart, useSTC); tempToken.textCount = token.length(); tempToken.makeStartAt(token.getOffset() + selCount); token = tempToken; nextX = painter.paint(token, g, nextX, y, host, this, clipStart); } } // Selection ends in this token else if (token.containsPosition(selEnd)) { tempToken.copyFrom(token); tempToken.textCount = selEnd - tempToken.getOffset(); nextX = painter.paintSelected(tempToken, g, nextX, y, host, this, clipStart, useSTC); tempToken.textCount = token.length(); tempToken.makeStartAt(selEnd); token = tempToken; nextX = painter.paint(token, g, nextX, y, host, this, clipStart); } // This token is entirely selected else if (token.getOffset() >= selStart && token.getEndOffset() <= selEnd) { nextX = painter.paintSelected(token, g, nextX, y, host, this, clipStart, useSTC); } // This token is entirely unselected else { nextX = painter.paint(token, g, nextX, y, host, this, clipStart); } token = token.getNextToken(); } // NOTE: We should re-use code from Token (paintBackground()) here, // but don't because I'm just too lazy. if (host.getEOLMarkersVisible()) { g.setColor(host.getForegroundForTokenType(Token.WHITESPACE)); g.setFont(host.getFontForTokenType(Token.WHITESPACE)); g.drawString("\u00B6", nextX, y); } // Return the x-coordinate at the end of the painted text. return nextX; }
/** {@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); } }