1 21 22 package org.armedbear.j; 23 24 import gnu.regexp.RE; 25 import gnu.regexp.REMatch; 26 import gnu.regexp.UncheckedRE; 27 import java.awt.event.KeyEvent ; 28 import java.awt.event.MouseEvent ; 29 import javax.swing.JMenuItem ; 30 import javax.swing.JPopupMenu ; 31 import javax.swing.undo.CompoundEdit ; 32 33 public class JavaMode extends AbstractMode implements Constants, Mode 34 { 35 private static final String [] javaConditionals = { 36 "if", 37 "else", 38 "do", 39 "while", 40 "for", 41 "switch", 42 "try", 43 "catch", 44 "finally", 45 "synchronized" 46 }; 47 48 private static Mode mode; 49 private static Object jdb; 50 51 protected String [] conditionals; 52 53 private JavaMode() 54 { 55 super(JAVA_MODE, JAVA_MODE_NAME); 56 keywords = new Keywords(this); 57 conditionals = javaConditionals; 58 } 59 60 protected JavaMode(int id, String displayName) 61 { 62 super(id, displayName); 63 } 64 65 public static Mode getMode() 68 { 69 if (mode == null) 70 mode = new JavaMode(); 71 return mode; 72 } 73 74 public static final Object getJdb() 75 { 76 return jdb; 77 } 78 79 public static final void setJdb(Object obj) 80 { 81 jdb = obj; 82 } 83 84 public boolean canIndent() 85 { 86 return true; 87 } 88 89 public SyntaxIterator getSyntaxIterator(Position pos) 90 { 91 return new JavaSyntaxIterator(pos); 92 } 93 94 public String getCommentStart() 95 { 96 return "// "; 97 } 98 99 public Formatter getFormatter(Buffer buffer) 100 { 101 return new JavaFormatter(buffer); 102 } 103 104 protected void setKeyMapDefaults(KeyMap km) 105 { 106 km.mapKey('{', "electricOpenBrace"); 107 km.mapKey('}', "electricCloseBrace"); 108 km.mapKey(KeyEvent.VK_TAB, CTRL_MASK, "insertTab"); 109 km.mapKey(KeyEvent.VK_TAB, 0, "tab"); 110 km.mapKey(KeyEvent.VK_ENTER, 0, "newlineAndIndent"); 111 km.mapKey(';', "electricSemi"); 112 km.mapKey(':', "electricColon"); 113 km.mapKey('*', "electricStar"); 114 km.mapKey(KeyEvent.VK_T, CTRL_MASK, "findTag"); 115 km.mapKey(KeyEvent.VK_PERIOD, ALT_MASK, "findTagAtDot"); 116 km.mapKey(KeyEvent.VK_COMMA, ALT_MASK, "listMatchingTagsAtDot"); 117 km.mapKey(KeyEvent.VK_PERIOD, CTRL_MASK | ALT_MASK, "findTagAtDotOtherWindow"); 118 km.mapKey(')', "closeParen"); 119 km.mapKey(KeyEvent.VK_I, ALT_MASK, "cycleIndentSize"); 120 121 km.mapKey(KeyEvent.VK_9, CTRL_MASK | SHIFT_MASK, "insertParentheses"); 122 km.mapKey(KeyEvent.VK_0, CTRL_MASK | SHIFT_MASK, "movePastCloseAndReindent"); 123 124 km.mapKey(KeyEvent.VK_OPEN_BRACKET, CTRL_MASK | SHIFT_MASK, "insertBraces"); 125 km.mapKey(KeyEvent.VK_BRACELEFT, CTRL_MASK | SHIFT_MASK, "insertBraces"); 127 128 km.mapKey(KeyEvent.VK_F12, 0, "wrapComment"); 129 130 km.mapKey(0xffc9, 0, "wrapComment"); 133 km.mapKey(KeyEvent.VK_OPEN_BRACKET, CTRL_MASK, "fold"); 134 km.mapKey(KeyEvent.VK_CLOSE_BRACKET, CTRL_MASK, "unfold"); 135 136 km.mapKey(KeyEvent.VK_F9, 0, "compile"); 137 km.mapKey(KeyEvent.VK_F9, CTRL_MASK, "recompile"); 138 km.mapKey(KeyEvent.VK_F1, ALT_MASK, "jdkHelp"); 139 km.mapKey(KeyEvent.VK_F1, CTRL_MASK, "source"); 140 141 km.mapKey(KeyEvent.VK_COMMA, CTRL_MASK | SHIFT_MASK, "htmlInsertTag"); 143 km.mapKey(0x7c, CTRL_MASK | SHIFT_MASK, "htmlInsertTag"); 145 146 if (Editor.checkExperimental()) { 147 km.mapKey(KeyEvent.VK_SEMICOLON, ALT_MASK, "JavaMode.insertComment"); 148 km.mapKey(KeyEvent.VK_ENTER, ALT_MASK, "JavaMode.newlineAndIndentForComment"); 149 } 150 151 if (Platform.isPlatformLinux()) { 152 km.mapKey(0x68, CTRL_MASK | SHIFT_MASK, "insertParentheses"); 155 km.mapKey(0x69, CTRL_MASK | SHIFT_MASK, "movePastCloseAndReindent"); 156 km.mapKey(0xbb, CTRL_MASK | SHIFT_MASK, "insertBraces"); 157 } 158 } 159 160 public void populateModeMenu(Editor editor, Menu menu) 161 { 162 menu.add(editor, "Compile...", 'C', "compile"); 163 menu.add(editor, "Recompile", 'R', "recompile"); 164 boolean enabled = CompilationCommands.getCompilationBuffer() != null; 165 menu.addSeparator(); 166 menu.add(editor, "Next Error", 'N', "nextError", enabled); 167 menu.add(editor, "Previous Error", 'P', "previousError", enabled); 168 menu.add(editor, "Show Error Message", 'M', "showMessage", enabled); 169 menu.addSeparator(); 170 MenuItem jdbMenuItem = menu.add(editor, "Debug...", 'D', "jdb"); 171 if (jdb != null) 172 jdbMenuItem.setEnabled(false); 173 else { 174 try { 175 Class.forName("com.sun.jdi.Bootstrap"); 176 } 177 catch (ClassNotFoundException e) { 178 jdbMenuItem.setEnabled(false); 179 } 180 } 181 } 182 183 public JPopupMenu getContextMenu(Editor editor) 184 { 185 final JPopupMenu popup = new JPopupMenu (); 186 if (jdb != null) { 187 final Line line = editor.getDotLine(); 188 if (line != null) { 189 final Dispatcher dispatcher = editor.getDispatcher(); 190 JMenuItem menuItem = 191 addContextMenuItem("Set breakpoint", 192 "jdbSetBreakpoint", popup, dispatcher); 193 if (line.isBlank() || line.getAnnotation() != null) 194 menuItem.setEnabled(false); 195 menuItem = 196 addContextMenuItem("Delete breakpoint", 197 "jdbDeleteBreakpoint", popup, dispatcher); 198 if (line.getAnnotation() == null) 199 menuItem.setEnabled(false); 200 menuItem = 201 addContextMenuItem("Run to current line", 202 "jdbRunToCurrentLine", popup, dispatcher); 203 if (line.isBlank()) 204 menuItem.setEnabled(false); 205 popup.addSeparator(); 206 } 207 } 208 addDefaultContextMenuItems(editor, popup); 209 popup.pack(); 210 return popup; 211 } 212 213 public NavigationComponent getSidebarComponent(Editor editor) 214 { 215 if (getId() == JAVA_MODE) { 216 View view = editor.getCurrentView(); 217 if (view == null) 218 return null; if (view.getSidebarComponent() == null) 220 view.setSidebarComponent(new JavaTree(editor)); 221 return view.getSidebarComponent(); 222 } 223 return super.getSidebarComponent(editor); 225 } 226 227 public Tagger getTagger(SystemBuffer buffer) 228 { 229 return new JavaTagger(buffer); 230 } 231 232 public boolean isTaggable() 233 { 234 return true; 235 } 236 237 public boolean hasQualifiedNames() 238 { 239 return true; 240 } 241 242 public boolean isQualifiedName(String s) 243 { 244 return s.indexOf('.') >= 0; 245 } 246 247 public int getCorrectIndentation(final Line line, final Buffer buffer) 248 { 249 if (line.flags() == STATE_COMMENT) 250 return indentComment(line, buffer); 251 final String text = line.trim(); 252 final char textFirstChar = text.length() > 0 ? text.charAt(0) : 0; 253 if (textFirstChar == '}') 254 return indentClosingBrace(line, buffer); 255 256 if (textFirstChar == 'c' || textFirstChar == 'd') { 257 final String firstIdentifier = getFirstIdentifier(text); 259 if (firstIdentifier.equals("case") || firstIdentifier.equals("default")) 260 return indentSwitchLabel(line, buffer); 261 } else if (textFirstChar == 'e') { 263 final String firstIdentifier = getFirstIdentifier(text); 265 if (firstIdentifier.equals("else") || firstIdentifier.equals("elseif")) { 266 Position match = matchElse(new Position(line, 0)); 267 if (match != null) 268 return buffer.getIndentation(match.getLine()); 269 } 270 } 272 273 Position paren = findEnclosingParen(new Position(line, 0)); 274 if (paren != null) 275 return indentInParen(paren, buffer); 276 277 final Line model = findModel(line); 278 if (model == null) 279 return 0; 280 281 final int indentSize = buffer.getIndentSize(); 282 283 final String firstIdentifier = getFirstIdentifier(text); 284 if (firstIdentifier.equals("throws") || 285 firstIdentifier.equals("implements")) 286 { 287 Position pos = findBeginningOfStatement(new Position(model, 0)); 288 return buffer.getIndentation(pos.getLine()) + indentSize; 289 } 290 291 final String modelText = trimSyntacticWhitespace(model.getText()); 292 293 final char modelLastChar = modelText.charAt(modelText.length()-1); 295 296 if (modelLastChar == '{') 297 return indentAfterOpeningBrace(model, modelText, buffer); 298 299 if (modelLastChar == ')') 300 return indentAfterCloseParen(model, text, textFirstChar, buffer); 301 302 final String lastIdentifier = getLastIdentifier(modelText); 303 if (lastIdentifier != null && lastIdentifier.equals("else")) 304 return indentAfterElse(model, text, textFirstChar, buffer); 305 306 final char modelFirstChar = modelText.charAt(0); 307 if (modelFirstChar == 'c' || modelFirstChar == 'd') { 308 final String modelFirstIdentifier = getFirstIdentifier(modelText); 309 if (modelFirstIdentifier.equals("case") || 310 modelFirstIdentifier.equals("default")) 311 return indentAfterSwitchLabel(model, text, textFirstChar, buffer); 312 } 314 315 final int indent = getIndentationOfEnclosingScope(line, buffer); 316 317 if (textFirstChar == '{') { 318 if (buffer.getBooleanProperty(Property.INDENT_BEFORE_BRACE)) { 319 if (!isOpeningBraceOfClassOrMethod(line)) 321 return indent + indentSize; 322 } 323 return indent; 324 } 325 326 if (modelLastChar == ',') { 327 if (buffer.getModeId() == CPP_MODE && modelFirstChar == ':') { 328 return indent + 2; 331 } 332 if (isInArrayInitializer(line)) 333 return indent; 334 return indent + indentSize; 336 } 337 338 if (isContinued(modelText, modelLastChar)) 340 return indent + indentSize; 341 342 return indent; 343 } 344 345 private final int indentComment(Line line, Buffer buffer) 346 { 347 final Line model = findModel(line); 348 if (model == null) 349 return 0; 350 int indent = buffer.getIndentation(model); 351 if (model.trim().startsWith("/*")) 352 if (line.trim().startsWith("*")) 353 return indent+1; 354 return indent; 355 } 356 357 private final int indentClosingBrace(Line line, Buffer buffer) 358 { 359 Position pos = matchClosingBrace(new Position(line, 0)); 360 if (isOpeningBraceOfClassOrMethod(pos.getLine())) 361 pos = findBeginningOfStatement(pos); 362 else if (!pos.getLine().trim().startsWith("{")) 363 pos = findPreviousConditional(pos); 364 return buffer.getIndentation(pos.getLine()); 365 } 366 367 private final int indentSwitchLabel(Line line, Buffer buffer) 368 { 369 Line switchLine = findSwitch(line); 370 if (switchLine != null) 371 return buffer.getIndentation(switchLine) + buffer.getIndentSize(); 372 return 0; 373 } 374 375 private final int indentInParen(Position posParen, Buffer buffer) 376 { 377 final Line line = posParen.getLine(); 378 if (line.trim().endsWith("(") || !buffer.getBooleanProperty(Property.LINEUP_ARGLIST)) 379 return buffer.getIndentation(line) + buffer.getIndentSize(); 380 final int limit = line.length(); 381 int offset = posParen.getOffset(); 382 do { 383 ++offset; 384 } while (offset < limit && line.charAt(offset) <= ' '); 385 if (offset <= limit) 386 return buffer.getCol(line, offset); 387 return 0; 388 } 389 390 private final int indentAfterOpeningBrace(Line model, String modelText, 391 Buffer buffer) 392 { 393 final int indentSize = buffer.getIndentSize(); 394 if (isOpeningBraceOfClassOrMethod(model)) { 395 Position pos = findBeginningOfStatement(new Position(model, 0)); 396 int indent = buffer.getIndentation(pos.getLine()); 397 if (buffer.getBooleanProperty(Property.INDENT_AFTER_OPENING_BRACE)) 398 indent += indentSize; 399 return indent; 400 } 401 Position pos = new Position(model, model.length()-1); 402 if (modelText.charAt(0) != '{') 403 pos = findPreviousConditional(pos); 404 int indent = buffer.getIndentation(pos.getLine()); 405 if (buffer.getBooleanProperty(Property.INDENT_AFTER_BRACE)) 406 indent += indentSize; 407 final boolean indentBeforeBrace = 408 buffer.getBooleanProperty(Property.INDENT_BEFORE_BRACE); 409 if (indentBeforeBrace && pos.getLine() != model) 410 indent += indentSize; 411 return indent; 412 } 413 414 private final int indentAfterElse(Line model, String text, 415 char textFirstChar, Buffer buffer) 416 { 417 int indent = buffer.getIndentation(model); 418 final boolean indentBeforeBrace = 419 buffer.getBooleanProperty(Property.INDENT_BEFORE_BRACE); 420 if (indentBeforeBrace || textFirstChar != '{') 421 return indent + buffer.getIndentSize(); 422 else 423 return indent; 424 } 425 426 private final int indentAfterSwitchLabel(Line model, String text, 427 char textFirstChar, Buffer buffer) 428 { 429 int indent = buffer.getIndentation(model); 430 final boolean indentBeforeBrace = 431 buffer.getBooleanProperty(Property.INDENT_BEFORE_BRACE); 432 if (indentBeforeBrace || textFirstChar != '{') 433 return indent + buffer.getIndentSize(); 434 else 435 return indent; 436 } 437 438 private final int indentAfterCloseParen(Line model, String text, 439 char textFirstChar, Buffer buffer) 440 { 441 SyntaxIterator it = getSyntaxIterator(new Position(model, model.length())); 443 char c; 444 do { 445 c = it.prevChar(); 446 } while (c != SyntaxIterator.DONE && c != ')'); 447 Position pos = it.getPosition(); 448 pos = matchClosingParen(pos); 449 boolean indent = false; 450 final String s = getIdentifierBefore(pos); 451 final String [] indentAfter = {"if", "while", "for", "switch", "catch"}; 452 if (Utilities.isOneOf(s, indentAfter)) { 453 indent = true; 454 } else if (buffer.getModeId() == JAVA_MODE) { 455 if (s.equals("synchronized")) 456 indent = true; 457 } else if (buffer.getModeId() == PHP_MODE) { 458 if (s.equals("elseif") || s.equals("foreach")) 459 indent = true; 460 } 461 if (indent) { 462 int modelIndent = buffer.getIndentation(pos.getLine()); 463 if (textFirstChar != '{' || 464 buffer.getBooleanProperty(Property.INDENT_BEFORE_BRACE)) 465 return modelIndent + buffer.getIndentSize(); 466 else 467 return modelIndent; 468 } 469 if (buffer.getModeId() == JAVA_MODE) { 470 RE re = new UncheckedRE("\\s+new\\s+"); 471 if (re.getMatch(pos.getLine().getText().substring(0, pos.getOffset())) != null) 472 indent = true; 473 } 474 int modelIndent = 475 buffer.getIndentation(findBeginningOfStatement(pos).getLine()); 476 if (indent && (textFirstChar != '{' || buffer.getBooleanProperty(Property.INDENT_BEFORE_BRACE))) 477 return modelIndent + buffer.getIndentSize(); 478 else 479 return modelIndent; 480 } 481 482 private final int getIndentationOfEnclosingScope(Line line, Buffer buffer) 483 { 484 SyntaxIterator it = getSyntaxIterator(new Position(line, 0)); 485 loop: 486 while (true) { 487 switch (it.prevChar()) { 488 case ')': { 489 Position pos = matchClosingParen(it.getPosition()); 490 it = getSyntaxIterator(pos); 491 break; 492 } 493 case '}': { 494 Position pos = matchClosingBrace(it.getPosition()); 495 pos = findBeginningOfStatement(pos); 496 return buffer.getIndentation(pos.getLine()); 497 } 498 case '{': { 499 Line model = it.getLine(); 500 String modelText = trimSyntacticWhitespace(model.getText()); 501 if (modelText.equals("{")) 502 return buffer.getIndentation(model) + buffer.getIndentSize(); 503 return indentAfterOpeningBrace(model, modelText, buffer); 504 } 505 case ':': { 506 String firstIdentifier = getFirstIdentifier(it.getLine()); 507 if (firstIdentifier.equals("case") || firstIdentifier.equals("default")) 508 return buffer.getIndentation(it.getLine()) + buffer.getIndentSize(); 509 break; 510 } 511 case SyntaxIterator.DONE: 512 return 0; 513 } 514 } 515 } 516 517 private boolean isInArrayInitializer(Line line) 518 { 519 Position match = matchClosingBrace(new Position(line, 0)); 521 SyntaxIterator it = getSyntaxIterator(match); 522 char c; 523 do { 524 c = it.prevChar(); 525 } while (c != SyntaxIterator.DONE && Character.isWhitespace(c)); 526 if (c == '=' || c == ']') 527 return true; 528 return false; 529 } 530 531 protected static Line findModel(Line line) 532 { 533 Line model = line.previous(); 534 if (line.flags() == STATE_COMMENT) { 535 while (model != null && model.isBlank()) 537 model = model.previous(); 538 } else { 539 while (model != null) { 540 if (isAcceptableModel(model)) 541 break; else 543 model = model.previous(); 544 } 545 } 546 return model; 547 } 548 549 private static final boolean isAcceptableModel(Line line) 550 { 551 int flags = line.flags(); 552 if (flags == STATE_COMMENT || flags == STATE_QUOTE) 553 return false; 554 if (line.isBlank()) 555 return false; 556 String trim = line.trim(); 557 char firstChar = trim.charAt(0); 558 if (firstChar == '/') { 559 if (trim.length() > 1 && trim.charAt(1) =='/') 560 return false; 561 } else if (firstChar == '#') 562 return false; 563 String s = trimSyntacticWhitespace(line.getText()); 564 if (s.length() == 0) 565 return false; 566 return true; 567 } 568 569 private boolean isOpeningBraceOfClassOrMethod(Line line) 571 { 572 if (line.length() == 0) 573 return false; 574 String text = trimSyntacticWhitespace(line.getText()); 575 if (!text.endsWith("{")) 576 return false; 577 if (text.equals("{")) { 578 Line modelLine = findModel(line); 579 if (modelLine == null) 580 return true; 581 Position beginningOfStatement = 582 findBeginningOfStatement(new Position(modelLine, 0)); 583 text = beginningOfStatement.getLine().trim(); 584 } else 585 text = text.substring(0, text.length()-1).trim(); 586 if (text.indexOf('=') >= 0) 587 return false; 588 final String firstIdentifier = getFirstIdentifier(text); 589 if (Utilities.isOneOf(firstIdentifier, conditionals)) 590 return false; 591 if (firstIdentifier.equals("case") || firstIdentifier.equals("default")) 592 return false; 593 return true; 594 } 595 596 protected String getFirstIdentifier(String s) 597 { 598 return Utilities.getFirstIdentifier(s, this); 599 } 600 601 protected final String getFirstIdentifier(Line line) 602 { 603 return getFirstIdentifier(line.trim()); 604 } 605 606 protected final String getLastIdentifier(String s) 607 { 608 int i = s.length()-1; 609 while (i >= 0) { 610 if (isIdentifierPart(s.charAt(i))) { 611 if (i > 0) 612 --i; 613 else 614 break; 615 } else { 616 ++i; 617 break; 618 } 619 } 620 if (i >= 0 && i < s.length()) 621 return s.substring(i); 622 return null; 623 } 624 625 private final String getIdentifierBefore(Position pos) 626 { 627 while (pos.prev()) 628 if (!Character.isWhitespace(pos.getChar())) 629 break; 630 while (isIdentifierPart(pos.getChar()) && pos.prev()) 631 ; 632 while (!isIdentifierStart(pos.getChar()) && pos.next()) 633 ; 634 return pos.getIdentifier(this); 635 } 636 637 protected final Position matchClosingBrace(Position start) 638 { 639 SyntaxIterator it = getSyntaxIterator(start); 640 int count = 1; 641 while (true) { 642 switch (it.prevChar()) { 643 case '}': 644 ++count; 645 break; 646 case '{': 647 --count; 648 if (count == 0) 649 return it.getPosition(); 650 break; 651 case SyntaxIterator.DONE: 652 return it.getPosition(); 653 default: 654 break; 655 } 656 } 657 } 658 659 protected final Position matchClosingParen(Position start) 660 { 661 SyntaxIterator it = getSyntaxIterator(start); 662 int count = 1; 663 while (true) { 664 switch (it.prevChar()) { 665 case ')': 666 ++count; 667 break; 668 case '(': 669 --count; 670 if (count == 0) 671 return it.getPosition(); 672 break; 673 case SyntaxIterator.DONE: 674 return it.getPosition(); 675 default: 676 break; 677 } 678 } 679 } 680 681 protected Position findEnclosingParen(Position start) 684 { 685 SyntaxIterator it = getSyntaxIterator(start); 686 int parenCount = 0; 687 int braceCount = 0; 688 boolean seenBrace = false; 689 while (true) { 690 switch (it.prevChar()) { 691 case '{': 692 if (braceCount == 0) 693 return null; --braceCount; 695 seenBrace = true; 696 break; 697 case '}': 698 ++braceCount; 699 seenBrace = true; 700 break; 701 case ';': 702 if (seenBrace) 703 return null; 704 break; 705 case ')': 706 ++parenCount; 707 break; 708 case '(': 709 if (parenCount == 0) 710 return it.getPosition(); --parenCount; 712 break; 713 case SyntaxIterator.DONE: 714 return null; 715 default: 716 break; 717 } 718 } 719 } 720 721 private final Position findEnclosingBrace(Position start) 722 { 723 SyntaxIterator it = getSyntaxIterator(start); 724 int count = 0; 725 while (true) { 726 switch (it.prevChar()) { 727 case '}': 728 ++count; 729 break; 730 case '{': 731 if (count == 0) 732 return it.getPosition(); --count; 734 break; 735 case SyntaxIterator.DONE: 736 return null; 737 default: 738 break; 739 } 740 } 741 } 742 743 protected final Line findSwitch(Line line) 745 { 746 Position pos = findEnclosingBrace(new Position(line, 0)); 747 if (pos != null) { 748 line = pos.getLine(); 749 do { 750 String s = getFirstIdentifier(line); 751 if (s.equals("switch")) 752 return line; 753 } while ((line = line.previous()) != null); 754 } 755 return null; 756 } 757 758 private Position matchElse(Position start) 759 { 760 SyntaxIterator it = getSyntaxIterator(start); 761 int count = 1; 762 char c; 763 while ((c = it.prevChar()) != SyntaxIterator.DONE) { 764 if (c == '}') { 765 Position match = matchClosingBrace(it.getPosition()); 766 it = getSyntaxIterator(match); 767 continue; 768 } 769 if (c == 'e') { 770 Position pos = it.getPosition(); 771 if (pos.getIdentifier(this).equals("else")) { 772 ++count; 773 continue; 774 } 775 } 776 if (c == 'i') { 777 Position pos = it.getPosition(); 778 if (pos.getIdentifier(this).equals("if")) { 779 --count; 780 if (count == 0) 781 return pos; 782 continue; 783 } 784 } 785 } 786 return null; 787 } 788 789 public Position findBeginningOfStatement(Position start) 790 { 791 Position pos = new Position(start); 792 793 final Position posParen = findEnclosingParen(pos); 794 795 if (posParen != null) 796 pos = posParen; 797 798 final String trim = trimSyntacticWhitespace(pos.getLine().getText()); 799 final String lastIdentifier = getLastIdentifier(trim); 800 if (lastIdentifier != null && lastIdentifier.equals("else")) 801 return new Position(pos.getLine(), 0); final String firstIdentifier = getFirstIdentifier(trim); 803 if (firstIdentifier != null && 804 (firstIdentifier.equals("case") || firstIdentifier.equals("default"))) 805 return new Position(pos.getLine(), 0); 806 807 while (pos.getLine().trim().startsWith("}") && pos.getPreviousLine() != null) { 808 pos.moveTo(pos.getPreviousLine(), pos.getPreviousLine().length()); 809 pos = matchClosingBrace(pos); 810 } 811 812 SyntaxIterator it = getSyntaxIterator(pos); 813 boolean inParen = false; 814 int count = 0; 815 while (true) { 816 char c = it.prevChar(); 817 if (c == SyntaxIterator.DONE) 818 return it.getPosition(); 819 if (inParen) { 820 if (c == ')') 821 ++count; 822 else if (c == '(') { 823 --count; 824 if (count == 0) inParen = false; 826 } 827 continue; 828 } 829 if (c == ')') { 830 inParen = true; 831 count = 1; 832 continue; 833 } 834 if (c == '{') { 835 pos = it.getPosition(); char ch; 839 do { 840 ch = it.prevChar(); 841 } while (ch != SyntaxIterator.DONE && Character.isWhitespace(ch)); 842 if (ch == '=' || ch == ']') { 843 pos = it.getPosition(); 845 pos.moveTo(pos.getLine(), 0); 846 return pos; 847 } 848 it = getSyntaxIterator(pos); } 852 if (";{}:".indexOf(c) >= 0) { 853 do { 854 c = it.nextChar(); 855 } while (c != SyntaxIterator.DONE && Character.isWhitespace(c)); 856 pos = it.getPosition(); 857 pos.setOffset(0); 858 return pos; 859 } 860 } 861 } 862 863 public Position findPreviousConditional(Position start) 864 { 865 Position pos = start.copy(); 866 Position posParen = findEnclosingParen(pos); 867 if (posParen != null) 868 pos = posParen; 869 while (pos.getLine().trim().startsWith("}") && pos.getPreviousLine() != null) { 870 pos.moveTo(pos.getPreviousLine(), pos.getPreviousLine().length()); 871 pos = matchClosingBrace(pos); 872 } 873 while (true) { 874 if (pos.getLine().flags() != STATE_COMMENT) { 875 String text = pos.getLine().trim(); 876 if (text.startsWith("}")) 878 text = text.substring(1).trim(); 879 String firstIdentifier = getFirstIdentifier(text); 880 if (Utilities.isOneOf(firstIdentifier, conditionals)) { 881 pos.setOffset(pos.getLine().getText().indexOf(firstIdentifier)); 882 return pos; 883 } 884 } 885 Line previousLine = pos.getPreviousLine(); 886 if (previousLine == null) 887 return new Position(pos.getLine(), 0); 888 pos.moveTo(previousLine, previousLine.length()); 889 if (pos.getLine().flags() == STATE_COMMENT) 890 continue; 891 posParen = findEnclosingParen(pos); 892 if (posParen != null) { 893 pos = posParen; 894 continue; 895 } 896 String s = trimSyntacticWhitespace(pos.getLine().getText()); 897 if (s.length() > 0) { 898 if (s.charAt(0) == '#') break; 900 char lastChar = s.charAt(s.length()-1); 901 if (lastChar == ';' || lastChar == '{' || lastChar == '}' || 902 lastChar == ':') 903 break; 904 } 905 } 906 return start; 908 } 909 910 protected final boolean isContinued(String text, char lastChar) 911 { 912 switch (lastChar) { 913 case '+': 914 return !text.endsWith("++"); 915 case '/': 916 return !text.endsWith("//"); 917 case '=': 918 return (!text.endsWith("==") && !text.endsWith("!=")); 919 case ',': 920 return true; 921 case '.': 922 return true; 923 case '|': 924 return text.endsWith("||"); 925 case '&': 926 return text.endsWith("&&"); 927 default: 928 return false; 929 } 930 } 931 932 protected static String trimSyntacticWhitespace(String s) 935 { 936 JavaSyntaxIterator it = new JavaSyntaxIterator(null); 937 return new String (it.hideSyntacticWhitespace(s)).trim(); 938 } 939 940 public boolean isIdentifierStart(char c) 941 { 942 return Character.isJavaIdentifierStart(c); 943 } 944 945 public boolean isIdentifierPart(char c) 946 { 947 return Character.isJavaIdentifierPart(c); 948 } 949 950 public boolean isInComment(Buffer buffer, Position pos) 951 { 952 if (buffer == null || pos == null) { 953 Debug.bug(); 954 return false; 955 } 956 final Line line = pos.getLine(); 957 final String text = line.getText(); 958 if (text == null) 959 return false; 960 final char[] chars = text.toCharArray(); 961 final int offset = pos.getOffset(); 962 if (buffer.needsParsing()) 963 buffer.getFormatter().parseBuffer(); 964 int state = line.flags(); 965 final int length = chars.length; 966 for (int i = 0; i < length; i++) { 967 if (i == offset) 968 return state == STATE_COMMENT; 969 char c = chars[i]; 970 if (c == '\\' && i < length-1) { 971 continue; 973 } 974 if (state == STATE_QUOTE) { 975 if (c == '"') 976 state = STATE_NEUTRAL; 977 continue; 978 } 979 if (state == STATE_SINGLEQUOTE) { 980 if (c == '\'') 981 state = STATE_NEUTRAL; 982 continue; 983 } 984 if (state == STATE_COMMENT) { 985 if (c == '*' && i < length-1 && chars[i+1] == '/') { 986 state = STATE_NEUTRAL; 988 } 989 continue; 990 } 991 if (c == '"') { 993 state = STATE_QUOTE; 994 continue; 995 } 996 if (c == '\'') { 997 state = STATE_SINGLEQUOTE; 998 continue; 999 } 1000 if (c == '/') { 1001 if (i < length-1) { 1002 if (chars[i+1] == '*') { 1003 state = STATE_COMMENT; 1005 continue; 1006 } 1007 if (chars[i+1] == '/') { 1008 return true; 1010 } 1011 } 1012 } 1013 } 1014 return state == STATE_COMMENT; 1015 } 1016 1017 public boolean isCommentLine(Line line) 1018 { 1019 return line.trim().startsWith("//"); 1020 } 1021 1022 public static void insertComment() 1023 { 1024 if (!Editor.checkExperimental()) 1025 return; 1026 final Editor editor = Editor.currentEditor(); 1027 String toBeInserted = 1028 Editor.preferences().getStringProperty(Property.JAVA_MODE_INSERT_COMMENT_TEXT); 1029 if (toBeInserted == null) 1030 toBeInserted = "/**\\n * |\\n */"; 1031 Position caretPos = null; 1032 CompoundEdit compoundEdit = editor.beginCompoundEdit(); 1033 final int limit = toBeInserted.length(); 1034 for (int i = 0; i < limit; i++) { 1035 char c = toBeInserted.charAt(i); 1036 if (c == '|') { 1037 caretPos = new Position(editor.getDot()); 1038 continue; 1039 } 1040 if (c == '\\' && i < limit-1) { 1041 c = toBeInserted.charAt(++i); 1042 if (c == 'n') { 1043 editor.newlineAndIndent(); 1044 continue; 1045 } 1046 } 1048 editor.insertChar(c); 1049 } 1050 if (caretPos != null) 1051 editor.moveDotTo(caretPos); 1052 editor.moveCaretToDotCol(); 1053 editor.endCompoundEdit(compoundEdit); 1054 editor.getFormatter().parseBuffer(); 1055 } 1056 1057 public static void newlineAndIndentForComment() 1058 { 1059 final Editor editor = Editor.currentEditor(); 1060 if (!editor.checkReadOnly()) 1061 return; 1062 final Buffer buffer = editor.getBuffer(); 1063 final Display display = editor.getDisplay(); 1064 String commentPrefix = null; 1065 String s = editor.getDotLine().getText().trim(); 1066 int flags = editor.getDotLine().flags(); 1067 if (flags == STATE_COMMENT) { 1068 if (s.startsWith("*") && !s.endsWith("*/")) 1069 commentPrefix = "* "; 1070 } else { 1071 if (s.startsWith("/*") && !s.endsWith("*/")) 1073 commentPrefix = "* "; 1074 else if (s.startsWith("//")) 1075 commentPrefix = "// "; 1076 } 1077 if (commentPrefix == null){ 1078 editor.newlineAndIndent(); 1080 return; 1081 } 1082 CompoundEdit compoundEdit = buffer.beginCompoundEdit(); 1083 if (editor.getMark() != null) 1084 editor.deleteRegion(); 1085 editor.addUndo(SimpleEdit.INSERT_LINE_SEP); 1086 editor.insertLineSeparator(); 1087 editor.addUndo(SimpleEdit.LINE_EDIT); 1090 editor.getDotLine().setText(editor.getDotLine().getText().trim()); 1091 editor.addUndo(SimpleEdit.INSERT_STRING); 1093 editor.insertStringInternal(commentPrefix); 1094 editor.getDotLine().setFlags(flags); 1097 editor.indentLine(); 1098 editor.moveDotToIndentation(); 1100 editor.getDot().skip(commentPrefix.length()); 1101 display.moveCaretToDotCol(); 1102 buffer.endCompoundEdit(compoundEdit); 1103 } 1104 1105 public String getToolTipText(Editor editor, MouseEvent e) 1106 { 1107 if (editor.getModeId() == JAVA_MODE) { 1108 if (editor.getBuffer().getBooleanProperty(Property.ENABLE_TOOL_TIPS)) { 1109 Position pos = 1110 editor.getDisplay().positionFromPoint(e.getPoint()); 1111 if (pos != null) { 1112 final String name = getQualifiedName(pos); 1113 if (name != null) { 1114 JavaContext context = new JavaContext(editor); 1115 context.parseContext(pos); 1116 JavaVariable var = context.findDeclaration(name); 1117 if (var != null) 1118 return var.toString(); 1119 } 1120 } 1121 } 1122 } 1123 return null; 1124 } 1125 1126 private String getQualifiedName(Position pos) 1127 { 1128 Line line = pos.getLine(); 1129 int offset = pos.getOffset(); 1130 final int limit = line.length(); 1131 if (offset < limit) { 1132 char c = line.charAt(offset); 1133 if (isIdentifierPart(c)) { 1134 while (offset > 0) { 1135 --offset; 1136 c = line.charAt(offset); 1137 if (!isIdentifierPart(c) && c != '.') { 1138 ++offset; 1139 break; 1140 } 1141 } 1142 c = line.charAt(offset); 1144 if (isIdentifierStart(c)) { 1145 FastStringBuffer sb = new FastStringBuffer(); 1146 sb.append(c); 1147 while (++offset < limit) { 1148 c = line.charAt(offset); 1149 if (isIdentifierPart(c)) { 1150 sb.append(c); 1151 } else if (c == '.' && offset < pos.getOffset()) { 1152 sb.append(c); 1155 } else 1156 break; 1157 } 1158 return sb.toString(); 1159 } 1160 } 1161 } 1162 return null; 1163 } 1164 1165 public Expression getExpressionAtDot(final Editor editor, final boolean exact) 1166 { 1167 if (editor.getModeId() == OBJC_MODE) 1168 return super.getExpressionAtDot(editor, exact); 1169 if (editor.getDot() == null) 1170 return null; 1171 Position begin; 1172 if (editor.getMark() != null) { 1173 Region r = new Region(editor); 1175 begin = r.getBegin(); 1176 } else 1177 begin = editor.getDot(); 1178 final Line line = begin.getLine(); 1179 final int offset = begin.getOffset(); 1180 Position posExpr = null; 1181 if (exact) { 1182 if (offset < line.length() && isIdentifierPart(line.charAt(offset))) 1183 posExpr = findIdentifierStart(line, offset); 1184 if (posExpr == null) 1185 return null; 1186 } 1187 if (posExpr == null) { 1188 RE re = new UncheckedRE("([A-Za-z_$]+[A-Za-z_$0-9]*)\\s*\\("); 1190 final String text = editor.getDotLine().getText(); 1191 int index = 0; 1192 REMatch match; 1193 while ((match = re.getMatch(text, index)) != null) { 1194 String identifier = match.toString(1); 1195 if (!isKeyword(identifier)) { 1196 posExpr = new Position(line, match.getStartIndex()); 1197 if (match.getEndIndex() > offset) 1199 break; 1200 } 1201 index = match.getEndIndex(); 1202 } 1203 } 1204 if (posExpr == null) { 1205 if (offset < line.length() && isIdentifierStart(line.charAt(offset))) 1207 posExpr = findIdentifierStart(line, offset); 1208 } 1209 if (posExpr == null) 1210 return null; 1211 Position pos = posExpr.copy(); 1212 FastStringBuffer sb = new FastStringBuffer(); 1214 while (true) { 1215 char c = pos.getChar(); 1216 if (!isIdentifierPart(c)) 1217 break; 1218 sb.append(c); 1219 if (!pos.next()) 1220 break; 1221 } 1222 String name = sb.toString().trim(); 1223 while (true) { 1225 char c = pos.getChar(); 1226 if (!Character.isWhitespace(c)) 1227 break; 1228 if (!pos.next()) 1229 break; 1230 } 1231 final int arity; 1232 if (editor.getModeId() == JAVASCRIPT_MODE) 1233 arity = -1; else 1235 arity = getArity(editor, pos); 1236 if (arity >= 0) 1237 return new JavaExpression(name, arity); 1238 else 1239 return new JavaExpression(name, arity, TAG_UNKNOWN); 1240 } 1241 1242 private int getArity(Editor editor, Position pos) 1243 { 1244 if (pos.getChar() != '(') 1245 return -1; 1246 if (!pos.next()) 1247 return -1; 1248 final Position start = pos.copy(); 1249 int parenCount = 0; 1250 int arity = 0; 1251 char quoteChar = '\0'; 1252 boolean inQuote = false; 1253 while (!pos.atEnd()) { 1254 char c = pos.getChar(); 1255 if (inQuote) { 1256 if (c == quoteChar) 1257 inQuote = false; 1258 pos.next(); 1259 continue; 1260 } 1261 if (c == '"' || c == '\'') { 1263 inQuote = true; 1264 quoteChar = c; 1265 pos.next(); 1266 continue; 1267 } 1268 if (c == ',') { 1269 if (parenCount == 0) ++arity; 1271 pos.next(); 1272 continue; 1273 } 1274 if (c == '(') { 1275 ++parenCount; 1276 pos.next(); 1277 continue; 1278 } 1279 if (c == ')') { 1280 --parenCount; 1281 if (parenCount < 0) { 1282 if (arity == 0) { 1284 Region r = new Region(editor.getBuffer(), start, pos); 1286 if (r.toString().trim().length() > 0) 1287 arity = 1; 1288 } else 1289 ++arity; 1290 return arity; 1291 } 1292 pos.next(); 1293 continue; 1294 } 1295 pos.next(); 1296 } 1297 return -1; 1298 } 1299} 1300 | Popular Tags |