1 19 20 package org.netbeans.modules.editor.completion; 21 22 import java.awt.*; 23 import java.awt.event.*; 24 import java.lang.ref.WeakReference ; 25 import java.util.ArrayList ; 26 import java.util.Collection ; 27 import java.util.Collections ; 28 import java.util.HashMap ; 29 import java.util.Iterator ; 30 import java.util.List ; 31 import javax.swing.*; 32 import javax.swing.event.ListSelectionListener ; 33 import javax.swing.event.CaretListener ; 34 import javax.swing.event.ChangeListener ; 35 import javax.swing.event.DocumentListener ; 36 import javax.swing.plaf.TextUI ; 37 import javax.swing.text.*; 38 39 import org.netbeans.api.editor.mimelookup.MimeLookup; 40 import org.netbeans.api.editor.mimelookup.MimePath; 41 import org.netbeans.editor.BaseDocument; 42 import org.netbeans.editor.BaseKit; 43 import org.netbeans.editor.Settings; 44 import org.netbeans.editor.SettingsChangeListener; 45 import org.netbeans.editor.SettingsNames; 46 import org.netbeans.lib.editor.util.swing.DocumentUtilities; 47 import org.netbeans.lib.editor.util.swing.DocumentListenerPriority; 48 import org.netbeans.editor.Registry; 49 import org.netbeans.editor.Utilities; 50 import org.netbeans.editor.ext.ExtKit; 51 import org.netbeans.spi.editor.completion.*; 52 import org.openide.ErrorManager; 53 import org.openide.util.Lookup; 54 import org.openide.util.NbBundle; 55 56 70 71 public class CompletionImpl extends MouseAdapter implements DocumentListener , 72 CaretListener , KeyListener, FocusListener, ListSelectionListener , ChangeListener , SettingsChangeListener { 73 74 private static final boolean debug = Boolean.getBoolean("org.netbeans.modules.editor.completion.debug"); 75 private static final boolean allowFallbacks = !Boolean.getBoolean("org.netbeans.modules.editor.completion.noFallbacks"); 76 private static final boolean alphaSort = Boolean.getBoolean("org.netbeans.modules.editor.completion.alphabeticalSort"); 78 private static CompletionImpl singleton = null; 79 80 private static final String NO_SUGGESTIONS = NbBundle.getMessage(CompletionImpl.class, "completion-no-suggestions"); 81 private static final String PLEASE_WAIT = NbBundle.getMessage(CompletionImpl.class, "completion-please-wait"); 82 83 private static final String COMPLETION_SHOW = "completion-show"; private static final String COMPLETION_ALL_SHOW = "completion-all-show"; private static final String DOC_SHOW = "doc-show"; private static final String TOOLTIP_SHOW = "tooltip-show"; 88 private static final int PLEASE_WAIT_TIMEOUT = 750; 89 private static final int PRESCAN = 50; 90 91 public static CompletionImpl get() { 92 if (singleton == null) 93 singleton = new CompletionImpl(); 94 return singleton; 95 } 96 97 static LazyListModel.Filter filter = new LazyListModel.Filter() { 98 public boolean accept(Object obj) { 99 if (obj instanceof LazyCompletionItem) 100 return ((LazyCompletionItem)obj).accept(); 101 return true; 102 } 103 public void scheduleUpdate(Runnable run) { 104 SwingUtilities.invokeLater( run ); 105 } 106 }; 107 108 109 private WeakReference <JTextComponent> activeComponent = null; 110 111 112 private WeakReference <Document> activeDocument = null; 113 114 115 private InputMap inputMap; 116 117 118 private ActionMap actionMap; 119 120 121 private final CompletionLayout layout = new CompletionLayout(); 122 123 124 private CompletionProvider[] activeProviders = null; 125 126 127 private HashMap <String , CompletionProvider[]> providersCache = new HashMap <String , CompletionProvider[]>(); 128 129 136 private Result completionResult; 137 138 145 private Result docResult; 146 147 154 private Result toolTipResult; 155 156 157 private Timer completionAutoPopupTimer; 158 159 private Timer docAutoPopupTimer; 160 161 private Timer pleaseWaitTimer; 162 163 private boolean refreshedQuery = false; 164 165 private boolean explicitQuery = false; 166 167 private WeakReference <CompletionItem> lastSelectedItem = null; 168 169 170 private int autoModEndOffset; 171 172 private boolean pleaseWaitDisplayed = false; 173 174 private CompletionImpl() { 175 Registry.addChangeListener(this); 176 completionAutoPopupTimer = new Timer(0, new ActionListener() { 177 public void actionPerformed(ActionEvent e) { 178 Result localCompletionResult; 179 synchronized (this) { 180 localCompletionResult = completionResult; 181 } 182 if (localCompletionResult != null && !localCompletionResult.isQueryInvoked()) { 183 pleaseWaitTimer.restart(); 184 queryResultSets(localCompletionResult.getResultSets()); 185 localCompletionResult.queryInvoked(); 186 } 187 } 188 }); 189 completionAutoPopupTimer.setRepeats(false); 190 191 docAutoPopupTimer = new Timer(0, new ActionListener() { 192 public void actionPerformed(ActionEvent e) { 193 if (lastSelectedItem == null || lastSelectedItem.get() != layout.getSelectedCompletionItem()) 194 showDocumentation(); 195 } 196 }); 197 docAutoPopupTimer.setRepeats(false); 198 199 pleaseWaitTimer = new Timer(PLEASE_WAIT_TIMEOUT, new ActionListener() { 200 public void actionPerformed(ActionEvent e) { 201 String waitText = PLEASE_WAIT; 202 Result localCompletionResult; 203 synchronized (this) { 204 localCompletionResult = completionResult; 205 } 206 if (localCompletionResult != null) { 207 for (Iterator it = localCompletionResult.getResultSets().iterator(); it.hasNext();) { 208 CompletionResultSetImpl resultSet = (CompletionResultSetImpl)it.next(); 209 if (resultSet.getWaitText() != null) { 210 waitText = resultSet.getWaitText(); 211 break; 212 } 213 } 214 } 215 layout.showCompletion(Collections.singletonList(waitText), 216 null, -1, CompletionImpl.this, false, 0); 217 pleaseWaitDisplayed = true; 218 } 219 }); 220 pleaseWaitTimer.setRepeats(false); 221 Settings.addSettingsChangeListener(this); 222 } 223 224 private JTextComponent getActiveComponent() { 225 return activeComponent != null ? activeComponent.get() : null; 226 } 227 228 private Document getActiveDocument() { 229 return activeDocument != null ? activeDocument.get() : null; 230 } 231 232 int getSortType() { 233 return alphaSort ? CompletionResultSet.TEXT_SORT_TYPE : CompletionResultSet.PRIORITY_SORT_TYPE; 234 } 235 236 public void insertUpdate(javax.swing.event.DocumentEvent e) { 237 if (!SwingUtilities.isEventDispatchThread()) { 239 return; 240 } 241 if (!DocumentUtilities.isTypingModification(e)) { 243 return; 244 } 245 246 if (activeProviders != null) { 247 try { 248 int modEndOffset = e.getOffset() + e.getLength(); 249 if (getActiveComponent().getSelectionStart() != modEndOffset) 250 return; 251 252 String typedText = e.getDocument().getText(e.getOffset(), e.getLength()); 253 for (int i = 0; i < activeProviders.length; i++) { 254 int type = activeProviders[i].getAutoQueryTypes(getActiveComponent(), typedText); 255 boolean completionResultNull; 256 synchronized (this) { 257 completionResultNull = (completionResult == null); 258 } 259 if (completionResultNull && (type & CompletionProvider.COMPLETION_QUERY_TYPE) != 0 && 260 CompletionSettings.INSTANCE.completionAutoPopup()) { 261 autoModEndOffset = modEndOffset; 262 showCompletion(false, true, CompletionProvider.COMPLETION_QUERY_TYPE); 263 } 264 265 boolean tooltipResultNull; 266 synchronized (this) { 267 tooltipResultNull = (toolTipResult == null); 268 } 269 if (tooltipResultNull && (type & CompletionProvider.TOOLTIP_QUERY_TYPE) != 0) { 270 showToolTip(); 271 } 272 } 273 } catch (BadLocationException ex) {} 274 if (completionAutoPopupTimer.isRunning()) 275 restartCompletionAutoPopupTimer(); 276 } 277 } 278 279 public void removeUpdate(javax.swing.event.DocumentEvent e) { 280 if (!SwingUtilities.isEventDispatchThread()) { 282 return; 283 } 284 } 285 286 public void changedUpdate(javax.swing.event.DocumentEvent e) { 287 } 288 289 public synchronized void caretUpdate(javax.swing.event.CaretEvent e) { 290 assert (SwingUtilities.isEventDispatchThread()); 291 292 if (activeProviders != null) { 293 Result localCompletionResult; 297 synchronized (this) { 298 localCompletionResult = completionResult; 299 } 300 if ((completionAutoPopupTimer.isRunning() || localCompletionResult != null) 301 && (!layout.isCompletionVisible() || pleaseWaitDisplayed) 302 && e.getDot() != autoModEndOffset) { 303 hideCompletion(false); 304 } 305 306 SwingUtilities.invokeLater(new Runnable () { 307 public void run() { 308 completionRefresh(); 309 toolTipRefresh(); 310 } 311 }); 312 } 313 } 314 315 public void keyPressed(KeyEvent e) { 316 dispatchKeyEvent(e); 317 } 318 319 public void keyReleased(KeyEvent e) { 320 dispatchKeyEvent(e); 321 } 322 323 public void keyTyped(KeyEvent e) { 324 dispatchKeyEvent(e); 325 } 326 327 public void focusGained(FocusEvent e) { 328 } 329 330 public void focusLost(FocusEvent e) { 331 hideAll(); 332 } 333 334 public void mouseClicked(MouseEvent e) { 335 hideAll(); 336 } 337 338 public void hideAll() { 339 hideToolTip(); 340 hideCompletion(true); 341 hideDocumentation(true); 342 } 343 344 347 public void valueChanged(javax.swing.event.ListSelectionEvent e) { 348 assert (SwingUtilities.isEventDispatchThread()); 349 350 if (layout.isDocumentationVisible() || CompletionSettings.INSTANCE.documentationAutoPopup()) { 351 restartDocumentationAutoPopupTimer(); 352 } 353 } 354 355 358 public void stateChanged(javax.swing.event.ChangeEvent e) { 359 assert (SwingUtilities.isEventDispatchThread()); 361 boolean cancel = false; 362 JTextComponent component = Registry.getMostActiveComponent(); 363 if (component != getActiveComponent()) { 364 activeProviders = getCompletionProvidersForComponent(component); 365 if (debug) { 366 StringBuffer sb = new StringBuffer ("Completion PROVIDERS:\n"); if (activeProviders != null) { 368 for (int i = 0; i < activeProviders.length; i++) { 369 sb.append("providers["); sb.append(i); 371 sb.append("]: "); sb.append(activeProviders[i].getClass()); 373 sb.append('\n'); 374 } 375 } 376 System.err.println(sb.toString()); 377 } 378 if (getActiveComponent() != null) { 379 getActiveComponent().removeCaretListener(this); 380 getActiveComponent().removeKeyListener(this); 381 getActiveComponent().removeFocusListener(this); 382 getActiveComponent().removeMouseListener(this); 383 } 384 if (component != null) { 385 if (activeProviders != null) { 386 component.addCaretListener(this); 387 component.addKeyListener(this); 388 component.addFocusListener(this); 389 component.addMouseListener(this); 390 } 391 } 392 activeComponent = component != null ? new WeakReference <JTextComponent>(component) : null; 393 CompletionSettings.INSTANCE.notifyEditorComponentChange(getActiveComponent()); 394 layout.setEditorComponent(getActiveComponent()); 395 installKeybindings(); 396 cancel = true; 397 } 398 Document document = Registry.getMostActiveDocument(); 399 if (component != null && document == component.getDocument() && document != getActiveDocument()) { 400 activeProviders = getCompletionProvidersForComponent(component); 401 if (debug) { 402 StringBuffer sb = new StringBuffer ("Completion PROVIDERS:\n"); if (activeProviders != null) { 404 for (int i = 0; i < activeProviders.length; i++) { 405 sb.append("providers["); sb.append(i); 407 sb.append("]: "); sb.append(activeProviders[i].getClass()); 409 sb.append('\n'); 410 } 411 } 412 System.err.println(sb.toString()); 413 } 414 if (getActiveDocument() != null) 415 DocumentUtilities.removeDocumentListener(getActiveDocument(), this, 416 DocumentListenerPriority.AFTER_CARET_UPDATE); 417 if (activeProviders != null) 418 DocumentUtilities.addDocumentListener(document, this, 419 DocumentListenerPriority.AFTER_CARET_UPDATE); 420 activeDocument = new WeakReference <Document>(document); 421 cancel = true; 422 } 423 if (cancel) 424 completionCancel(); 425 } 426 427 private void restartCompletionAutoPopupTimer() { 428 assert (SwingUtilities.isEventDispatchThread()); 430 int completionDelay = CompletionSettings.INSTANCE.completionAutoPopupDelay(); 431 completionAutoPopupTimer.setInitialDelay(completionDelay); 432 completionAutoPopupTimer.restart(); 433 } 434 435 private void restartDocumentationAutoPopupTimer() { 436 assert (SwingUtilities.isEventDispatchThread()); 438 int docDelay = CompletionSettings.INSTANCE.documentationAutoPopupDelay(); 439 docAutoPopupTimer.setInitialDelay(docDelay); 440 docAutoPopupTimer.restart(); 441 } 442 443 private CompletionProvider[] getCompletionProvidersForComponent(JTextComponent component) { 444 assert (SwingUtilities.isEventDispatchThread()); 445 446 if (component == null) 447 return null; 448 449 Object mimeTypeObj = component.getDocument().getProperty("mimeType"); String mimeType; 451 452 if (mimeTypeObj instanceof String ) 453 mimeType = (String ) mimeTypeObj; 454 else { 455 BaseKit kit = Utilities.getKit(component); 456 457 if (kit == null) { 458 return new CompletionProvider[0]; 459 } 460 461 mimeType = kit.getContentType(); 462 } 463 464 if (providersCache.containsKey(mimeType)) 465 return (CompletionProvider[])providersCache.get(mimeType); 466 467 Lookup lookup = MimeLookup.getLookup(MimePath.get(mimeType)); 468 Collection <? extends CompletionProvider> col = lookup.lookupAll(CompletionProvider.class); 469 int size = col.size(); 470 CompletionProvider[] ret = size == 0 ? null : col.toArray(new CompletionProvider[size]); 471 providersCache.put(mimeType, ret); 472 return ret; 473 } 474 475 private void dispatchKeyEvent(KeyEvent e) { 476 if (e == null) 477 return; 478 KeyStroke ks = KeyStroke.getKeyStrokeForEvent(e); 479 JTextComponent comp = getActiveComponent(); 480 boolean compEditable = (comp != null && comp.isEditable()); 481 Object obj = inputMap.get(ks); 482 if (obj != null) { 483 Action action = actionMap.get(obj); 484 if (action != null) { 485 if (compEditable) 486 action.actionPerformed(null); 487 e.consume(); 488 return; 489 } 490 } 491 if (layout.isCompletionVisible()) { 492 CompletionItem item = layout.getSelectedCompletionItem(); 493 if (item != null) { 494 if (compEditable) 495 item.processKeyEvent(e); 496 if (e.isConsumed()) { 497 return; 498 } 499 if (e.getKeyCode() == KeyEvent.VK_ENTER && e.getID() == KeyEvent.KEY_PRESSED) { 501 e.consume(); 502 if (compEditable) 503 item.defaultAction(getActiveComponent()); 504 return; 505 } 506 } else if (e.getKeyCode() == KeyEvent.VK_UP || e.getKeyCode() == KeyEvent.VK_DOWN 507 || e.getKeyCode() == KeyEvent.VK_PAGE_UP || e.getKeyCode() == KeyEvent.VK_PAGE_DOWN 508 || e.getKeyCode() == KeyEvent.VK_HOME || e.getKeyCode() == KeyEvent.VK_END) { 509 hideCompletion(false); 510 } 511 if (e.getKeyCode() == KeyEvent.VK_TAB) { 512 e.consume(); 513 if (compEditable) 514 insertCommonPrefix(); 515 return; 516 } 517 } 518 layout.processKeyEvent(e); 519 } 520 521 private void completionQuery(boolean delayQuery, int queryType) { 522 refreshedQuery = false; 523 524 Result newCompletionResult = new Result(activeProviders.length); 525 synchronized (this) { 526 assert (completionResult == null); 527 completionResult = newCompletionResult; 528 } 529 List <CompletionResultSetImpl> completionResultSets = newCompletionResult.getResultSets(); 530 531 for (int i = 0; i < activeProviders.length; i++) { 533 CompletionTask compTask = activeProviders[i].createTask( 534 queryType, getActiveComponent()); 535 if (compTask != null) { 536 CompletionResultSetImpl resultSet = new CompletionResultSetImpl( 537 this, newCompletionResult, compTask, queryType); 538 completionResultSets.add(resultSet); 539 } 540 } 541 542 if (completionResultSets.size() > 0) { 543 if (delayQuery) { 545 restartCompletionAutoPopupTimer(); 546 } else { 547 pleaseWaitTimer.restart(); 548 queryResultSets(completionResultSets); 549 newCompletionResult.queryInvoked(); 550 } 551 } else { 552 completionCancel(); 553 layout.showCompletion(Collections.singletonList(NO_SUGGESTIONS), null, -1, CompletionImpl.this, false, 0); 554 pleaseWaitDisplayed = false; 555 } 556 } 557 558 563 private void completionRefresh() { 564 Result localCompletionResult; 565 synchronized (this) { 566 localCompletionResult = completionResult; 567 } 568 if (localCompletionResult != null) { 569 refreshedQuery = true; 570 Result refreshResult = localCompletionResult.createRefreshResult(); 571 synchronized (this) { 572 completionResult = refreshResult; 573 } 574 refreshResult.invokeRefresh(); 575 } 576 } 577 578 private void completionCancel() { 579 Result oldCompletionResult; 580 synchronized (this) { 581 oldCompletionResult = completionResult; 582 completionResult = null; 583 } 584 if (oldCompletionResult != null) { 585 oldCompletionResult.cancel(); 586 } 587 } 588 589 594 private void insertCommonPrefix() { 595 JTextComponent c = getActiveComponent(); 596 Result localCompletionResult; 597 synchronized (this) { 598 localCompletionResult = completionResult; 599 } 600 if (localCompletionResult != null) { 601 CharSequence commonText = null; 602 int anchorOffset = -1; 603 outer: for (Iterator it = localCompletionResult.getResultSets().iterator(); it.hasNext();) { 604 CompletionResultSetImpl resultSet = (CompletionResultSetImpl)it.next(); 605 if (anchorOffset == -1) 606 anchorOffset = resultSet.getAnchorOffset(); 607 for (Iterator itt = resultSet.getItems().iterator(); itt.hasNext();) { 608 CharSequence text = ((CompletionItem)itt.next()).getInsertPrefix(); 609 if (text == null) { 610 commonText = null; 611 break outer; 612 } 613 if (commonText == null) { 614 commonText = text; 615 } else { 616 int minLen = Math.min(text.length(), commonText.length()); 618 for (int commonInd = 0; commonInd < minLen; commonInd++) { 619 if (text.charAt(commonInd) != commonText.charAt(commonInd)) { 620 if (commonInd == 0) { 621 commonText = null; 622 break outer; } 624 commonText = commonText.subSequence(0, commonInd); 625 break; 626 } 627 } 628 } 629 } 630 } 631 if (commonText != null) { 632 int caretOffset = c.getSelectionStart(); 633 if (anchorOffset > -1 && caretOffset - anchorOffset < commonText.length()) { 634 commonText = commonText.subSequence(caretOffset - anchorOffset, commonText.length()); 635 BaseDocument doc = (BaseDocument)getActiveDocument(); 637 doc.atomicLock(); 638 try { 639 doc.insertString(caretOffset, commonText.toString(), null); 640 } catch (BadLocationException e) { 641 } finally { 642 doc.atomicUnlock(); 643 } 644 } 645 } 646 } 647 } 648 649 652 public void showCompletion() { 653 showCompletion(false, false, CompletionProvider.COMPLETION_QUERY_TYPE); 654 } 655 656 private void showCompletion(boolean explicitQuery, boolean delayQuery, int queryType) { 657 if (!SwingUtilities.isEventDispatchThread()) { 658 SwingUtilities.invokeLater(new ParamRunnable(ParamRunnable.SHOW_COMPLETION, explicitQuery, delayQuery, queryType)); 660 return; 661 } 662 663 this.explicitQuery = explicitQuery; 664 if (activeProviders != null) { 665 completionAutoPopupTimer.stop(); 666 synchronized(this) { 667 if (explicitQuery && completionResult != null) 668 queryType = CompletionProvider.COMPLETION_ALL_QUERY_TYPE; 669 } 670 completionCancel(); completionQuery(delayQuery, queryType); 672 } 673 } 674 675 680 void requestShowCompletionPane(Result result) { 681 pleaseWaitTimer.stop(); 682 683 int sortedResultsSize = 0; 685 int qType = 0; 686 List <CompletionResultSetImpl> completionResultSets = result.getResultSets(); 687 for (int i = completionResultSets.size() - 1; i >= 0; i--) { 688 CompletionResultSetImpl resultSet = completionResultSets.get(i); 689 sortedResultsSize += resultSet.getItems().size(); 690 qType = resultSet.getQueryType(); 691 } 692 693 final List <CompletionItem> sortedResultItems = new ArrayList <CompletionItem>(sortedResultsSize); 695 String title = null; 696 int anchorOffset = -1; 697 int cnt = 0; 698 for (int i = 0; i < completionResultSets.size(); i++) { 699 CompletionResultSetImpl resultSet = (CompletionResultSetImpl)completionResultSets.get(i); 700 List <? extends CompletionItem> resultItems = resultSet.getItems(); 701 if (resultItems.size() > 0) { 702 if (cnt < PRESCAN) { 703 for (CompletionItem item : resultItems) { 704 if (cnt < PRESCAN && !filter.accept(item)) 705 continue; 706 sortedResultItems.add(item); 707 cnt++; 708 } 709 } else { 710 sortedResultItems.addAll(resultItems); 711 } 712 if (title == null) 713 title = resultSet.getTitle(); 714 if (anchorOffset == -1) 715 anchorOffset = resultSet.getAnchorOffset(); 716 } 717 } 718 Collections.sort(sortedResultItems, CompletionItemComparator.get(getSortType())); 719 720 final String displayTitle = title; 722 final int displayAnchorOffset = anchorOffset; 723 final int queryType = qType; 724 final boolean noSuggestions = sortedResultsSize == 0; 725 Runnable requestShowRunnable = new Runnable () { 726 public void run() { 727 int caretOffset = getActiveComponent().getSelectionStart(); 728 if (sortedResultItems.size() == 1 && !refreshedQuery && explicitQuery 730 && CompletionSettings.INSTANCE.completionInstantSubstitution() 731 && getActiveComponent().isEditable()) { 732 try { 733 int[] block = Utilities.getIdentifierBlock(getActiveComponent(), caretOffset); 734 if (block == null || block[1] == caretOffset) { CompletionItem item = (CompletionItem) sortedResultItems.get(0); 736 if (item.instantSubstitution(getActiveComponent())) { 737 return; 738 } 739 } 740 } catch (BadLocationException ex) { 741 } 742 } 743 744 int selectedIndex = getCompletionPreSelectionIndex(sortedResultItems); 745 layout.showCompletion(noSuggestions ? Collections.singletonList(NO_SUGGESTIONS) : sortedResultItems, displayTitle, displayAnchorOffset, CompletionImpl.this, allowFallbacks && queryType == CompletionProvider.COMPLETION_QUERY_TYPE, selectedIndex); 746 pleaseWaitDisplayed = false; 747 748 if (CompletionSettings.INSTANCE.documentationAutoPopup()) { 750 if (noSuggestions) { 751 docAutoPopupTimer.stop(); documentationCancel(); 753 layout.hideDocumentation(); 754 } else { 755 restartDocumentationAutoPopupTimer(); 756 } 757 } 758 } 759 }; 760 runInAWT(requestShowRunnable); 761 } 762 763 private int getCompletionPreSelectionIndex(List <CompletionItem> items) { 764 String prefix = null; 765 BaseDocument doc = (BaseDocument)getActiveDocument(); 766 int caretOffset = getActiveComponent().getSelectionStart(); 767 try { 768 int[] block = Utilities.getIdentifierBlock(doc, caretOffset); 769 if (block != null) { 770 block[1] = caretOffset; 771 prefix = doc.getText(block); 772 } 773 } catch (BadLocationException ble) { 774 } 775 if (prefix != null && prefix.length() > 0) { 776 int idx = 0; 777 for (CompletionItem item : items) { 778 if (item.getInsertPrefix().toString().startsWith(prefix)) 779 return idx; 780 idx++; 781 } 782 } 783 return 0; 784 } 785 786 789 public boolean hideCompletion() { 790 return hideCompletion(true); 791 } 792 793 public boolean hideCompletion(boolean completionOnly) { 794 completionCancel(); 795 if (!SwingUtilities.isEventDispatchThread()) { 797 SwingUtilities.invokeLater(new ParamRunnable(ParamRunnable.HIDE_COMPLETION_PANE, completionOnly)); 798 return false; 799 } else { return hideCompletionPane(completionOnly); 801 } 802 } 803 804 807 private boolean hideCompletionPane(boolean completionOnly) { 808 completionAutoPopupTimer.stop(); pleaseWaitTimer.stop(); 810 boolean hidePerformed = layout.hideCompletion(); 811 pleaseWaitDisplayed = false; 812 if (!completionOnly && hidePerformed && CompletionSettings.INSTANCE.documentationAutoPopup()) { 813 hideDocumentation(true); 814 } 815 return hidePerformed; 816 } 817 818 821 public void showDocumentation() { 822 if (!SwingUtilities.isEventDispatchThread()) { 823 SwingUtilities.invokeLater(new ParamRunnable(ParamRunnable.SHOW_DOCUMENTATION)); 825 return; 826 } 827 828 if (activeProviders != null) { 829 documentationCancel(); 830 layout.clearDocumentationHistory(); 831 documentationQuery(); 832 } 833 } 834 835 840 void requestShowDocumentationPane(Result result) { 841 final CompletionResultSetImpl resultSet = findFirstValidResult(result.getResultSets()); 842 runInAWT(new Runnable () { 843 public void run() { 844 synchronized (CompletionImpl.this) { 845 if (resultSet != null) { 846 layout.showDocumentation( 847 resultSet.getDocumentation(), resultSet.getAnchorOffset()); 848 } else { 849 documentationCancel(); 850 layout.hideDocumentation(); 851 } 852 } 853 } 854 }); 855 } 856 857 860 private void documentationQuery() { 861 Result newDocumentationResult = new Result(1); synchronized (this) { 863 assert (docResult == null); 864 docResult = newDocumentationResult; 865 } 866 List <CompletionResultSetImpl> documentationResultSets = docResult.getResultSets(); 867 868 CompletionTask docTask; 869 CompletionItem selectedItem = layout.getSelectedCompletionItem(); 870 if (selectedItem != null) { 871 lastSelectedItem = new WeakReference <CompletionItem>(selectedItem); 872 docTask = selectedItem.createDocumentationTask(); 873 if (docTask != null) { CompletionResultSetImpl resultSet = new CompletionResultSetImpl( 875 this, newDocumentationResult, docTask, CompletionProvider.DOCUMENTATION_QUERY_TYPE); 876 documentationResultSets.add(resultSet); 877 } 878 } else { lastSelectedItem = null; 880 for (int i = 0; i < activeProviders.length; i++) { 881 docTask = activeProviders[i].createTask( 882 CompletionProvider.DOCUMENTATION_QUERY_TYPE, getActiveComponent()); 883 if (docTask != null) { 884 CompletionResultSetImpl resultSet = new CompletionResultSetImpl( 885 this, newDocumentationResult, docTask, CompletionProvider.DOCUMENTATION_QUERY_TYPE); 886 documentationResultSets.add(resultSet); 887 } 888 } 889 } 890 891 if (documentationResultSets.size() > 0) { 892 queryResultSets(documentationResultSets); 893 newDocumentationResult.queryInvoked(); 894 } else { 895 documentationCancel(); 896 layout.hideDocumentation(); 897 } 898 } 899 900 private void documentationCancel() { 901 Result oldDocumentationResult; 902 synchronized (this) { 903 oldDocumentationResult = docResult; 904 docResult = null; 905 } 906 if (oldDocumentationResult != null) { 907 oldDocumentationResult.cancel(); 908 } 909 } 910 911 914 public boolean hideDocumentation() { 915 return hideDocumentation(true); 916 } 917 918 boolean hideDocumentation(boolean documentationOnly) { 919 documentationCancel(); 920 if (!SwingUtilities.isEventDispatchThread()) { 922 SwingUtilities.invokeLater(new ParamRunnable(ParamRunnable.HIDE_DOCUMENTATION_PANE, documentationOnly)); 923 return false; 924 } else { return hideDocumentationPane(documentationOnly); 926 } 927 } 928 929 932 boolean hideDocumentationPane(boolean documentationOnly) { 933 docAutoPopupTimer.stop(); 935 boolean hidePerformed = layout.hideDocumentation(); 936 if (!documentationOnly && hidePerformed && CompletionSettings.INSTANCE.documentationAutoPopup()) { 938 hideCompletion(true); 939 } 940 return hidePerformed; 941 } 942 943 944 947 public void showToolTip() { 948 if (!SwingUtilities.isEventDispatchThread()) { 949 SwingUtilities.invokeLater(new ParamRunnable(ParamRunnable.SHOW_TOOL_TIP)); 951 return; 952 } 953 954 if (activeProviders != null) { 955 toolTipCancel(); 956 toolTipQuery(); 957 } 958 } 959 960 965 void requestShowToolTipPane(Result result) { 966 final CompletionResultSetImpl resultSet = findFirstValidResult(result.getResultSets()); 967 runInAWT(new Runnable () { 968 public void run() { 969 if (resultSet != null) { 970 layout.showToolTip( 971 resultSet.getToolTip(), resultSet.getAnchorOffset()); 972 } else { 973 hideToolTip(); 974 } 975 } 976 }); 977 } 978 979 982 private void toolTipQuery() { 983 Result newToolTipResult = new Result(1); 984 synchronized (this) { 985 assert (toolTipResult == null); 986 toolTipResult = newToolTipResult; 987 } 988 List <CompletionResultSetImpl> toolTipResultSets = newToolTipResult.getResultSets(); 989 990 CompletionTask toolTipTask; 991 CompletionItem selectedItem = layout.getSelectedCompletionItem(); 992 if (selectedItem != null && (toolTipTask = selectedItem.createToolTipTask()) != null) { 993 CompletionResultSetImpl resultSet = new CompletionResultSetImpl( 994 this, newToolTipResult, toolTipTask, CompletionProvider.TOOLTIP_QUERY_TYPE); 995 toolTipResultSets.add(resultSet); 996 } else { 997 for (int i = 0; i < activeProviders.length; i++) { 998 toolTipTask = activeProviders[i].createTask( 999 CompletionProvider.TOOLTIP_QUERY_TYPE, getActiveComponent()); 1000 if (toolTipTask != null) { 1001 CompletionResultSetImpl resultSet = new CompletionResultSetImpl( 1002 this, newToolTipResult, toolTipTask, CompletionProvider.TOOLTIP_QUERY_TYPE); 1003 toolTipResultSets.add(resultSet); 1004 } 1005 } 1006 } 1007 1008 queryResultSets(toolTipResultSets); 1009 newToolTipResult.queryInvoked(); 1010 } 1011 1012 private void toolTipRefresh() { 1013 Result localToolTipResult; 1014 synchronized (this) { 1015 localToolTipResult = toolTipResult; 1016 } 1017 if (localToolTipResult != null) { 1018 Result refreshResult = localToolTipResult.createRefreshResult(); 1019 synchronized (this) { 1020 toolTipResult = refreshResult; 1021 } 1022 refreshResult.invokeRefresh(); 1023 } 1024 } 1025 1026 1029 private void toolTipCancel() { 1030 Result oldToolTipResult; 1031 synchronized (this) { 1032 oldToolTipResult = toolTipResult; 1033 toolTipResult = null; 1034 } 1035 if (oldToolTipResult != null) { 1036 oldToolTipResult.cancel(); 1037 } 1038 } 1039 1040 1043 public boolean hideToolTip() { 1044 toolTipCancel(); 1045 if (!SwingUtilities.isEventDispatchThread()) { 1047 SwingUtilities.invokeLater(new ParamRunnable(ParamRunnable.HIDE_TOOL_TIP_PANE)); 1048 return false; 1049 } else { return hideToolTipPane(); 1051 } 1052 } 1053 1054 1057 boolean hideToolTipPane() { 1058 return layout.hideToolTip(); 1059 } 1060 1061 1062 private KeyStroke[] findEditorKeys(String editorActionName, KeyStroke defaultKey) { 1063 KeyStroke[] ret = new KeyStroke[] { defaultKey }; 1066 if (editorActionName != null && getActiveComponent() != null) { 1067 TextUI ui = getActiveComponent().getUI(); 1068 Keymap km = getActiveComponent().getKeymap(); 1069 if (ui != null && km != null) { 1070 EditorKit kit = ui.getEditorKit(getActiveComponent()); 1071 if (kit instanceof BaseKit) { 1072 Action a = ((BaseKit)kit).getActionByName(editorActionName); 1073 if (a != null) { 1074 KeyStroke[] keys = km.getKeyStrokesForAction(a); 1075 if (keys != null && keys.length > 0) { 1076 ret = keys; 1077 } else { 1078 Keymap km2 = ((BaseKit)kit).getKeymap(); 1080 KeyStroke[] keys2 = km2.getKeyStrokesForAction(a); 1081 if (keys2 != null && keys2.length > 0) { 1082 ret = keys2; 1083 } 1084 } 1085 } 1086 } 1087 } 1088 } 1089 return ret; 1090 } 1091 1092 private void installKeybindings() { 1093 actionMap = new ActionMap(); 1094 inputMap = new InputMap(); 1095 1096 KeyStroke[] keys = findEditorKeys(ExtKit.completionShowAction, KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, InputEvent.CTRL_MASK)); 1098 for (int i = 0; i < keys.length; i++) { 1099 inputMap.put(keys[i], COMPLETION_SHOW); 1100 } 1101 actionMap.put(COMPLETION_SHOW, new CompletionShowAction(CompletionProvider.COMPLETION_QUERY_TYPE)); 1102 1103 keys = findEditorKeys(ExtKit.allCompletionShowAction, KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, (InputEvent.CTRL_MASK | InputEvent.ALT_MASK))); 1105 for (int i = 0; i < keys.length; i++) { 1106 inputMap.put(keys[i], COMPLETION_ALL_SHOW); 1107 } 1108 actionMap.put(COMPLETION_ALL_SHOW, new CompletionShowAction(CompletionProvider.COMPLETION_ALL_QUERY_TYPE)); 1109 1110 keys = findEditorKeys(ExtKit.documentationShowAction, KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, (InputEvent.CTRL_MASK | InputEvent.SHIFT_MASK))); 1112 for (int i = 0; i < keys.length; i++) { 1113 inputMap.put(keys[i], DOC_SHOW); 1114 } 1115 actionMap.put(DOC_SHOW, new DocShowAction()); 1116 1117 keys = findEditorKeys(ExtKit.completionTooltipShowAction, KeyStroke.getKeyStroke(KeyEvent.VK_P, InputEvent.ALT_MASK)); 1119 for (int i = 0; i < keys.length; i++) { 1120 inputMap.put(keys[i], TOOLTIP_SHOW); 1121 } 1122 actionMap.put(TOOLTIP_SHOW, new ToolTipShowAction()); 1123 } 1124 1125 1130 void finishNotify(CompletionResultSetImpl finishedResult) { 1131 switch (finishedResult.getQueryType()) { 1132 case CompletionProvider.COMPLETION_QUERY_TYPE: 1133 case CompletionProvider.COMPLETION_ALL_QUERY_TYPE: 1134 Result localCompletionResult; 1135 synchronized (this) { 1136 localCompletionResult = completionResult; 1137 } 1138 if (finishedResult.getResultId() == localCompletionResult) { 1139 if (isAllResultsFinished(localCompletionResult.getResultSets())) { 1140 requestShowCompletionPane(localCompletionResult); 1141 } 1142 } 1143 break; 1144 1145 case CompletionProvider.DOCUMENTATION_QUERY_TYPE: 1146 Result localDocumentationResult; 1147 synchronized (this) { 1148 localDocumentationResult = docResult; 1149 } 1150 if (finishedResult.getResultId() == localDocumentationResult) { 1151 if (isAllResultsFinished(localDocumentationResult.getResultSets())) { 1152 requestShowDocumentationPane(localDocumentationResult); 1153 } 1154 } 1155 break; 1156 1157 case CompletionProvider.TOOLTIP_QUERY_TYPE: 1158 Result localToolTipResult; 1159 synchronized (this) { 1160 localToolTipResult = toolTipResult; 1161 } 1162 if (finishedResult.getResultId() == localToolTipResult) { 1163 if (isAllResultsFinished(localToolTipResult.getResultSets())) { 1164 requestShowToolTipPane(localToolTipResult); 1165 } 1166 } 1167 break; 1168 1169 default: 1170 throw new IllegalStateException (); } 1172 } 1173 1174 private static boolean isAllResultsFinished(List <CompletionResultSetImpl> resultSets) { 1175 for (int i = resultSets.size() - 1; i >= 0; i--) { 1176 CompletionResultSetImpl result = resultSets.get(i); 1177 if (!result.isFinished()) { 1178 if (debug) { 1179 System.err.println("CompletionTask: " + result.getTask() + " not finished yet"); } 1182 return false; 1183 } 1184 } 1185 if (debug) { 1186 System.err.println("----- All tasks finished -----"); 1187 } 1188 return true; 1189 } 1190 1191 1197 private static CompletionResultSetImpl findFirstValidResult(List <CompletionResultSetImpl> resultSets) { 1198 for (int i = 0; i < resultSets.size(); i++) { 1199 CompletionResultSetImpl result = resultSets.get(i); 1200 switch (result.getQueryType()) { 1201 case CompletionProvider.DOCUMENTATION_QUERY_TYPE: 1202 if (result.getDocumentation() != null) { 1203 return result; 1204 } 1205 break; 1206 1207 case CompletionProvider.TOOLTIP_QUERY_TYPE: 1208 if (result.getToolTip() != null) { 1209 return result; 1210 } 1211 break; 1212 1213 default: 1214 throw new IllegalStateException (); 1215 } 1216 } 1217 return null; 1218 } 1219 1220 private static void runInAWT(Runnable r) { 1221 if (SwingUtilities.isEventDispatchThread()) { 1222 r.run(); 1223 } else { 1224 SwingUtilities.invokeLater(r); 1225 } 1226 } 1227 1228 1230 CompletionLayout testGetCompletionLayout() { 1231 return layout; 1232 } 1233 1234 void testSetActiveComponent(JTextComponent component) { 1235 activeComponent = new WeakReference <JTextComponent>(component); 1236 } 1237 1238 1240 private final class CompletionShowAction extends AbstractAction { 1241 private int queryType; 1242 1243 private CompletionShowAction(int queryType) { 1244 this.queryType = queryType; 1245 } 1246 1247 public void actionPerformed(ActionEvent e) { 1248 showCompletion(true, false, queryType); 1249 } 1250 } 1251 1252 private final class DocShowAction extends AbstractAction { 1253 public void actionPerformed(ActionEvent e) { 1254 showDocumentation(); 1255 } 1256 } 1257 1258 private final class ToolTipShowAction extends AbstractAction { 1259 public void actionPerformed(ActionEvent e) { 1260 showToolTip(); 1261 } 1262 } 1263 1264 private final class ParamRunnable implements Runnable { 1265 1266 private static final int SHOW_COMPLETION = 0; 1267 private static final int SHOW_DOCUMENTATION = 1; 1268 private static final int SHOW_TOOL_TIP = 2; 1269 private static final int HIDE_COMPLETION_PANE = 3; 1270 private static final int HIDE_DOCUMENTATION_PANE = 4; 1271 private static final int HIDE_TOOL_TIP_PANE = 5; 1272 1273 private final int opCode; 1274 private final boolean explicit; 1275 private final boolean delayQuery; 1276 private final int type; 1277 1278 ParamRunnable(int opCode) { 1279 this(opCode, false); 1280 } 1281 1282 ParamRunnable(int opCode, boolean explicit) { 1283 this(opCode, explicit, false, CompletionProvider.COMPLETION_QUERY_TYPE); 1284 } 1285 1286 ParamRunnable(int opCode, boolean explicit, boolean delayQuery, int type) { 1287 this.opCode = opCode; 1288 this.explicit = explicit; 1289 this.delayQuery = delayQuery; 1290 this.type = type; 1291 } 1292 1293 public void run() { 1294 switch (opCode) { 1295 case SHOW_COMPLETION: 1296 showCompletion(explicitQuery, delayQuery, type); 1297 break; 1298 1299 case SHOW_DOCUMENTATION: 1300 showDocumentation(); 1301 break; 1302 1303 case SHOW_TOOL_TIP: 1304 showToolTip(); 1305 break; 1306 1307 case HIDE_COMPLETION_PANE: 1308 hideCompletionPane(explicit); 1309 break; 1310 1311 case HIDE_DOCUMENTATION_PANE: 1312 hideDocumentationPane(explicit); 1313 break; 1314 1315 case HIDE_TOOL_TIP_PANE: 1316 hideToolTipPane(); 1317 break; 1318 1319 default: 1320 throw new IllegalStateException (); 1321 } 1322 } 1323 } 1324 1325 private static void queryResultSets(List <CompletionResultSetImpl> resultSets) { 1326 for (int i = 0; i < resultSets.size(); i++) { 1327 CompletionResultSetImpl resultSet = resultSets.get(i); 1328 resultSet.getTask().query(resultSet.getResultSet()); 1329 } 1330 } 1331 1332 private static void createRefreshResultSets(List <CompletionResultSetImpl> resultSets, Result refreshResult) { 1333 List <CompletionResultSetImpl> refreshResultSets = refreshResult.getResultSets(); 1334 int size = resultSets.size(); 1335 for (int i = 0; i < size; i++) { 1337 CompletionResultSetImpl result = resultSets.get(i); 1338 result.markInactive(); 1339 result = new CompletionResultSetImpl(result.getCompletionImpl(), 1340 refreshResult, result.getTask(), result.getQueryType()); 1341 refreshResultSets.add(result); 1342 } 1343 } 1344 1345 private static void refreshResultSets(List <CompletionResultSetImpl> resultSets, boolean beforeQuery) { 1346 try { 1347 int size = resultSets.size(); 1348 for (int i = 0; i < size; i++) { 1349 CompletionResultSetImpl result = resultSets.get(i); 1350 result.getTask().refresh(beforeQuery ? null : result.getResultSet()); 1351 1352 } 1353 } catch (Exception ex) { 1354 ErrorManager.getDefault().notify(ex); 1355 } 1356 } 1357 1358 private static void cancelResultSets(List <CompletionResultSetImpl> resultSets) { 1359 int size = resultSets.size(); 1360 for (int i = 0; i < size; i++) { 1361 CompletionResultSetImpl result = resultSets.get(i); 1362 result.markInactive(); 1363 result.getTask().cancel(); 1364 } 1365 } 1366 1367 public void settingsChange(org.netbeans.editor.SettingsChangeEvent evt) { 1368 if( evt == null) { 1369 return; 1370 } 1371 String settingName = evt.getSettingName(); 1372 if (SettingsNames.KEY_BINDING_LIST.equals(settingName) || settingName == null){ 1373 Utilities.runInEventDispatchThread(new Runnable (){ 1374 public void run(){ 1375 installKeybindings(); 1376 } 1377 }); 1378 } 1379 } 1380 1381 1392 static final class Result { 1393 1394 private final List <CompletionResultSetImpl> resultSets; 1395 1396 private boolean invoked; 1397 private boolean cancelled; 1398 private boolean beforeQuery = true; 1399 1400 Result(int resultSetsSize) { 1401 resultSets = new ArrayList <CompletionResultSetImpl>(resultSetsSize); 1402 } 1403 1404 1409 List <CompletionResultSetImpl> getResultSets() { 1410 return resultSets; 1411 } 1412 1413 1421 void cancel() { 1422 boolean fin; 1423 synchronized (this) { 1424 assert (!cancelled); 1425 fin = invoked; 1426 if (!invoked) { 1427 cancelled = true; 1428 } 1429 } 1430 1431 if (fin) { cancelResultSets(resultSets); 1433 } 1434 } 1435 1436 synchronized boolean isQueryInvoked() { 1437 return invoked; 1438 } 1439 1440 1444 boolean queryInvoked() { 1445 boolean canc; 1446 synchronized (this) { 1447 assert (!invoked); 1448 invoked = true; 1449 canc = cancelled; 1450 beforeQuery = false; 1451 } 1452 if (canc) { 1453 cancelResultSets(resultSets); 1454 } 1455 return canc; 1456 } 1457 1458 1462 Result createRefreshResult() { 1463 synchronized (this) { 1464 if (cancelled) { 1465 return null; 1466 } 1467 if (beforeQuery) { 1468 return this; 1469 } 1470 assert (invoked); invoked = false; 1472 } 1473 Result refreshResult = new Result(getResultSets().size()); 1474 refreshResult.beforeQuery = beforeQuery; 1475 createRefreshResultSets(resultSets, refreshResult); 1476 return refreshResult; 1477 } 1478 1479 1484 void invokeRefresh() { 1485 refreshResultSets(getResultSets(), beforeQuery); 1486 if (!beforeQuery) 1487 queryInvoked(); 1488 } 1489 1490 } 1491 1492 public CompletionResultSetImpl createTestResultSet(CompletionTask task, int queryType) { 1493 return new CompletionResultSetImpl(this, "TestResult", task, queryType); 1494 } 1495} 1496 | Popular Tags |