--- a/editor.completion/nbproject/project.properties
+++ a/editor.completion/nbproject/project.properties
@@ -44,4 +44,4 @@
javac.source=1.7
javadoc.arch=${basedir}/arch.xml
javadoc.apichanges=${basedir}/apichanges.xml
-spec.version.base=1.44.0
+spec.version.base=1.45.0
--- a/editor.completion/nbproject/project.xml
+++ a/editor.completion/nbproject/project.xml
@@ -112,6 +112,15 @@
+ org.netbeans.modules.lexer
+
+
+
+ 2
+ 1.63
+
+
+
org.netbeans.modules.sampler
--- a/editor.completion/src/org/netbeans/modules/editor/completion/CompletionImpl.java
+++ a/editor.completion/src/org/netbeans/modules/editor/completion/CompletionImpl.java
@@ -54,8 +54,11 @@
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
+import java.util.Map;
+import java.util.Set;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
@@ -75,6 +78,8 @@
import org.netbeans.api.editor.mimelookup.MimeLookup;
import org.netbeans.api.editor.mimelookup.MimePath;
import org.netbeans.api.editor.settings.KeyBindingSettings;
+import org.netbeans.api.lexer.TokenHierarchy;
+import org.netbeans.api.lexer.TokenSequence;
import org.netbeans.editor.BaseDocument;
import org.netbeans.editor.BaseKit;
import org.netbeans.editor.GuardedDocument;
@@ -189,7 +194,7 @@
/* Completion providers registered for the active component (its mime-type). Changed in AWT only. */
private CompletionProvider[] activeProviders = null;
- /** Mapping of mime-type to array of providers. Changed in AWT only. */
+ /** Mapping of mime-path to array of providers. Changed in AWT only. */
private HashMap providersCache = new HashMap();
/**
@@ -539,8 +544,15 @@
}
private boolean ensureActiveProviders() {
- if (activeProviders != null)
- return true;
+ if (activeProviders != null) {
+ String mime = getMimePath(getActiveComponent());
+ if (mime == null) {
+ return false;
+ }
+ if (mime.equals(currentMimePath)) {
+ return true;
+ }
+ }
JTextComponent component = getActiveComponent();
activeProviders = (component != null)
? getCompletionProvidersForComponent(component, false)
@@ -574,35 +586,103 @@
int docDelay = CompletionSettings.getInstance(getActiveComponent()).documentationAutoPopupDelay();
docAutoPopupTimer.setInitialDelay(docDelay);
- docAutoPopupTimer.restart();
+ docAutoPopupTimer.restart();
}
+ /**
+ * Gives MimePath of the caret position as String
+ * @param component
+ * @return
+ */
+ private String getMimePath(JTextComponent component) {
+ final int offset = component.getCaretPosition();
+ final MimePath[] mimePathR = new MimePath[1];
+ final Document doc = component.getDocument();
+
+ doc.render(new Runnable() {
+ @Override
+ public void run() {
+ List> seqs = TokenHierarchy.get(doc).embeddedTokenSequences(offset, true);
+ TokenSequence> seq;
+
+ if (seqs.isEmpty()) {
+ seq = TokenHierarchy.get(doc).tokenSequence();
+ } else {
+ seq = seqs.get(seqs.size() - 1);
+ }
+
+ if (seq != null) {
+ mimePathR[0] = MimePath.parse(seq.languagePath().mimePath());
+ }
+ }
+ });
+ if (mimePathR[0] != null) {
+ return mimePathR[0].getPath();
+ }
+ // original mimeType code
+ Object mimeTypeObj = DocumentUtilities.getMimeType(doc); //NOI18N
+ String mimeType;
+
+ if (mimeTypeObj instanceof String) {
+ mimeType = (String) mimeTypeObj;
+ } else {
+ BaseKit kit = Utilities.getKit(component);
+
+ if (kit == null) {
+ return null;
+ }
+
+ mimeType = kit.getContentType();
+ }
+ return mimeType;
+ }
+
+ private String currentMimePath;
+
+ /**
+ * Gets a complete MIME path for the given offset in the document
+ * @param doc the document
+ * @param offset point of interest
+ * @return MimePath
+ */
+ static MimePath getMimePath(final Document doc, final int offset) {
+ final MimePath[] mimePathR = new MimePath[1];
+ doc.render(new Runnable() {
+ @Override
+ public void run() {
+ List> seqs = TokenHierarchy.get(doc).embeddedTokenSequences(offset, true);
+ TokenSequence> seq = seqs.isEmpty() ? null : seqs.get(seqs.size() - 1);
+ seq = seq == null ? TokenHierarchy.get(doc).tokenSequence() : seq;
+ mimePathR[0] = seq == null ? MimePath.parse(DocumentUtilities.getMimeType(doc)) : MimePath.parse(seq.languagePath().mimePath());
+ }
+ });
+ return mimePathR[0];
+ }
+
+ /**
+ * Has side effects !
+ * @param component
+ * @param asyncWarmUp
+ * @return
+ */
private CompletionProvider[] getCompletionProvidersForComponent(JTextComponent component, boolean asyncWarmUp) {
assert (SwingUtilities.isEventDispatchThread());
if (component == null)
return null;
- Object mimeTypeObj = component.getDocument().getProperty("mimeType"); //NOI18N
- String mimeType;
-
- if (mimeTypeObj instanceof String)
- mimeType = (String) mimeTypeObj;
- else {
- BaseKit kit = Utilities.getKit(component);
-
- if (kit == null) {
- return new CompletionProvider[0];
- }
-
- mimeType = kit.getContentType();
+ String mimePathString = getMimePath(component);
+
+ if (mimePathString == null) {
+ return null;
}
-
- if (providersCache.containsKey(mimeType))
- return providersCache.get(mimeType);
+ if (providersCache.containsKey(mimePathString)) {
+ currentMimePath = mimePathString;
+ return providersCache.get(mimePathString);
+ }
if (asyncWarmUpTask != null) {
- if (asyncWarmUp && mimeType != null && mimeType.equals(asyncWarmUpMimeType))
+ if (asyncWarmUp && mimePathString != null && mimePathString.equals(asyncWarmUpMimeType))
return null;
if (!asyncWarmUpTask.cancel()) {
asyncWarmUpTask.waitFinished();
@@ -610,9 +690,10 @@
asyncWarmUpTask = null;
asyncWarmUpMimeType = null;
}
- final Lookup lookup = MimeLookup.getLookup(MimePath.get(mimeType));
+ MimePath path = MimePath.parse(mimePathString);
if (asyncWarmUp) {
- asyncWarmUpMimeType = mimeType;
+ final Lookup lookup = MimeLookup.getLookup(path);
+ asyncWarmUpMimeType = mimePathString;
asyncWarmUpTask = RequestProcessor.getDefault().post(new Runnable() {
@Override
public void run() {
@@ -621,10 +702,37 @@
});
return null;
}
- Collection extends CompletionProvider> col = lookup.lookupAll(CompletionProvider.class);
- int size = col.size();
- CompletionProvider[] ret = size == 0 ? null : col.toArray(new CompletionProvider[size]);
- providersCache.put(mimeType, ret);
+ // Note 1:
+ // it's not possible to compare instances of CompletionProvider; each MimeLookup has its own instances, so if e.g. MyLookup
+ // is registered for text/html then MimeLookup for text/x-jsp/text/html and MimeLookup for text/html produce a *different*
+ // instances of the provider.
+ // the registered classes could be used as key instead BUT there would be no way how to disambiguate the registration, IF
+ // it was really needed for whatever reason. So I decided to match the providers based on Lookup.Item.getID(), which evaluates
+ // (usually) to the registration filename, which the developer can alter, if for some reason the provider is needed.
+
+ List allProviders = new ArrayList<>();
+ Lookup lookup;
+ Set seenProviders = new HashSet<>();
+
+ for (int pref = path.size(); pref >= 1; pref--) {
+ lookup = MimeLookup.getLookup(path.getPrefix(pref));
+ Collection extends Lookup.Item> allItems = lookup.lookupResult(CompletionProvider.class).allItems();
+ for (Lookup.Item i : allItems) {
+ String id = i.getId();
+ int lastSlash = id.lastIndexOf('/'); // NOI18N
+ if (lastSlash > 0) {
+ String fname = id.substring(lastSlash + 1, id.length());
+ if (!seenProviders.add(fname)) {
+ // the provider has been already seen in this list; do not add it.
+ continue;
+ }
+ }
+ allProviders.add((CompletionProvider)i.getInstance());
+ }
+ }
+ currentMimePath = mimePathString;
+ CompletionProvider[] ret;
+ providersCache.put(mimePathString, ret = allProviders.toArray(new CompletionProvider[allProviders.size()]));
return ret;
}