1 19 package org.netbeans.modules.ruby; 20 21 import java.io.IOException ; 22 import java.util.ArrayList ; 23 import java.util.Collections ; 24 import java.util.Iterator ; 25 import java.util.List ; 26 27 import javax.swing.text.BadLocationException ; 28 import javax.swing.text.Caret ; 29 import javax.swing.text.Document ; 30 31 import org.jruby.ast.NewlineNode; 32 import org.jruby.ast.Node; 33 import org.netbeans.api.gsf.CompilationInfo; 34 import org.netbeans.api.gsf.GsfTokenId; 35 import org.netbeans.api.gsf.OffsetRange; 36 import org.netbeans.api.lexer.Token; 37 import org.netbeans.api.lexer.TokenHierarchy; 38 import org.netbeans.api.lexer.TokenId; 39 import org.netbeans.api.lexer.TokenSequence; 40 import org.netbeans.editor.BaseDocument; 41 import org.netbeans.editor.BaseDocument; 42 import org.netbeans.editor.Utilities; 43 import org.netbeans.modules.ruby.lexer.LexUtilities; 44 import org.netbeans.modules.ruby.lexer.RubyTokenId; 45 import org.openide.util.Exceptions; 46 47 48 86 public class BracketCompleter implements org.netbeans.api.gsf.BracketCompletion { 87 88 private static TokenId[] STRING_TOKENS = { 90 RubyTokenId.STRING_LITERAL, RubyTokenId.QUOTED_STRING_LITERAL, RubyTokenId.CHAR_LITERAL, 91 RubyTokenId.STRING_END, RubyTokenId.QUOTED_STRING_END 92 }; 93 94 95 private static TokenId[] REGEXP_TOKENS = { RubyTokenId.REGEXP_LITERAL, RubyTokenId.REGEXP_END }; 97 98 102 private int previousAdjustmentOffset = -1; 103 104 108 private int previousAdjustmentIndent; 109 110 public BracketCompleter() { 111 } 112 113 114 public int beforeBreak(Document document, int offset, Caret caret) 115 throws BadLocationException { 116 BaseDocument doc = (BaseDocument)document; 117 TokenSequence<?extends GsfTokenId> ts = LexUtilities.getTokenSequence(doc); 118 119 if (ts == null) { 120 return -1; 121 } 122 123 ts.move(offset); 124 125 if (!ts.moveNext() && !ts.movePrevious()) { 126 return -1; 127 } 128 129 Token<?extends GsfTokenId> token = ts.token(); 130 TokenId id = token.id(); 131 132 if ((id == RubyTokenId.ERROR) && (ts.offset() == (offset - 6)) && 134 token.text().toString().startsWith("=begin")) { 135 doc.insertString(offset, "\n=end", null); 136 caret.setDot(offset); 137 138 return -1; 139 } else if ((id == RubyTokenId.STRING_BEGIN) || (id == RubyTokenId.QUOTED_STRING_BEGIN)) { 140 String text = token.text().toString(); 141 142 if (text.startsWith("<<")) { 143 StringBuilder markerBuilder = new StringBuilder (); 146 147 for (int i = 2, n = text.length(); i < n; i++) { 148 char c = text.charAt(i); 149 150 if ((c == '\n') || (c == '\r')) { 151 break; 152 } 153 154 markerBuilder.append(c); 155 } 156 157 String marker = markerBuilder.toString(); 158 159 boolean addEndMarker = true; 161 162 if (ts.moveNext()) { 163 token = ts.token(); 164 id = token.id(); 165 text = token.text().toString(); 166 167 if (text.endsWith(marker)) { 168 addEndMarker = false; 169 } 170 } 171 172 if (addEndMarker) { 173 String end = "\n" + marker; 175 176 if (offset == doc.getLength()) { 177 end = end + "\n"; 180 } 181 182 doc.insertString(offset, end, null); 183 caret.setDot(offset); 184 185 return -1; 186 } 187 } 188 } 189 190 boolean[] insertEndResult = new boolean[1]; 192 boolean[] insertRBraceResult = new boolean[1]; 193 int[] indentResult = new int[1]; 194 boolean insert = 195 isEndMissing(doc, offset, false, insertEndResult, insertRBraceResult, null, indentResult); 196 197 if (insert) { 198 boolean insertEnd = insertEndResult[0]; 199 boolean insertRBrace = insertRBraceResult[0]; 200 int indent = indentResult[0]; 201 202 StringBuilder sb = new StringBuilder (); 205 sb.append("\n"); LexUtilities.indent(sb, indent); 207 208 if (insertEnd) { 209 sb.append("end"); } else { 211 assert insertRBrace; 212 sb.append("}"); } 214 215 int insertOffset = offset; doc.insertString(insertOffset, sb.toString(), null); 217 caret.setDot(insertOffset); 218 } 219 220 if ((id == RubyTokenId.RBRACE) && (Utilities.getRowLastNonWhite(doc, offset) == offset)) { 234 int indent = LexUtilities.getLineIndent(doc, offset); 235 StringBuilder sb = new StringBuilder (); 236 sb.append("\n"); LexUtilities.indent(sb, indent); 238 239 int insertOffset = offset; doc.insertString(insertOffset, sb.toString(), null); 241 caret.setDot(insertOffset); 242 } 243 244 return -1; 245 } 246 247 273 static boolean isEndMissing(BaseDocument doc, int offset, boolean skipJunk, 274 boolean[] insertEndResult, boolean[] insertRBraceResult, int[] startOffsetResult, 275 int[] indentResult) throws BadLocationException { 276 int length = doc.getLength(); 277 278 if (startOffsetResult != null) { 286 startOffsetResult[0] = Utilities.getRowFirstNonWhite(doc, offset); 287 } 288 289 int beginEndBalance = LexUtilities.getBeginEndLineBalance(doc, offset); 290 int braceBalance = 291 LexUtilities.getLineBalance(doc, offset, RubyTokenId.LBRACE, RubyTokenId.RBRACE); 292 293 if ((beginEndBalance == 1) || (braceBalance == 1)) { 294 int indent = LexUtilities.getLineIndent(doc, offset); 297 298 boolean insertEnd = beginEndBalance > 0; 301 boolean insertRBrace = braceBalance > 0; 302 int next = Utilities.getRowEnd(doc, offset) + 1; 303 304 for (; next < length; next = Utilities.getRowEnd(doc, next) + 1) { 305 if (Utilities.isRowEmpty(doc, next) || Utilities.isRowWhite(doc, next) || 306 LexUtilities.isCommentOnlyLine(doc, next)) { 307 continue; 308 } 309 310 int nextIndent = LexUtilities.getLineIndent(doc, next); 311 312 if (nextIndent > indent) { 313 insertEnd = false; 314 insertRBrace = false; 315 } else if (nextIndent == indent) { 316 if (insertEnd) { 317 if (LexUtilities.getBeginEndLineBalance(doc, next) < 0) { 318 insertEnd = false; 319 } else { 320 int lineBegin = Utilities.getRowFirstNonWhite(doc, next); 324 325 Token<?extends GsfTokenId> token = 326 LexUtilities.getToken(doc, lineBegin); 327 328 if ((token != null) && LexUtilities.isIndentToken(token.id()) && 329 !LexUtilities.isBeginToken(token.id())) { 330 insertEnd = false; 331 } 332 } 333 } else if (insertRBrace && 334 (LexUtilities.getLineBalance(doc, next, RubyTokenId.LBRACE, 335 RubyTokenId.RBRACE) < 0)) { 336 insertRBrace = false; 337 } 338 } 339 340 break; 341 } 342 343 if (insertEndResult != null) { 344 insertEndResult[0] = insertEnd; 345 } 346 347 if (insertRBraceResult != null) { 348 insertRBraceResult[0] = insertRBrace; 349 } 350 351 if (indentResult != null) { 352 indentResult[0] = indent; 353 } 354 355 return insertEnd || insertRBrace; 356 } 357 358 return false; 359 } 360 361 public boolean beforeCharInserted(Document document, int caretOffset, Caret caret, char ch) 362 throws BadLocationException { 363 BaseDocument doc = (BaseDocument)document; 364 365 367 if (caretOffset == 0) { 370 return false; 371 } 372 373 TokenSequence<?extends GsfTokenId> ts = LexUtilities.getTokenSequence(doc); 374 375 if (ts == null) { 376 return false; 377 } 378 379 ts.move(caretOffset); 380 381 if (!ts.moveNext() && !ts.movePrevious()) { 382 return false; 383 } 384 385 Token<?extends GsfTokenId> token = ts.token(); 386 TokenId id = token.id(); 387 TokenId[] stringTokens = null; 388 TokenId beginTokenId = null; 389 390 if (ch == '\"') { 391 stringTokens = STRING_TOKENS; 392 beginTokenId = RubyTokenId.QUOTED_STRING_BEGIN; 393 } else if (ch == '\'') { 394 stringTokens = STRING_TOKENS; 395 beginTokenId = RubyTokenId.STRING_BEGIN; 396 } else if (ch == '/') { 397 stringTokens = REGEXP_TOKENS; 399 beginTokenId = RubyTokenId.REGEXP_BEGIN; 400 } else if (id == RubyTokenId.ERROR) { 401 String text = token.text().toString(); 402 403 if (text.equals("%")) { 404 if (!Character.isLetter(ch)) { stringTokens = STRING_TOKENS; 407 beginTokenId = RubyTokenId.QUOTED_STRING_BEGIN; 408 } 409 } else if ((text.length() == 2) && (text.charAt(0) == '%') && 410 Character.isLetter(text.charAt(1))) { 411 char c = text.charAt(1); 412 413 switch (c) { 414 case 'q': 415 stringTokens = STRING_TOKENS; 416 beginTokenId = RubyTokenId.STRING_BEGIN; 417 418 break; 419 420 case 'Q': 421 stringTokens = STRING_TOKENS; 422 beginTokenId = RubyTokenId.QUOTED_STRING_BEGIN; 423 424 break; 425 426 case 'r': 427 stringTokens = REGEXP_TOKENS; 428 beginTokenId = RubyTokenId.REGEXP_BEGIN; 429 430 break; 431 432 default: 433 stringTokens = STRING_TOKENS; 435 beginTokenId = RubyTokenId.QUOTED_STRING_BEGIN; 436 } 437 } else { 438 ts.movePrevious(); 439 440 TokenId prevId = ts.token().id(); 441 442 if ((prevId == RubyTokenId.STRING_BEGIN) || 443 (prevId == RubyTokenId.QUOTED_STRING_BEGIN)) { 444 stringTokens = STRING_TOKENS; 445 beginTokenId = prevId; 446 } else if (prevId == RubyTokenId.REGEXP_BEGIN) { 447 stringTokens = REGEXP_TOKENS; 448 beginTokenId = RubyTokenId.REGEXP_BEGIN; 449 } 450 } 451 } else if (((((id == RubyTokenId.STRING_BEGIN) || (id == RubyTokenId.QUOTED_STRING_BEGIN)) && 452 (caretOffset == (ts.offset() + 1))))) { 453 if (!Character.isLetter(ch)) { stringTokens = STRING_TOKENS; 455 beginTokenId = id; 456 } 457 } else if (((id == RubyTokenId.STRING_BEGIN) && (caretOffset == (ts.offset() + 2))) || 458 (id == RubyTokenId.STRING_END)) { 459 stringTokens = STRING_TOKENS; 460 beginTokenId = RubyTokenId.STRING_BEGIN; 461 } else if (((id == RubyTokenId.QUOTED_STRING_BEGIN) && (caretOffset == (ts.offset() + 2))) || 462 (id == RubyTokenId.QUOTED_STRING_END)) { 463 stringTokens = STRING_TOKENS; 464 beginTokenId = RubyTokenId.QUOTED_STRING_BEGIN; 465 } else if (((id == RubyTokenId.REGEXP_BEGIN) && (caretOffset == (ts.offset() + 2))) || 466 (id == RubyTokenId.REGEXP_END)) { 467 stringTokens = REGEXP_TOKENS; 468 beginTokenId = RubyTokenId.REGEXP_BEGIN; 469 } 470 471 if (stringTokens != null) { 472 boolean inserted = 473 completeQuote(doc, caretOffset, caret, ch, stringTokens, beginTokenId); 474 475 if (inserted) { 476 caret.setDot(caretOffset + 1); 477 478 return true; 479 } else { 480 return false; 481 } 482 } 483 484 return false; 485 } 486 487 512 524 public boolean afterCharInserted(Document document, int dotPos, Caret caret, char ch) 525 throws BadLocationException { 526 BaseDocument doc = (BaseDocument)document; 527 528 if (previousAdjustmentOffset != -1) { 532 if (dotPos == previousAdjustmentOffset) { 533 TokenSequence<?extends GsfTokenId> ts = LexUtilities.getTokenSequence(doc); 537 538 if (ts != null) { 539 ts.move(dotPos); 540 } 541 542 if (ts.moveNext() && (ts.offset() < dotPos)) { 543 LexUtilities.setLineIndentation(doc, dotPos, previousAdjustmentIndent); 544 } 545 } 546 547 previousAdjustmentOffset = -1; 548 } 549 550 switch (ch) { 552 case '}': 553 case '{': 554 case ')': 555 case ']': 556 case '(': 557 case '[': { 558 Token<?extends GsfTokenId> token = LexUtilities.getToken(doc, dotPos); 559 TokenId id = token.id(); 560 561 if (id == RubyTokenId.IDENTIFIER) { 562 int length = token.length(); 563 564 if ((length == 2) && "[]".equals(token.text().toString())) { skipClosingBracket(doc, caret, ch, RubyTokenId.RBRACKET); 566 567 return true; 568 } 569 } 570 571 if (((id == RubyTokenId.IDENTIFIER) && (token.length() == 1)) || 572 (id == RubyTokenId.LBRACKET) || (id == RubyTokenId.RBRACKET) || 573 (id == RubyTokenId.LBRACE) || (id == RubyTokenId.RBRACE) || 574 (id == RubyTokenId.LPAREN) || (id == RubyTokenId.RPAREN)) { 575 if (ch == ']') { 576 skipClosingBracket(doc, caret, ch, RubyTokenId.RBRACKET); 577 } else if (ch == ')') { 578 skipClosingBracket(doc, caret, ch, RubyTokenId.RPAREN); 579 } else if (ch == '}') { 580 skipClosingBracket(doc, caret, ch, RubyTokenId.RBRACE); 581 } else if ((ch == '[') || (ch == '(') || (ch == '{')) { 582 completeOpeningBracket(doc, dotPos, caret, ch); 583 } 584 } 585 586 if (ch == '}') { 588 reindent(doc, dotPos, RubyTokenId.RBRACE, caret); 589 } 590 } 591 592 break; 593 594 case 'd': 595 reindent(doc, dotPos, RubyTokenId.END, caret); 597 598 break; 599 600 case 'e': 601 reindent(doc, dotPos, RubyTokenId.ELSE, caret); 603 reindent(doc, dotPos, RubyTokenId.ENSURE, caret); 604 reindent(doc, dotPos, RubyTokenId.RESCUE, caret); 605 606 break; 607 608 case 'f': 609 reindent(doc, dotPos, RubyTokenId.ELSIF, caret); 611 612 break; 613 614 case 'n': 615 reindent(doc, dotPos, RubyTokenId.WHEN, caret); 617 } 618 619 return true; 620 } 621 622 private void reindent(BaseDocument doc, int offset, TokenId id, Caret caret) 623 throws BadLocationException { 624 TokenSequence<?extends GsfTokenId> ts = LexUtilities.getTokenSequence(doc); 625 626 if (ts != null) { 627 ts.move(offset); 628 629 if (!ts.moveNext() && !ts.movePrevious()) { 630 return; 631 } 632 633 Token<?extends GsfTokenId> token = ts.token(); 634 635 if ((token.id() == id)) { 636 if (ts.offset() > Utilities.getRowFirstNonWhite(doc, offset)) { 638 return; 639 } 640 641 OffsetRange begin; 642 643 if (id == RubyTokenId.RBRACE) { 644 begin = LexUtilities.findBwd(ts, RubyTokenId.LBRACE, RubyTokenId.RBRACE); 645 } else { 646 begin = LexUtilities.findBegin(ts); 647 } 648 649 if (begin != OffsetRange.NONE) { 650 int beginOffset = begin.getStart(); 651 int indent = LexUtilities.getLineIndent(doc, beginOffset); 652 previousAdjustmentIndent = LexUtilities.getLineIndent(doc, offset); 653 LexUtilities.setLineIndentation(doc, offset, indent); 654 previousAdjustmentOffset = caret.getDot(); 655 } 656 } 657 } 658 } 659 660 public OffsetRange findMatching(Document document, int offset ) { 661 BaseDocument doc = (BaseDocument)document; 662 663 TokenSequence<?extends GsfTokenId> ts = LexUtilities.getTokenSequence(doc); 664 665 if (ts != null) { 666 ts.move(offset); 667 668 if (!ts.moveNext()) { 669 return OffsetRange.NONE; 670 } 671 672 Token<?extends GsfTokenId> token = ts.token(); 673 674 if (token == null) { 675 return OffsetRange.NONE; 676 } 677 678 TokenId id = token.id(); 679 680 if (id == RubyTokenId.WHITESPACE) { 681 ts.move(offset + 1); 686 687 if (ts.moveNext() && (ts.offset() <= (offset + 1))) { 688 token = ts.token(); 689 id = token.id(); 690 } 691 } 692 693 if (id == RubyTokenId.QUOTED_STRING_BEGIN) { 694 return LexUtilities.findFwd(ts, RubyTokenId.QUOTED_STRING_BEGIN, 695 RubyTokenId.QUOTED_STRING_END); 696 } else if (id == RubyTokenId.QUOTED_STRING_END) { 697 return LexUtilities.findBwd(ts, RubyTokenId.QUOTED_STRING_BEGIN, 698 RubyTokenId.QUOTED_STRING_END); 699 } else if (id == RubyTokenId.STRING_BEGIN) { 700 return LexUtilities.findFwd(ts, RubyTokenId.STRING_BEGIN, RubyTokenId.STRING_END); 701 } else if (id == RubyTokenId.STRING_END) { 702 return LexUtilities.findBwd(ts, RubyTokenId.STRING_BEGIN, RubyTokenId.STRING_END); 703 } else if (id == RubyTokenId.REGEXP_BEGIN) { 704 return LexUtilities.findFwd(ts, RubyTokenId.REGEXP_BEGIN, RubyTokenId.REGEXP_END); 705 } else if (id == RubyTokenId.REGEXP_END) { 706 return LexUtilities.findBwd(ts, RubyTokenId.REGEXP_BEGIN, RubyTokenId.REGEXP_END); 707 } else if (id == RubyTokenId.LPAREN) { 708 return LexUtilities.findFwd(ts, RubyTokenId.LPAREN, RubyTokenId.RPAREN); 709 } else if (id == RubyTokenId.RPAREN) { 710 return LexUtilities.findBwd(ts, RubyTokenId.LPAREN, RubyTokenId.RPAREN); 711 } else if (id == RubyTokenId.LBRACE) { 712 return LexUtilities.findFwd(ts, RubyTokenId.LBRACE, RubyTokenId.RBRACE); 713 } else if (id == RubyTokenId.RBRACE) { 714 return LexUtilities.findBwd(ts, RubyTokenId.LBRACE, RubyTokenId.RBRACE); 715 } else if (id == RubyTokenId.LBRACKET) { 716 return LexUtilities.findFwd(ts, RubyTokenId.LBRACKET, RubyTokenId.RBRACKET); 717 } else if (id == RubyTokenId.RBRACKET) { 718 return LexUtilities.findBwd(ts, RubyTokenId.LBRACKET, RubyTokenId.RBRACKET); 719 } else if (id.primaryCategory().equals("keyword")) { 720 if (LexUtilities.isBeginToken(id)) { 721 return LexUtilities.findEnd(ts); 722 } else if ((id == RubyTokenId.END) || LexUtilities.isIndentToken(id)) { 724 return LexUtilities.findBegin(ts); 725 } 726 } 727 } 728 729 return OffsetRange.NONE; 730 } 731 732 741 public boolean charBackspaced(Document document, int dotPos, Caret caret, char ch) 742 throws BadLocationException { 743 BaseDocument doc = (BaseDocument)document; 744 745 if ((ch == '(') || (ch == '[')) { 746 char tokenAtDot = LexUtilities.getTokenChar(doc, dotPos); 747 748 if (((tokenAtDot == ']') && 749 (LexUtilities.getTokenBalance(doc, RubyTokenId.LBRACKET, RubyTokenId.RBRACKET) != 0)) || 750 ((tokenAtDot == ')') && 751 (LexUtilities.getTokenBalance(doc, RubyTokenId.LPAREN, RubyTokenId.RPAREN) != 0))) { 752 doc.remove(dotPos, 1); 753 } 754 } else if (ch == '\"') { 755 char[] match = doc.getChars(dotPos, 1); 756 757 if ((match != null) && (match[0] == '\"')) { 758 doc.remove(dotPos, 1); 759 } 760 } else if (ch == '\'') { 761 char[] match = doc.getChars(dotPos, 1); 762 763 if ((match != null) && (match[0] == '\'')) { 764 doc.remove(dotPos, 1); 765 } 766 } 767 768 return true; 769 } 770 771 781 private void skipClosingBracket(BaseDocument doc, Caret caret, char bracket, TokenId bracketId) 782 throws BadLocationException { 783 int caretOffset = caret.getDot(); 784 785 if (isSkipClosingBracket(doc, caretOffset, bracketId)) { 786 doc.remove(caretOffset - 1, 1); 787 caret.setDot(caretOffset); } 789 } 790 791 800 private boolean isSkipClosingBracket(BaseDocument doc, int caretOffset, TokenId bracketId) 801 throws BadLocationException { 802 if (caretOffset == doc.getLength()) { 805 return false; } 807 808 boolean skipClosingBracket = false; 810 TokenSequence<?extends GsfTokenId> ts = LexUtilities.getTokenSequence(doc); 811 812 if (ts == null) { 813 return false; 814 } 815 816 ts.move(caretOffset); 819 820 if (!ts.moveNext()) { 821 return false; 822 } 823 824 Token<?extends GsfTokenId> token = ts.token(); 825 826 if ((token != null) && (token.id() == bracketId)) { 828 int bracketIntId = bracketId.ordinal(); 829 int leftBracketIntId = 830 (bracketIntId == RubyTokenId.RPAREN.ordinal()) ? RubyTokenId.LPAREN.ordinal() 831 : RubyTokenId.LBRACKET.ordinal(); 832 833 ts.moveNext(); 835 836 Token<?extends GsfTokenId> nextToken = ts.token(); 837 838 while ((nextToken != null) && (nextToken.id() == bracketId)) { 839 token = nextToken; 840 841 if (!ts.moveNext()) { 842 break; 843 } 844 845 nextToken = ts.token(); 846 } 847 848 int braceBalance = 0; int bracketBalance = -1; Token<?extends GsfTokenId> lastRBracket = token; 854 ts.movePrevious(); 855 token = ts.token(); 856 857 boolean finished = false; 858 859 while (!finished && (token != null)) { 860 int tokenIntId = token.id().ordinal(); 861 862 if ((token.id() == RubyTokenId.LPAREN) || (token.id() == RubyTokenId.LBRACKET)) { 863 if (tokenIntId == bracketIntId) { 864 bracketBalance++; 865 866 if (bracketBalance == 0) { 867 if (braceBalance != 0) { 868 bracketBalance = 1; 877 } 878 879 finished = true; 880 } 881 } 882 } else if ((token.id() == RubyTokenId.RPAREN) || 883 (token.id() == RubyTokenId.RBRACKET)) { 884 if (tokenIntId == bracketIntId) { 885 bracketBalance--; 886 } 887 } else if (token.id() == RubyTokenId.LBRACE) { 888 braceBalance++; 889 890 if (braceBalance > 0) { finished = true; 892 } 893 } else if (token.id() == RubyTokenId.RBRACE) { 894 braceBalance--; 895 } 896 897 if (!ts.movePrevious()) { 898 break; 899 } 900 901 token = ts.token(); 902 } 903 904 if (bracketBalance != 0) { skipClosingBracket = true; 907 } else { braceBalance = 0; 914 bracketBalance = 1; 916 TokenHierarchy<BaseDocument> th = TokenHierarchy.get(doc); 918 919 int ofs = lastRBracket.offset(th); 920 921 ts.move(ofs); 922 ts.moveNext(); 923 token = ts.token(); 924 finished = false; 925 926 while (!finished && (token != null)) { 927 if ((token.id() == RubyTokenId.LPAREN) || (token.id() == RubyTokenId.LBRACKET)) { 929 if (token.id().ordinal() == leftBracketIntId) { 930 bracketBalance++; 931 } 932 } else if ((token.id() == RubyTokenId.RPAREN) || 933 (token.id() == RubyTokenId.RBRACKET)) { 934 if (token.id().ordinal() == bracketIntId) { 935 bracketBalance--; 936 937 if (bracketBalance == 0) { 938 if (braceBalance != 0) { 939 bracketBalance = -1; 947 } 948 949 finished = true; 950 } 951 } 952 } else if (token.id() == RubyTokenId.LBRACE) { 953 braceBalance++; 954 } else if (token.id() == RubyTokenId.RBRACE) { 955 braceBalance--; 956 957 if (braceBalance < 0) { finished = true; 959 } 960 } 961 962 if (!ts.movePrevious()) { 964 break; 965 } 966 967 token = ts.token(); 968 } 969 970 skipClosingBracket = (bracketBalance == 0); 973 } 974 } 975 976 return skipClosingBracket; 977 } 978 979 987 private void completeOpeningBracket(BaseDocument doc, int dotPos, Caret caret, char bracket) 988 throws BadLocationException { 989 if (isCompletablePosition(doc, dotPos + 1)) { 990 String matchinBracket = "" + matching(bracket); 991 doc.insertString(dotPos + 1, matchinBracket, null); 992 caret.setDot(dotPos + 1); 993 } 994 } 995 996 private boolean isEscapeSequence(BaseDocument doc, int dotPos) 1000 throws BadLocationException { 1001 if (dotPos <= 0) { 1002 return false; 1003 } 1004 1005 char previousChar = doc.getChars(dotPos - 1, 1)[0]; 1006 1007 return previousChar == '\\'; 1008 } 1009 1010 1018 private boolean completeQuote(BaseDocument doc, int dotPos, Caret caret, char bracket, 1019 TokenId[] stringTokens, TokenId beginToken) throws BadLocationException { 1020 if (isEscapeSequence(doc, dotPos)) { 1022 return false; 1023 } 1024 1025 if (doc.getLength() < dotPos) { 1027 return false; 1028 } 1029 1030 TokenSequence<?extends GsfTokenId> ts = LexUtilities.getTokenSequence(doc); 1031 1032 if (ts == null) { 1033 return false; 1034 } 1035 1036 ts.move(dotPos); 1037 1038 if (!ts.moveNext() && !ts.movePrevious()) { 1039 return false; 1040 } 1041 1042 Token<?extends GsfTokenId> token = ts.token(); 1043 Token<?extends GsfTokenId> previousToken = null; 1044 1045 if (ts.movePrevious()) { 1046 previousToken = ts.token(); 1047 } 1048 1049 int lastNonWhite = Utilities.getRowLastNonWhite(doc, dotPos); 1050 1051 boolean eol = lastNonWhite < dotPos; 1053 1054 if ((token.id() == RubyTokenId.BLOCK_COMMENT) || (token.id() == RubyTokenId.LINE_COMMENT)) { 1055 return false; 1056 } else if ((token.id() == RubyTokenId.WHITESPACE) && eol && ((dotPos - 1) > 0)) { 1057 token = LexUtilities.getToken(doc, dotPos - 1); 1059 1060 if (token.id() == RubyTokenId.LINE_COMMENT) { 1061 return false; 1062 } 1063 } 1064 1065 boolean completablePosition = isQuoteCompletablePosition(doc, dotPos); 1066 1067 boolean insideString = false; 1068 TokenId id = token.id(); 1069 1070 for (TokenId currId : stringTokens) { 1071 if (id == currId) { 1072 insideString = true; 1073 } 1074 } 1075 1076 if ((id == RubyTokenId.ERROR) && (previousToken != null) && 1077 (previousToken.id() == beginToken)) { 1078 insideString = true; 1079 } 1080 1081 if (!insideString) { 1082 if ((token.id() == RubyTokenId.WHITESPACE) && eol) { 1085 if ((dotPos - 1) > 0) { 1086 token = LexUtilities.getToken(doc, dotPos - 1); 1087 insideString = (token.id() == RubyTokenId.STRING_LITERAL) || 1089 (token.id() == RubyTokenId.CHAR_LITERAL); 1090 } 1091 } 1092 } 1093 1094 if (insideString) { 1095 if (eol) { 1096 return false; } else { 1098 char chr = doc.getChars(dotPos, 1)[0]; 1100 1101 if (chr == bracket) { 1102 doc.insertString(dotPos, "" + bracket, null); doc.remove(dotPos, 1); 1104 1105 return true; 1106 } 1107 } 1108 } 1109 1110 if ((completablePosition && !insideString) || eol) { 1111 doc.insertString(dotPos, "" + bracket + matching(bracket), null); 1113 return true; 1114 } 1115 1116 return false; 1117 } 1118 1119 1126 private boolean isCompletablePosition(BaseDocument doc, int dotPos) 1127 throws BadLocationException { 1128 if (dotPos == doc.getLength()) { 1130 return true; 1131 } else { 1132 char chr = doc.getChars(dotPos, 1)[0]; 1134 1135 return ((chr == ')') || (chr == ',') || (chr == '\"') || (chr == '\'') || (chr == ' ') || 1136 (chr == ']') || (chr == '}') || (chr == '\n') || (chr == '\t') || (chr == ';')); 1137 } 1138 } 1139 1140 private boolean isQuoteCompletablePosition(BaseDocument doc, int dotPos) 1141 throws BadLocationException { 1142 if (dotPos == doc.getLength()) { 1144 return true; 1145 } else { 1146 int eol = Utilities.getRowEnd(doc, dotPos); 1148 1149 if ((dotPos == eol) || (eol == -1)) { 1150 return false; 1151 } 1152 1153 int firstNonWhiteFwd = Utilities.getFirstNonWhiteFwd(doc, dotPos, eol); 1154 1155 if (firstNonWhiteFwd == -1) { 1156 return false; 1157 } 1158 1159 char chr = doc.getChars(firstNonWhiteFwd, 1)[0]; 1160 1161 return ((chr == ')') || (chr == ',') || (chr == '+') || (chr == '}') || (chr == ';') || 1162 (chr == ']')); 1163 } 1164 } 1165 1166 1170 private char matching(char bracket) { 1171 switch (bracket) { 1172 case '(': 1173 return ')'; 1174 1175 case '/': 1176 return '/'; 1177 1178 case '[': 1179 return ']'; 1180 1181 case '\"': 1182 return '\"'; 1184 case '\'': 1185 return '\''; 1186 1187 case '{': 1188 return '}'; 1189 1190 case '}': 1191 return '{'; 1192 1193 default: 1194 return bracket; 1195 } 1196 } 1197 1198 public List <OffsetRange> findLogicalRanges(CompilationInfo info, int caretOffset) { 1199 Node root = AstUtilities.getRoot(info); 1200 1201 if (root == null) { 1202 return Collections.emptyList(); 1203 } 1204 1205 AstPath path = new AstPath(root, caretOffset); 1206 List <OffsetRange> ranges = new ArrayList <OffsetRange>(); 1207 1208 try { 1211 BaseDocument doc = (BaseDocument)info.getDocument(); 1212 Token<?extends GsfTokenId> token = LexUtilities.getToken(doc, caretOffset); 1213 1214 if ((token != null) && (token.id() == RubyTokenId.LINE_COMMENT)) { 1215 int begin = Utilities.getRowStart(doc, caretOffset); 1217 int end = Utilities.getRowEnd(doc, caretOffset); 1218 1219 if (LexUtilities.isCommentOnlyLine(doc, caretOffset)) { 1220 ranges.add(new OffsetRange(begin, end)); 1221 1222 int lineBegin = begin; 1223 int lineEnd = end; 1224 1225 while (true) { 1226 int newBegin = Utilities.getRowStart(doc, begin - 1); 1227 1228 if ((newBegin <= 0) || !LexUtilities.isCommentOnlyLine(doc, newBegin)) { 1229 break; 1230 } 1231 1232 begin = newBegin; 1233 } 1234 1235 int length = doc.getLength(); 1236 1237 while (true) { 1238 int newEnd = Utilities.getRowEnd(doc, end + 1); 1239 1240 if ((newEnd >= length) || !LexUtilities.isCommentOnlyLine(doc, newEnd)) { 1241 break; 1242 } 1243 1244 end = newEnd; 1245 } 1246 1247 if ((lineBegin > begin) || (lineEnd < end)) { 1248 ranges.add(new OffsetRange(begin, end)); 1249 } 1250 } else { 1251 TokenHierarchy<Document > th = TokenHierarchy.get((Document )doc); 1253 int offset = token.offset(th); 1254 ranges.add(new OffsetRange(offset, offset + token.length())); 1255 } 1256 } 1257 } catch (BadLocationException ble) { 1258 Exceptions.printStackTrace(ble); 1259 } catch (IOException ioe) { 1260 Exceptions.printStackTrace(ioe); 1261 } 1262 1263 Iterator <Node> it = path.leafToRoot(); 1264 1265 while (it.hasNext()) { 1266 Node node = it.next(); 1267 1268 if (node instanceof NewlineNode) { 1270 continue; 1271 } 1272 1273 OffsetRange range = AstUtilities.getRange(node); 1274 1275 if (range.containsInclusive(caretOffset)) { 1278 ranges.add(range); 1279 } 1280 } 1281 1282 return ranges; 1283 } 1284} 1285 | Popular Tags |