/*******************************************************************************
* Copyright (c) 2005, 2007 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
*******************************************************************************/
package org.eclipse.vjet.eclipse.internal.ui.text;
import java.util.regex.Pattern;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.dltk.mod.core.IScriptProject;
import org.eclipse.dltk.mod.ui.DLTKUIPlugin;
import org.eclipse.dltk.mod.ui.PreferenceConstants;
import org.eclipse.dltk.mod.ui.text.util.AutoEditUtils;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.DocumentCommand;
import org.eclipse.jface.text.DocumentRewriteSession;
import org.eclipse.jface.text.DocumentRewriteSessionType;
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.jface.text.rules.FastPartitioner;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.texteditor.ITextEditorExtension3;
import org.eclipse.vjet.eclipse.core.VjetPlugin;
import org.eclipse.vjet.eclipse.internal.ui.scriptdoc.IScanner;
import org.eclipse.vjet.eclipse.internal.ui.scriptdoc.JavaHeuristicScanner;
import org.eclipse.vjet.eclipse.internal.ui.scriptdoc.JavaIndenter;
import org.eclipse.vjet.eclipse.internal.ui.scriptdoc.PublicScanner;
import org.eclipse.vjet.eclipse.ui.VjetPreferenceConstants;
import org.eclipse.vjet.eclipse.ui.VjetUIPlugin;
import org.eclipse.vjet.vjo.meta.VjoKeywords;
/**
* Auto indent strategy sensitive to brackets. Copied from
* JavascriptAutoEditStrategy, extend to support vjo's special syntax
*/
public class VjoAutoEditStrategy extends DefaultIndentLineAutoEditStrategy {
private static final String LINE_SEPARATOR = System
.getProperty("line.separator");
/** The line comment introducer. Value is "{@value} " */
private static final String LINE_COMMENT = "//"; //$NON-NLS-1$
private static final char FORWARD_SLASH = '/';
// private static final String START_MULTILINE_COMMENT = "/**";
//
// private static final String END_MULTILINE_COMMENT = "*/";
private static final char CONTINUE_MULTILINE_COMMENT = '*';
// private static class CompilationUnitInfo {
//
// char[] buffer;
// int delta;
//
// CompilationUnitInfo(char[] buffer, int delta) {
// this.buffer = buffer;
// this.delta = delta;
// }
// }
private boolean fCloseBrace;
// private boolean fCloseComment;
private boolean fIsSmartMode;
private String fPartitioning;
final IScriptProject fProject;
private static IScanner fgScanner = new PublicScanner(false, false, false,
3, null, null, false);
/**
* Creates a new Java auto indent strategy for the given document
* partitioning.
*
* @param partitioning
* the document partitioning
* @param project
* the project to get formatting preferences from, or null to use
* default preferences
*/
public VjoAutoEditStrategy(String partitioning, IScriptProject project) {
fPartitioning = partitioning;
fProject = project;
// fix 2537
this.prefs = new JsPreferenceInterpreter(VjetUIPlugin.getDefault()
.getPreferenceStore());
}
private int getBracketCount(IDocument d, int startOffset, int endOffset,
boolean ignoreCloseBrackets) throws BadLocationException {
int bracketCount = 0;
while (startOffset < endOffset) {
char curr = d.getChar(startOffset);
startOffset++;
switch (curr) {
case FORWARD_SLASH:
if (startOffset < endOffset) {
char next = d.getChar(startOffset);
if (next == CONTINUE_MULTILINE_COMMENT) {
// a comment starts, advance to the comment end
startOffset = getMultiLineCommentEnd(d,
startOffset + 1, endOffset);
} else if (next == FORWARD_SLASH) {
// '//'-comment: nothing to do anymore on this line
startOffset = getLineCommentEnd(d, startOffset + 1,
endOffset);
}
}
break;
case CONTINUE_MULTILINE_COMMENT:
if (startOffset < endOffset) {
char next = d.getChar(startOffset);
if (next == FORWARD_SLASH) {
// we have been in a comment: forget what we read before
bracketCount = 0;
startOffset++;
}
}
break;
case '{':
bracketCount++;
ignoreCloseBrackets = false;
break;
case '}':
if (!ignoreCloseBrackets) {
bracketCount--;
}
break;
case '"':
case '\'':
startOffset = getStringEnd(d, startOffset, endOffset, curr);
break;
default:
}
}
return bracketCount;
}
// ----------- bracket counting
// ------------------------------------------------------
private int getMultiLineCommentEnd(IDocument d, int offset, int endOffset)
throws BadLocationException {
while (offset < endOffset) {
char curr = d.getChar(offset);
offset++;
if (curr == CONTINUE_MULTILINE_COMMENT) {
if (offset < endOffset && d.getChar(offset) == FORWARD_SLASH) {
return offset + 1;
}
}
}
return endOffset;
}
private int getLineCommentEnd(IDocument d, int offset, int endOffset)
throws BadLocationException {
while (offset < endOffset) {
char curr = d.getChar(offset);
offset++;
if (curr == '\n') {
return offset;
}
}
return endOffset;
}
private String getIndentOfLine(IDocument d, int line)
throws BadLocationException {
if (line > -1) {
int start = d.getLineOffset(line);
int end = start + d.getLineLength(line) - 1;
int whiteEnd = findEndOfWhiteSpace(d, start, end);
return d.get(start, whiteEnd - start);
} else {
return ""; //$NON-NLS-1$
}
}
private int getStringEnd(IDocument d, int offset, int endOffset, char ch)
throws BadLocationException {
while (offset < endOffset) {
char curr = d.getChar(offset);
offset++;
if (curr == '\\') {
// ignore escaped characters
offset++;
} else if (curr == ch) {
return offset;
}
}
return endOffset;
}
private void smartIndentAfterClosingBracket(IDocument d, DocumentCommand c) {
if (c.offset == -1 || d.getLength() == 0)
return;
try {
int p = (c.offset == d.getLength() ? c.offset - 1 : c.offset);
int line = d.getLineOfOffset(p);
int start = d.getLineOffset(line);
int whiteend = findEndOfWhiteSpace(d, start, c.offset);
JavaHeuristicScanner scanner = new JavaHeuristicScanner(d);
VjoIndenter indenter = new VjoIndenter(d, scanner, fProject);
// shift only when line does not contain any text up to the closing
// bracket
if (whiteend == c.offset) {
// evaluate the line with the opening bracket that matches out
// closing bracket
int reference = indenter.findReferencePosition(c.offset, false,
true, false, false);
// Add by Oliver. Begin. 2009-10-31. Because the reference value
// is wrong, I recompute the match "{" line information.
// TODO indenter.findReferencePosition is incorrect, need to
// investigate in the future.
String beforeCursor = d.get(0, c.offset).trim();
int lastMatch = beforeCursor.lastIndexOf("{");
if (lastMatch > reference) {
reference = lastMatch;
}
// Add by Oliver. End.
int indLine = d.getLineOfOffset(reference);
if (indLine != -1 && indLine != line) {
// take the indent of the found line
StringBuffer replaceText = new StringBuffer(
getIndentOfLine(d, indLine));
// add the rest of the current line including the just added
// close bracket
replaceText.append(d.get(whiteend, c.offset - whiteend));
replaceText.append(c.text);
// modify document command
c.length += c.offset - start;
c.offset = start;
c.text = replaceText.toString();
}
}
} catch (BadLocationException e) {
DLTKUIPlugin.log(e);
}
}
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) {
VjoIndenter indenter = new VjoIndenter(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) {
DLTKUIPlugin.log(e);
}
}
private void smartIndentAfterNewLine(IDocument d, DocumentCommand c) {
int indexOf = c.text.indexOf('\t');
if (indexOf != -1) {
c.text = c.text.substring(0, indexOf);
}
indexOf = c.text.indexOf(' ');
if (indexOf != -1) {
c.text = c.text.substring(0, indexOf);
}
JavaHeuristicScanner scanner = new JavaHeuristicScanner(d);
VjoIndenter indenter = new VjoIndenter(d, scanner, fProject);
// Add by oliver begin. 2009-10-10. Recalculate the offset value, if the
// current offset lies at the new line. We will use the nearest line's
// indentation as the new line indentation.
// int offset = c.offset;
// if (offset < d.getLength()) {
// try {
// if (d.get(0, offset).endsWith(LINE_SEPARATOR)
// || d.get(0, offset).endsWith("\t")) {
// String beforeCursor = d.get(0, offset).trim();
// offset = beforeCursor.length();
// }
// } catch (BadLocationException e) {
// DLTKUIPlugin.log(e);
// }
// }
// Add by oliver end.
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 (IJavaScriptPartitions.JS_DOC.equals(region.getType()))
start = d.getLineInformationOfOffset(region.getOffset())
.getOffset();
// insert closing brace on new line after an unclosed opening brace
if (closeBrace() && !isBlockBalanced(d)
&&( isAfterOpenBrace(d, c.offset) || !isClosedBrace(d, c.offset))) {
// Comment by Oliver. 2009-09-29. This is an extra indentation.
// buf.append('\t');
c.caretOffset = c.offset + buf.length();
c.shiftsCaret = false;
buf.append('}'); // TODO more testing here
// copy old content of line behind insertion point to new line
// unless we think we are inserting an anonymous type definition
copyContent(d, c, buf, lineEnd, contentStart);
appendReference(d, c, indenter, buf, lineEnd, start);
}
// deleted below code, use VjoDocIndentStrategy instead.
// continue multi-line comment
// else if (isContinueComment(d, c.offset) && closeComment()) {
// copyContent(d, c, buf, lineEnd, contentStart);
// buf.append(CONTINUE_MULTILINE_COMMENT);
// c.caretOffset = c.offset + buf.length();
// c.shiftsCaret = false;
// }
// // close multi-line and single-line comment
// else if (getCountMultiLineComments(d) > 0 && closeComment()) {
// // Modify by Oliver. 2009-03-27. Before the offset of line end
// // is actual position of this line. Now only use the cursor
// // position.
// copyContent(d, c, buf, c.offset, contentStart);
// buf.append(" " + CONTINUE_MULTILINE_COMMENT);
// c.caretOffset = c.offset + buf.length();
// appendReference(d, c, indenter, buf, lineEnd, start);
// buf.append(" " + END_MULTILINE_COMMENT);
//
// // Add by Oliver. 2009-03-27. Add new line after the end of
// // comment block.
// buf.append(TextUtilities.getDefaultLineDelimiter(d));
//
// c.shiftsCaret = false;
// }
// 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;
appendReference(d, c, indenter, buf, lineEnd, start);
}
}
c.text = buf.toString();
} catch (BadLocationException e) {
DLTKUIPlugin.log(e);
}
}
private boolean isAfterOpenBrace(IDocument d, int offset) {
while(true){
try {
char ch = d.getChar(offset);
if(ch=='{'){
return true;
}else if(!Character.isWhitespace(ch)){
return false;
}
offset--;
} catch (BadLocationException e) {
return false;
}
}
}
private void copyContent(IDocument d, DocumentCommand c, StringBuffer buf,
int lineEnd, int contentStart) throws BadLocationException {
if (c.offset == 0
|| !((computeAnonymousPosition(d, c.offset - 1, fPartitioning,
lineEnd) != -1) || isBraceInParenthis(d, c.offset))) {
if (lineEnd - contentStart > 0) {
c.length = lineEnd - c.offset;
buf.append(d.get(contentStart, lineEnd - contentStart)
.toCharArray());
}
}
}
private boolean isBraceInParenthis(IDocument d, int offset) {
String sm = d.get();
String lsm = sm.substring(offset, sm.length());
if (lsm.length() == 0) {
return false;
}
char c = lsm.charAt(0);
return c == ')' || c == '}';
}
private void appendReference(IDocument d, DocumentCommand c,
VjoIndenter indenter, StringBuffer buf, int lineEnd, int start)
throws BadLocationException {
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);
}
private boolean isClosedBrace(IDocument d, int offset) {
int start, startCurlyBrace, end, endBraceCurly;
String sm = d.get();
String lsm = sm.substring(offset, sm.length());
// Don't consider the situation that cursor is at the end of .props or
// .protos.
String tsm = sm.substring(0, offset).trim();
if (tsm.endsWith("({")) {
tsm = tsm.substring(0, tsm.length() - 2).trim();
if (tsm.endsWith(VjoKeywords.PROPS)
|| tsm.endsWith(VjoKeywords.PROTOS)) {
return true;
}
}
// Add by Oliver. for bug:4901
try {
IRegion line = d.getLineInformationOfOffset(offset);
// modify by patrick, fix for JIRA task VFOUR-199
int position = lsm.indexOf(LINE_SEPARATOR);
String afterOffset = position != -1 ? sm.substring(offset, offset
+ position) : lsm;
// end modify
if (afterOffset.trim().startsWith("})")) {
return true;
}
} catch (BadLocationException e1) {
VjetUIPlugin.log(e1);
}
// gets the block and counts braces
start = sm.substring(0, offset).lastIndexOf("({");
// Add by Oliver begin,2009-10-12. if inner type is above with this
// {, the range needs to end with "})" of inner type.
startCurlyBrace = sm.substring(0, offset).lastIndexOf("})");
// if (startCurlyBrace != -1 && startCurlyBrace > start) {
// start = Math.max(startCurlyBrace, start);
// }
// Add by Oliver end.
// Add by Oliver begin,2009-10-12. if inner type is following with this
// {, the range needs to end with "({" of inner type.
endBraceCurly = sm.indexOf("({", offset);
// end = sm.indexOf("})", offset);
// Add by Oliver. Bug:8748
end = getLegalEnd(sm, offset);
if (endBraceCurly != -1 && endBraceCurly > start) {
end = Math.min(endBraceCurly, end);
}
// Add by Oliver end.
if (start < 0 || end < 0) {
// if the next char is "}" will return true, otherwise return false
end = lsm.indexOf("}");
if (end < 0)
return false;
return !(lsm.substring(0, end).trim().length() > 0);
} else {
// counts braces in the block
try {
int count = getBracketCount(d, start + "({".length(), end,
false);
if (count <= 0)
return true;
} catch (BadLocationException e) {
VjetPlugin.getDefault().getLog().log(
new Status(IStatus.ERROR, VjetPlugin.PLUGIN_ID,
IStatus.ERROR,
"Error during counting of braces", e));
}
}
return false;
// String s = lsm.substring(end);
//
// int start = sm.lastIndexOf("function ", offset);
// int lastOpen = sm.lastIndexOf("{", start);
// if (lastOpen == -1)
// lastOpen = 0;
// int lastClosed = sm.lastIndexOf("}", start);
// if (lastClosed == -1)
// lastClosed = 0;
// while (lastOpen > lastClosed) {
// start = sm.lastIndexOf("function ", lastOpen);
// lastOpen = sm.lastIndexOf("{", start);
// if (lastOpen == -1)
// lastOpen = 0;
// lastClosed = sm.lastIndexOf("}", start);
// if (lastClosed == -1)
// lastClosed = 0;
//
// }
//
// int end = sm.indexOf("function ", offset);
// if (end == -1) {
// end = sm.length();
// } else {
// lastOpen = sm.lastIndexOf("{", end);
// if (lastOpen == -1)
// lastOpen = 0;
// lastClosed = sm.lastIndexOf("}", end);
// if (lastClosed == -1)
// lastClosed = 0;
// while (lastOpen > lastClosed) {
// int end2 = sm.indexOf("function ", lastOpen);
// if (end == end2)
// break;
// end = end2;
// lastOpen = sm.lastIndexOf("{", end);
// if (lastOpen == -1)
// lastOpen = 0;
// lastClosed = sm.lastIndexOf("}", end);
// if (lastClosed == -1)
// lastClosed = 0;
//
// }
// }
// int level = 0;
// boolean qm = false;
// char charp = 0;
// for (int a = start; a < end; a++) {
// char charAt = sm.charAt(a);
// if (!qm) {
// if (charAt == '{')
// level++;
// if (charAt == '}')
// level--;
// }
// if (charAt == '"' || charAt == '\'') {
// if (charp != '\\')
// qm = !qm;
// }
// charp = charAt;
// }
// return level <= 0;
}
// private boolean isContinueComment(IDocument d, int offset) {
// String sm = d.get();
// IRegion pos = null;
// try {
// pos = d.getLineInformationOfOffset(offset);
// } catch (BadLocationException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
// // gets position of start of the line where cursor present
// if (pos == null)
// return false;
//
// sm = sm.substring(pos.getOffset(), pos.getOffset() + pos.getLength())
// .trim();
// if (sm.length() < 0 || sm.startsWith(END_MULTILINE_COMMENT))
// return false;
// if (sm.startsWith(CONTINUE_MULTILINE_COMMENT + ""))
// return true;
//
// return false;
// }
//
// private int getCountMultiLineComments(IDocument d) {
// int count = 0;
// int offset = 0;
// String sm = d.get();
//
// while (offset < sm.length() - START_MULTILINE_COMMENT.length() - 1) {
// if (sm.substring(offset, offset + START_MULTILINE_COMMENT.length())
// .equals(START_MULTILINE_COMMENT))
// count++;
// else if (sm.substring(offset,
// offset + END_MULTILINE_COMMENT.length()).equals(
// END_MULTILINE_COMMENT))
// count--;
// offset++;
// }
//
// return count;
// }
public int getLegalEnd(String sm, int offset) {
int end = sm.indexOf("})", offset);
if (end != -1 && end < sm.length()) {
if (sm.substring(end).startsWith("});")) {
end = getLegalEnd(sm, end + "});".length());
} else {
return end;
}
}
return end;
}
/**
* 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;
while (true) {
int startScan = closingParen + 1;
closingParen = scanner.scanForward(startScan, scanTo, ')');
if (closingParen == -1)
break;
int 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;
}
/**
* 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;
}
/**
* Checks whether the content of <code>document</code> in the range (
* <code>offset</code>, <code>length</code>) contains the <code>new</code>
* keyword.
*
* @param document
* the document being modified
* @param offset
* the first character position in <code>document</code> to be
* considered
* @param length
* the length of the character range to be considered
* @param partitioning
* the document partitioning
* @return <code>true</code> if the specified character range contains a
* <code>new</code> keyword, <code>false</code> otherwise.
*/
private static boolean isNewMatch(IDocument document, int offset,
int length, String partitioning) {
Assert.isTrue(length >= 0);
Assert.isTrue(offset >= 0);
Assert.isTrue(offset + length < document.getLength() + 1);
try {
String text = document.get(offset, length);
int pos = text.indexOf("new"); //$NON-NLS-1$
while (pos != -1
&& !isDefaultPartition(document, pos + offset, partitioning))
pos = text.indexOf("new", pos + 2); //$NON-NLS-1$
if (pos < 0)
return false;
if (pos != 0
&& Character.isJavaIdentifierPart(text.charAt(pos - 1)))
return false;
if (pos + 3 < length
&& Character.isJavaIdentifierPart(text.charAt(pos + 3)))
return false;
return true;
} catch (BadLocationException e) {
}
return false;
}
/**
* 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 position
* the first character position in <code>document</code> to be
* considered
* @param partitioning
* the document partitioning
* @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;
}
/**
* Checks whether <code>position</code> resides in a default (Java)
* partition of <code>document</code>.
*
* @param document
* the document being modified
* @param position
* the position to be checked
* @param partitioning
* the document partitioning
* @return <code>true</code> if <code>position</code> is in the default
* partition of <code>document</code>, <code>false</code> otherwise
*/
private static boolean isDefaultPartition(IDocument document, int position,
String partitioning) {
Assert.isTrue(position >= 0);
Assert.isTrue(position <= document.getLength());
try {
ITypedRegion region = TextUtilities.getPartition(document,
partitioning, position, false);
return region.getType().equals(IDocument.DEFAULT_CONTENT_TYPE);
} catch (BadLocationException e) {
}
return false;
}
/**
* Installs a java partitioner with <code>document</code>.
*
* @param document
* the document
*/
private static void installJavaStuff(Document document) {
String[] types = new String[] { IJavaScriptPartitions.JS_DOC,
IJavaScriptPartitions.JS_SINGLE_COMMENT,
IJavaScriptPartitions.JS_MULTI_COMMENT,
IJavaScriptPartitions.JS_PARTITIONING,
IJavaScriptPartitions.JS_STRING, IDocument.DEFAULT_CONTENT_TYPE };
FastPartitioner partitioner = new FastPartitioner(
new JavascriptPartitionScanner(), types);
partitioner.connect(document);
document.setDocumentPartitioner(IJavaScriptPartitions.JS_PARTITIONING,
partitioner);
}
/**
* Installs a java partitioner with <code>document</code>.
*
* @param document
* the document
*/
private static void removeJavaStuff(Document document) {
document.setDocumentPartitioner(IJavaScriptPartitions.JS_PARTITIONING,
null);
}
private void smartPaste(IDocument document, DocumentCommand command) {
int newOffset = command.offset;
int newLength = command.length;
String newText = command.text;
try {
JavaHeuristicScanner scanner = new JavaHeuristicScanner(document);
VjoIndenter indenter = new VjoIndenter(document, scanner, fProject);
int offset = newOffset;
// reference position to get the indent from
int refOffset = indenter.findReferencePosition(offset);
if (refOffset == JavaHeuristicScanner.NOT_FOUND)
return;
int peerOffset = getPeerPosition(document, command);
peerOffset = indenter.findReferencePosition(peerOffset);
refOffset = Math.min(refOffset, peerOffset);
// eat any WS before the insertion to the beginning of the line
int firstLine = 1; // don't format the first line per default, as
// it has other content before it
IRegion line = document.getLineInformationOfOffset(offset);
String notSelected = document.get(line.getOffset(), offset
- line.getOffset());
if (notSelected.trim().length() == 0) {
newLength += notSelected.length();
newOffset = line.getOffset();
firstLine = 0;
}
// prefix: the part we need for formatting but won't paste
IRegion refLine = document.getLineInformationOfOffset(refOffset);
String prefix = document.get(refLine.getOffset(), newOffset
- refLine.getOffset());
/**
* modify by kevin, 2009-10-31. the modification(2009-10-09) break
* some features, refine to fix bug 2537
*/
// Add by Oliver. 2009-10-09. We remove the new line if we paste the
// text after new line. Ignore the scenario that last line end with
// comma.
if (prefix.endsWith(LINE_SEPARATOR)
&& !prefix.endsWith("{" + LINE_SEPARATOR)
&& !prefix.endsWith(";" + LINE_SEPARATOR)
&& !prefix.trim().endsWith(",")) {
offset = document.get(0, offset).trim().length();
refLine = document.getLineInformationOfOffset(offset);
prefix = document.get(refLine.getOffset(), offset
- refLine.getOffset());
}
// handle the indentation computation inside a temporary document
Document temp = new Document(prefix + newText);
DocumentRewriteSession session = temp
.startRewriteSession(DocumentRewriteSessionType.STRICTLY_SEQUENTIAL);
scanner = new JavaHeuristicScanner(temp);
indenter = new VjoIndenter(temp, scanner, fProject);
installJavaStuff(temp);
// indent the first and second line
// compute the relative indentation difference from the second line
// (as the first might be partially selected) and use the value to
// indent all other lines.
boolean isIndentDetected = false;
StringBuffer addition = new StringBuffer();
int insertLength = 0;
int first = document.computeNumberOfLines(prefix) + firstLine; // don't
// format
// first
// line
int lines = temp.getNumberOfLines();
int tabLength = getVisualTabLengthPreference();
boolean changed = false;
for (int l = first; l < lines; l++) { // we don't change the
// number of lines while
// adding indents
IRegion r = temp.getLineInformation(l);
int lineOffset = r.getOffset();
int lineLength = r.getLength();
if (lineLength == 0) // don't modify empty lines
continue;
if (!isIndentDetected) {
// indent the first pasted line
String current = getCurrentIndent(temp, l);
StringBuffer correct = indenter
.computeIndentation(lineOffset);
if (correct == null)
return; // bail out
insertLength = subtractIndent(correct, current, addition,
tabLength);
if (l != first
&& temp.get(lineOffset, lineLength).trim().length() != 0) {
isIndentDetected = true;
if (insertLength == 0) {
// no adjustment needed, bail out
if (firstLine == 0) {
// but we still need to adjust the first line
command.offset = newOffset;
command.length = newLength;
if (changed)
break; // still need to get the leading
// indent of the first line
}
return;
}
removeJavaStuff(temp);
} else {
changed = insertLength != 0;
}
}
// relatively indent all pasted lines
if (insertLength > 0)
addIndent(temp, l, addition, tabLength);
else if (insertLength < 0)
cutIndent(temp, l, -insertLength, tabLength);
}
temp.stopRewriteSession(session);
newText = temp.get(prefix.length(), temp.getLength()
- prefix.length());
command.offset = newOffset;
command.length = newLength;
command.text = newText;
} catch (BadLocationException e) {
DLTKUIPlugin.log(e);
}
}
/**
* Returns the indentation of the line <code>line</code> in
* <code>document</code>. The returned string may contain pairs of leading
* slashes that are considered part of the indentation. The space before the
* asterisk in a javadoc-like comment is not considered part of the
* indentation.
*
* @param document
* the document
* @param line
* the line
* @return the indentation of <code>line</code> in <code>document</code>
* @throws BadLocationException
* if the document is changed concurrently
*/
private static String getCurrentIndent(Document document, int line)
throws BadLocationException {
IRegion region = document.getLineInformation(line);
int from = region.getOffset();
int endOffset = region.getOffset() + region.getLength();
// go behind line comments
int to = from;
while (to < endOffset - 2 && document.get(to, 2).equals(LINE_COMMENT))
to += 2;
while (to < endOffset) {
char ch = document.getChar(to);
if (!Character.isWhitespace(ch))
break;
to++;
}
// don't count the space before javadoc like, asterisk-style comment
// lines
if (to > from && to < endOffset - 1
&& document.get(to - 1, 2).equals(" *")) { //$NON-NLS-1$
String type = TextUtilities.getContentType(document,
IJavaScriptPartitions.JS_PARTITIONING, to, true);
if (type.equals(IJavaScriptPartitions.JS_DOC)
|| type.equals(IJavaScriptPartitions.JS_SINGLE_COMMENT)
|| type.equals(IJavaScriptPartitions.JS_MULTI_COMMENT))
to--;
}
return document.get(from, to - from);
}
/**
* Computes the difference of two indentations and returns the difference in
* length of current and correct. If the return value is positive,
* <code>addition</code> is initialized with a substring of that length of
* <code>correct</code>.
*
* @param correct
* the correct indentation
* @param current
* the current indentation (might contain non-whitespace)
* @param difference
* a string buffer - if the return value is positive, it will be
* cleared and set to the substring of <code>current</code> of
* that length
* @param tabLength
* the length of a tab
* @return the difference in length of <code>correct</code> and
* <code>current</code>
*/
private int subtractIndent(CharSequence correct, CharSequence current,
StringBuffer difference, int tabLength) {
int c1 = computeVisualLength(correct, tabLength);
int c2 = computeVisualLength(current, tabLength);
int diff = c1 - c2;
if (diff <= 0)
return diff;
difference.setLength(0);
int len = 0, i = 0;
while (len < diff) {
char c = correct.charAt(i++);
difference.append(c);
len += computeVisualLength(c, tabLength);
}
return diff;
}
/**
* Indents line <code>line</code> in <code>document</code> with
* <code>indent</code>. Leaves leading comment signs alone.
*
* @param document
* the document
* @param line
* the line
* @param indent
* the indentation to insert
* @param tabLength
* the length of a tab
* @throws BadLocationException
* on concurrent document modification
*/
private void addIndent(Document document, int line, CharSequence indent,
int tabLength) throws BadLocationException {
IRegion region = document.getLineInformation(line);
int insert = region.getOffset();
int endOffset = region.getOffset() + region.getLength();
// Compute insert after all leading line comment markers
int newInsert = insert;
while (newInsert < endOffset - 2
&& document.get(newInsert, 2).equals(LINE_COMMENT))
newInsert += 2;
// Heuristic to check whether it is commented code or just a comment
if (newInsert > insert) {
int whitespaceCount = 0;
int i = newInsert;
while (i < endOffset - 1) {
char ch = document.get(i, 1).charAt(0);
if (!Character.isWhitespace(ch))
break;
whitespaceCount = whitespaceCount
+ computeVisualLength(ch, tabLength);
i++;
}
if (whitespaceCount != 0 && whitespaceCount >= 4)
insert = newInsert;
}
// Insert indent
document.replace(insert, 0, indent.toString());
}
/**
* Cuts the visual equivalent of <code>toDelete</code> characters out of the
* indentation of line <code>line</code> in <code>document</code>. Leaves
* leading comment signs alone.
*
* @param document
* the document
* @param line
* the line
* @param toDelete
* the number of space equivalents to delete
* @param tabLength
* the length of a tab
* @throws BadLocationException
* on concurrent document modification
*/
private void cutIndent(Document document, int line, int toDelete,
int tabLength) throws BadLocationException {
IRegion region = document.getLineInformation(line);
int from = region.getOffset();
int endOffset = region.getOffset() + region.getLength();
// go behind line comments
while (from < endOffset - 2
&& document.get(from, 2).equals(LINE_COMMENT))
from += 2;
int to = from;
while (toDelete > 0 && to < endOffset) {
char ch = document.getChar(to);
if (!Character.isWhitespace(ch))
break;
toDelete -= computeVisualLength(ch, tabLength);
if (toDelete >= 0)
to++;
else
break;
}
document.replace(from, to - from, ""); //$NON-NLS-1$
}
/**
* Returns the visual length of a given <code>CharSequence</code> taking
* into account the visual tabulator length.
*
* @param seq
* the string to measure
* @param tabLength
* the length of a tab
* @return the visual length of <code>seq</code>
*/
private int computeVisualLength(CharSequence seq, int tabLength) {
int size = 0;
for (int i = 0; i < seq.length(); i++) {
char ch = seq.charAt(i);
if (ch == '\t') {
if (tabLength != 0)
size += tabLength - size % tabLength;
// else: size stays the same
} else {
size++;
}
}
return size;
}
/**
* Returns the visual length of a given character taking into account the
* visual tabulator length.
*
* @param ch
* the character to measure
* @param tabLength
* the length of a tab
* @return the visual length of <code>ch</code>
*/
private int computeVisualLength(char ch, int tabLength) {
if (ch == '\t')
return tabLength;
else
return 1;
}
/**
* The preference setting for the visual tabulator display.
*
* @return the number of spaces displayed for a tabulator in the editor
*/
private int getVisualTabLengthPreference() {
return 4;
}
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:
VjoIndenter indenter = new VjoIndenter(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
}
}
}
/**
* Skips the scope opened by <code>token</code> in <code>document</code>,
* returns either the position of the
*
* @param pos
* @param token
* @return the position after the scope
*/
private static int skipScope(JavaHeuristicScanner scanner, int pos,
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 = pos;
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;
}
}
}
private boolean isLineDelimiter(IDocument document, String text) {
String[] delimiters = document.getLegalLineDelimiters();
if (delimiters != null)
return (TextUtilities.startsWith(delimiters, text) > -1)
&& text.trim().length() == 0;
return false;
}
/**
* Processes command in work with brackets, strings, etc
*
* @param d
* @param c
*/
JsPreferenceInterpreter prefs;
private void autoClose(IDocument d, DocumentCommand c) {
if (c.offset == -1)
return;
try {
if (d.getChar(c.offset - 1) == '\\')
return;
} catch (BadLocationException e1) {
}
if ('\"' == c.text.charAt(0) && !prefs.closeStrings())
return;
if ('\'' == c.text.charAt(0) && !prefs.closeStrings())
return;
if (!prefs.closeBrackets()
&& ('[' == c.text.charAt(0) || '(' == c.text.charAt(0) || '{' == c.text
.charAt(0)))
return;
try {
switch (c.text.charAt(0)) {
case '\"':
case '\'':
// if we close existing quote, do nothing
if ('\"' == c.text.charAt(0) && c.offset > 0
&& "\"".equals(d.get(c.offset - 1, 1)))
return;
if ('\'' == c.text.charAt(0) && c.offset > 0
&& "\'".equals(d.get(c.offset - 1, 1)))
return;
if (c.offset != d.getLength()
&& c.text.charAt(0) == d.get(c.offset, 1).charAt(0))
c.text = "";
else {
c.text += c.text;
// dont set the length, because of the length > 0 then a
// selection has to be replaced
// c.length = 0;
}
c.shiftsCaret = false;
c.caretOffset = c.offset + 1;
break;
case '(':
case '{':
case '[':
// check partition
if (AutoEditUtils.getRegionType(d, fPartitioning, c.offset) != IDocument.DEFAULT_CONTENT_TYPE)
return;
if (c.offset != d.getLength()
&& c.text.charAt(0) == d.get(c.offset, 1).charAt(0))
return;
try { // in class closing
String regex = "^\\s*class\\s+.*";
String regex2 = ".*\\(.*\\).*";
int start = d.getLineOffset(d.getLineOfOffset(c.offset));
String curLine = d.get(start, c.offset - start);
if (Pattern.matches(regex, curLine)
&& !Pattern.matches(regex2, curLine)) {
c.text = "():";
c.shiftsCaret = false;
c.caretOffset = c.offset + 1;
return;
}
} catch (BadLocationException e) {
}
// add closing peer
c.text = c.text + AutoEditUtils.getBracePair(c.text.charAt(0));
// dont set the length, because of the length > 0 then a
// selection has to be replaced
// c.length = 0;
c.shiftsCaret = false;
c.caretOffset = c.offset + 1;
break;
case '}':
case ']':
case ')':
// check partition
if (AutoEditUtils.getRegionType(d, fPartitioning, c.offset) != IDocument.DEFAULT_CONTENT_TYPE)
return;
if (!prefs.closeBrackets())
return;
// if we already have bracket we should jump over it
if (c.offset != d.getLength()
&& c.text.charAt(0) == d.get(c.offset, 1).charAt(0)) {
c.text = "";
c.shiftsCaret = false;
c.caretOffset = c.offset + 1;
return;
}
break;
}
} catch (BadLocationException e) {
e.printStackTrace();
}
}
private void smartIndentOnKeypress(IDocument document,
DocumentCommand command) {
switch (command.text.charAt(0)) {
case '}':
smartIndentAfterClosingBracket(document, command);
break;
case '{':
smartIndentAfterOpeningBracket(document, command);
break;
case '\"':
case '\'':
case '(':
case '[':
autoClose(document, command);
break;
case 'e':
smartIndentUponE(document, command);
break;
}
}
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) {
VjoIndenter indenter = new VjoIndenter(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) {
DLTKUIPlugin.log(e);
}
}
/*
* @see
* org.eclipse.jface.text.IAutoIndentStrategy#customizeDocumentCommand(org
* .eclipse.jface.text.IDocument, org.eclipse.jface.text.DocumentCommand)
*/
public void customizeDocumentCommand(IDocument d, DocumentCommand c) {
if (c.doit == false)
return;
clearCachedValues();
if (!isSmartMode()) {
super.customizeDocumentCommand(d, c);
return;
}
if (c.length == 0 && c.text != null && isLineDelimiter(d, c.text))
smartIndentAfterNewLine(d, c);
else if (c.text.length() == 1)
smartIndentOnKeypress(d, c);
else if (c.text.length() > 1
&& getPreferenceStore().getBoolean(
PreferenceConstants.EDITOR_SMART_PASTE))
smartPaste(d, c); // no smart backspace for paste
}
private static IPreferenceStore getPreferenceStore() {
return VjetUIPlugin.getDefault().getPreferenceStore();
}
private boolean closeBrace() {
return fCloseBrace;
}
private boolean isSmartMode() {
return fIsSmartMode;
}
// private boolean closeComment() {
// return fCloseComment;
// }
private void clearCachedValues() {
IPreferenceStore preferenceStore = getPreferenceStore();
fCloseBrace = preferenceStore
.getBoolean(VjetPreferenceConstants.EDITOR_CLOSE_BRACES);
// fCloseComment = preferenceStore
// .getBoolean(VjetPreferenceConstants.EDITOR_CLOSE_COMMENTS);
fIsSmartMode = computeSmartMode();
}
private boolean computeSmartMode() {
IWorkbenchPage page = DLTKUIPlugin.getActivePage();
if (page != null) {
IEditorPart part = page.getActiveEditor();
if (part instanceof ITextEditorExtension3) {
ITextEditorExtension3 extension = (ITextEditorExtension3) part;
return extension.getInsertMode() == ITextEditorExtension3.SMART_INSERT;
}
}
return false;
}
// private static CompilationUnitInfo getCompilationUnitForMethod(
// IDocument document, int offset, String partitioning) {
// 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) {
// DLTKUIPlugin.log(e);
// }
//
// return null;
// }
/**
* Returns true if all blocks are balanced,
*
* @param document
* @return true if blocks are balanced
*/
private static boolean isBlockBalanced(IDocument document) {
JavaHeuristicScanner scanner = new JavaHeuristicScanner(document);
int start = 0;
int end = document.getLength();
int numOfBlocks = 0;
while (true) {
int token = scanner.nextToken(start, end);
if(token==JavaHeuristicScanner.NOT_FOUND){
break;
}
switch (token) {
case JavaHeuristicScanner.TokenLBRACE:
numOfBlocks++;
break;
case JavaHeuristicScanner.TokenRBRACE:
numOfBlocks--;
break;
}
start = scanner.getPosition();
}
return numOfBlocks==0;
}
// private static IRegion getToken(IDocument document, IRegion scanRegion,
// int tokenId) {
//
// try {
//
// final String source = document.get(scanRegion.getOffset(),
// scanRegion.getLength());
//
// fgScanner.setSource(source.toCharArray());
//
// int id = fgScanner.getNextToken();
// while (id != ITerminalSymbols.TokenNameEOF && id != tokenId)
// id = fgScanner.getNextToken();
//
// if (id == ITerminalSymbols.TokenNameEOF)
// return null;
//
// int tokenOffset = fgScanner.getCurrentTokenStartPosition();
// int tokenLength = fgScanner.getCurrentTokenEndPosition() + 1
// - tokenOffset; // inclusive end
// return new Region(tokenOffset + scanRegion.getOffset(), tokenLength);
//
// } catch (InvalidInputException x) {
// return null;
// } catch (BadLocationException x) {
// return null;
// }
// }
}