org.eclipse.xtext.idea.completion.AbstractCompletionContributor.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.xtext.idea.completion.AbstractCompletionContributor.java

Source

/**
 * Copyright (c) 2015 itemis AG (http://www.itemis.eu) and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 */
package org.eclipse.xtext.idea.completion;

import com.google.common.base.Objects;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Multimap;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.intellij.codeInsight.completion.CompletionContext;
import com.intellij.codeInsight.completion.CompletionContributor;
import com.intellij.codeInsight.completion.CompletionInitializationContext;
import com.intellij.codeInsight.completion.CompletionParameters;
import com.intellij.codeInsight.completion.CompletionProvider;
import com.intellij.codeInsight.completion.CompletionResult;
import com.intellij.codeInsight.completion.CompletionResultSet;
import com.intellij.codeInsight.completion.CompletionService;
import com.intellij.codeInsight.completion.CompletionSorter;
import com.intellij.codeInsight.completion.CompletionType;
import com.intellij.codeInsight.completion.LegacyCompletionContributor;
import com.intellij.codeInsight.completion.OffsetMap;
import com.intellij.codeInsight.completion.PrefixMatcher;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.lookup.LookupElementWeigher;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.application.Application;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.ex.EditorEx;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.tree.TokenSet;
import com.intellij.util.Consumer;
import com.intellij.util.ProcessingContext;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.xtend.lib.annotations.Data;
import org.eclipse.xtext.AbstractElement;
import org.eclipse.xtext.Grammar;
import org.eclipse.xtext.IGrammarAccess;
import org.eclipse.xtext.Keyword;
import org.eclipse.xtext.ide.editor.contentassist.ContentAssistContext;
import org.eclipse.xtext.ide.editor.contentassist.IFollowElementAcceptor;
import org.eclipse.xtext.ide.editor.contentassist.antlr.ContentAssistContextFactory;
import org.eclipse.xtext.ide.editor.contentassist.antlr.FollowElement;
import org.eclipse.xtext.ide.editor.contentassist.antlr.FollowElementComputer;
import org.eclipse.xtext.ide.editor.contentassist.antlr.IContentAssistParser;
import org.eclipse.xtext.idea.completion.CompletionExtensions;
import org.eclipse.xtext.idea.editorActions.TokenSetProvider;
import org.eclipse.xtext.idea.lang.AbstractXtextLanguage;
import org.eclipse.xtext.psi.impl.BaseXtextFile;
import org.eclipse.xtext.resource.XtextResource;
import org.eclipse.xtext.util.TextRegion;
import org.eclipse.xtext.xbase.lib.CollectionLiterals;
import org.eclipse.xtext.xbase.lib.Conversions;
import org.eclipse.xtext.xbase.lib.Extension;
import org.eclipse.xtext.xbase.lib.ObjectExtensions;
import org.eclipse.xtext.xbase.lib.Procedures.Procedure1;
import org.eclipse.xtext.xbase.lib.Pure;
import org.eclipse.xtext.xbase.lib.util.ToStringBuilder;

@SuppressWarnings("all")
public abstract class AbstractCompletionContributor extends CompletionContributor {
    @Data
    public static class KeywordLookupElement extends LookupElement {
        private final Keyword keyword;

        @Override
        public String getLookupString() {
            return this.keyword.getValue();
        }

        public KeywordLookupElement(final Keyword keyword) {
            super();
            this.keyword = keyword;
        }

        @Override
        @Pure
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + ((this.keyword == null) ? 0 : this.keyword.hashCode());
            return result;
        }

        @Override
        @Pure
        public boolean equals(final Object obj) {
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (getClass() != obj.getClass())
                return false;
            AbstractCompletionContributor.KeywordLookupElement other = (AbstractCompletionContributor.KeywordLookupElement) obj;
            if (this.keyword == null) {
                if (other.keyword != null)
                    return false;
            } else if (!this.keyword.equals(other.keyword))
                return false;
            return true;
        }

        @Override
        @Pure
        public String toString() {
            String result = new ToStringBuilder(this).addAllFields().toString();
            return result;
        }

