/* * 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.antlr.works.editor.antlr4.completion; import org.antlr.v4.runtime.CharStream; import org.antlr.v4.runtime.CommonTokenFactory; import org.antlr.v4.runtime.Token; import org.antlr.v4.runtime.TokenFactory; import org.antlr.v4.runtime.TokenSource; import org.antlr.v4.runtime.misc.Tuple; import org.antlr.v4.runtime.misc.Tuple2; /** * * @author Sam Harwell */ public class CodeCompletionTokenSource implements TokenSource { private final int caretOffset; private final TokenSource source; private final Tuple2<? extends TokenSource, CharStream> tokenFactorySourcePair; private TokenFactory tokenFactory = CommonTokenFactory.DEFAULT; private Token caretToken; public CodeCompletionTokenSource(int caretOffset, TokenSource source) { this.caretOffset = caretOffset; this.source = source; this.tokenFactorySourcePair = Tuple.create(source, source.getInputStream()); } @Override public Token nextToken() { if (caretToken == null) { Token token = source.nextToken(); if (token.getStopIndex() + 1 < caretOffset) { // the caret is after this token, nothing special to do } else if (token.getStartIndex() > caretOffset) { // the token is after the caret, no need to include it token = new CaretToken(tokenFactorySourcePair, Token.DEFAULT_CHANNEL, caretOffset, caretOffset); caretToken = token; } else { if (token.getStopIndex() + 1 == caretOffset && token.getStopIndex() >= token.getStartIndex()) { // the caret is at the end of this token, and this // isn't a word token or a zero-length token if (!isWordToken(token)) { return token; } } // the caret is in the middle of or at the end of this token token = new CaretToken(token); caretToken = token; } return token; } throw new UnsupportedOperationException("Attempted to look past the caret."); } @Override public int getLine() { return source.getLine(); } @Override public int getCharPositionInLine() { return source.getCharPositionInLine(); } @Override public CharStream getInputStream() { return source.getInputStream(); } @Override public String getSourceName() { return source.getSourceName(); } @Override public TokenFactory getTokenFactory() { return tokenFactory; } @Override public void setTokenFactory(TokenFactory tokenFactory) { source.setTokenFactory(tokenFactory); this.tokenFactory = tokenFactory != null ? tokenFactory : CommonTokenFactory.DEFAULT; } protected boolean isWordToken(Token token) { return AbstractCompletionQuery.WORD_PATTERN.matcher(token.getText()).matches(); } protected Token emitEOF() { return tokenFactory.create(tokenFactorySourcePair, Token.EOF, (String)null, Token.DEFAULT_CHANNEL, getInputStream().index(), getInputStream().index() - 1, getLine(), getCharPositionInLine()); } }