/** * Finds a closing parenthesis to the left of <code>position</code> in document, where that * parenthesis is only separated by whitespace from <code>position</code>. If no such parenthesis * can be found, <code>position</code> is returned. * * @param scanner the java heuristic scanner set up on the document * @param position the first character position in <code>document</code> to be considered * @return the position of a closing parenthesis left to <code>position</code> separated only by * whitespace, or <code>position</code> if no parenthesis can be found */ private static int findClosingParenToLeft(JavaHeuristicScanner scanner, int position) { if (position < 1) return position; if (scanner.previousToken(position - 1, JavaHeuristicScanner.UNBOUND) == Symbols.TokenRPAREN) return scanner.getPosition() + 1; return position; }
/** * Skips the scope opened by <code>token</code>. * * @param scanner the scanner * @param start the start position * @param token the token * @return the position after the scope or <code>JavaHeuristicScanner.NOT_FOUND</code> */ private static int skipScope(JavaHeuristicScanner scanner, int start, int token) { int openToken = token; int closeToken; switch (token) { case Symbols.TokenLPAREN: closeToken = Symbols.TokenRPAREN; break; case Symbols.TokenLBRACKET: closeToken = Symbols.TokenRBRACKET; break; case Symbols.TokenLBRACE: closeToken = Symbols.TokenRBRACE; break; default: Assert.isTrue(false); return -1; // dummy } int depth = 1; int p = start; while (true) { int tok = scanner.nextToken(p, JavaHeuristicScanner.UNBOUND); p = scanner.getPosition(); if (tok == openToken) { depth++; } else if (tok == closeToken) { depth--; if (depth == 0) return p + 1; } else if (tok == Symbols.TokenEOF) { return JavaHeuristicScanner.NOT_FOUND; } } }
/** * Returns the block balance, i.e. zero if the blocks are balanced at <code>offset</code>, a * negative number if there are more closing than opening braces, and a positive number if there * are more opening than closing braces. * * @param document the document * @param offset the offset * @param partitioning the partitioning * @return the block balance */ private static int getBlockBalance(IDocument document, int offset, String partitioning) { if (offset < 1) return -1; if (offset >= document.getLength()) return 1; int begin = offset; int end = offset - 1; JavaHeuristicScanner scanner = new JavaHeuristicScanner(document); while (true) { begin = scanner.findOpeningPeer(begin - 1, '{', '}'); end = scanner.findClosingPeer(end + 1, '{', '}'); if (begin == -1 && end == -1) return 0; if (begin == -1) return -1; if (end == -1) return 1; } }
/** * Computes an insert position for an opening brace if <code>offset</code> maps to a position in * <code>document</code> with a expression in parenthesis that will take a block after the closing * parenthesis. * * @param document the document being modified * @param offset the offset of the caret position, relative to the line start. * @param partitioning the document partitioning * @param max the max position * @return an insert position relative to the line start if <code>line</code> contains a * parenthesized expression that can be followed by a block, -1 otherwise */ private static int computeAnonymousPosition( IDocument document, int offset, String partitioning, int max) { // find the opening parenthesis for every closing parenthesis on the current line after offset // return the position behind the closing parenthesis if it looks like a method declaration // or an expression for an if, while, for, catch statement JavaHeuristicScanner scanner = new JavaHeuristicScanner(document); int pos = offset; int length = max; int scanTo = scanner.scanForward(pos, length, '}'); if (scanTo == -1) scanTo = length; int closingParen = findClosingParenToLeft(scanner, pos) - 1; boolean hasNewToken = looksLikeAnonymousClassDef(document, partitioning, scanner, pos); int openingParen = -1; while (true) { int startScan = closingParen + 1; closingParen = scanner.scanForward(startScan, scanTo, ')'); if (closingParen == -1) { if (hasNewToken && openingParen != -1) return openingParen + 1; break; } openingParen = scanner.findOpeningPeer(closingParen - 1, '(', ')'); // no way an expression at the beginning of the document can mean anything if (openingParen < 1) break; // only select insert positions for parenthesis currently embracing the caret if (openingParen > pos) continue; if (looksLikeAnonymousClassDef(document, partitioning, scanner, openingParen - 1)) return closingParen + 1; } return -1; }
private void smartIndentAfterOpeningBracket(IDocument d, DocumentCommand c) { if (c.offset < 1 || d.getLength() == 0) return; JavaHeuristicScanner scanner = new JavaHeuristicScanner(d); int p = (c.offset == d.getLength() ? c.offset - 1 : c.offset); try { // current line int line = d.getLineOfOffset(p); int lineOffset = d.getLineOffset(line); // make sure we don't have any leading comments etc. if (d.get(lineOffset, p - lineOffset).trim().length() != 0) return; // line of last Java code int pos = scanner.findNonWhitespaceBackward(p, JavaHeuristicScanner.UNBOUND); if (pos == -1) return; int lastLine = d.getLineOfOffset(pos); // only shift if the last java line is further up and is a braceless block candidate if (lastLine < line) { JavaIndenter indenter = new JavaIndenter(d, scanner, fProject); StringBuffer indent = indenter.computeIndentation(p, true); String toDelete = d.get(lineOffset, c.offset - lineOffset); if (indent != null && !indent.toString().equals(toDelete)) { c.text = indent.append(c.text).toString(); c.length += c.offset - lineOffset; c.offset = lineOffset; } } } catch (BadLocationException e) { JavaPlugin.log(e); } }
private static CompilationUnitInfo getCompilationUnitForMethod(IDocument document, int offset) { try { JavaHeuristicScanner scanner = new JavaHeuristicScanner(document); IRegion sourceRange = scanner.findSurroundingBlock(offset); if (sourceRange == null) return null; String source = document.get(sourceRange.getOffset(), sourceRange.getLength()); StringBuffer contents = new StringBuffer(); contents.append("class ____C{void ____m()"); // $NON-NLS-1$ final int methodOffset = contents.length(); contents.append(source); contents.append('}'); char[] buffer = contents.toString().toCharArray(); return new CompilationUnitInfo(buffer, sourceRange.getOffset() - methodOffset); } catch (BadLocationException e) { JavaPlugin.log(e); } return null; }
/** * Checks whether the content of <code>document</code> at <code>position</code> looks like an * anonymous class definition. <code>position</code> must be to the left of the opening * parenthesis of the definition's parameter list. * * @param document the document being modified * @param partitioning the document partitioning * @param scanner the scanner * @param position the first character position in <code>document</code> to be considered * @return <code>true</code> if the content of <code>document</code> looks like an anonymous class * definition, <code>false</code> otherwise */ private static boolean looksLikeAnonymousClassDef( IDocument document, String partitioning, JavaHeuristicScanner scanner, int position) { int previousCommaParenEqual = scanner.scanBackward( position - 1, JavaHeuristicScanner.UNBOUND, new char[] {',', '(', '='}); if (previousCommaParenEqual == -1 || position < previousCommaParenEqual + 5) // 2 for borders, 3 for "new" return false; if (isNewMatch( document, previousCommaParenEqual + 1, position - previousCommaParenEqual - 2, partitioning)) return true; return false; }
private int getPeerPosition(IDocument document, DocumentCommand command) { if (document.getLength() == 0) return 0; /* * Search for scope closers in the pasted text and find their opening peers * in the document. */ Document pasted = new Document(command.text); installJavaStuff(pasted); int firstPeer = command.offset; JavaHeuristicScanner pScanner = new JavaHeuristicScanner(pasted); JavaHeuristicScanner dScanner = new JavaHeuristicScanner(document); // add scope relevant after context to peer search int afterToken = dScanner.nextToken(command.offset + command.length, JavaHeuristicScanner.UNBOUND); try { switch (afterToken) { case Symbols.TokenRBRACE: pasted.replace(pasted.getLength(), 0, "}"); // $NON-NLS-1$ break; case Symbols.TokenRPAREN: pasted.replace(pasted.getLength(), 0, ")"); // $NON-NLS-1$ break; case Symbols.TokenRBRACKET: pasted.replace(pasted.getLength(), 0, "]"); // $NON-NLS-1$ break; } } catch (BadLocationException e) { // cannot happen Assert.isTrue(false); } int pPos = 0; // paste text position (increasing from 0) int dPos = Math.max(0, command.offset - 1); // document position (decreasing from paste offset) while (true) { int token = pScanner.nextToken(pPos, JavaHeuristicScanner.UNBOUND); pPos = pScanner.getPosition(); switch (token) { case Symbols.TokenLBRACE: case Symbols.TokenLBRACKET: case Symbols.TokenLPAREN: pPos = skipScope(pScanner, pPos, token); if (pPos == JavaHeuristicScanner.NOT_FOUND) return firstPeer; break; // closed scope -> keep searching case Symbols.TokenRBRACE: int peer = dScanner.findOpeningPeer(dPos, '{', '}'); dPos = peer - 1; if (peer == JavaHeuristicScanner.NOT_FOUND) return firstPeer; firstPeer = peer; break; // keep searching case Symbols.TokenRBRACKET: peer = dScanner.findOpeningPeer(dPos, '[', ']'); dPos = peer - 1; if (peer == JavaHeuristicScanner.NOT_FOUND) return firstPeer; firstPeer = peer; break; // keep searching case Symbols.TokenRPAREN: peer = dScanner.findOpeningPeer(dPos, '(', ')'); dPos = peer - 1; if (peer == JavaHeuristicScanner.NOT_FOUND) return firstPeer; firstPeer = peer; break; // keep searching case Symbols.TokenCASE: case Symbols.TokenDEFAULT: JavaIndenter indenter = new JavaIndenter(document, dScanner, fProject); peer = indenter.findReferencePosition(dPos, false, false, false, true); if (peer == JavaHeuristicScanner.NOT_FOUND) return firstPeer; firstPeer = peer; break; // keep searching case Symbols.TokenEOF: return firstPeer; default: // keep searching } } }
private void smartIndentAfterNewLine(IDocument d, DocumentCommand c) { JavaHeuristicScanner scanner = new JavaHeuristicScanner(d); JavaIndenter indenter = new JavaIndenter(d, scanner, fProject); StringBuffer indent = indenter.computeIndentation(c.offset); if (indent == null) indent = new StringBuffer(); int docLength = d.getLength(); if (c.offset == -1 || docLength == 0) return; try { int p = (c.offset == docLength ? c.offset - 1 : c.offset); int line = d.getLineOfOffset(p); StringBuffer buf = new StringBuffer(c.text + indent); IRegion reg = d.getLineInformation(line); int lineEnd = reg.getOffset() + reg.getLength(); int contentStart = findEndOfWhiteSpace(d, c.offset, lineEnd); c.length = Math.max(contentStart - c.offset, 0); int start = reg.getOffset(); ITypedRegion region = TextUtilities.getPartition(d, fPartitioning, start, true); if (IJavaPartitions.JAVA_DOC.equals(region.getType())) start = d.getLineInformationOfOffset(region.getOffset()).getOffset(); // insert closing brace on new line after an unclosed opening brace if (getBracketCount(d, start, c.offset, true) > 0 && closeBrace() && !isClosed(d, c.offset, c.length)) { c.caretOffset = c.offset + buf.length(); c.shiftsCaret = false; // copy old content of line behind insertion point to new line // unless we think we are inserting an anonymous type definition if (c.offset == 0 || computeAnonymousPosition(d, c.offset - 1, fPartitioning, lineEnd) == -1) { if (lineEnd - contentStart > 0) { c.length = lineEnd - c.offset; buf.append(d.get(contentStart, lineEnd - contentStart).toCharArray()); } } buf.append(TextUtilities.getDefaultLineDelimiter(d)); StringBuffer reference = null; int nonWS = findEndOfWhiteSpace(d, start, lineEnd); if (nonWS < c.offset && d.getChar(nonWS) == '{') reference = new StringBuffer(d.get(start, nonWS - start)); else reference = indenter.getReferenceIndentation(c.offset); if (reference != null) buf.append(reference); buf.append('}'); } // insert extra line upon new line between two braces else if (c.offset > start && contentStart < lineEnd && d.getChar(contentStart) == '}') { int firstCharPos = scanner.findNonWhitespaceBackward(c.offset - 1, start); if (firstCharPos != JavaHeuristicScanner.NOT_FOUND && d.getChar(firstCharPos) == '{') { c.caretOffset = c.offset + buf.length(); c.shiftsCaret = false; StringBuffer reference = null; int nonWS = findEndOfWhiteSpace(d, start, lineEnd); if (nonWS < c.offset && d.getChar(nonWS) == '{') reference = new StringBuffer(d.get(start, nonWS - start)); else reference = indenter.getReferenceIndentation(c.offset); buf.append(TextUtilities.getDefaultLineDelimiter(d)); if (reference != null) buf.append(reference); } } c.text = buf.toString(); } catch (BadLocationException e) { JavaPlugin.log(e); } }
private void smartIndentUponE(IDocument d, DocumentCommand c) { if (c.offset < 4 || d.getLength() == 0) return; try { String content = d.get(c.offset - 3, 3); if (content.equals("els")) { // $NON-NLS-1$ JavaHeuristicScanner scanner = new JavaHeuristicScanner(d); int p = c.offset - 3; // current line int line = d.getLineOfOffset(p); int lineOffset = d.getLineOffset(line); // make sure we don't have any leading comments etc. if (d.get(lineOffset, p - lineOffset).trim().length() != 0) return; // line of last Java code int pos = scanner.findNonWhitespaceBackward(p - 1, JavaHeuristicScanner.UNBOUND); if (pos == -1) return; int lastLine = d.getLineOfOffset(pos); // only shift if the last java line is further up and is a braceless block candidate if (lastLine < line) { JavaIndenter indenter = new JavaIndenter(d, scanner, fProject); int ref = indenter.findReferencePosition(p, true, false, false, false); if (ref == JavaHeuristicScanner.NOT_FOUND) return; int refLine = d.getLineOfOffset(ref); String indent = getIndentOfLine(d, refLine); if (indent != null) { c.text = indent.toString() + "else"; // $NON-NLS-1$ c.length += c.offset - lineOffset; c.offset = lineOffset; } } return; } if (content.equals("cas")) { // $NON-NLS-1$ JavaHeuristicScanner scanner = new JavaHeuristicScanner(d); int p = c.offset - 3; // current line int line = d.getLineOfOffset(p); int lineOffset = d.getLineOffset(line); // make sure we don't have any leading comments etc. if (d.get(lineOffset, p - lineOffset).trim().length() != 0) return; // line of last Java code int pos = scanner.findNonWhitespaceBackward(p - 1, JavaHeuristicScanner.UNBOUND); if (pos == -1) return; int lastLine = d.getLineOfOffset(pos); // only shift if the last java line is further up and is a braceless block candidate if (lastLine < line) { JavaIndenter indenter = new JavaIndenter(d, scanner, fProject); int ref = indenter.findReferencePosition(p, false, false, false, true); if (ref == JavaHeuristicScanner.NOT_FOUND) return; int refLine = d.getLineOfOffset(ref); int nextToken = scanner.nextToken(ref, JavaHeuristicScanner.UNBOUND); String indent; if (nextToken == Symbols.TokenCASE || nextToken == Symbols.TokenDEFAULT) indent = getIndentOfLine(d, refLine); else // at the brace of the switch indent = indenter.computeIndentation(p).toString(); if (indent != null) { c.text = indent.toString() + "case"; // $NON-NLS-1$ c.length += c.offset - lineOffset; c.offset = lineOffset; } } return; } } catch (BadLocationException e) { JavaPlugin.log(e); } }