        @Pure
        public Keyword getKeyword() {
            return this.keyword;
        }
    }

    public static class DispreferKeywordsWeigher extends LookupElementWeigher {
        public DispreferKeywordsWeigher() {
            super("dispreferKeywords");
        }

        @Override
        public Boolean weigh(final LookupElement element) {
            return Boolean.valueOf((element instanceof AbstractCompletionContributor.KeywordLookupElement));
        }
    }

    @Inject(optional = true)
    private Provider<ContentAssistContextFactory> delegates;

    @Inject
    @Extension
    protected CompletionExtensions _completionExtensions;

    @Inject
    protected IGrammarAccess grammarAccess;

    @Inject
    @Extension
    protected TokenSetProvider _tokenSetProvider;

    @Inject(optional = true)
    private IContentAssistParser contentAssistParser;

    @Inject(optional = true)
    private FollowElementComputer followElementComputer;

    private ExecutorService pool = Executors.newFixedThreadPool(3);

    private final Map<CompletionType, Multimap<TokenSet, CompletionProvider<CompletionParameters>>> myContributors = CollectionLiterals
            .<CompletionType, Multimap<TokenSet, CompletionProvider<CompletionParameters>>>newHashMap();

    private final Map<CompletionType, Map<TokenSet, Multimap<AbstractElement, CompletionProvider<CompletionParameters>>>> myFollowElementBasedContributors = CollectionLiterals
            .<CompletionType, Map<TokenSet, Multimap<AbstractElement, CompletionProvider<CompletionParameters>>>>newHashMap();

    public AbstractCompletionContributor(final AbstractXtextLanguage lang) {
        lang.injectMembers(this);
    }

    protected void extend(final CompletionType type, final CompletionProvider<CompletionParameters> contrib) {
        TokenSet _defaultTokens = this._tokenSetProvider.getDefaultTokens();
        this.extend(type, new TokenSet[] { _defaultTokens }, contrib);
    }

    protected void extend(final CompletionType type, final TokenSet[] tokenSets,
            final CompletionProvider<CompletionParameters> contrib) {
        boolean _containsKey = this.myContributors.containsKey(type);
        boolean _not = (!_containsKey);
        if (_not) {
            ArrayListMultimap<TokenSet, CompletionProvider<CompletionParameters>> _create = ArrayListMultimap
                    .<TokenSet, CompletionProvider<CompletionParameters>>create();
            this.myContributors.put(type, _create);
        }
        for (final TokenSet tokenSet : tokenSets) {
            Multimap<TokenSet, CompletionProvider<CompletionParameters>> _get = this.myContributors.get(type);
            _get.put(tokenSet, contrib);
        }
    }

    protected void extend(final CompletionType type, final EStructuralFeature feature,
            final CompletionProvider<CompletionParameters> contrib) {
        TokenSet _defaultTokens = this._tokenSetProvider.getDefaultTokens();
        this.extend(type, new TokenSet[] { _defaultTokens }, feature, contrib);
    }

    protected void extend(final CompletionType type, final TokenSet[] tokenSets, final EStructuralFeature feature,
            final CompletionProvider<CompletionParameters> contrib) {
        if ((this.followElementComputer == null)) {
            throw new IllegalStateException(
                    "followElementComputer is not injected, probably IDE project is missing");
        }
        Grammar _grammar = this.grammarAccess.getGrammar();
        final IFollowElementAcceptor _function = (AbstractElement it) -> {
            this.extend(type, tokenSets, it, contrib);
        };
        this.followElementComputer.collectAbstractElements(_grammar, feature, _function);
    }

    protected void extend(final CompletionType type, final AbstractElement followElement,
            final CompletionProvider<CompletionParameters> contrib) {
        TokenSet _defaultTokens = this._tokenSetProvider.getDefaultTokens();
        this.extend(type, new TokenSet[] { _defaultTokens }, followElement, contrib);
    }

    protected void extend(final CompletionType type, final TokenSet[] tokenSets,
            final AbstractElement followElement, final CompletionProvider<CompletionParameters> contrib) {
        boolean _containsKey = this.myFollowElementBasedContributors.containsKey(type);
        boolean _not = (!_containsKey);
        if (_not) {
            HashMap<TokenSet, Multimap<AbstractElement, CompletionProvider<CompletionParameters>>> _newHashMap = CollectionLiterals
                    .<TokenSet, Multimap<AbstractElement, CompletionProvider<CompletionParameters>>>newHashMap();
            this.myFollowElementBasedContributors.put(type, _newHashMap);
        }
        final Map<TokenSet, Multimap<AbstractElement, CompletionProvider<CompletionParameters>>> map = this.myFollowElementBasedContributors
                .get(type);
        for (final TokenSet tokenSet : tokenSets) {
            {
                boolean _containsKey_1 = map.containsKey(tokenSet);
                boolean _not_1 = (!_containsKey_1);
                if (_not_1) {
                    ArrayListMultimap<AbstractElement, CompletionProvider<CompletionParameters>> _create = ArrayListMultimap
                            .<AbstractElement, CompletionProvider<CompletionParameters>>create();
                    map.put(tokenSet, _create);
                }
                final Multimap<AbstractElement, CompletionProvider<CompletionParameters>> providers = map
                        .get(tokenSet);
                providers.put(followElement, contrib);
            }
        }
    }

    @Override
    public void fillCompletionVariants(final CompletionParameters parameters, final CompletionResultSet result) {
        CompletionSorter _completionSorter = this.getCompletionSorter(parameters, result);
        final CompletionResultSet sortedResult = result.withRelevanceSorter(_completionSorter);
        final Procedure1<CompletionResult> _function = (CompletionResult it) -> {
            LookupElement _lookupElement = it.getLookupElement();
            boolean _isValidProposal = this.isValidProposal(_lookupElement, parameters);
            if (_isValidProposal) {
                LookupElement _lookupElement_1 = it.getLookupElement();
                sortedResult.addElement(_lookupElement_1);
            }
        };
        final Procedure1<CompletionResult> filteredConsumer = _function;
        CompletionService _completionService = CompletionService.getCompletionService();
        final CompletionResultSet filteredResult = _completionService.createResultSet(parameters,
                new Consumer<CompletionResult>() {
                    public void consume(CompletionResult t) {
                        filteredConsumer.apply(t);
                    }
                }, this);
        this.createMatcherBasedProposals(parameters, filteredResult);
        this.createReferenceBasedProposals(parameters, filteredResult);
        this.createTokenSetBasedProposals(parameters, filteredResult);
        this.createFollowElementBasedProposals(parameters, filteredResult);
        this.createParserBasedProposals(parameters, filteredResult);
        result.stopHere();
    }

    protected void createTokenSetBasedProposals(final CompletionParameters parameters,
            final CompletionResultSet result) {
        if ((this.myContributors.isEmpty() || (!this.myContributors.containsKey(parameters.getCompletionType())))) {
            return;
        }
        Editor _editor = parameters.getEditor();
        int _offset = parameters.getOffset();
        final TokenSet tokenSet = this._tokenSetProvider.getTokenSet(((EditorEx) _editor), _offset);
        CompletionType _completionType = parameters.getCompletionType();
        Multimap<TokenSet, CompletionProvider<CompletionParameters>> _get = this.myContributors
                .get(_completionType);
        final Collection<CompletionProvider<CompletionParameters>> providers = _get.get(tokenSet);
        boolean _equals = Objects.equal(providers, null);
        if (_equals) {
            return;
        }
        final HashSet<CompletionProvider<CompletionParameters>> calledProviders = CollectionLiterals
                .<CompletionProvider<CompletionParameters>>newHashSet();
        final ProcessingContext context = new ProcessingContext();
        for (final CompletionProvider<CompletionParameters> provider : providers) {
            boolean _add = calledProviders.add(provider);
            if (_add) {
                provider.addCompletionVariants(parameters, context, result);
                boolean _isStopped = result.isStopped();
                if (_isStopped) {
                    return;
                }
            }
        }
    }

    protected void createFollowElementBasedProposals(final CompletionParameters parameters,
            final CompletionResultSet result) {
        if ((this.myFollowElementBasedContributors.isEmpty()
                || (!this.myFollowElementBasedContributors.containsKey(parameters.getCompletionType())))) {
            return;
        }
        Editor _editor = parameters.getEditor();
        int _offset = parameters.getOffset();
        final TokenSet tokenSet = this._tokenSetProvider.getTokenSet(((EditorEx) _editor), _offset);
        CompletionType _completionType = parameters.getCompletionType();
        Map<TokenSet, Multimap<AbstractElement, CompletionProvider<CompletionParameters>>> _get = this.myFollowElementBasedContributors
                .get(_completionType);
        final Multimap<AbstractElement, CompletionProvider<CompletionParameters>> element2provider = _get
                .get(tokenSet);
        boolean _equals = Objects.equal(element2provider, null);
        if (_equals) {
            return;
        }
        final Set<AbstractElement> followElements = this.computeFollowElements(parameters);
        final HashSet<CompletionProvider<CompletionParameters>> calledProviders = CollectionLiterals
                .<CompletionProvider<CompletionParameters>>newHashSet();
        Set<AbstractElement> _keySet = element2provider.keySet();
        for (final AbstractElement followElement : _keySet) {
            {
                final ProcessingContext context = new ProcessingContext();
                boolean _contains = followElements.contains(followElement);
                if (_contains) {
                    final Collection<CompletionProvider<CompletionParameters>> providers = element2provider
                            .get(followElement);
                    for (final CompletionProvider<CompletionParameters> provider : providers) {
                        boolean _add = calledProviders.add(provider);
                        if (_add) {
                            provider.addCompletionVariants(parameters, context, result);
                            boolean _isStopped = result.isStopped();
                            if (_isStopped) {
                                return;
                            }
                        }
                    }
                }
            }
        }
    }

    protected Set<AbstractElement> computeFollowElements(final CompletionParameters parameters) {
        Editor _editor = parameters.getEditor();
        Document _document = _editor.getDocument();
        PsiElement _position = parameters.getPosition();
        ASTNode _node = _position.getNode();
        int _startOffset = _node.getStartOffset();
        TextRange _textRange = new TextRange(0, _startOffset);
        final String text = _document.getText(_textRange);
        final Collection<FollowElement> followElements = this.contentAssistParser.getFollowElements(text, false);
        final HashSet<AbstractElement> allElements = CollectionLiterals.<AbstractElement>newHashSet();
        this.followElementComputer.computeFollowElements(followElements, allElements);
        return allElements;
    }

    protected CompletionSorter getCompletionSorter(final CompletionParameters parameters,
            final CompletionResultSet result) {
        PrefixMatcher _prefixMatcher = result.getPrefixMatcher();
        CompletionSorter _defaultSorter = CompletionSorter.defaultSorter(parameters, _prefixMatcher);
        AbstractCompletionContributor.DispreferKeywordsWeigher _dispreferKeywordsWeigher = new AbstractCompletionContributor.DispreferKeywordsWeigher();
        return _defaultSorter.weighBefore("liftShorter", _dispreferKeywordsWeigher);
    }

    protected boolean isValidProposal(final LookupElement proposal, final CompletionParameters parameters) {
        return true;
    }

    protected void createMatcherBasedProposals(final CompletionParameters parameters,
            final CompletionResultSet result) {
        super.fillCompletionVariants(parameters, result);
    }

    protected boolean createReferenceBasedProposals(final CompletionParameters parameters,
            final CompletionResultSet result) {
        return LegacyCompletionContributor.completeReference(parameters, result);
    }

    protected void createParserBasedProposals(final CompletionParameters parameters,
            final CompletionResultSet result) {
        Editor _editor = parameters.getEditor();
        int _offset = parameters.getOffset();
        final TokenSet tokenSet = this._tokenSetProvider.getTokenSet(((EditorEx) _editor), _offset);
        boolean _supportParserBasedProposals = this.supportParserBasedProposals(tokenSet);
        boolean _not = (!_supportParserBasedProposals);
        if (_not) {
            return;
        }
        final ContentAssistContextFactory delegate = this.getParserBasedDelegate();
        boolean _equals = Objects.equal(delegate, null);
        if (_equals) {
            return;
        }
        String _text = this.getText(parameters);
        TextRegion _selection = this.getSelection(parameters);
        int _offset_1 = parameters.getOffset();
        XtextResource _resource = this.getResource(parameters);
        final ContentAssistContext[] contexts = delegate.create(_text, _selection, _offset_1, _resource);
        final java.util.function.Consumer<ContentAssistContext> _function = (ContentAssistContext c) -> {
            ImmutableList<AbstractElement> _firstSetGrammarElements = c.getFirstSetGrammarElements();
            final java.util.function.Consumer<AbstractElement> _function_1 = (AbstractElement e) -> {
                this.createProposal(e, c, parameters, result);
            };
            _firstSetGrammarElements.forEach(_function_1);
        };
        ((List<ContentAssistContext>) Conversions.doWrapArray(contexts)).forEach(_function);
    }

    protected boolean supportParserBasedProposals(final TokenSet tokenSet) {
        TokenSet _defaultTokens = this._tokenSetProvider.getDefaultTokens();
        return Objects.equal(tokenSet, _defaultTokens);
    }

    protected ContentAssistContextFactory getParserBasedDelegate() {
        ContentAssistContextFactory _xblockexpression = null;
        {
            boolean _equals = Objects.equal(this.delegates, null);
            if (_equals) {
                return null;
            }
            ContentAssistContextFactory _get = this.delegates.get();
            final Procedure1<ContentAssistContextFactory> _function = (ContentAssistContextFactory it) -> {
                it.setPool(this.pool);
            };
            _xblockexpression = ObjectExtensions.<ContentAssistContextFactory>operator_doubleArrow(_get, _function);
        }
        return _xblockexpression;
    }

    protected String getText(final CompletionParameters parameters) {
        final Computable<String> _function = () -> {
            PsiFile _originalFile = parameters.getOriginalFile();
            return _originalFile.getText();
        };
        return this.<String>runReadAction(_function);
    }

    protected TextRegion getSelection(final CompletionParameters parameters) {
        TextRegion _xblockexpression = null;
        {
            final OffsetMap offsets = this.getOffsets(parameters);
            final int startOffset = offsets.getOffset(CompletionInitializationContext.START_OFFSET);
            final int endOffset = offsets.getOffset(CompletionInitializationContext.SELECTION_END_OFFSET);
            _xblockexpression = new TextRegion(startOffset, (endOffset - startOffset));
        }
        return _xblockexpression;
    }

    protected OffsetMap getOffsets(final CompletionParameters parameters) {
        final Computable<OffsetMap> _function = () -> {
            PsiElement _position = parameters.getPosition();
            CompletionContext _userData = _position
                    .<CompletionContext>getUserData(CompletionContext.COMPLETION_CONTEXT_KEY);
            return _userData.getOffsetMap();
        };
        return this.<OffsetMap>runReadAction(_function);
    }

    protected XtextResource getResource(final CompletionParameters parameters) {
        final Computable<XtextResource> _function = () -> {
            PsiFile _originalFile = parameters.getOriginalFile();
            return ((BaseXtextFile) _originalFile).getResource();
        };
        return this.<XtextResource>runReadAction(_function);
    }

    protected <T extends Object> T runReadAction(final Computable<T> computable) {
        Application _application = ApplicationManager.getApplication();
        return _application.<T>runReadAction(computable);
    }

    protected void createProposal(final AbstractElement grammarElement, final ContentAssistContext context,
            final CompletionParameters parameters, final CompletionResultSet result) {
        boolean _matched = false;
        if (grammarElement instanceof Keyword) {
            _matched = true;
            this.createKeyWordProposal(((Keyword) grammarElement), context, parameters, result);
        }
    }

    protected void createKeyWordProposal(final Keyword keyword, final ContentAssistContext context,
            final CompletionParameters parameters, final CompletionResultSet result) {
        boolean _isKeywordWorthyToPropose = this.isKeywordWorthyToPropose(keyword);
        if (_isKeywordWorthyToPropose) {
            AbstractCompletionContributor.KeywordLookupElement _keywordLookupElement = new AbstractCompletionContributor.KeywordLookupElement(
                    keyword);
            this._completionExtensions.operator_add(result, _keywordLookupElement);
        }
    }

    protected boolean isKeywordWorthyToPropose(final Keyword keyword) {
        return true;
    }
}