This Bugzilla instance is a read-only archive of historic NetBeans bug reports. To report a bug in NetBeans please follow the project's instructions for reporting issues.

View | Details | Raw Unified | Return to bug 258139
Collapse All | Expand All

(-)a/editor.completion/nbproject/project.properties (-1 / +1 lines)
Lines 44-47 Link Here
44
javac.source=1.7
44
javac.source=1.7
45
javadoc.arch=${basedir}/arch.xml
45
javadoc.arch=${basedir}/arch.xml
46
javadoc.apichanges=${basedir}/apichanges.xml
46
javadoc.apichanges=${basedir}/apichanges.xml
47
spec.version.base=1.44.0
47
spec.version.base=1.45.0
(-)a/editor.completion/nbproject/project.xml (+9 lines)
Lines 112-117 Link Here
112
                    </run-dependency>
112
                    </run-dependency>
113
                </dependency>
113
                </dependency>
114
                <dependency>
114
                <dependency>
115
                    <code-name-base>org.netbeans.modules.lexer</code-name-base>
116
                    <build-prerequisite/>
117
                    <compile-dependency/>
118
                    <run-dependency>
119
                        <release-version>2</release-version>
120
                        <specification-version>1.63</specification-version>
121
                    </run-dependency>
122
                </dependency>
123
                <dependency>
115
                    <code-name-base>org.netbeans.modules.sampler</code-name-base>
124
                    <code-name-base>org.netbeans.modules.sampler</code-name-base>
116
                    <build-prerequisite/>
125
                    <build-prerequisite/>
117
                    <compile-dependency/>
126
                    <compile-dependency/>
(-)a/editor.completion/src/org/netbeans/modules/editor/completion/CompletionImpl.java (-27 / +135 lines)
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
    

Return to bug 258139