1 19 20 package org.netbeans.modules.editor.java; 21 22 23 import javax.swing.text.BadLocationException ; 24 import javax.swing.text.Caret ; 25 import javax.swing.text.Document ; 26 import org.netbeans.editor.BaseDocument; 27 import org.netbeans.editor.SyntaxSupport; 28 import org.netbeans.editor.TokenID; 29 import org.netbeans.editor.TokenProcessor; 30 import org.netbeans.editor.TokenContextPath; 31 import org.netbeans.editor.ext.ExtSyntaxSupport; 32 import org.netbeans.editor.ext.java.JavaTokenContext; 33 import org.netbeans.editor.TokenItem; 34 import org.netbeans.editor.Settings; 35 import org.netbeans.editor.Utilities; 36 import org.netbeans.editor.ext.java.JavaSettingsNames; 37 38 39 40 47 class BracketCompletion { 48 49 61 static void charInserted(BaseDocument doc, 62 int dotPos, 63 Caret caret, 64 char ch) throws BadLocationException { 65 SyntaxSupport syntaxSupport = doc.getSyntaxSupport(); 66 if (!(syntaxSupport instanceof ExtSyntaxSupport) || !completionSettingEnabled()){ 67 return; 68 } 69 70 ExtSyntaxSupport support = (ExtSyntaxSupport)syntaxSupport; 71 if (ch == ')'|| ch == ']'|| ch =='('|| ch =='[') { 72 TokenID tokenAtDot = support.getTokenID(dotPos); 73 if (tokenAtDot == JavaTokenContext.RBRACKET || tokenAtDot == JavaTokenContext.RPAREN) { 74 skipClosingBracket(doc, caret, ch); 75 } else if (tokenAtDot == JavaTokenContext.LBRACKET || tokenAtDot == JavaTokenContext.LPAREN) { 76 completeOpeningBracket(doc, dotPos, caret, ch); 77 } 78 } else if (ch == ';') { 79 moveSemicolon(doc, dotPos, caret); 80 } 81 } 82 83 private static void moveSemicolon(BaseDocument doc, int dotPos, Caret caret) throws BadLocationException { 84 int eolPos = Utilities.getRowEnd(doc, dotPos); 85 ExtSyntaxSupport ssup = (ExtSyntaxSupport)doc.getSyntaxSupport(); 86 int lastParenPos = dotPos; 87 TokenItem token = ssup.getTokenChain(dotPos, eolPos); 88 for (TokenItem item = token.getNext(); item != null && item.getOffset() <= eolPos; item = item.getNext()) { 89 TokenID tokenID = item.getTokenID(); 90 if (tokenID == JavaTokenContext.RPAREN) { 91 lastParenPos = item.getOffset(); 92 } else if (tokenID != JavaTokenContext.WHITESPACE) { 93 return; 94 } 95 } 96 if (isForLoopSemicolon(token) || posWithinAnyQuote(doc,dotPos)) { 97 return; 98 } 99 doc.remove(dotPos, 1); 100 doc.insertString(lastParenPos, ";", null); caret.setDot(lastParenPos + 1); 102 } 103 104 private static boolean isForLoopSemicolon(TokenItem token) { 105 if (token == null || token.getTokenID() != JavaTokenContext.SEMICOLON) { 106 return false; 107 } 108 int parDepth = 0; int braceDepth = 0; boolean semicolonFound = false; token = token.getPrevious(); while (token != null) { 113 if (token.getTokenID() == JavaTokenContext.LPAREN) { 114 if (parDepth == 0) { token = token.getPrevious(); 116 while(token !=null && (token.getTokenID() == JavaTokenContext.WHITESPACE || token.getTokenID() == JavaTokenContext.BLOCK_COMMENT || token.getTokenID() == JavaTokenContext.LINE_COMMENT)) { 117 token = token.getPrevious(); 118 } 119 if (token.getTokenID() == JavaTokenContext.FOR) { 120 return true; 121 } 122 return false; 123 } else { parDepth--; 125 } 126 } else if (token.getTokenID() == JavaTokenContext.RPAREN) { 127 parDepth++; 128 } else if (token.getTokenID() == JavaTokenContext.LBRACE) { 129 if (braceDepth == 0) { return false; 131 } 132 braceDepth--; 133 } else if (token.getTokenID() == JavaTokenContext.RBRACE) { 134 braceDepth++; 135 136 } else if (token.getTokenID() == JavaTokenContext.SEMICOLON) { 137 if (semicolonFound) { return false; 139 } 140 semicolonFound = true; 141 } 142 token = token.getPrevious(); 143 } 144 return false; 145 } 146 147 156 static void charBackspaced(BaseDocument doc, 157 int dotPos, 158 Caret caret, 159 char ch) throws BadLocationException 160 { 161 if (completionSettingEnabled()) { 162 if (ch == '(' || ch == '[') { 163 TokenID tokenAtDot = ((ExtSyntaxSupport)doc. 164 getSyntaxSupport()).getTokenID(dotPos); 165 if ((tokenAtDot == JavaTokenContext.RBRACKET && tokenBalance(doc, JavaTokenContext.LBRACKET, JavaTokenContext.RBRACKET) != 0) || 166 (tokenAtDot == JavaTokenContext.RPAREN && tokenBalance(doc, JavaTokenContext.LPAREN, JavaTokenContext.RPAREN) != 0) ) { 167 doc.remove(dotPos, 1); 168 } 169 } 170 else if (ch == '\"') { 171 char match [] = doc.getChars(dotPos, 1); 172 if (match != null && match[0] == '\"') { 173 doc.remove(dotPos, 1); 174 } 175 } 176 else if (ch == '\'') { 177 char match [] = doc.getChars(dotPos, 1); 178 if (match != null && match[0] == '\'') { 179 doc.remove(dotPos, 1); 180 } 181 } 182 } 183 } 184 185 201 static boolean isAddRightBrace(BaseDocument doc, int caretOffset) 202 throws BadLocationException { 203 boolean addRightBrace = false; 204 if (completionSettingEnabled()) { 205 if (caretOffset > 0) { 206 int tokenOffset = caretOffset; 209 TokenItem token = ((ExtSyntaxSupport)doc.getSyntaxSupport()). 210 getTokenChain(tokenOffset -1, tokenOffset); 211 212 addRightBrace = true; 214 int off = (caretOffset - token.getOffset()); 217 if (off > 0 && off < token.getImage().length()) { switch (token.getTokenID().getNumericID()) { 219 case JavaTokenContext.WHITESPACE_ID: 220 case JavaTokenContext.LINE_COMMENT_ID: 221 break; 223 default: 224 addRightBrace = false; 226 } 227 } 228 229 if (addRightBrace) { int caretRowStartOffset = Utilities.getRowStart(doc, caretOffset); 231 232 while (token != null && token.getOffset() >= caretRowStartOffset) { 236 boolean ignore = false; 237 switch (token.getTokenID().getNumericID()) { 239 case JavaTokenContext.WHITESPACE_ID: 240 case JavaTokenContext.BLOCK_COMMENT_ID: 241 case JavaTokenContext.LINE_COMMENT_ID: 242 ignore = true; 244 break; 245 } 246 247 if (ignore) { 248 token = token.getPrevious(); 249 } else { break; 251 } 252 } 253 254 if (token == null 255 || token.getTokenID() != JavaTokenContext.LBRACE || token.getOffset() < caretRowStartOffset ) { 258 addRightBrace = false; 259 } 260 261 } 262 263 if (addRightBrace) { addRightBrace = (braceBalance(doc) > 0); 265 } 266 } 267 } 268 return addRightBrace; 269 } 270 271 276 static int getRowOrBlockEnd(BaseDocument doc, int caretOffset) throws BadLocationException { 277 int rowEnd = Utilities.getRowLastNonWhite(doc, caretOffset); 278 if (rowEnd == -1 || caretOffset >= rowEnd){ 279 return caretOffset; 280 } 281 rowEnd += 1; 282 int parenBalance = 0; 283 int braceBalance = 0; 284 int bracketBalance = 0; 285 ExtSyntaxSupport ssup = (ExtSyntaxSupport)doc.getSyntaxSupport(); 286 TokenItem token = ssup.getTokenChain(caretOffset, rowEnd); 287 while (token != null && token.getOffset() < rowEnd) { 288 switch (token.getTokenID().getNumericID()) { 289 case JavaTokenContext.LPAREN_ID: 290 parenBalance++; 291 break; 292 case JavaTokenContext.RPAREN_ID: 293 if (parenBalance-- == 0) 294 return token.getOffset(); 295 case JavaTokenContext.LBRACE_ID: 296 braceBalance++; 297 break; 298 case JavaTokenContext.RBRACE_ID: 299 if (braceBalance-- == 0) 300 return token.getOffset(); 301 case JavaTokenContext.LBRACKET_ID: 302 bracketBalance++; 303 break; 304 case JavaTokenContext.RBRACKET_ID: 305 if (bracketBalance-- == 0) 306 return token.getOffset(); 307 } 308 token = token.getNext(); 309 } 310 return rowEnd; 311 } 312 313 319 private static int braceBalance(BaseDocument doc) 320 throws BadLocationException 321 { 322 return tokenBalance(doc, JavaTokenContext.LBRACE, JavaTokenContext.RBRACE); 323 } 324 325 331 private static int tokenBalance(BaseDocument doc, TokenID open, TokenID close) 332 throws BadLocationException { 333 334 ExtSyntaxSupport sup = (ExtSyntaxSupport)doc.getSyntaxSupport(); 335 BalanceTokenProcessor balanceTP = new BalanceTokenProcessor(open, close); 336 sup.tokenizeText(balanceTP, 0, doc.getLength(), true); 337 return balanceTP.getBalance(); 338 } 339 340 350 private static void skipClosingBracket(BaseDocument doc, Caret caret, char bracket) 351 throws BadLocationException { 352 353 TokenID bracketId = (bracket == ')') 354 ? JavaTokenContext.RPAREN 355 : JavaTokenContext.RBRACKET; 356 357 int caretOffset = caret.getDot(); 358 if (isSkipClosingBracket(doc, caretOffset, bracketId)) { 359 doc.remove(caretOffset - 1, 1); 360 caret.setDot(caretOffset); } 362 } 363 364 373 static boolean isSkipClosingBracket(BaseDocument doc, int caretOffset, TokenID bracketId) 374 throws BadLocationException { 375 376 if (caretOffset == doc.getLength()) { 379 return false; } 381 382 boolean skipClosingBracket = false; 384 TokenItem token = ((ExtSyntaxSupport)doc.getSyntaxSupport()).getTokenChain( 386 caretOffset, caretOffset + 1); 387 388 if (token != null && token.getTokenID() == bracketId) { 390 int bracketIntId = bracketId.getNumericID(); 391 int leftBracketIntId = (bracketIntId == JavaTokenContext.RPAREN_ID) 392 ? JavaTokenContext.LPAREN_ID 393 : JavaTokenContext.LBRACKET_ID; 394 395 TokenItem nextToken = token.getNext(); 397 while (nextToken != null && nextToken.getTokenID() == bracketId) { 398 token = nextToken; 399 nextToken = nextToken.getNext(); 400 } 401 int braceBalance = 0; int bracketBalance = -1; TokenItem lastRBracket = token; 407 token = token.getPrevious(); 408 boolean finished = false; 409 while (!finished && token != null) { 410 int tokenIntId = token.getTokenID().getNumericID(); 411 switch (tokenIntId) { 412 case JavaTokenContext.LPAREN_ID: 413 case JavaTokenContext.LBRACKET_ID: 414 if (tokenIntId == bracketIntId) { 415 bracketBalance++; 416 if (bracketBalance == 0) { 417 if (braceBalance != 0) { 418 bracketBalance = 1; 427 } 428 finished = true; 429 } 430 } 431 break; 432 433 case JavaTokenContext.RPAREN_ID: 434 case JavaTokenContext.RBRACKET_ID: 435 if (tokenIntId == bracketIntId) { 436 bracketBalance--; 437 } 438 break; 439 440 case JavaTokenContext.LBRACE_ID: 441 braceBalance++; 442 if (braceBalance > 0) { finished = true; 444 } 445 break; 446 447 case JavaTokenContext.RBRACE_ID: 448 braceBalance--; 449 break; 450 451 } 452 453 token = token.getPrevious(); } 455 456 if (bracketBalance != 0) { skipClosingBracket = true; 459 460 } else { braceBalance = 0; 467 bracketBalance = 1; token = lastRBracket.getNext(); 469 finished = false; 470 while (!finished && token != null) { 471 int tokenIntId = token.getTokenID().getNumericID(); 472 switch (tokenIntId) { 473 case JavaTokenContext.LPAREN_ID: 474 case JavaTokenContext.LBRACKET_ID: 475 if (tokenIntId == leftBracketIntId) { 476 bracketBalance++; 477 } 478 break; 479 480 case JavaTokenContext.RPAREN_ID: 481 case JavaTokenContext.RBRACKET_ID: 482 if (tokenIntId == bracketIntId) { 483 bracketBalance--; 484 if (bracketBalance == 0) { 485 if (braceBalance != 0) { 486 bracketBalance = -1; 494 } 495 finished = true; 496 } 497 } 498 break; 499 500 case JavaTokenContext.LBRACE_ID: 501 braceBalance++; 502 break; 503 504 case JavaTokenContext.RBRACE_ID: 505 braceBalance--; 506 if (braceBalance < 0) { finished = true; 508 } 509 break; 510 511 } 512 513 token = token.getPrevious(); } 515 516 skipClosingBracket = (bracketBalance == 0); 519 } 520 } 521 return skipClosingBracket; 522 } 523 524 532 private static void completeOpeningBracket(BaseDocument doc, 533 int dotPos, 534 Caret caret, 535 char bracket) throws BadLocationException 536 { 537 if (isCompletablePosition(doc, dotPos+1)) { 538 String matchinBracket = "" + matching(bracket); 539 doc.insertString(dotPos + 1, matchinBracket,null); 540 caret.setDot(dotPos+1); 541 } 542 } 543 544 private static boolean isEscapeSequence(BaseDocument doc, int dotPos) throws BadLocationException { 545 if (dotPos <= 0) return false; 546 char previousChar = doc.getChars(dotPos-1,1)[0]; 547 return previousChar == '\\'; 548 } 549 550 558 static boolean completeQuote(BaseDocument doc, int dotPos, Caret caret, 559 char bracket) throws BadLocationException { 560 561 if (!completionSettingEnabled()){ 562 return false; 563 } 564 565 if (isEscapeSequence(doc, dotPos)){ return false; 567 } 568 569 SyntaxSupport s = doc.getSyntaxSupport(); 570 if (!(s instanceof ExtSyntaxSupport)){ 571 return false; 572 } 573 574 ExtSyntaxSupport syntax = (ExtSyntaxSupport)s; 575 TokenID token = null; 577 if (doc.getLength() > dotPos){ 578 token = syntax.getTokenID(dotPos); 579 } 580 581 int lastNonWhite = Utilities.getRowLastNonWhite(doc, dotPos); 582 boolean eol = lastNonWhite < dotPos; 584 585 if (token == JavaTokenContext.BLOCK_COMMENT || token == JavaTokenContext.LINE_COMMENT){ 586 return false; 587 } else if (token == JavaTokenContext.WHITESPACE && eol && dotPos-1 > 0){ 588 token = syntax.getTokenID(dotPos-1); 590 if (token == JavaTokenContext.LINE_COMMENT){ 591 return false; 592 } 593 } 594 595 boolean completablePosition = isQuoteCompletablePosition(doc, dotPos); 596 boolean insideString = 597 token == JavaTokenContext.STRING_LITERAL || 598 token == JavaTokenContext.CHAR_LITERAL; 599 600 if (!insideString){ 601 if (token == JavaTokenContext.WHITESPACE && eol){ 604 if (dotPos-1 > 0){ 605 token = syntax.getTokenID(dotPos-1); 606 insideString = 607 token == JavaTokenContext.STRING_LITERAL || 608 token == JavaTokenContext.CHAR_LITERAL; 609 } 610 } 611 } 612 613 if (insideString){ 614 if (eol){ 615 return false; } else { 617 char chr = doc.getChars(dotPos,1)[0]; 619 if (chr == bracket){ 620 doc.insertString(dotPos, "" + bracket , null); doc.remove(dotPos, 1); 622 return true; 623 } 624 } 625 } 626 627 if ((completablePosition && !insideString) || eol){ 628 doc.insertString(dotPos, "" + bracket + bracket , null); return true; 630 } 631 632 return false; 633 } 634 635 642 private static boolean isCompletablePosition(BaseDocument doc, int dotPos) 643 throws BadLocationException 644 { 645 if (dotPos == doc.getLength()) return true; 647 else { 648 char chr = doc.getChars(dotPos,1)[0]; 650 return (chr == ')' || 651 chr == ',' || 652 chr == '\"'|| 653 chr == '\''|| 654 chr == ' ' || 655 chr == ']' || 656 chr == '}' || 657 chr == '\n'|| 658 chr == '\t'|| 659 chr == ';'); 660 } 661 } 662 663 private static boolean isQuoteCompletablePosition(BaseDocument doc, int dotPos) 664 throws BadLocationException { 665 if (dotPos == doc.getLength()) return true; 667 else { 668 int eol = Utilities.getRowEnd(doc, dotPos); 670 if (dotPos == eol || eol == -1){ 671 return false; 672 } 673 int firstNonWhiteFwd = Utilities.getFirstNonWhiteFwd(doc, dotPos, eol); 674 if (firstNonWhiteFwd == -1){ 675 return false; 676 } 677 char chr = doc.getChars(firstNonWhiteFwd,1)[0]; 678 return (chr == ')' || 679 chr == ',' || 680 chr == '+'|| 681 chr == '}' || 682 chr == ';'); 683 } 684 } 685 686 689 private static boolean completionSettingEnabled() { 690 return ((Boolean )Settings.getValue(JavaKit.class, JavaSettingsNames.PAIR_CHARACTERS_COMPLETION)).booleanValue(); 691 } 692 693 697 private static char matching(char bracket) { 698 switch (bracket) { 699 case '(' : return ')'; 700 case '[' : return ']'; 701 case '\"' : return '\"'; case '\'' : return '\''; 703 default: return ' '; 704 } 705 } 706 707 708 709 715 static boolean posWithinString(BaseDocument doc, int dotPos) { 716 return posWithinQuotes(doc, dotPos, '\"', JavaTokenContext.STRING_LITERAL); 717 } 718 719 727 static boolean posWithinQuotes(BaseDocument doc, int dotPos, char quote, TokenID tokenID) 728 { 729 try { 730 MyTokenProcessor proc = new MyTokenProcessor(); 731 doc.getSyntaxSupport().tokenizeText( proc, 732 dotPos-1, 733 doc.getLength(), true); 734 return proc.tokenID == tokenID && 735 (dotPos - proc.tokenStart == 1 || doc.getChars(dotPos-1,1)[0]!=quote); 736 } catch (BadLocationException ex) { 737 return false; 738 } 739 } 740 741 static boolean posWithinAnyQuote(BaseDocument doc, int dotPos) { 742 try { 743 MyTokenProcessor proc = new MyTokenProcessor(); 744 doc.getSyntaxSupport().tokenizeText( proc, 745 dotPos-1, 746 doc.getLength(), true); 747 if(proc.tokenID == JavaTokenContext.STRING_LITERAL || 748 proc.tokenID == JavaTokenContext.CHAR_LITERAL) { 749 char[] ch = doc.getChars(dotPos-1,1); 750 return dotPos - proc.tokenStart == 1 || (ch[0]!='\"' && ch[0]!='\''); 751 } 752 return false; 753 } catch (BadLocationException ex) { 754 return false; 755 } 756 } 757 758 759 static boolean isUnclosedStringAtLineEnd(BaseDocument doc, int dotPos) { 760 try { 761 MyTokenProcessor proc = new MyTokenProcessor(); 762 doc.getSyntaxSupport().tokenizeText(proc, Utilities.getRowLastNonWhite(doc, dotPos), doc.getLength(), true); 763 return proc.tokenID == JavaTokenContext.STRING_LITERAL; 764 } catch (BadLocationException ex) { 765 return false; 766 } 767 } 768 769 772 static class MyTokenProcessor implements TokenProcessor { 773 public TokenID tokenID = null; 774 public int tokenStart = -1; 775 776 public boolean token(TokenID tokenID, TokenContextPath tcp, 777 int tokBuffOffset, int tokLength) { 778 this.tokenStart = tokenBuffer2DocumentOffset(tokBuffOffset); 779 this.tokenID = tokenID; 780 781 784 785 return false; 786 } 787 788 public int eot(int offset) { return 0;} 790 791 public void nextBuffer(char [] buffer, int offset, int len, int startPos, int preScan, boolean lastBuffer) { 792 794 this.bufferStartPos = startPos - offset; 795 } 796 797 private int bufferStartPos = 0; 798 private int tokenBuffer2DocumentOffset(int offs) { return offs + bufferStartPos;} 799 } 800 801 804 private static class BalanceTokenProcessor implements TokenProcessor { 805 806 private TokenID leftTokenID; 807 private TokenID rightTokenID; 808 809 private int balance; 810 811 BalanceTokenProcessor(TokenID leftTokenID, TokenID rightTokenID) { 812 this.leftTokenID = leftTokenID; 813 this.rightTokenID = rightTokenID; 814 } 815 816 public boolean token(TokenID tokenID, TokenContextPath tcp, 817 int tokBuffOffset, int tokLength) { 818 819 if (tokenID == leftTokenID) { 820 balance++; 821 } else if (tokenID == rightTokenID) { 822 balance--; 823 } 824 825 return true; 826 } 827 828 public int eot(int offset) { 829 return 0; 830 } 831 832 public void nextBuffer(char [] buffer, int offset, int len, int startPos, int preScan, boolean lastBuffer) { 833 } 834 835 public int getBalance() { 836 return balance; 837 } 838 839 } 840 841 842 } 843 | Popular Tags |