* on the same document as the one we get.
*/
private JavaHeuristicScanner fScanner;
-
+
/**
* Creates a new instance.
*
fDocument= document;
fScanner= scanner;
}
-
+
/**
* Computes the indentation at the reference point of <code>position</code>.
*
public StringBuffer getReferenceIndentation(int offset) {
return getReferenceIndentation(offset, false);
}
-
+
/**
* Computes the indentation at the reference point of <code>position</code>.
*
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>.
*
public StringBuffer computeIndentation(int offset) {
return computeIndentation(offset, false);
}
-
+
/**
* Computes the indentation at <code>offset</code>.
*
* determined
*/
public StringBuffer computeIndentation(int offset, boolean assumeOpeningBrace) {
-
+
StringBuffer indent= getReferenceIndentation(offset, assumeOpeningBrace);
-
+
// handle special alignment
if (fAlign != JavaHeuristicScanner.NOT_FOUND) {
try {
return null;
}
}
-
+
if (indent == null)
return null;
-
+
// add additional indent
indent.append(createIndent(fIndent));
if (fIndent < 0)
unindent(indent);
-
+
return indent;
}
int i= indent.lastIndexOf(oneIndent.toString()); //$NON-NLS-1$
if (i != -1) {
indent.delete(i, i + oneIndent.length());
- }
+ }
}
/**
* by <code>start</code> and <code>indent</code>
*/
private StringBuffer createIndent(int start, int indent) {
- final int tabLen= prefTabLength();
+ 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;
}
}
-
+
start++;
}
// remainder
else
while (spaces-- > 0)
ret.append(' ');
-
+
} catch (BadLocationException e) {
}
-
+
return ret;
}
*
* @return the indentation specified by <code>indent</code>
*/
- private StringBuffer createIndent(int indent) {
- StringBuffer oneIndent= createIndent();
+ 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..)
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>.
}
return Symbols.TokenEOF;
}
-
+
/**
* Returns the reference position regarding to indentation for <code>position</code>,
* or <code>NOT_FOUND</code>.
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
+ // also account for a dangling else
if (offset < fDocument.getLength()) {
try {
IRegion line= fDocument.getLineInformationOfOffset(offset);
boolean isFirstTokenOnLine= fDocument.get(lineOffset, prevPos + 1 - lineOffset).trim().length() == 0;
int prevToken= fScanner.previousToken(prevPos, JavaHeuristicScanner.UNBOUND);
boolean bracelessBlockStart= fScanner.isBracelessBlockStart(prevPos, JavaHeuristicScanner.UNBOUND);
-
+
switch (nextToken) {
case Symbols.TokenEOF:
case Symbols.TokenELSE:
}
} else {
// assume an else could come if we are at the end of file
- danglingElse= true;
+ danglingElse= true;
}
-
+
int ref= findReferencePosition(offset, danglingElse, matchBrace, matchParen, matchCase);
if (unindent)
fIndent--;
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
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
// return pos;
// }
// }
-
+
// align parenthesis'
if (matchParen) {
if (skipScope(Symbols.TokenLPAREN, Symbols.TokenRPAREN))
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:
if (!skipScope())
fPosition= pos;
case Symbols.TokenSEMICOLON:
- // this is the 90% case: after a statement block
+ // 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);
fIndent= prefTernaryIndent();
return fPosition;
}
-
+
// indentation for blockless introducers:
case Symbols.TokenDO:
case Symbols.TokenWHILE:
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 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();
-
+
}
}
private int skipToStatementStart(boolean danglingElse, boolean isInBlock) {
while (true) {
nextToken();
-
+
if (isInBlock) {
switch (fToken) {
// exit on all block introducers
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
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
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:
if (skipScope())
break;
else
- return pos;
-
+ 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
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
+ // or the end of a do - while
pos= fPosition;
if (hasMatchingDo()) {
- // continue searching from the DO on
+ // continue searching from the DO on
break;
} else {
// continue searching from the WHILE on
}
default:
// keep searching
-
+
}
}
}
-
+
/**
* Returns true if the colon at the current position is part of a conditional
* (ternary) expression, false otherwise.
while (true) {
nextToken();
switch (fToken) {
-
+
// search for case, otherwise return true
case Symbols.TokenIDENT:
continue;
case Symbols.TokenCASE:
return false;
-
+
default:
return true;
}
// // 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;
-//
+//
// }
// }
// }
int startPosition= fPosition;
while (true) {
nextToken();
-
+
// if any line item comes with its own indentation, adapt to it
if (fLine < startLine) {
try {
}
return startPosition;
}
-
+
switch (fToken) {
// scopes: skip them
case Symbols.TokenRPAREN:
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:
}
case Symbols.TokenEOF:
return 0;
-
+
}
}
}
-
+
/**
* 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
// scope introduction: special treat who special is
case Symbols.TokenLPAREN:
int pos= fPosition; // store
-
+
// special: method declaration deep indentation
if (looksLikeMethodDecl()) {
if (prefMethodDeclDeepIndent())
} 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())
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
*/
private boolean skipNextIF() {
Assert.isTrue(fToken == Symbols.TokenELSE);
-
+
while (true) {
nextToken();
switch (fToken) {
case Symbols.TokenRBRACE:
skipScope();
break;
-
+
case Symbols.TokenIF:
// found it, return
return true;
// recursively skip else-if blocks
skipNextIF();
break;
-
+
// shortcut scope starts
case Symbols.TokenLPAREN:
case Symbols.TokenLBRACE:
}
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 false;
}
-
+
/**
* Reads the next token in backward direction from the heuristic scanner
* and sets the fields <code>fToken, fPreviousPosition</code> and <code>fPosition</code>
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>
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
* 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
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.
* otherwise
*/
private boolean skipScope(int openToken, int closeToken) {
-
+
int depth= 1;
while (true) {
nextToken();
-
+
if (fToken == closeToken) {
depth++;
} else if (fToken == openToken) {
}
// TODO adjust once there are per-project settings
-
+
private int prefTabLength() {
int tabLen;
// JavaCore core= JavaCore.getJavaCore();
// 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
+ // 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
return tabLen;
}
-
+
private boolean prefArrayDimensionsDeepIndent() {
return true; // sensible default
}
// ignore and return default
}
}
-
+
return prefContinuationIndent(); // default
}
// ignore and return default
}
}
-
+
return true;
}
// ignore and return default
}
}
-
+
return prefContinuationIndent();
}
else
return 0;
}
-
+
return 0; // sun standard
}
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)))
// ignore and return default
}
}
-
+
return true;
}
// 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);
// ignore and return default
}
}
-
+
return false; // sensible default
}
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) {
// ignore and return default
}
}
-
+
return 2; // sensible default
}