Lines 54-61
Link Here
|
54 |
import java.util.Collection; |
54 |
import java.util.Collection; |
55 |
import java.util.Collections; |
55 |
import java.util.Collections; |
56 |
import java.util.HashMap; |
56 |
import java.util.HashMap; |
|
|
57 |
import java.util.HashSet; |
57 |
import java.util.Iterator; |
58 |
import java.util.Iterator; |
58 |
import java.util.List; |
59 |
import java.util.List; |
|
|
60 |
import java.util.Map; |
61 |
import java.util.Set; |
59 |
import java.util.logging.Level; |
62 |
import java.util.logging.Level; |
60 |
import java.util.logging.LogRecord; |
63 |
import java.util.logging.LogRecord; |
61 |
import java.util.logging.Logger; |
64 |
import java.util.logging.Logger; |
Lines 75-80
Link Here
|
75 |
import org.netbeans.api.editor.mimelookup.MimeLookup; |
78 |
import org.netbeans.api.editor.mimelookup.MimeLookup; |
76 |
import org.netbeans.api.editor.mimelookup.MimePath; |
79 |
import org.netbeans.api.editor.mimelookup.MimePath; |
77 |
import org.netbeans.api.editor.settings.KeyBindingSettings; |
80 |
import org.netbeans.api.editor.settings.KeyBindingSettings; |
|
|
81 |
import org.netbeans.api.lexer.TokenHierarchy; |
82 |
import org.netbeans.api.lexer.TokenSequence; |
78 |
import org.netbeans.editor.BaseDocument; |
83 |
import org.netbeans.editor.BaseDocument; |
79 |
import org.netbeans.editor.BaseKit; |
84 |
import org.netbeans.editor.BaseKit; |
80 |
import org.netbeans.editor.GuardedDocument; |
85 |
import org.netbeans.editor.GuardedDocument; |
Lines 189-195
Link Here
|
189 |
/* Completion providers registered for the active component (its mime-type). Changed in AWT only. */ |
194 |
/* Completion providers registered for the active component (its mime-type). Changed in AWT only. */ |
190 |
private CompletionProvider[] activeProviders = null; |
195 |
private CompletionProvider[] activeProviders = null; |
191 |
|
196 |
|
192 |
/** Mapping of mime-type to array of providers. Changed in AWT only. */ |
197 |
/** Mapping of mime-path to array of providers. Changed in AWT only. */ |
193 |
private HashMap<String, CompletionProvider[]> providersCache = new HashMap<String, CompletionProvider[]>(); |
198 |
private HashMap<String, CompletionProvider[]> providersCache = new HashMap<String, CompletionProvider[]>(); |
194 |
|
199 |
|
195 |
/** |
200 |
/** |
Lines 539-546
Link Here
|
539 |
} |
544 |
} |
540 |
|
545 |
|
541 |
private boolean ensureActiveProviders() { |
546 |
private boolean ensureActiveProviders() { |
542 |
if (activeProviders != null) |
547 |
if (activeProviders != null) { |
543 |
return true; |
548 |
String mime = getMimePath(getActiveComponent()); |
|
|
549 |
if (mime == null) { |
550 |
return false; |
551 |
} |
552 |
if (mime.equals(currentMimePath)) { |
553 |
return true; |
554 |
} |
555 |
} |
544 |
JTextComponent component = getActiveComponent(); |
556 |
JTextComponent component = getActiveComponent(); |
545 |
activeProviders = (component != null) |
557 |
activeProviders = (component != null) |
546 |
? getCompletionProvidersForComponent(component, false) |
558 |
? getCompletionProvidersForComponent(component, false) |
Lines 574-608
Link Here
|
574 |
|
586 |
|
575 |
int docDelay = CompletionSettings.getInstance(getActiveComponent()).documentationAutoPopupDelay(); |
587 |
int docDelay = CompletionSettings.getInstance(getActiveComponent()).documentationAutoPopupDelay(); |
576 |
docAutoPopupTimer.setInitialDelay(docDelay); |
588 |
docAutoPopupTimer.setInitialDelay(docDelay); |
577 |
docAutoPopupTimer.restart(); |
589 |
docAutoPopupTimer.restart(); |
578 |
} |
590 |
} |
579 |
|
591 |
|
|
|
592 |
/** |
593 |
* Gives MimePath of the caret position as String |
594 |
* @param component |
595 |
* @return |
596 |
*/ |
597 |
private String getMimePath(JTextComponent component) { |
598 |
final int offset = component.getCaretPosition(); |
599 |
final MimePath[] mimePathR = new MimePath[1]; |
600 |
final Document doc = component.getDocument(); |
601 |
|
602 |
doc.render(new Runnable() { |
603 |
@Override |
604 |
public void run() { |
605 |
List<TokenSequence<?>> seqs = TokenHierarchy.get(doc).embeddedTokenSequences(offset, true); |
606 |
TokenSequence<?> seq; |
607 |
|
608 |
if (seqs.isEmpty()) { |
609 |
seq = TokenHierarchy.get(doc).tokenSequence(); |
610 |
} else { |
611 |
seq = seqs.get(seqs.size() - 1); |
612 |
} |
613 |
|
614 |
if (seq != null) { |
615 |
mimePathR[0] = MimePath.parse(seq.languagePath().mimePath()); |
616 |
} |
617 |
} |
618 |
}); |
619 |
if (mimePathR[0] != null) { |
620 |
return mimePathR[0].getPath(); |
621 |
} |
622 |
// original mimeType code |
623 |
Object mimeTypeObj = DocumentUtilities.getMimeType(doc); //NOI18N |
624 |
String mimeType; |
625 |
|
626 |
if (mimeTypeObj instanceof String) { |
627 |
mimeType = (String) mimeTypeObj; |
628 |
} else { |
629 |
BaseKit kit = Utilities.getKit(component); |
630 |
|
631 |
if (kit == null) { |
632 |
return null; |
633 |
} |
634 |
|
635 |
mimeType = kit.getContentType(); |
636 |
} |
637 |
return mimeType; |
638 |
} |
639 |
|
640 |
private String currentMimePath; |
641 |
|
642 |
/** |
643 |
* Gets a complete MIME path for the given offset in the document |
644 |
* @param doc the document |
645 |
* @param offset point of interest |
646 |
* @return MimePath |
647 |
*/ |
648 |
static MimePath getMimePath(final Document doc, final int offset) { |
649 |
final MimePath[] mimePathR = new MimePath[1]; |
650 |
doc.render(new Runnable() { |
651 |
@Override |
652 |
public void run() { |
653 |
List<TokenSequence<?>> seqs = TokenHierarchy.get(doc).embeddedTokenSequences(offset, true); |
654 |
TokenSequence<?> seq = seqs.isEmpty() ? null : seqs.get(seqs.size() - 1); |
655 |
seq = seq == null ? TokenHierarchy.get(doc).tokenSequence() : seq; |
656 |
mimePathR[0] = seq == null ? MimePath.parse(DocumentUtilities.getMimeType(doc)) : MimePath.parse(seq.languagePath().mimePath()); |
657 |
} |
658 |
}); |
659 |
return mimePathR[0]; |
660 |
} |
661 |
|
662 |
/** |
663 |
* Has side effects ! |
664 |
* @param component |
665 |
* @param asyncWarmUp |
666 |
* @return |
667 |
*/ |
580 |
private CompletionProvider[] getCompletionProvidersForComponent(JTextComponent component, boolean asyncWarmUp) { |
668 |
private CompletionProvider[] getCompletionProvidersForComponent(JTextComponent component, boolean asyncWarmUp) { |
581 |
assert (SwingUtilities.isEventDispatchThread()); |
669 |
assert (SwingUtilities.isEventDispatchThread()); |
582 |
|
670 |
|
583 |
if (component == null) |
671 |
if (component == null) |
584 |
return null; |
672 |
return null; |
585 |
|
673 |
|
586 |
Object mimeTypeObj = component.getDocument().getProperty("mimeType"); //NOI18N |
674 |
String mimePathString = getMimePath(component); |
587 |
String mimeType; |
675 |
|
588 |
|
676 |
if (mimePathString == null) { |
589 |
if (mimeTypeObj instanceof String) |
677 |
return null; |
590 |
mimeType = (String) mimeTypeObj; |
|
|
591 |
else { |
592 |
BaseKit kit = Utilities.getKit(component); |
593 |
|
594 |
if (kit == null) { |
595 |
return new CompletionProvider[0]; |
596 |
} |
597 |
|
598 |
mimeType = kit.getContentType(); |
599 |
} |
678 |
} |
600 |
|
679 |
if (providersCache.containsKey(mimePathString)) { |
601 |
if (providersCache.containsKey(mimeType)) |
680 |
currentMimePath = mimePathString; |
602 |
return providersCache.get(mimeType); |
681 |
return providersCache.get(mimePathString); |
|
|
682 |
} |
603 |
|
683 |
|
604 |
if (asyncWarmUpTask != null) { |
684 |
if (asyncWarmUpTask != null) { |
605 |
if (asyncWarmUp && mimeType != null && mimeType.equals(asyncWarmUpMimeType)) |
685 |
if (asyncWarmUp && mimePathString != null && mimePathString.equals(asyncWarmUpMimeType)) |
606 |
return null; |
686 |
return null; |
607 |
if (!asyncWarmUpTask.cancel()) { |
687 |
if (!asyncWarmUpTask.cancel()) { |
608 |
asyncWarmUpTask.waitFinished(); |
688 |
asyncWarmUpTask.waitFinished(); |
Lines 610-618
Link Here
|
610 |
asyncWarmUpTask = null; |
690 |
asyncWarmUpTask = null; |
611 |
asyncWarmUpMimeType = null; |
691 |
asyncWarmUpMimeType = null; |
612 |
} |
692 |
} |
613 |
final Lookup lookup = MimeLookup.getLookup(MimePath.get(mimeType)); |
693 |
MimePath path = MimePath.parse(mimePathString); |
614 |
if (asyncWarmUp) { |
694 |
if (asyncWarmUp) { |
615 |
asyncWarmUpMimeType = mimeType; |
695 |
final Lookup lookup = MimeLookup.getLookup(path); |
|
|
696 |
asyncWarmUpMimeType = mimePathString; |
616 |
asyncWarmUpTask = RequestProcessor.getDefault().post(new Runnable() { |
697 |
asyncWarmUpTask = RequestProcessor.getDefault().post(new Runnable() { |
617 |
@Override |
698 |
@Override |
618 |
public void run() { |
699 |
public void run() { |
Lines 621-630
Link Here
|
621 |
}); |
702 |
}); |
622 |
return null; |
703 |
return null; |
623 |
} |
704 |
} |
624 |
Collection<? extends CompletionProvider> col = lookup.lookupAll(CompletionProvider.class); |
705 |
// Note 1: |
625 |
int size = col.size(); |
706 |
// it's not possible to compare instances of CompletionProvider; each MimeLookup has its own instances, so if e.g. MyLookup |
626 |
CompletionProvider[] ret = size == 0 ? null : col.toArray(new CompletionProvider[size]); |
707 |
// is registered for text/html then MimeLookup for text/x-jsp/text/html and MimeLookup for text/html produce a *different* |
627 |
providersCache.put(mimeType, ret); |
708 |
// instances of the provider. |
|
|
709 |
// the registered classes could be used as key instead BUT there would be no way how to disambiguate the registration, IF |
710 |
// it was really needed for whatever reason. So I decided to match the providers based on Lookup.Item.getID(), which evaluates |
711 |
// (usually) to the registration filename, which the developer can alter, if for some reason the provider is needed. |
712 |
|
713 |
List<CompletionProvider> allProviders = new ArrayList<>(); |
714 |
Lookup lookup; |
715 |
Set<String> seenProviders = new HashSet<>(); |
716 |
|
717 |
for (int pref = path.size(); pref >= 1; pref--) { |
718 |
lookup = MimeLookup.getLookup(path.getPrefix(pref)); |
719 |
Collection<? extends Lookup.Item> allItems = lookup.lookupResult(CompletionProvider.class).allItems(); |
720 |
for (Lookup.Item i : allItems) { |
721 |
String id = i.getId(); |
722 |
int lastSlash = id.lastIndexOf('/'); // NOI18N |
723 |
if (lastSlash > 0) { |
724 |
String fname = id.substring(lastSlash + 1, id.length()); |
725 |
if (!seenProviders.add(fname)) { |
726 |
// the provider has been already seen in this list; do not add it. |
727 |
continue; |
728 |
} |
729 |
} |
730 |
allProviders.add((CompletionProvider)i.getInstance()); |
731 |
} |
732 |
} |
733 |
currentMimePath = mimePathString; |
734 |
CompletionProvider[] ret; |
735 |
providersCache.put(mimePathString, ret = allProviders.toArray(new CompletionProvider[allProviders.size()])); |
628 |
return ret; |
736 |
return ret; |
629 |
} |
737 |
} |
630 |
|
738 |
|