1 12 package org.eclipse.jdt.internal.ui.text.java; 13 14 15 import org.eclipse.core.runtime.Assert; 16 17 import org.eclipse.jface.preference.IPreferenceStore; 18 19 import org.eclipse.jface.text.BadLocationException; 20 import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy; 21 import org.eclipse.jface.text.Document; 22 import org.eclipse.jface.text.DocumentCommand; 23 import org.eclipse.jface.text.DocumentRewriteSession; 24 import org.eclipse.jface.text.DocumentRewriteSessionType; 25 import org.eclipse.jface.text.IDocument; 26 import org.eclipse.jface.text.IRegion; 27 import org.eclipse.jface.text.ITypedRegion; 28 import org.eclipse.jface.text.Region; 29 import org.eclipse.jface.text.TextUtilities; 30 import org.eclipse.jface.text.rules.FastPartitioner; 31 32 import org.eclipse.ui.IEditorPart; 33 import org.eclipse.ui.IWorkbenchPage; 34 import org.eclipse.ui.texteditor.ITextEditorExtension3; 35 36 import org.eclipse.jdt.core.IJavaProject; 37 import org.eclipse.jdt.core.ToolFactory; 38 import org.eclipse.jdt.core.compiler.IProblem; 39 import org.eclipse.jdt.core.compiler.IScanner; 40 import org.eclipse.jdt.core.compiler.ITerminalSymbols; 41 import org.eclipse.jdt.core.compiler.InvalidInputException; 42 import org.eclipse.jdt.core.dom.AST; 43 import org.eclipse.jdt.core.dom.ASTNode; 44 import org.eclipse.jdt.core.dom.ASTParser; 45 import org.eclipse.jdt.core.dom.CompilationUnit; 46 import org.eclipse.jdt.core.dom.DoStatement; 47 import org.eclipse.jdt.core.dom.Expression; 48 import org.eclipse.jdt.core.dom.ForStatement; 49 import org.eclipse.jdt.core.dom.IfStatement; 50 import org.eclipse.jdt.core.dom.Statement; 51 import org.eclipse.jdt.core.dom.WhileStatement; 52 53 import org.eclipse.jdt.internal.corext.dom.NodeFinder; 54 import org.eclipse.jdt.internal.corext.util.CodeFormatterUtil; 55 56 import org.eclipse.jdt.ui.PreferenceConstants; 57 import org.eclipse.jdt.ui.text.IJavaPartitions; 58 59 import org.eclipse.jdt.internal.ui.JavaPlugin; 60 import org.eclipse.jdt.internal.ui.text.FastJavaPartitionScanner; 61 import org.eclipse.jdt.internal.ui.text.JavaHeuristicScanner; 62 import org.eclipse.jdt.internal.ui.text.JavaIndenter; 63 import org.eclipse.jdt.internal.ui.text.Symbols; 64 65 68 public class JavaAutoIndentStrategy extends DefaultIndentLineAutoEditStrategy { 69 70 71 private static final String LINE_COMMENT= "//"; 73 private static class CompilationUnitInfo { 74 75 char[] buffer; 76 int delta; 77 78 CompilationUnitInfo(char[] buffer, int delta) { 79 this.buffer= buffer; 80 this.delta= delta; 81 } 82 } 83 84 85 private boolean fCloseBrace; 86 private boolean fIsSmartMode; 87 88 private String fPartitioning; 89 private final IJavaProject fProject; 90 private static IScanner fgScanner= ToolFactory.createScanner(false, false, false, false); 91 92 98 public JavaAutoIndentStrategy(String partitioning, IJavaProject project) { 99 fPartitioning= partitioning; 100 fProject= project; 101 } 102 103 private int getBracketCount(IDocument d, int startOffset, int endOffset, boolean ignoreCloseBrackets) throws BadLocationException { 104 105 int bracketCount= 0; 106 while (startOffset < endOffset) { 107 char curr= d.getChar(startOffset); 108 startOffset++; 109 switch (curr) { 110 case '/' : 111 if (startOffset < endOffset) { 112 char next= d.getChar(startOffset); 113 if (next == '*') { 114 startOffset= getCommentEnd(d, startOffset + 1, endOffset); 116 } else if (next == '/') { 117 startOffset= endOffset; 119 } 120 } 121 break; 122 case '*' : 123 if (startOffset < endOffset) { 124 char next= d.getChar(startOffset); 125 if (next == '/') { 126 bracketCount= 0; 128 startOffset++; 129 } 130 } 131 break; 132 case '{' : 133 bracketCount++; 134 ignoreCloseBrackets= false; 135 break; 136 case '}' : 137 if (!ignoreCloseBrackets) { 138 bracketCount--; 139 } 140 break; 141 case '"' : 142 case '\'' : 143 startOffset= getStringEnd(d, startOffset, endOffset, curr); 144 break; 145 default : 146 } 147 } 148 return bracketCount; 149 } 150 151 153 private int getCommentEnd(IDocument d, int offset, int endOffset) throws BadLocationException { 154 while (offset < endOffset) { 155 char curr= d.getChar(offset); 156 offset++; 157 if (curr == '*') { 158 if (offset < endOffset && d.getChar(offset) == '/') { 159 return offset + 1; 160 } 161 } 162 } 163 return endOffset; 164 } 165 166 private String getIndentOfLine(IDocument d, int line) throws BadLocationException { 167 if (line > -1) { 168 int start= d.getLineOffset(line); 169 int end= start + d.getLineLength(line) - 1; 170 int whiteEnd= findEndOfWhiteSpace(d, start, end); 171 return d.get(start, whiteEnd - start); 172 } else { 173 return ""; } 175 } 176 177 private int getStringEnd(IDocument d, int offset, int endOffset, char ch) throws BadLocationException { 178 while (offset < endOffset) { 179 char curr= d.getChar(offset); 180 offset++; 181 if (curr == '\\') { 182 offset++; 184 } else if (curr == ch) { 185 return offset; 186 } 187 } 188 return endOffset; 189 } 190 191 private void smartIndentAfterClosingBracket(IDocument d, DocumentCommand c) { 192 if (c.offset == -1 || d.getLength() == 0) 193 return; 194 195 try { 196 int p= (c.offset == d.getLength() ? c.offset - 1 : c.offset); 197 int line= d.getLineOfOffset(p); 198 int start= d.getLineOffset(line); 199 int whiteend= findEndOfWhiteSpace(d, start, c.offset); 200 201 JavaHeuristicScanner scanner= new JavaHeuristicScanner(d); 202 JavaIndenter indenter= new JavaIndenter(d, scanner, fProject); 203 204 if (whiteend == c.offset) { 206 int reference= indenter.findReferencePosition(c.offset, false, true, false, false); 208 int indLine= d.getLineOfOffset(reference); 209 if (indLine != -1 && indLine != line) { 210 StringBuffer replaceText= new StringBuffer (getIndentOfLine(d, indLine)); 212 replaceText.append(d.get(whiteend, c.offset - whiteend)); 214 replaceText.append(c.text); 215 c.length += c.offset - start; 217 c.offset= start; 218 c.text= replaceText.toString(); 219 } 220 } 221 } catch (BadLocationException e) { 222 JavaPlugin.log(e); 223 } 224 } 225 226 private void smartIndentAfterOpeningBracket(IDocument d, DocumentCommand c) { 227 if (c.offset < 1 || d.getLength() == 0) 228 return; 229 230 JavaHeuristicScanner scanner= new JavaHeuristicScanner(d); 231 232 int p= (c.offset == d.getLength() ? c.offset - 1 : c.offset); 233 234 try { 235 int line= d.getLineOfOffset(p); 237 int lineOffset= d.getLineOffset(line); 238 239 if (d.get(lineOffset, p - lineOffset).trim().length() != 0) 241 return; 242 243 int pos= scanner.findNonWhitespaceBackward(p, JavaHeuristicScanner.UNBOUND); 245 if (pos == -1) 246 return; 247 int lastLine= d.getLineOfOffset(pos); 248 249 if (lastLine < line) { 251 252 JavaIndenter indenter= new JavaIndenter(d, scanner, fProject); 253 StringBuffer indent= indenter.computeIndentation(p, true); 254 String toDelete= d.get(lineOffset, c.offset - lineOffset); 255 if (indent != null && !indent.toString().equals(toDelete)) { 256 c.text= indent.append(c.text).toString(); 257 c.length += c.offset - lineOffset; 258 c.offset= lineOffset; 259 } 260 } 261 262 } catch (BadLocationException e) { 263 JavaPlugin.log(e); 264 } 265 266 } 267 268 private void smartIndentAfterNewLine(IDocument d, DocumentCommand c) { 269 JavaHeuristicScanner scanner= new JavaHeuristicScanner(d); 270 JavaIndenter indenter= new JavaIndenter(d, scanner, fProject); 271 StringBuffer indent= indenter.computeIndentation(c.offset); 272 if (indent == null) 273 indent= new StringBuffer (); 274 275 int docLength= d.getLength(); 276 if (c.offset == -1 || docLength == 0) 277 return; 278 279 try { 280 int p= (c.offset == docLength ? c.offset - 1 : c.offset); 281 int line= d.getLineOfOffset(p); 282 283 StringBuffer buf= new StringBuffer (c.text + indent); 284 285 286 IRegion reg= d.getLineInformation(line); 287 int lineEnd= reg.getOffset() + reg.getLength(); 288 289 int contentStart= findEndOfWhiteSpace(d, c.offset, lineEnd); 290 c.length= Math.max(contentStart - c.offset, 0); 291 292 int start= reg.getOffset(); 293 ITypedRegion region= TextUtilities.getPartition(d, fPartitioning, start, true); 294 if (IJavaPartitions.JAVA_DOC.equals(region.getType())) 295 start= d.getLineInformationOfOffset(region.getOffset()).getOffset(); 296 297 if (getBracketCount(d, start, c.offset, true) > 0 && closeBrace() && !isClosed(d, c.offset, c.length)) { 299 c.caretOffset= c.offset + buf.length(); 300 c.shiftsCaret= false; 301 302 if (c.offset == 0 || !(computeAnonymousPosition(d, c.offset - 1, fPartitioning, lineEnd) != -1)) { 305 if (lineEnd - contentStart > 0) { 306 c.length= lineEnd - c.offset; 307 buf.append(d.get(contentStart, lineEnd - contentStart).toCharArray()); 308 } 309 } 310 311 buf.append(TextUtilities.getDefaultLineDelimiter(d)); 312 StringBuffer reference= null; 313 int nonWS= findEndOfWhiteSpace(d, start, lineEnd); 314 if (nonWS < c.offset && d.getChar(nonWS) == '{') 315 reference= new StringBuffer (d.get(start, nonWS - start)); 316 else 317 reference= indenter.getReferenceIndentation(c.offset); 318 if (reference != null) 319 buf.append(reference); 320 buf.append('}'); 321 } 322 else if (c.offset > start && contentStart < lineEnd && d.getChar(contentStart) == '}') { 324 int firstCharPos= scanner.findNonWhitespaceBackward(c.offset - 1, start); 325 if (firstCharPos != JavaHeuristicScanner.NOT_FOUND && d.getChar(firstCharPos) == '{') { 326 c.caretOffset= c.offset + buf.length(); 327 c.shiftsCaret= false; 328 329 StringBuffer reference= null; 330 int nonWS= findEndOfWhiteSpace(d, start, lineEnd); 331 if (nonWS < c.offset && d.getChar(nonWS) == '{') 332 reference= new StringBuffer (d.get(start, nonWS - start)); 333 else 334 reference= indenter.getReferenceIndentation(c.offset); 335 336 buf.append(TextUtilities.getDefaultLineDelimiter(d)); 337 338 if (reference != null) 339 buf.append(reference); 340 } 341 } 342 c.text= buf.toString(); 343 344 } catch (BadLocationException e) { 345 JavaPlugin.log(e); 346 } 347 } 348 349 359 private static int computeAnonymousPosition(IDocument document, int offset, String partitioning, int max) { 360 364 JavaHeuristicScanner scanner= new JavaHeuristicScanner(document); 365 int pos= offset; 366 int length= max; 367 int scanTo= scanner.scanForward(pos, length, '}'); 368 if (scanTo == -1) 369 scanTo= length; 370 371 int closingParen= findClosingParenToLeft(scanner, pos) - 1; 372 373 while (true) { 374 int startScan= closingParen + 1; 375 closingParen= scanner.scanForward(startScan, scanTo, ')'); 376 if (closingParen == -1) 377 break; 378 379 int openingParen= scanner.findOpeningPeer(closingParen - 1, '(', ')'); 380 381 if (openingParen < 1) 383 break; 384 385 if (openingParen > pos) 387 continue; 388 389 if (looksLikeAnonymousClassDef(document, partitioning, scanner, openingParen - 1)) 390 return closingParen + 1; 391 392 } 393 394 return -1; 395 } 396 397 405 private static int findClosingParenToLeft(JavaHeuristicScanner scanner, int position) { 406 if (position < 1) 407 return position; 408 409 if (scanner.previousToken(position - 1, JavaHeuristicScanner.UNBOUND) == Symbols.TokenRPAREN) 410 return scanner.getPosition() + 1; 411 return position; 412 } 413 414 424 private static boolean isNewMatch(IDocument document, int offset, int length, String partitioning) { 425 Assert.isTrue(length >= 0); 426 Assert.isTrue(offset >= 0); 427 Assert.isTrue(offset + length < document.getLength() + 1); 428 429 try { 430 String text= document.get(offset, length); 431 int pos= text.indexOf("new"); 433 while (pos != -1 && !isDefaultPartition(document, pos + offset, partitioning)) 434 pos= text.indexOf("new", pos + 2); 436 if (pos < 0) 437 return false; 438 439 if (pos != 0 && Character.isJavaIdentifierPart(text.charAt(pos - 1))) 440 return false; 441 442 if (pos + 3 < length && Character.isJavaIdentifierPart(text.charAt(pos + 3))) 443 return false; 444 445 return true; 446 447 } catch (BadLocationException e) { 448 } 449 return false; 450 } 451 452 462 private static boolean looksLikeAnonymousClassDef(IDocument document, String partitioning, JavaHeuristicScanner scanner, int position) { 463 int previousCommaParenEqual= scanner.scanBackward(position - 1, JavaHeuristicScanner.UNBOUND, new char[] {',', '(', '='}); 464 if (previousCommaParenEqual == -1 || position < previousCommaParenEqual + 5) return false; 466 467 if (isNewMatch(document, previousCommaParenEqual + 1, position - previousCommaParenEqual - 2, partitioning)) 468 return true; 469 470 return false; 471 } 472 473 481 private static boolean isDefaultPartition(IDocument document, int position, String partitioning) { 482 Assert.isTrue(position >= 0); 483 Assert.isTrue(position <= document.getLength()); 484 485 try { 486 ITypedRegion region= TextUtilities.getPartition(document, partitioning, position, false); 487 return region.getType().equals(IDocument.DEFAULT_CONTENT_TYPE); 488 489 } catch (BadLocationException e) { 490 } 491 492 return false; 493 } 494 495 private boolean isClosed(IDocument document, int offset, int length) { 496 497 CompilationUnitInfo info= getCompilationUnitForMethod(document, offset, fPartitioning); 498 if (info == null) 499 return false; 500 501 CompilationUnit compilationUnit= null; 502 try { 503 ASTParser parser= ASTParser.newParser(AST.JLS3); 504 parser.setSource(info.buffer); 505 compilationUnit= (CompilationUnit) parser.createAST(null); 506 } catch (ArrayIndexOutOfBoundsException x) { 507 return false; 509 } 510 511 IProblem[] problems= compilationUnit.getProblems(); 512 for (int i= 0; i != problems.length; ++i) { 513 if (problems[i].getID() == IProblem.UnmatchedBracket) 514 return true; 515 } 516 517 final int relativeOffset= offset - info.delta; 518 519 ASTNode node= NodeFinder.perform(compilationUnit, relativeOffset, length); 520 521 if (length == 0) { 522 while (node != null && (relativeOffset == node.getStartPosition() || relativeOffset == node.getStartPosition() + node.getLength())) 523 node= node.getParent(); 524 } 525 526 if (node == null) 527 return false; 528 529 switch (node.getNodeType()) { 530 case ASTNode.BLOCK: 531 return getBlockBalance(document, offset, fPartitioning) <= 0; 532 533 case ASTNode.IF_STATEMENT: 534 { 535 IfStatement ifStatement= (IfStatement) node; 536 Expression expression= ifStatement.getExpression(); 537 IRegion expressionRegion= createRegion(expression, info.delta); 538 Statement thenStatement= ifStatement.getThenStatement(); 539 IRegion thenRegion= createRegion(thenStatement, info.delta); 540 541 if (expressionRegion.getOffset() + expressionRegion.getLength() <= offset && offset + length <= thenRegion.getOffset()) 543 return thenStatement != null; 544 545 Statement elseStatement= ifStatement.getElseStatement(); 546 IRegion elseRegion= createRegion(elseStatement, info.delta); 547 548 if (elseStatement != null) { 549 int sourceOffset= thenRegion.getOffset() + thenRegion.getLength(); 550 int sourceLength= elseRegion.getOffset() - sourceOffset; 551 IRegion elseToken= getToken(document, new Region(sourceOffset, sourceLength), ITerminalSymbols.TokenNameelse); 552 return elseToken != null && elseToken.getOffset() + elseToken.getLength() <= offset && offset + length < elseRegion.getOffset(); 553 } 554 } 555 break; 556 557 case ASTNode.WHILE_STATEMENT: 558 case ASTNode.FOR_STATEMENT: 559 { 560 Expression expression= node.getNodeType() == ASTNode.WHILE_STATEMENT ? ((WhileStatement) node).getExpression() : ((ForStatement) node).getExpression(); 561 IRegion expressionRegion= createRegion(expression, info.delta); 562 Statement body= node.getNodeType() == ASTNode.WHILE_STATEMENT ? ((WhileStatement) node).getBody() : ((ForStatement) node).getBody(); 563 IRegion bodyRegion= createRegion(body, info.delta); 564 565 if (expressionRegion.getOffset() + expressionRegion.getLength() <= offset && offset + length <= bodyRegion.getOffset()) 567 return body != null; 568 } 569 break; 570 571 case ASTNode.DO_STATEMENT: 572 { 573 DoStatement doStatement= (DoStatement) node; 574 IRegion doRegion= createRegion(doStatement, info.delta); 575 Statement body= doStatement.getBody(); 576 IRegion bodyRegion= createRegion(body, info.delta); 577 578 if (doRegion.getOffset() + doRegion.getLength() <= offset && offset + length <= bodyRegion.getOffset()) 579 return body != null; 580 } 581 break; 582 } 583 584 return true; 585 } 586 587 592 private static void installJavaStuff(Document document) { 593 String [] types= new String [] { 594 IJavaPartitions.JAVA_DOC, 595 IJavaPartitions.JAVA_MULTI_LINE_COMMENT, 596 IJavaPartitions.JAVA_SINGLE_LINE_COMMENT, 597 IJavaPartitions.JAVA_STRING, 598 IJavaPartitions.JAVA_CHARACTER, 599 IDocument.DEFAULT_CONTENT_TYPE 600 }; 601 FastPartitioner partitioner= new FastPartitioner(new FastJavaPartitionScanner(), types); 602 partitioner.connect(document); 603 document.setDocumentPartitioner(IJavaPartitions.JAVA_PARTITIONING, partitioner); 604 } 605 606 611 private static void removeJavaStuff(Document document) { 612 document.setDocumentPartitioner(IJavaPartitions.JAVA_PARTITIONING, null); 613 } 614 615 private void smartPaste(IDocument document, DocumentCommand command) { 616 int newOffset= command.offset; 617 int newLength= command.length; 618 String newText= command.text; 619 620 try { 621 JavaHeuristicScanner scanner= new JavaHeuristicScanner(document); 622 JavaIndenter indenter= new JavaIndenter(document, scanner, fProject); 623 int offset= newOffset; 624 625 int refOffset= indenter.findReferencePosition(offset); 627 if (refOffset == JavaHeuristicScanner.NOT_FOUND) 628 return; 629 int peerOffset= getPeerPosition(document, command); 630 peerOffset= indenter.findReferencePosition(peerOffset); 631 refOffset= Math.min(refOffset, peerOffset); 632 633 int firstLine= 1; IRegion line= document.getLineInformationOfOffset(offset); 636 String notSelected= document.get(line.getOffset(), offset - line.getOffset()); 637 if (notSelected.trim().length() == 0) { 638 newLength += notSelected.length(); 639 newOffset= line.getOffset(); 640 firstLine= 0; 641 } 642 643 IRegion refLine= document.getLineInformationOfOffset(refOffset); 645 String prefix= document.get(refLine.getOffset(), newOffset - refLine.getOffset()); 646 647 Document temp= new Document(prefix + newText); 649 DocumentRewriteSession session= temp.startRewriteSession(DocumentRewriteSessionType.STRICTLY_SEQUENTIAL); 650 scanner= new JavaHeuristicScanner(temp); 651 indenter= new JavaIndenter(temp, scanner, fProject); 652 installJavaStuff(temp); 653 654 boolean isIndentDetected= false; 659 StringBuffer addition= new StringBuffer (); 660 int insertLength= 0; 661 int first= document.computeNumberOfLines(prefix) + firstLine; int lines= temp.getNumberOfLines(); 663 int tabLength= getVisualTabLengthPreference(); 664 boolean changed= false; 665 for (int l= first; l < lines; l++) { 667 IRegion r= temp.getLineInformation(l); 668 int lineOffset= r.getOffset(); 669 int lineLength= r.getLength(); 670 671 if (lineLength == 0) continue; 673 674 if (!isIndentDetected) { 675 676 String current= getCurrentIndent(temp, l); 678 StringBuffer correct= indenter.computeIndentation(lineOffset); 679 if (correct == null) 680 return; 682 insertLength= subtractIndent(correct, current, addition, tabLength); 683 if (l != first && temp.get(lineOffset, lineLength).trim().length() != 0) { 684 isIndentDetected= true; 685 if (insertLength == 0) { 686 if (firstLine == 0) { 688 command.offset= newOffset; 690 command.length= newLength; 691 if (changed) 692 break; } 694 return; 695 } 696 removeJavaStuff(temp); 697 } else { 698 changed= insertLength != 0; 699 } 700 } 701 702 if (insertLength > 0) 704 addIndent(temp, l, addition, tabLength); 705 else if (insertLength < 0) 706 cutIndent(temp, l, -insertLength, tabLength); 707 708 } 709 710 temp.stopRewriteSession(session); 711 newText= temp.get(prefix.length(), temp.getLength() - prefix.length()); 712 713 command.offset= newOffset; 714 command.length= newLength; 715 command.text= newText; 716 717 } catch (BadLocationException e) { 718 JavaPlugin.log(e); 719 } 720 721 } 722 723 734 private static String getCurrentIndent(Document document, int line) throws BadLocationException { 735 IRegion region= document.getLineInformation(line); 736 int from= region.getOffset(); 737 int endOffset= region.getOffset() + region.getLength(); 738 739 int to= from; 741 while (to < endOffset - 2 && document.get(to, 2).equals(LINE_COMMENT)) 742 to += 2; 743 744 while (to < endOffset) { 745 char ch= document.getChar(to); 746 if (!Character.isWhitespace(ch)) 747 break; 748 to++; 749 } 750 751 if (to > from && to < endOffset - 1 && document.get(to - 1, 2).equals(" *")) { String type= TextUtilities.getContentType(document, IJavaPartitions.JAVA_PARTITIONING, to, true); 754 if (type.equals(IJavaPartitions.JAVA_DOC) || type.equals(IJavaPartitions.JAVA_MULTI_LINE_COMMENT)) 755 to--; 756 } 757 758 return document.get(from, to - from); 759 } 760 761 772 private int subtractIndent(CharSequence correct, CharSequence current, StringBuffer difference, int tabLength) { 773 int c1= computeVisualLength(correct, tabLength); 774 int c2= computeVisualLength(current, tabLength); 775 int diff= c1 - c2; 776 if (diff <= 0) 777 return diff; 778 779 difference.setLength(0); 780 int len= 0, i= 0; 781 while (len < diff) { 782 char c= correct.charAt(i++); 783 difference.append(c); 784 len += computeVisualLength(c, tabLength); 785 } 786 787 788 return diff; 789 } 790 791 801 private void addIndent(Document document, int line, CharSequence indent, int tabLength) throws BadLocationException { 802 IRegion region= document.getLineInformation(line); 803 int insert= region.getOffset(); 804 int endOffset= region.getOffset() + region.getLength(); 805 806 int newInsert= insert; 808 while (newInsert < endOffset - 2 && document.get(newInsert, 2).equals(LINE_COMMENT)) 809 newInsert += 2; 810 811 if (newInsert > insert) { 813 int whitespaceCount= 0; 814 int i= newInsert; 815 while (i < endOffset - 1) { 816 char ch= document.get(i, 1).charAt(0); 817 if (!Character.isWhitespace(ch)) 818 break; 819 whitespaceCount= whitespaceCount + computeVisualLength(ch, tabLength); 820 i++; 821 } 822 823 if (whitespaceCount != 0 && whitespaceCount >= CodeFormatterUtil.getIndentWidth(fProject)) 824 insert= newInsert; 825 } 826 827 document.replace(insert, 0, indent.toString()); 829 } 830 831 842 private void cutIndent(Document document, int line, int toDelete, int tabLength) throws BadLocationException { 843 IRegion region= document.getLineInformation(line); 844 int from= region.getOffset(); 845 int endOffset= region.getOffset() + region.getLength(); 846 847 while (from < endOffset - 2 && document.get(from, 2).equals(LINE_COMMENT)) 849 from += 2; 850 851 int to= from; 852 while (toDelete > 0 && to < endOffset) { 853 char ch= document.getChar(to); 854 if (!Character.isWhitespace(ch)) 855 break; 856 toDelete -= computeVisualLength(ch, tabLength); 857 if (toDelete >= 0) 858 to++; 859 else 860 break; 861 } 862 863 document.replace(from, to - from, ""); } 865 866 874 private int computeVisualLength(CharSequence seq, int tabLength) { 875 int size= 0; 876 877 for (int i= 0; i < seq.length(); i++) { 878 char ch= seq.charAt(i); 879 if (ch == '\t') { 880 if (tabLength != 0) 881 size += tabLength - size % tabLength; 882 } else { 884 size++; 885 } 886 } 887 return size; 888 } 889 890 898 private int computeVisualLength(char ch, int tabLength) { 899 if (ch == '\t') 900 return tabLength; 901 else 902 return 1; 903 } 904 905 910 private int getVisualTabLengthPreference() { 911 return CodeFormatterUtil.getTabWidth(fProject); 912 } 913 914 private int getPeerPosition(IDocument document, DocumentCommand command) { 915 if (document.getLength() == 0) 916 return 0; 917 921 Document pasted= new Document(command.text); 922 installJavaStuff(pasted); 923 int firstPeer= command.offset; 924 925 JavaHeuristicScanner pScanner= new JavaHeuristicScanner(pasted); 926 JavaHeuristicScanner dScanner= new JavaHeuristicScanner(document); 927 928 int afterToken= dScanner.nextToken(command.offset + command.length, JavaHeuristicScanner.UNBOUND); 930 try { 931 switch (afterToken) { 932 case Symbols.TokenRBRACE: 933 pasted.replace(pasted.getLength(), 0, "}"); break; 935 case Symbols.TokenRPAREN: 936 pasted.replace(pasted.getLength(), 0, ")"); break; 938 case Symbols.TokenRBRACKET: 939 pasted.replace(pasted.getLength(), 0, "]"); break; 941 } 942 } catch (BadLocationException e) { 943 Assert.isTrue(false); 945 } 946 947 int pPos= 0; int dPos= Math.max(0, command.offset - 1); while (true) { 950 int token= pScanner.nextToken(pPos, JavaHeuristicScanner.UNBOUND); 951 pPos= pScanner.getPosition(); 952 switch (token) { 953 case Symbols.TokenLBRACE: 954 case Symbols.TokenLBRACKET: 955 case Symbols.TokenLPAREN: 956 pPos= skipScope(pScanner, pPos, token); 957 if (pPos == JavaHeuristicScanner.NOT_FOUND) 958 return firstPeer; 959 break; case Symbols.TokenRBRACE: 961 int peer= dScanner.findOpeningPeer(dPos, '{', '}'); 962 dPos= peer - 1; 963 if (peer == JavaHeuristicScanner.NOT_FOUND) 964 return firstPeer; 965 firstPeer= peer; 966 break; case Symbols.TokenRBRACKET: 968 peer= dScanner.findOpeningPeer(dPos, '[', ']'); 969 dPos= peer - 1; 970 if (peer == JavaHeuristicScanner.NOT_FOUND) 971 return firstPeer; 972 firstPeer= peer; 973 break; case Symbols.TokenRPAREN: 975 peer= dScanner.findOpeningPeer(dPos, '(', ')'); 976 dPos= peer - 1; 977 if (peer == JavaHeuristicScanner.NOT_FOUND) 978 return firstPeer; 979 firstPeer= peer; 980 break; case Symbols.TokenCASE: 982 case Symbols.TokenDEFAULT: 983 JavaIndenter indenter= new JavaIndenter(document, dScanner, fProject); 984 peer= indenter.findReferencePosition(dPos, false, false, false, true); 985 if (peer == JavaHeuristicScanner.NOT_FOUND) 986 return firstPeer; 987 firstPeer= peer; 988 break; 990 case Symbols.TokenEOF: 991 return firstPeer; 992 default: 993 } 995 } 996 } 997 998 1005 private static int skipScope(JavaHeuristicScanner scanner, int pos, int token) { 1006 int openToken= token; 1007 int closeToken; 1008 switch (token) { 1009 case Symbols.TokenLPAREN: 1010 closeToken= Symbols.TokenRPAREN; 1011 break; 1012 case Symbols.TokenLBRACKET: 1013 closeToken= Symbols.TokenRBRACKET; 1014 break; 1015 case Symbols.TokenLBRACE: 1016 closeToken= Symbols.TokenRBRACE; 1017 break; 1018 default: 1019 Assert.isTrue(false); 1020 return -1; } 1022 1023 int depth= 1; 1024 int p= pos; 1025 1026 while (true) { 1027 int tok= scanner.nextToken(p, JavaHeuristicScanner.UNBOUND); 1028 p= scanner.getPosition(); 1029 1030 if (tok == openToken) { 1031 depth++; 1032 } else if (tok == closeToken) { 1033 depth--; 1034 if (depth == 0) 1035 return p + 1; 1036 } else if (tok == Symbols.TokenEOF) { 1037 return JavaHeuristicScanner.NOT_FOUND; 1038 } 1039 } 1040 } 1041 1042 private boolean isLineDelimiter(IDocument document, String text) { 1043 String [] delimiters= document.getLegalLineDelimiters(); 1044 if (delimiters != null) 1045 return TextUtilities.equals(delimiters, text) > -1; 1046 return false; 1047 } 1048 1049 private void smartIndentOnKeypress(IDocument document, DocumentCommand command) { 1050 switch (command.text.charAt(0)) { 1051 case '}': 1052 smartIndentAfterClosingBracket(document, command); 1053 break; 1054 case '{': 1055 smartIndentAfterOpeningBracket(document, command); 1056 break; 1057 case 'e': 1058 smartIndentUponE(document, command); 1059 break; 1060 } 1061 } 1062 1063 private void smartIndentUponE(IDocument d, DocumentCommand c) { 1064 if (c.offset < 4 || d.getLength() == 0) 1065 return; 1066 1067 try { 1068 String content= d.get(c.offset - 3, 3); 1069 if (content.equals("els")) { JavaHeuristicScanner scanner= new JavaHeuristicScanner(d); 1071 int p= c.offset - 3; 1072 1073 int line= d.getLineOfOffset(p); 1075 int lineOffset= d.getLineOffset(line); 1076 1077 if (d.get(lineOffset, p - lineOffset).trim().length() != 0) 1079 return; 1080 1081 int pos= scanner.findNonWhitespaceBackward(p - 1, JavaHeuristicScanner.UNBOUND); 1083 if (pos == -1) 1084 return; 1085 int lastLine= d.getLineOfOffset(pos); 1086 1087 if (lastLine < line) { 1089 1090 JavaIndenter indenter= new JavaIndenter(d, scanner, fProject); 1091 int ref= indenter.findReferencePosition(p, true, false, false, false); 1092 if (ref == JavaHeuristicScanner.NOT_FOUND) 1093 return; 1094 int refLine= d.getLineOfOffset(ref); 1095 String indent= getIndentOfLine(d, refLine); 1096 1097 if (indent != null) { 1098 c.text= indent.toString() + "else"; c.length += c.offset - lineOffset; 1100 c.offset= lineOffset; 1101 } 1102 } 1103 1104 return; 1105 } 1106 1107 if (content.equals("cas")) { JavaHeuristicScanner scanner= new JavaHeuristicScanner(d); 1109 int p= c.offset - 3; 1110 1111 int line= d.getLineOfOffset(p); 1113 int lineOffset= d.getLineOffset(line); 1114 1115 if (d.get(lineOffset, p - lineOffset).trim().length() != 0) 1117 return; 1118 1119 int pos= scanner.findNonWhitespaceBackward(p - 1, JavaHeuristicScanner.UNBOUND); 1121 if (pos == -1) 1122 return; 1123 int lastLine= d.getLineOfOffset(pos); 1124 1125 if (lastLine < line) { 1127 1128 JavaIndenter indenter= new JavaIndenter(d, scanner, fProject); 1129 int ref= indenter.findReferencePosition(p, false, false, false, true); 1130 if (ref == JavaHeuristicScanner.NOT_FOUND) 1131 return; 1132 int refLine= d.getLineOfOffset(ref); 1133 int nextToken= scanner.nextToken(ref, JavaHeuristicScanner.UNBOUND); 1134 String indent; 1135 if (nextToken == Symbols.TokenCASE || nextToken == Symbols.TokenDEFAULT) 1136 indent= getIndentOfLine(d, refLine); 1137 else indent= indenter.computeIndentation(p).toString(); 1139 1140 if (indent != null) { 1141 c.text= indent.toString() + "case"; c.length += c.offset - lineOffset; 1143 c.offset= lineOffset; 1144 } 1145 } 1146 1147 return; 1148 } 1149 1150 } catch (BadLocationException e) { 1151 JavaPlugin.log(e); 1152 } 1153 } 1154 1155 1158 public void customizeDocumentCommand(IDocument d, DocumentCommand c) { 1159 1160 if (c.doit == false) 1161 return; 1162 1163 clearCachedValues(); 1164 if (!isSmartMode()) { 1165 super.customizeDocumentCommand(d, c); 1166 return; 1167 } 1168 1169 if (c.length == 0 && c.text != null && isLineDelimiter(d, c.text)) 1170 smartIndentAfterNewLine(d, c); 1171 else if (c.text.length() == 1) 1172 smartIndentOnKeypress(d, c); 1173 else if (c.text.length() > 1 && getPreferenceStore().getBoolean(PreferenceConstants.EDITOR_SMART_PASTE)) 1174 smartPaste(d, c); 1176 } 1177 1178 private static IPreferenceStore getPreferenceStore() { 1179 return JavaPlugin.getDefault().getCombinedPreferenceStore(); 1180 } 1181 1182 private boolean closeBrace() { 1183 return fCloseBrace; 1184 } 1185 1186 private boolean isSmartMode() { 1187 return fIsSmartMode; 1188 } 1189 1190 private void clearCachedValues() { 1191 IPreferenceStore preferenceStore= getPreferenceStore(); 1192 fCloseBrace= preferenceStore.getBoolean(PreferenceConstants.EDITOR_CLOSE_BRACES); 1193 fIsSmartMode= computeSmartMode(); 1194 } 1195 1196 private boolean computeSmartMode() { 1197 IWorkbenchPage page= JavaPlugin.getActivePage(); 1198 if (page != null) { 1199 IEditorPart part= page.getActiveEditor(); 1200 if (part instanceof ITextEditorExtension3) { 1201 ITextEditorExtension3 extension= (ITextEditorExtension3) part; 1202 return extension.getInsertMode() == ITextEditorExtension3.SMART_INSERT; 1203 } 1204 } 1205 return false; 1206 } 1207 1208 private static CompilationUnitInfo getCompilationUnitForMethod(IDocument document, int offset, String partitioning) { 1209 try { 1210 JavaHeuristicScanner scanner= new JavaHeuristicScanner(document); 1211 1212 IRegion sourceRange= scanner.findSurroundingBlock(offset); 1213 if (sourceRange == null) 1214 return null; 1215 String source= document.get(sourceRange.getOffset(), sourceRange.getLength()); 1216 1217 StringBuffer contents= new StringBuffer (); 1218 contents.append("class ____C{void ____m()"); final int methodOffset= contents.length(); 1220 contents.append(source); 1221 contents.append('}'); 1222 1223 char[] buffer= contents.toString().toCharArray(); 1224 1225 return new CompilationUnitInfo(buffer, sourceRange.getOffset() - methodOffset); 1226 1227 } catch (BadLocationException e) { 1228 JavaPlugin.log(e); 1229 } 1230 1231 return null; 1232 } 1233 1234 1244 private static int getBlockBalance(IDocument document, int offset, String partitioning) { 1245 if (offset < 1) 1246 return -1; 1247 if (offset >= document.getLength()) 1248 return 1; 1249 1250 int begin= offset; 1251 int end= offset - 1; 1252 1253 JavaHeuristicScanner scanner= new JavaHeuristicScanner(document); 1254 1255 while (true) { 1256 begin= scanner.findOpeningPeer(begin - 1, '{', '}'); 1257 end= scanner.findClosingPeer(end + 1, '{', '}'); 1258 if (begin == -1 && end == -1) 1259 return 0; 1260 if (begin == -1) 1261 return -1; 1262 if (end == -1) 1263 return 1; 1264 } 1265 } 1266 1267 private static IRegion createRegion(ASTNode node, int delta) { 1268 return node == null ? null : new Region(node.getStartPosition() + delta, node.getLength()); 1269 } 1270 1271 private static IRegion getToken(IDocument document, IRegion scanRegion, int tokenId) { 1272 1273 try { 1274 1275 final String source= document.get(scanRegion.getOffset(), scanRegion.getLength()); 1276 1277 fgScanner.setSource(source.toCharArray()); 1278 1279 int id= fgScanner.getNextToken(); 1280 while (id != ITerminalSymbols.TokenNameEOF && id != tokenId) 1281 id= fgScanner.getNextToken(); 1282 1283 if (id == ITerminalSymbols.TokenNameEOF) 1284 return null; 1285 1286 int tokenOffset= fgScanner.getCurrentTokenStartPosition(); 1287 int tokenLength= fgScanner.getCurrentTokenEndPosition() + 1 - tokenOffset; return new Region(tokenOffset + scanRegion.getOffset(), tokenLength); 1289 1290 } catch (InvalidInputException x) { 1291 return null; 1292 } catch (BadLocationException x) { 1293 return null; 1294 } 1295 } 1296} 1297 | Popular Tags |