org.tvl.goworks.editor.go.completion.GoCompletionProvider.java Source code

Java tutorial

Introduction

Here is the source code for org.tvl.goworks.editor.go.completion.GoCompletionProvider.java

Source

/*
 *  Copyright (c) 2012 Sam Harwell, Tunnel Vision Laboratories LLC
 *  All rights reserved.
 *
 *  The source code of this document is proprietary work, and is not licensed for
 *  distribution. For information about licensing, contact Sam Harwell at:
 *      sam@tunnelvisionlabs.com
 */
package org.tvl.goworks.editor.go.completion;

import com.tvl.spi.editor.completion.CompletionProvider;
import java.util.EnumSet;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.text.AbstractDocument;
import javax.swing.text.Document;
import javax.swing.text.JTextComponent;
import org.antlr.netbeans.editor.classification.TokenTag;
import org.antlr.netbeans.editor.tagging.TaggedPositionRegion;
import org.antlr.netbeans.editor.tagging.Tagger;
import org.antlr.netbeans.editor.text.DocumentSnapshot;
import org.antlr.netbeans.editor.text.NormalizedSnapshotPositionRegionCollection;
import org.antlr.netbeans.editor.text.OffsetRegion;
import org.antlr.netbeans.editor.text.SnapshotPositionRegion;
import org.antlr.netbeans.editor.text.VersionedDocumentUtilities;
import org.antlr.netbeans.parsing.spi.ParserData;
import org.antlr.netbeans.parsing.spi.ParserDataOptions;
import org.antlr.netbeans.parsing.spi.ParserTaskManager;
import org.antlr.v4.runtime.Lexer;
import org.antlr.v4.runtime.Token;
import org.antlr.works.editor.antlr4.completion.AbstractCompletionProvider;
import org.antlr.works.editor.antlr4.completion.AbstractCompletionQuery;
import org.netbeans.api.editor.mimelookup.MimeRegistration;
import org.openide.util.Lookup;
import org.openide.util.NbBundle;
import org.openide.util.Parameters;
import org.tvl.goworks.editor.GoEditorKit;
import org.tvl.goworks.editor.go.GoParserDataDefinitions;
import org.tvl.goworks.editor.go.parser.GoLexer;

/**
 *
 * @author Sam Harwell
 */
@MimeRegistration(mimeType = GoEditorKit.GO_MIME_TYPE, service = CompletionProvider.class)
@NbBundle.Messages({ "GCP-imported-items=", "GCP-instance-members=" })
public class GoCompletionProvider extends AbstractCompletionProvider {
    // -J-Dorg.tvl.goworks.editor.go.completion.GoCompletionProvider.level=FINE
    private static final Logger LOGGER = Logger.getLogger(GoCompletionProvider.class.getName());

    private static String goCompletionAutoPopupTriggers = ".";
    private static String goCompletionSelectors = "{}[]()<>.,:;+-*/%&|^!~=?@#'\"\\ ";

    public static void incompleteCompletionSupport() {
        assert false : "Editor code completion for Go is not yet complete.";
    }

    public static void incompleteCompletionSupport(String message) {
        assert false : message;
    }

    @Override
    public int getAutoQueryTypes(JTextComponent component, String typedText) {
        if (typedText == null || typedText.length() != 1) {
            return 0;
        }

        boolean triggered = getCompletionAutoPopupTriggers().indexOf(typedText.charAt(0)) >= 0;
        if (triggered || (autoPopupOnIdentifierPart() && GoCompletionQuery.isIdentifierPart(typedText))) {
            int offset = component.getSelectionStart() - 1;
            Token contextToken = getContext(component, offset);
            if (contextToken == null) {
                return 0;
            }

            if (!triggered) {
                // the caret must be at the end of the identifier. note that the
                // offset is already 1 position before the caret, so no need to
                // add 1 to contextToken.getStopIndex().
                if (offset != contextToken.getStopIndex()) {
                    return 0;
                }

                // only trigger for the first character of the identifier
                if (contextToken.getStopIndex() > contextToken.getStartIndex()) {
                    return 0;
                }
            }

            boolean allowInStrings = false;
            if (isGoContext(contextToken, offset, allowInStrings)) {
                return COMPLETION_QUERY_TYPE | AUTO_QUERY_TYPE;
            }
        }

        return 0;
    }

    @Override
    protected AbstractCompletionQuery createCompletionQuery(int queryType, int caretOffset, boolean extend) {
        return new GoCompletionQuery(this, queryType, caretOffset, true, extend);
    }

