+++ /dev/null
-/*******************************************************************************
- * Copyright (c) 2000, 2004 IBM Corporation and others.
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Common Public License v1.0
- * which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/cpl-v10.html
- *
- * Contributors:
- * IBM Corporation - initial API and implementation
- *******************************************************************************/
-package net.sourceforge.phpdt.internal.ui.text;
-
-import net.sourceforge.phpdt.core.JavaCore;
-import net.sourceforge.phpdt.core.formatter.DefaultCodeFormatterConstants;
-import net.sourceforge.phpeclipse.ui.WebUI;
-//import net.sourceforge.phpeclipse.PHPeclipsePlugin;
-
-import org.eclipse.core.runtime.Plugin;
-//incastrix
-//import org.eclipse.jface.text.Assert;
-import org.eclipse.core.runtime.Assert;
-import org.eclipse.jface.text.BadLocationException;
-import org.eclipse.jface.text.IDocument;
-import org.eclipse.jface.text.IRegion;
-import org.eclipse.jface.text.ITypedRegion;
-import org.eclipse.jface.text.TextUtilities;
-import org.eclipse.ui.texteditor.AbstractDecoratedTextEditorPreferenceConstants;
-
-/**
- * Uses the {@link net.sourceforge.phpdt.internal.ui.text.JavaHeuristicScanner}to
- * get the indentation level for a certain position in a document.
- *
- * <p>
- * An instance holds some internal position in the document and is therefore not
- * threadsafe.
- * </p>
- *
- * @since 3.0
- */
-public class JavaIndenter {
-
- /** The document being scanned. */
- private IDocument fDocument;
-
- /** The indentation accumulated by <code>findPreviousIndenationUnit</code>. */
- private int fIndent;
-
- /**
- * The absolute (character-counted) indentation offset for special cases
- * (method defs, array initializers)
- */
- private int fAlign;
-
- /** The stateful scanposition for the indentation methods. */
- private int fPosition;
-
- /** The previous position. */
- private int fPreviousPos;
-
- /** The most recent token. */
- private int fToken;
-
- /** The line of <code>fPosition</code>. */
- private int fLine;
-
- /**
- * The scanner we will use to scan the document. It has to be installed on
- * the same document as the one we get.
- */
- private JavaHeuristicScanner fScanner;
-
- /**
- * Creates a new instance.
- *
- * @param document
- * the document to scan
- * @param scanner
- * the {@link JavaHeuristicScanner} to be used for scanning the
- * document. It must be installed on the same
- * <code>IDocument</code>.
- */
- public JavaIndenter(IDocument document, JavaHeuristicScanner scanner) {
- Assert.isNotNull(document);
- Assert.isNotNull(scanner);
- fDocument = document;
- fScanner = scanner;
- }
-
- /**
- * Computes the indentation at the reference point of <code>position</code>.
- *
- * @param offset
- * the offset in the document
- * @return a String which reflects the indentation at the line in which the
- * reference position to <code>offset</code> resides, or
- * <code>null</code> if it cannot be determined
- */
- public StringBuffer getReferenceIndentation(int offset) {
- return getReferenceIndentation(offset, false);
- }
-
- /**
- * Computes the indentation at the reference point of <code>position</code>.
- *
- * @param offset
- * the offset in the document
- * @param assumeOpeningBrace
- * <code>true</code> if an opening brace should be assumed
- * @return a String which reflects the indentation at the line in which the
- * reference position to <code>offset</code> resides, or
- * <code>null</code> if it cannot be determined
- */
- private StringBuffer getReferenceIndentation(int offset,
- boolean assumeOpeningBrace) {
-
- int unit;
- if (assumeOpeningBrace)
- unit = findReferencePosition(offset, Symbols.TokenLBRACE);
- else
- unit = findReferencePosition(offset, peekChar(offset));
-
- // if we were unable to find anything, return null
- if (unit == JavaHeuristicScanner.NOT_FOUND)
- return null;
-
- return getLeadingWhitespace(unit);
-
- }
-
- /**
- * Computes the indentation at <code>offset</code>.
- *
- * @param offset
- * the offset in the document
- * @return a String which reflects the correct indentation for the line in
- * which offset resides, or <code>null</code> if it cannot be
- * determined
- */
- public StringBuffer computeIndentation(int offset) {
- return computeIndentation(offset, false);
- }
-
- /**
- * Computes the indentation at <code>offset</code>.
- *
- * @param offset
- * the offset in the document
- * @param assumeOpeningBrace
- * <code>true</code> if an opening brace should be assumed
- * @return a String which reflects the correct indentation for the line in
- * which offset resides, or <code>null</code> if it cannot be
- * determined
- */
- public StringBuffer computeIndentation(int offset,
- boolean assumeOpeningBrace) {
-
- StringBuffer indent = getReferenceIndentation(offset,
- assumeOpeningBrace);
-
- // handle special alignment
- if (fAlign != JavaHeuristicScanner.NOT_FOUND) {
- try {
- // a special case has been detected.
- IRegion line = fDocument.getLineInformationOfOffset(fAlign);
- int lineOffset = line.getOffset();
- return createIndent(lineOffset, fAlign);
- } catch (BadLocationException e) {
- return null;
- }
- }
-
- if (indent == null)
- return null;
-
- // add additional indent
- //indent.append(createIndent(fIndent));
- indent.insert(0, createIndent(fIndent));
- if (fIndent < 0)
- unindent(indent);
-
- return indent;
- }
-
- /**
- * Returns the indentation of the line at <code>offset</code> as a
- * <code>StringBuffer</code>. If the offset is not valid, the empty
- * string is returned.
- *
- * @param offset
- * the offset in the document
- * @return the indentation (leading whitespace) of the line in which
- * <code>offset</code> is located
- */
- private StringBuffer getLeadingWhitespace(int offset) {
- StringBuffer indent = new StringBuffer();
- try {
- IRegion line = fDocument.getLineInformationOfOffset(offset);
- int lineOffset = line.getOffset();
- int nonWS = fScanner.findNonWhitespaceForwardInAnyPartition(
- lineOffset, lineOffset + line.getLength());
- indent.append(fDocument.get(lineOffset, nonWS - lineOffset));
- return indent;
- } catch (BadLocationException e) {
- return indent;
- }
- }
-
- /**
- * Reduces indentation in <code>indent</code> by one indentation unit.
- *
- * @param indent
- * the indentation to be modified
- */
- private void unindent(StringBuffer indent) {
- CharSequence oneIndent = createIndent();
- int i = indent.lastIndexOf(oneIndent.toString()); //$NON-NLS-1$
- if (i != -1) {
- indent.delete(i, i + oneIndent.length());
- }
- }
-
- /**
- * Creates an indentation string of the length indent - start + 1,
- * consisting of the content in <code>fDocument</code> in the range
- * [start, indent), with every character replaced by a space except for
- * tabs, which are kept as such.
- *
- * <p>
- * Every run of the number of spaces that make up a tab are replaced by a
- * tab character.
- * </p>
- *
- * @return the indentation corresponding to the document content specified
- * by <code>start</code> and <code>indent</code>
- */
- private StringBuffer createIndent(int start, int indent) {
- final int tabLen = prefTabLength();
- StringBuffer ret = new StringBuffer();
- try {
- int spaces = 0;
- while (start < indent) {
-
- char ch = fDocument.getChar(start);
- if (ch == '\t') {
- ret.append('\t');
- spaces = 0;
- } else if (tabLen == -1) {
- ret.append(' ');
- } else {
- spaces++;
- if (spaces == tabLen) {
- ret.append('\t');
- spaces = 0;
- }
- }
-
- start++;
- }
- // remainder
- if (spaces == tabLen)
- ret.append('\t');
- else
- while (spaces-- > 0)
- ret.append(' ');
-
- } catch (BadLocationException e) {
- }
-
- return ret;
- }
-
- /**
- * Creates a string that represents the given number of indents (can be
- * spaces or tabs..)
- *
- * @param indent
- * the requested indentation level.
- *
- * @return the indentation specified by <code>indent</code>
- */
- public StringBuffer createIndent(int indent) {
- StringBuffer oneIndent = createIndent();
-
- StringBuffer ret = new StringBuffer();
- while (indent-- > 0)
- ret.append(oneIndent);
-
- return ret;
- }
-
- /**
- * Creates a string that represents one indent (can be spaces or tabs..)
- *
- * @return one indentation
- */
- private StringBuffer createIndent() {
- // get a sensible default when running without the infrastructure for
- // testing
- StringBuffer oneIndent = new StringBuffer();
- // JavaCore plugin= JavaCore.getJavaCore();
- WebUI plugin = WebUI.getDefault();
- if (plugin == null) {
- oneIndent.append('\t');
- } else {
- if (JavaCore.SPACE
- .equals(JavaCore
- .getOption(DefaultCodeFormatterConstants.FORMATTER_TAB_CHAR))) {
- int tabLen = Integer
- .parseInt(JavaCore
- .getOption(DefaultCodeFormatterConstants.FORMATTER_TAB_SIZE));
- for (int i = 0; i < tabLen; i++)
- oneIndent.append(' ');
- } else if (JavaCore.TAB
- .equals(JavaCore
- .getOption(DefaultCodeFormatterConstants.FORMATTER_TAB_CHAR)))
- oneIndent.append('\t');
- else
- oneIndent.append('\t'); // default
- }
- return oneIndent;
- }
-
- /**
- * Returns the reference position regarding to indentation for
- * <code>offset</code>, or <code>NOT_FOUND</code>. This method calls
- * {@link #findReferencePosition(int, int) findReferencePosition(offset, nextChar)}
- * where <code>nextChar</code> is the next character after
- * <code>offset</code>.
- *
- * @param offset
- * the offset for which the reference is computed
- * @return the reference statement relative to which <code>offset</code>
- * should be indented, or {@link JavaHeuristicScanner#NOT_FOUND}
- */
- public int findReferencePosition(int offset) {
- return findReferencePosition(offset, peekChar(offset));
- }
-
- /**
- * Peeks the next char in the document that comes after <code>offset</code>
- * on the same line as <code>offset</code>.
- *
- * @param offset
- * the offset into document
- * @return the token symbol of the next element, or TokenEOF if there is
- * none
- */
- private int peekChar(int offset) {
- if (offset < fDocument.getLength()) {
- try {
- IRegion line = fDocument.getLineInformationOfOffset(offset);
- int lineOffset = line.getOffset();
- int next = fScanner.nextToken(offset, lineOffset
- + line.getLength());
- return next;
- } catch (BadLocationException e) {
- }
- }
- return Symbols.TokenEOF;
- }
-
- /**
- * Returns the reference position regarding to indentation for
- * <code>position</code>, or <code>NOT_FOUND</code>.
- *
- * <p>
- * If <code>peekNextChar</code> is <code>true</code>, the next token
- * after <code>offset</code> is read and taken into account when computing
- * the indentation. Currently, if the next token is the first token on the
- * line (i.e. only preceded by whitespace), the following tokens are
- * specially handled:
- * <ul>
- * <li><code>switch</code> labels are indented relative to the switch
- * block</li>
- * <li>opening curly braces are aligned correctly with the introducing code</li>
- * <li>closing curly braces are aligned properly with the introducing code
- * of the matching opening brace</li>
- * <li>closing parenthesis' are aligned with their opening peer</li>
- * <li>the <code>else</code> keyword is aligned with its <code>if</code>,
- * anything else is aligned normally (i.e. with the base of any introducing
- * statements).</li>
- * <li>if there is no token on the same line after <code>offset</code>,
- * the indentation is the same as for an <code>else</code> keyword</li>
- * </ul>
- *
- * @param offset
- * the offset for which the reference is computed
- * @param nextToken
- * the next token to assume in the document
- * @return the reference statement relative to which <code>offset</code>
- * should be indented, or {@link JavaHeuristicScanner#NOT_FOUND}
- */
- public int findReferencePosition(int offset, int nextToken) {
- boolean danglingElse = false;
- boolean unindent = false;
- boolean indent = false;
- boolean matchBrace = false;
- boolean matchParen = false;
- boolean matchCase = false;
-
- // account for unindenation characters already typed in, but after
- // position
- // if they are on a line by themselves, the indentation gets adjusted
- // accordingly
- //
- // also account for a dangling else
- if (offset < fDocument.getLength()) {
- try {
- IRegion line = fDocument.getLineInformationOfOffset(offset);
- int lineOffset = line.getOffset();
- int prevPos = Math.max(offset - 1, 0);
- boolean isFirstTokenOnLine = fDocument.get(lineOffset,
- prevPos + 1 - lineOffset).trim().length() == 0;
- int prevToken = fScanner.previousToken(prevPos,
- JavaHeuristicScanner.UNBOUND);
- if (prevToken == Symbols.TokenEOF && nextToken == Symbols.TokenEOF) {
- ITypedRegion partition = TextUtilities.getPartition(fDocument, IPHPPartitions.PHP_PARTITIONING, offset, true);
- if (partition.getType().equals(IPHPPartitions.PHP_SINGLELINE_COMMENT)) {
- fAlign = fScanner.getPosition();
- } else {
- fAlign = JavaHeuristicScanner.NOT_FOUND;
- }
- return JavaHeuristicScanner.NOT_FOUND;
- }
- boolean bracelessBlockStart = fScanner.isBracelessBlockStart(
- prevPos, JavaHeuristicScanner.UNBOUND);
-
- switch (nextToken) {
- case Symbols.TokenEOF:
- case Symbols.TokenELSE:
- danglingElse = true;
- break;
- case Symbols.TokenCASE:
- case Symbols.TokenDEFAULT:
- if (isFirstTokenOnLine)
- matchCase = true;
- break;
- case Symbols.TokenLBRACE: // for opening-brace-on-new-line
- // style
- // if (bracelessBlockStart && !prefIndentBracesForBlocks())
- // unindent= true;
- // else if ((prevToken == Symbols.TokenCOLON || prevToken ==
- // Symbols.TokenEQUAL || prevToken == Symbols.TokenRBRACKET) &&
- // !prefIndentBracesForArrays())
- // unindent= true;
- // else if (!bracelessBlockStart &&
- // prefIndentBracesForMethods())
- // indent= true;
- // break;
- if (bracelessBlockStart)
- unindent = true;
- else if ((prevToken == Symbols.TokenCOLON
- || prevToken == Symbols.TokenEQUAL || prevToken == Symbols.TokenRBRACKET))
- unindent = true;
- else if (!bracelessBlockStart)
- indent = true;
- break;
- case Symbols.TokenRBRACE: // closing braces get unindented
- if (isFirstTokenOnLine)
- matchBrace = true;
- break;
- case Symbols.TokenRPAREN:
- if (isFirstTokenOnLine)
- matchParen = true;
- break;
- }
- } catch (BadLocationException e) {
- }
- } else {
- // assume an else could come if we are at the end of file
- danglingElse = true;
- }
-
- int ref = findReferencePosition(offset, danglingElse, matchBrace,
- matchParen, matchCase);
- if (unindent)
- fIndent--;
- if (indent)
- fIndent++;
- return ref;
- }
-
- /**
- * Returns the reference position regarding to indentation for
- * <code>position</code>, or <code>NOT_FOUND</code>.<code>fIndent</code>
- * will contain the relative indentation (in indentation units, not
- * characters) after the call. If there is a special alignment (e.g. for a
- * method declaration where parameters should be aligned),
- * <code>fAlign</code> will contain the absolute position of the alignment
- * reference in <code>fDocument</code>, otherwise <code>fAlign</code>
- * is set to <code>JavaHeuristicScanner.NOT_FOUND</code>.
- *
- * @param offset
- * the offset for which the reference is computed
- * @param danglingElse
- * whether a dangling else should be assumed at
- * <code>position</code>
- * @param matchBrace
- * whether the position of the matching brace should be returned
- * instead of doing code analysis
- * @param matchParen
- * whether the position of the matching parenthesis should be
- * returned instead of doing code analysis
- * @param matchCase
- * whether the position of a switch statement reference should be
- * returned (either an earlier case statement or the switch block
- * brace)
- * @return the reference statement relative to which <code>position</code>
- * should be indented, or {@link JavaHeuristicScanner#NOT_FOUND}
- */
- public int findReferencePosition(int offset, boolean danglingElse,
- boolean matchBrace, boolean matchParen, boolean matchCase) {
- fIndent = 0; // the indentation modification
- fAlign = JavaHeuristicScanner.NOT_FOUND;
- fPosition = offset;
-
- // forward cases
- // an unindentation happens sometimes if the next token is special,
- // namely on braces, parens and case labels
- // align braces, but handle the case where we align with the method
- // declaration start instead of
- // the opening brace.
- if (matchBrace) {
- if (skipScope(Symbols.TokenLBRACE, Symbols.TokenRBRACE)) {
- try {
- // align with the opening brace that is on a line by its own
- int lineOffset = fDocument.getLineOffset(fLine);
- if (lineOffset <= fPosition
- && fDocument
- .get(lineOffset, fPosition - lineOffset)
- .trim().length() == 0)
- return fPosition;
- } catch (BadLocationException e) {
- // concurrent modification - walk default path
- }
- // if the opening brace is not on the start of the line, skip to
- // the start
- int pos = skipToStatementStart(true, true);
- fIndent = 0; // indent is aligned with reference position
- return pos;
- } else {
- // if we can't find the matching brace, the heuristic is to
- // unindent
- // by one against the normal position
- int pos = findReferencePosition(offset, danglingElse, false,
- matchParen, matchCase);
- fIndent--;
- return pos;
- }
- }
-
- // align parenthesis'
- if (matchParen) {
- if (skipScope(Symbols.TokenLPAREN, Symbols.TokenRPAREN))
- return fPosition;
- else {
- // if we can't find the matching paren, the heuristic is to
- // unindent
- // by one against the normal position
- int pos = findReferencePosition(offset, danglingElse,
- matchBrace, false, matchCase);
- fIndent--;
- return pos;
- }
- }
-
- // the only reliable way to get case labels aligned (due to many
- // different styles of using braces in a block)
- // is to go for another case statement, or the scope opening brace
- if (matchCase) {
- return matchCaseAlignment();
- }
-
- nextToken();
- switch (fToken) {
- case Symbols.TokenRBRACE:
- // skip the block and fall through
- // if we can't complete the scope, reset the scan position
- int pos = fPosition;
- if (!skipScope())
- fPosition = pos;
- case Symbols.TokenSEMICOLON:
- // this is the 90% case: after a statement block
- // the end of the previous statement / block previous.end
- // search to the end of the statement / block before the previous;
- // the token just after that is previous.start
- return skipToStatementStart(danglingElse, false);
-
- // scope introduction: special treat who special is
- case Symbols.TokenLPAREN:
- case Symbols.TokenLBRACE:
- case Symbols.TokenLBRACKET:
- return handleScopeIntroduction(offset + 1);
-
- case Symbols.TokenEOF:
- // trap when hitting start of document
- return 0;
-
- case Symbols.TokenEQUAL:
- // indent assignments
- fIndent = prefAssignmentIndent();
- return fPosition;
-
- case Symbols.TokenCOLON:
- // TODO handle ternary deep indentation
- fIndent = prefCaseBlockIndent();
- return fPosition;
-
- case Symbols.TokenQUESTIONMARK:
- if (prefTernaryDeepAlign()) {
- setFirstElementAlignment(fPosition, offset + 1);
- return fPosition;
- } else {
- fIndent = prefTernaryIndent();
- return fPosition;
- }
-
- // indentation for blockless introducers:
- case Symbols.TokenDO:
- case Symbols.TokenWHILE:
- case Symbols.TokenELSE:
- fIndent = prefSimpleIndent();
- return fPosition;
- case Symbols.TokenRPAREN:
- int line = fLine;
- if (skipScope(Symbols.TokenLPAREN, Symbols.TokenRPAREN)) {
- int scope = fPosition;
- nextToken();
- if (fToken == Symbols.TokenIF || fToken == Symbols.TokenWHILE
- || fToken == Symbols.TokenFOR) {
- fIndent = prefSimpleIndent();
- return fPosition;
- }
- fPosition = scope;
- if (looksLikeMethodDecl()) {
- return skipToStatementStart(danglingElse, false);
- }
- }
- // restore
- fPosition = offset;
- fLine = line;
- // else: fall through to default
-
- case Symbols.TokenCOMMA:
- // inside a list of some type
- // easy if there is already a list item before with its own
- // indentation - we just align
- // if not: take the start of the list ( LPAREN, LBRACE, LBRACKET )
- // and either align or
- // indent by list-indent
- default:
- // inside whatever we don't know about: similar to the list case:
- // if we are inside a continued expression, then either align with a
- // previous line that has indentation
- // or indent from the expression start line (either a scope
- // introducer or the start of the expr).
- return skipToPreviousListItemOrListStart();
-
- }
- }
-
- /**
- * Skips to the start of a statement that ends at the current position.
- *
- * @param danglingElse
- * whether to indent aligned with the last <code>if</code>
- * @param isInBlock
- * whether the current position is inside a block, which limits
- * the search scope to the next scope introducer
- * @return the reference offset of the start of the statement
- */
- private int skipToStatementStart(boolean danglingElse, boolean isInBlock) {
- while (true) {
- nextToken();
-
- if (isInBlock) {
- switch (fToken) {
- // exit on all block introducers
- case Symbols.TokenIF:
- case Symbols.TokenELSE:
- case Symbols.TokenSYNCHRONIZED:
- case Symbols.TokenCOLON:
- case Symbols.TokenSTATIC:
- case Symbols.TokenCATCH:
- case Symbols.TokenDO:
- case Symbols.TokenWHILE:
- case Symbols.TokenFINALLY:
- case Symbols.TokenFOR:
- case Symbols.TokenTRY:
- return fPosition;
-
- case Symbols.TokenSWITCH:
- fIndent = prefCaseIndent();
- return fPosition;
- }
- }
-
- switch (fToken) {
- // scope introduction through: LPAREN, LBRACE, LBRACKET
- // search stop on SEMICOLON, RBRACE, COLON, EOF
- // -> the next token is the start of the statement (i.e. previousPos
- // when backward scanning)
- case Symbols.TokenLPAREN:
- case Symbols.TokenLBRACE:
- case Symbols.TokenLBRACKET:
- case Symbols.TokenSEMICOLON:
- case Symbols.TokenEOF:
- return fPreviousPos;
-
- case Symbols.TokenCOLON:
- int pos = fPreviousPos;
- if (!isConditional())
- return pos;
- break;
-
- case Symbols.TokenRBRACE:
- // RBRACE is a little tricky: it can be the end of an array
- // definition, but
- // usually it is the end of a previous block
- pos = fPreviousPos; // store state
- if (skipScope() && looksLikeArrayInitializerIntro())
- continue; // it's an array
- else
- return pos; // it's not - do as with all the above
-
- // scopes: skip them
- case Symbols.TokenRPAREN:
- case Symbols.TokenRBRACKET:
- pos = fPreviousPos;
- if (skipScope())
- break;
- else
- return pos;
-
- // IF / ELSE: align the position after the conditional block
- // with the if
- // so we are ready for an else, except if danglingElse is false
- // in order for this to work, we must skip an else to its if
- case Symbols.TokenIF:
- if (danglingElse)
- return fPosition;
- else
- break;
- case Symbols.TokenELSE:
- // skip behind the next if, as we have that one covered
- pos = fPosition;
- if (skipNextIF())
- break;
- else
- return pos;
-
- case Symbols.TokenDO:
- // align the WHILE position with its do
- return fPosition;
-
- case Symbols.TokenWHILE:
- // this one is tricky: while can be the start of a while loop
- // or the end of a do - while
- pos = fPosition;
- if (hasMatchingDo()) {
- // continue searching from the DO on
- break;
- } else {
- // continue searching from the WHILE on
- fPosition = pos;
- break;
- }
- default:
- // keep searching
-
- }
-
- }
- }
-
- /**
- * Returns true if the colon at the current position is part of a
- * conditional (ternary) expression, false otherwise.
- *
- * @return true if the colon at the current position is part of a
- * conditional
- */
- private boolean isConditional() {
- while (true) {
- nextToken();
- switch (fToken) {
-
- // search for case, otherwise return true
- case Symbols.TokenIDENT:
- continue;
- case Symbols.TokenCASE:
- return false;
-
- default:
- return true;
- }
- }
- }
-
- /**
- * Returns as a reference any previous <code>switch</code> labels (<code>case</code>
- * or <code>default</code>) or the offset of the brace that scopes the
- * switch statement. Sets <code>fIndent</code> to
- * <code>prefCaseIndent</code> upon a match.
- *
- * @return the reference offset for a <code>switch</code> label
- */
- private int matchCaseAlignment() {
- while (true) {
- nextToken();
- switch (fToken) {
- // invalid cases: another case label or an LBRACE must come before a
- // case
- // -> bail out with the current position
- case Symbols.TokenLPAREN:
- case Symbols.TokenLBRACKET:
- case Symbols.TokenEOF:
- return fPosition;
- case Symbols.TokenLBRACE:
- // opening brace of switch statement
- fIndent = 1; //prefCaseIndent() is for Java
- return fPosition;
- case Symbols.TokenCASE:
- case Symbols.TokenDEFAULT:
- // align with previous label
- fIndent = 0;
- return fPosition;
- // scopes: skip them
- case Symbols.TokenRPAREN:
- case Symbols.TokenRBRACKET:
- case Symbols.TokenRBRACE:
- skipScope();
- break;
- default:
- // keep searching
- continue;
- }
- }
- }
-
- /**
- * Returns the reference position for a list element. The algorithm tries to
- * match any previous indentation on the same list. If there is none, the
- * reference position returned is determined depending on the type of list:
- * The indentation will either match the list scope introducer (e.g. for
- * method declarations), so called deep indents, or simply increase the
- * indentation by a number of standard indents. See also
- * {@link #handleScopeIntroduction(int)}.
- *
- * @return the reference position for a list item: either a previous list
- * item that has its own indentation, or the list introduction
- * start.
- */
- private int skipToPreviousListItemOrListStart() {
- int startLine = fLine;
- int startPosition = fPosition;
- while (true) {
- nextToken();
-
- // if any line item comes with its own indentation, adapt to it
- if (fLine < startLine) {
- try {
- int lineOffset = fDocument.getLineOffset(startLine);
- int bound = Math.min(fDocument.getLength(),
- startPosition + 1);
- fAlign = fScanner.findNonWhitespaceForwardInAnyPartition(
- lineOffset, bound);
- } catch (BadLocationException e) {
- // ignore and return just the position
- }
- return startPosition;
- }
-
- switch (fToken) {
- // scopes: skip them
- case Symbols.TokenRPAREN:
- case Symbols.TokenRBRACKET:
- case Symbols.TokenRBRACE:
- skipScope();
- break;
-
- // scope introduction: special treat who special is
- case Symbols.TokenLPAREN:
- case Symbols.TokenLBRACE:
- case Symbols.TokenLBRACKET:
- return handleScopeIntroduction(startPosition + 1);
-
- case Symbols.TokenSEMICOLON:
- return fPosition;
- case Symbols.TokenQUESTIONMARK:
- if (prefTernaryDeepAlign()) {
- setFirstElementAlignment(fPosition - 1, fPosition + 1);
- } else {
- fIndent = prefTernaryIndent();
- }
- return fPosition;
- case Symbols.TokenEOF:
- return 0;
-
- case Symbols.TokenEQUAL:
- // indent assignments
- fIndent= prefAssignmentIndent();
- return fPosition;
- }
- }
- }
-
- /**
- * Skips a scope and positions the cursor (<code>fPosition</code>) on
- * the token that opens the scope. Returns <code>true</code> if a matching
- * peer could be found, <code>false</code> otherwise. The current token
- * when calling must be one out of <code>Symbols.TokenRPAREN</code>,
- * <code>Symbols.TokenRBRACE</code>, and
- * <code>Symbols.TokenRBRACKET</code>.
- *
- * @return <code>true</code> if a matching peer was found,
- * <code>false</code> otherwise
- */
- private boolean skipScope() {
- switch (fToken) {
- case Symbols.TokenRPAREN:
- return skipScope(Symbols.TokenLPAREN, Symbols.TokenRPAREN);
- case Symbols.TokenRBRACKET:
- return skipScope(Symbols.TokenLBRACKET, Symbols.TokenRBRACKET);
- case Symbols.TokenRBRACE:
- return skipScope(Symbols.TokenLBRACE, Symbols.TokenRBRACE);
- default:
- Assert.isTrue(false);
- return false;
- }
- }
-
- /**
- * Handles the introduction of a new scope. The current token must be one
- * out of <code>Symbols.TokenLPAREN</code>,
- * <code>Symbols.TokenLBRACE</code>, and
- * <code>Symbols.TokenLBRACKET</code>. Returns as the reference position
- * either the token introducing the scope or - if available - the first java
- * token after that.
- *
- * <p>
- * Depending on the type of scope introduction, the indentation will align
- * (deep indenting) with the reference position (<code>fAlign</code> will
- * be set to the reference position) or <code>fIndent</code> will be set
- * to the number of indentation units.
- * </p>
- *
- * @param bound
- * the bound for the search for the first token after the scope
- * introduction.
- * @return
- */
- private int handleScopeIntroduction(int bound) {
- switch (fToken) {
- // scope introduction: special treat who special is
- case Symbols.TokenLPAREN:
- int pos = fPosition; // store
-
- // special: method declaration deep indentation
- if (looksLikeMethodDecl()) {
- if (prefMethodDeclDeepIndent())
- return setFirstElementAlignment(pos, bound);
- else {
- fIndent = prefMethodDeclIndent();
- return pos;
- }
- } else {
- fPosition = pos;
- if (looksLikeMethodCall()) {
- if (prefMethodCallDeepIndent())
- return setFirstElementAlignment(pos, bound);
- else {
- fIndent = prefMethodCallIndent();
- return pos;
- }
- } else if (prefParenthesisDeepIndent())
- return setFirstElementAlignment(pos, bound);
- }
-
- // normal: return the parenthesis as reference
- fIndent = prefParenthesisIndent();
- return pos;
-
- case Symbols.TokenLBRACE:
- pos = fPosition; // store
-
- // special: array initializer
- if (looksLikeArrayInitializerIntro())
- if (prefArrayDeepIndent())
- return setFirstElementAlignment(pos, bound);
- else
- fIndent = prefArrayIndent();
- else
- fIndent = prefBlockIndent();
-
- // normal: skip to the statement start before the scope introducer
- // opening braces are often on differently ending indents than e.g.
- // a method definition
- fPosition = pos; // restore
- return skipToStatementStart(true, true); // set to true to match
- // the first if
-
- case Symbols.TokenLBRACKET:
- pos = fPosition; // store
-
- // special: method declaration deep indentation
- if (prefArrayDimensionsDeepIndent()) {
- return setFirstElementAlignment(pos, bound);
- }
-
- // normal: return the bracket as reference
- fIndent = prefBracketIndent();
- return pos; // restore
-
- default:
- Assert.isTrue(false);
- return -1; // dummy
- }
- }
-
- /**
- * Sets the deep indent offset (<code>fAlign</code>) to either the
- * offset right after <code>scopeIntroducerOffset</code> or - if available -
- * the first Java token after <code>scopeIntroducerOffset</code>, but
- * before <code>bound</code>.
- *
- * @param scopeIntroducerOffset
- * the offset of the scope introducer
- * @param bound
- * the bound for the search for another element
- * @return the reference position
- */
- private int setFirstElementAlignment(int scopeIntroducerOffset, int bound) {
- int firstPossible = scopeIntroducerOffset + 1; // align with the first
- // position after the
- // scope intro
- fAlign = fScanner.findNonWhitespaceForwardInAnyPartition(firstPossible,
- bound);
- if (fAlign == JavaHeuristicScanner.NOT_FOUND)
- fAlign = firstPossible;
- return fAlign;
- }
-
- /**
- * Returns <code>true</code> if the next token received after calling
- * <code>nextToken</code> is either an equal sign or an array designator
- * ('[]').
- *
- * @return <code>true</code> if the next elements look like the start of
- * an array definition
- */
- private boolean looksLikeArrayInitializerIntro() {
- nextToken();
- if (fToken == Symbols.TokenEQUAL || skipBrackets()) {
- return true;
- }
- return false;
- }
-
- /**
- * Skips over the next <code>if</code> keyword. The current token when
- * calling this method must be an <code>else</code> keyword. Returns
- * <code>true</code> if a matching <code>if</code> could be found,
- * <code>false</code> otherwise. The cursor (<code>fPosition</code>)
- * is set to the offset of the <code>if</code> token.
- *
- * @return <code>true</code> if a matching <code>if</code> token was
- * found, <code>false</code> otherwise
- */
- private boolean skipNextIF() {
- Assert.isTrue(fToken == Symbols.TokenELSE);
-
- while (true) {
- nextToken();
- switch (fToken) {
- // scopes: skip them
- case Symbols.TokenRPAREN:
- case Symbols.TokenRBRACKET:
- case Symbols.TokenRBRACE:
- skipScope();
- break;
-
- case Symbols.TokenIF:
- // found it, return
- return true;
- case Symbols.TokenELSE:
- // recursively skip else-if blocks
- skipNextIF();
- break;
-
- // shortcut scope starts
- case Symbols.TokenLPAREN:
- case Symbols.TokenLBRACE:
- case Symbols.TokenLBRACKET:
- case Symbols.TokenEOF:
- return false;
- }
- }
- }
-
- /**
- * while(condition); is ambiguous when parsed backwardly, as it is a valid
- * statement by its own, so we have to check whether there is a matching do.
- * A <code>do</code> can either be separated from the while by a block, or
- * by a single statement, which limits our search distance.
- *
- * @return <code>true</code> if the <code>while</code> currently in
- * <code>fToken</code> has a matching <code>do</code>.
- */
- private boolean hasMatchingDo() {
- Assert.isTrue(fToken == Symbols.TokenWHILE);
- nextToken();
- switch (fToken) {
- case Symbols.TokenRBRACE:
- skipScope(); // and fall thru
- case Symbols.TokenSEMICOLON:
- skipToStatementStart(false, false);
- return fToken == Symbols.TokenDO;
- }
- return false;
- }
-
- /**
- * Skips brackets if the current token is a RBRACKET. There can be nothing
- * but whitespace in between, this is only to be used for <code>[]</code>
- * elements.
- *
- * @return <code>true</code> if a <code>[]</code> could be scanned, the
- * current token is left at the LBRACKET.
- */
- private boolean skipBrackets() {
- if (fToken == Symbols.TokenRBRACKET) {
- nextToken();
- if (fToken == Symbols.TokenLBRACKET) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * Reads the next token in backward direction from the heuristic scanner and
- * sets the fields <code>fToken, fPreviousPosition</code> and
- * <code>fPosition</code> accordingly.
- */
- private void nextToken() {
- nextToken(fPosition);
- }
-
- /**
- * Reads the next token in backward direction of <code>start</code> from
- * the heuristic scanner and sets the fields
- * <code>fToken, fPreviousPosition</code> and <code>fPosition</code>
- * accordingly.
- */
- private void nextToken(int start) {
- fToken = fScanner
- .previousToken(start - 1, JavaHeuristicScanner.UNBOUND);
- fPreviousPos = start;
- fPosition = fScanner.getPosition() + 1;
- try {
- fLine = fDocument.getLineOfOffset(fPosition);
- } catch (BadLocationException e) {
- fLine = -1;
- }
- }
-
- /**
- * Returns <code>true</code> if the current tokens look like a method
- * declaration header (i.e. only the return type and method name). The
- * heuristic calls <code>nextToken</code> and expects an identifier
- * (method name) and a type declaration (an identifier with optional
- * brackets) which also covers the visibility modifier of constructors; it
- * does not recognize package visible constructors.
- *
- * @return <code>true</code> if the current position looks like a method
- * declaration header.
- */
- private boolean looksLikeMethodDecl() {
- /*
- * TODO This heuristic does not recognize package private constructors
- * since those do have neither type nor visibility keywords. One option
- * would be to go over the parameter list, but that might be empty as
- * well - hard to do without an AST...
- */
-
- nextToken();
- if (fToken == Symbols.TokenIDENT) { // method name
- do
- nextToken();
- while (skipBrackets()); // optional brackets for array valued return
- // types
- return fToken == Symbols.TokenIDENT; // type name
-
- }
- return false;
- }
-
- /**
- * Returns <code>true</code> if the current tokens look like a method call
- * header (i.e. an identifier as opposed to a keyword taking parenthesized
- * parameters such as <code>if</code>).
- * <p>
- * The heuristic calls <code>nextToken</code> and expects an identifier
- * (method name).
- *
- * @return <code>true</code> if the current position looks like a method
- * call header.
- */
- private boolean looksLikeMethodCall() {
- nextToken();
- return fToken == Symbols.TokenIDENT; // method name
- }
-
- /**
- * Scans tokens for the matching opening peer. The internal cursor (<code>fPosition</code>)
- * is set to the offset of the opening peer if found.
- *
- * @return <code>true</code> if a matching token was found,
- * <code>false</code> otherwise
- */
- private boolean skipScope(int openToken, int closeToken) {
-
- int depth = 1;
-
- while (true) {
- nextToken();
-
- if (fToken == closeToken) {
- depth++;
- } else if (fToken == openToken) {
- depth--;
- if (depth == 0)
- return true;
- } else if (fToken == Symbols.TokenEOF) {
- return false;
- }
- }
- }
-
- // TODO adjust once there are per-project settings
-
- private int prefTabLength() {
- int tabLen;
- // JavaCore core= JavaCore.getJavaCore();
- WebUI plugin = WebUI.getDefault();
- // if (core != null && plugin != null)
- if (plugin != null)
- if (JavaCore.SPACE
- .equals(JavaCore
- .getOption(DefaultCodeFormatterConstants.FORMATTER_TAB_CHAR)))
- // if the formatter uses chars to mark indentation, then don't
- // substitute any chars
- tabLen = -1; // results in no tabs being substituted for
- // space runs
- else
- // if the formatter uses tabs to mark indentations, use the
- // visual setting from the editor
- // to get nicely aligned indentations
- tabLen = plugin
- .getPreferenceStore()
- .getInt(
- AbstractDecoratedTextEditorPreferenceConstants.EDITOR_TAB_WIDTH);
- else
- tabLen = 4; // sensible default for testing
-
- return tabLen;
- }
-
- private boolean prefArrayDimensionsDeepIndent() {
- return true; // sensible default
- }
-
- private int prefArrayIndent() {
- Plugin plugin = JavaCore.getPlugin();
- if (plugin != null) {
- String option = JavaCore
- .getOption(DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_EXPRESSIONS_IN_ARRAY_INITIALIZER);
- try {
- if (DefaultCodeFormatterConstants.getIndentStyle(option) == DefaultCodeFormatterConstants.INDENT_BY_ONE)
- return 1;
- } catch (IllegalArgumentException e) {
- // ignore and return default
- }
- }
-
- return prefContinuationIndent(); // default
- }
-
- private boolean prefArrayDeepIndent() {
- Plugin plugin = JavaCore.getPlugin();
- if (plugin != null) {
- String option = JavaCore
- .getOption(DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_EXPRESSIONS_IN_ARRAY_INITIALIZER);
- try {
- return DefaultCodeFormatterConstants.getIndentStyle(option) == DefaultCodeFormatterConstants.INDENT_ON_COLUMN;
- } catch (IllegalArgumentException e) {
- // ignore and return default
- }
- }
-
- return true;
- }
-
- private boolean prefTernaryDeepAlign() {
- Plugin plugin = JavaCore.getPlugin();
- if (plugin != null) {
- String option = JavaCore
- .getOption(DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_CONDITIONAL_EXPRESSION);
- try {
- return DefaultCodeFormatterConstants.getIndentStyle(option) == DefaultCodeFormatterConstants.INDENT_ON_COLUMN;
- } catch (IllegalArgumentException e) {
- // ignore and return default
- }
- }
- return false;
- }
-
- private int prefTernaryIndent() {
- Plugin plugin = JavaCore.getPlugin();
- if (plugin != null) {
- String option = JavaCore
- .getOption(DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_CONDITIONAL_EXPRESSION);
- try {
- if (DefaultCodeFormatterConstants.getIndentStyle(option) == DefaultCodeFormatterConstants.INDENT_BY_ONE)
- return 1;
- else
- return prefContinuationIndent();
- } catch (IllegalArgumentException e) {
- // ignore and return default
- }
- }
-
- return prefContinuationIndent();
- }
-
- private int prefCaseIndent() {
- Plugin plugin = JavaCore.getPlugin();
- if (plugin != null) {
- if (DefaultCodeFormatterConstants.TRUE
- .equals(JavaCore
- .getOption(DefaultCodeFormatterConstants.FORMATTER_INDENT_SWITCHSTATEMENTS_COMPARE_TO_SWITCH)))
- return prefBlockIndent();
- else
- return 0;
- }
-
- return 0; // sun standard
- }
-
- private int prefAssignmentIndent() {
- return prefBlockIndent();
- }
-
- private int prefCaseBlockIndent() {
- if (true)
- return prefBlockIndent();
-
- Plugin plugin = JavaCore.getPlugin();
- if (plugin != null) {
- if (DefaultCodeFormatterConstants.TRUE
- .equals(JavaCore
- .getOption(DefaultCodeFormatterConstants.FORMATTER_INDENT_SWITCHSTATEMENTS_COMPARE_TO_CASES)))
- return prefBlockIndent();
- else
- return 0;
- }
- return prefBlockIndent(); // sun standard
- }
-
- private int prefSimpleIndent() {
- return prefBlockIndent();
- }
-
- private int prefBracketIndent() {
- return prefBlockIndent();
- }
-
- private boolean prefMethodDeclDeepIndent() {
- Plugin plugin = JavaCore.getPlugin();
- if (plugin != null) {
- String option = JavaCore
- .getOption(DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_PARAMETERS_IN_METHOD_DECLARATION);
- try {
- return DefaultCodeFormatterConstants.getIndentStyle(option) == DefaultCodeFormatterConstants.INDENT_ON_COLUMN;
- } catch (IllegalArgumentException e) {
- // ignore and return default
- }
- }
-
- return true;
- }
-
- private int prefMethodDeclIndent() {
- Plugin plugin = JavaCore.getPlugin();
- if (plugin != null) {
- String option = JavaCore
- .getOption(DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_PARAMETERS_IN_METHOD_DECLARATION);
- try {
- if (DefaultCodeFormatterConstants.getIndentStyle(option) == DefaultCodeFormatterConstants.INDENT_BY_ONE)
- return 1;
- else
- return prefContinuationIndent();
- } catch (IllegalArgumentException e) {
- // ignore and return default
- }
- }
- return 1;
- }
-
- private boolean prefMethodCallDeepIndent() {
- Plugin plugin = JavaCore.getPlugin();
- if (plugin != null) {
- String option = JavaCore
- .getOption(DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_ARGUMENTS_IN_METHOD_INVOCATION);
- try {
- return DefaultCodeFormatterConstants.getIndentStyle(option) == DefaultCodeFormatterConstants.INDENT_ON_COLUMN;
- } catch (IllegalArgumentException e) {
- // ignore and return default
- }
- }
- return false; // sensible default
- }
-
- private int prefMethodCallIndent() {
- Plugin plugin = JavaCore.getPlugin();
- if (plugin != null) {
- String option = JavaCore
- .getOption(DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_ARGUMENTS_IN_METHOD_INVOCATION);
- try {
- if (DefaultCodeFormatterConstants.getIndentStyle(option) == DefaultCodeFormatterConstants.INDENT_BY_ONE)
- return 1;
- else
- return prefContinuationIndent();
- } catch (IllegalArgumentException e) {
- // ignore and return default
- }
- }
-
- return 1; // sensible default
- }
-
- private boolean prefParenthesisDeepIndent() {
-
- if (true) // don't do parenthesis deep indentation
- return false;
-
- Plugin plugin = JavaCore.getPlugin();
- if (plugin != null) {
- String option = JavaCore
- .getOption(DefaultCodeFormatterConstants.FORMATTER_CONTINUATION_INDENTATION);
- try {
- return DefaultCodeFormatterConstants.getIndentStyle(option) == DefaultCodeFormatterConstants.INDENT_ON_COLUMN;
- } catch (IllegalArgumentException e) {
- // ignore and return default
- }
- }
-
- return false; // sensible default
- }
-
- private int prefParenthesisIndent() {
- return prefContinuationIndent();
- }
-
- private int prefBlockIndent() {
- return 1; // sensible default
- }
-
- private boolean prefIndentBracesForBlocks() {
- Plugin plugin = JavaCore.getPlugin();
- if (plugin != null) {
- String option = JavaCore
- .getOption(DefaultCodeFormatterConstants.FORMATTER_BRACE_POSITION_FOR_BLOCK);
- return option
- .equals(DefaultCodeFormatterConstants.NEXT_LINE_SHIFTED);
- }
-
- return false; // sensible default
- }
-
- private boolean prefIndentBracesForArrays() {
- Plugin plugin = JavaCore.getPlugin();
- if (plugin != null) {
- String option = JavaCore
- .getOption(DefaultCodeFormatterConstants.FORMATTER_BRACE_POSITION_FOR_ARRAY_INITIALIZER);
- return option
- .equals(DefaultCodeFormatterConstants.NEXT_LINE_SHIFTED);
- }
-
- return false; // sensible default
- }
-
- private boolean prefIndentBracesForMethods() {
- Plugin plugin = JavaCore.getPlugin();
- if (plugin != null) {
- String option = JavaCore
- .getOption(DefaultCodeFormatterConstants.FORMATTER_BRACE_POSITION_FOR_METHOD_DECLARATION);
- return option
- .equals(DefaultCodeFormatterConstants.NEXT_LINE_SHIFTED);
- }
-
- return false; // sensible default
- }
-
- private int prefContinuationIndent() {
- Plugin plugin = JavaCore.getPlugin();
- if (plugin != null) {
- String option = JavaCore
- .getOption(DefaultCodeFormatterConstants.FORMATTER_CONTINUATION_INDENTATION);
- try {
- return Integer.parseInt(option);
- } catch (NumberFormatException e) {
- // ignore and return default
- }
- }
-
- return 2; // sensible default
- }
-
-}