    @Override
    public boolean autoPopupOnIdentifierPart() {
        return true;
    }

    @Override
    public String getCompletionAutoPopupTriggers() {
        return goCompletionAutoPopupTriggers;
    }

    @Override
    public String getCompletionSelectors() {
        return goCompletionSelectors;
    }

    @Override
    public Token getContext(JTextComponent component, int offset) {
        return getContext(component.getDocument(), offset);
    }

    @Override
    public Token getContext(Document document, int offset) {
        Parameters.notNull("document", document);

        if (document instanceof AbstractDocument) {
            ((AbstractDocument) document).readLock();
        }

        try {
            //            try {
            ParserTaskManager taskManager = Lookup.getDefault().lookup(ParserTaskManager.class);
            DocumentSnapshot snapshot = VersionedDocumentUtilities.getVersionedDocument(document)
                    .getCurrentSnapshot();
            Future<ParserData<Tagger<TokenTag<Token>>>> futureTokensData = taskManager.getData(snapshot,
                    GoParserDataDefinitions.LEXER_TOKENS, EnumSet.of(ParserDataOptions.SYNCHRONOUS));
            if (futureTokensData == null) {
                return null;
            }

            Tagger<TokenTag<Token>> tagger;
            try {
                tagger = futureTokensData.get().getData();
                if (tagger == null) {
                    return null;
                }
            } catch (InterruptedException | ExecutionException ex) {
                LOGGER.log(Level.WARNING, "An exception occurred while getting tokens.", ex);
                return null;
            }

            // get the token(s) at the cursor position, with affinity both directions
            OffsetRegion region = OffsetRegion.fromBounds(Math.max(0, offset - 1),
                    Math.min(snapshot.length(), offset + 1));
            Iterable<TaggedPositionRegion<TokenTag<Token>>> tags = tagger.getTags(
                    new NormalizedSnapshotPositionRegionCollection(new SnapshotPositionRegion(snapshot, region)));

            // TODO: cache tokens
            //                ANTLRInputStream input = new ANTLRInputStream(document.getText(0, document.getLength()));
            //                GoLexer lexer = new GoLexer(input);
            //                CommonTokenStream tokenStream = new CommonTokenStream(lexer);
            Token token = null;
            //                for (token = tokenStream.LT(1); token != null && token.getType() != Token.EOF; token = tokenStream.LT(1)) {
            //                    tokenStream.consume();
            //                    if (token.getStartIndex() <= offset && token.getStopIndex() >= offset) {
            //                        break;
            //                    }
            //                }
            for (TaggedPositionRegion<TokenTag<Token>> taggedRegion : tags) {
                if (taggedRegion.getTag().getToken().getChannel() != Lexer.DEFAULT_TOKEN_CHANNEL) {
                    continue;
                }

                token = taggedRegion.getTag().getToken();
                if (token.getStartIndex() <= offset && token.getStopIndex() >= offset) {
                    break;
                }
            }

            if (token == null) {
                // try again without skipping off-channel tokens
                for (TaggedPositionRegion<TokenTag<Token>> taggedRegion : tags) {
                    token = taggedRegion.getTag().getToken();
                    if (token.getStartIndex() <= offset && token.getStopIndex() >= offset) {
                        break;
                    }
                }
            }

            return token;
            //List<Token> tokens;
            //            } catch (BadLocationException ex) {
            //                Exceptions.printStackTrace(ex);
            //                return null;
            //            }
        } finally {
            if (document instanceof AbstractDocument) {
                ((AbstractDocument) document).readUnlock();
            }
        }
    }

    @Override
    public boolean isContext(Token token, int offset, int queryType) {
        return isGoContext(token, offset, false);
    }

    /*package*/ boolean isGoContext(JTextComponent component, int offset, boolean allowInStrings) {
        return isGoContext(getContext(component, offset), offset, allowInStrings);
    }

    /*package*/ static boolean isGoContext(Token token, int offset, boolean allowInStrings) {
        if (token == null) {
            return false;
        }

        switch (token.getType()) {
        case GoLexer.COMMENT:
            return false;

        case GoLexer.CharLiteral:
        case GoLexer.StringLiteral:
            return allowInStrings;

        case GoLexer.WS:
        case GoLexer.NEWLINE:
            return true;

        default:
            return token.getChannel() == Lexer.DEFAULT_TOKEN_CHANNEL;
        }
    }

}