package org.robotframework.ide.eclipse.main.plugin.tableeditor.source.colouring;
import static com.google.common.collect.Lists.newArrayList;
import java.util.ArrayList;
import java.util.Deque;
import java.util.List;
import java.util.Optional;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.rules.IToken;
import org.eclipse.jface.text.rules.Token;
import org.rf.ide.core.testdata.text.read.IRobotLineElement;
import org.rf.ide.core.testdata.text.read.RobotLine;
import org.robotframework.ide.eclipse.main.plugin.tableeditor.source.DocumentUtilities;
import org.robotframework.ide.eclipse.main.plugin.tableeditor.source.RobotDocument;
import org.robotframework.ide.eclipse.main.plugin.tableeditor.source.colouring.ISyntaxColouringRule.PositionedTextToken;
import org.robotframework.red.jface.text.rules.IRedTokenScanner;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Supplier;
public class RedTokenScanner implements IRedTokenScanner {
private RobotDocument document;
private final List<ISyntaxColouringRule> rules;
private Deque<IRobotLineElement> tokensToAnalyze;
private final List<IRobotLineElement> analyzedTokens = new ArrayList<>();
private Position lastTokenPosition;
private int rangeOffset;
private int rangeLength;
private int rangeLine;
private int currentOffsetInToken;
public RedTokenScanner(final ISyntaxColouringRule... rules) {
this.rules = newArrayList(rules);
}
@Override
public void resetPosition() {
lastTokenPosition = null;
}
@Override
public void setRange(final IDocument document, final int offset, final int length) {
this.document = (RobotDocument) document;
if (lastTokenPosition == null || lastTokenPosition.getOffset() + lastTokenPosition.getLength() != offset) {
this.tokensToAnalyze = null;
this.analyzedTokens.clear();
this.rangeOffset = offset;
this.rangeLength = length;
this.rangeLine = DocumentUtilities.getLine(document, offset);
this.currentOffsetInToken = 0;
}
}
@Override
public IToken nextToken() {
return nextToken(new Supplier<Deque<IRobotLineElement>>() {
@Override
public Deque<IRobotLineElement> get() {
try {
final List<RobotLine> lines = document.getNewestModel().getFileContent();
return new RedTokensQueueBuilder().buildQueue(rangeOffset, rangeLength, lines, rangeLine);
} catch (final InterruptedException e) {
throw new UnableToScanTokensException("Unable to build tokens queue", e);
}
}
});
}
@VisibleForTesting
IToken nextToken(final Supplier<Deque<IRobotLineElement>> elementsQueueSupplier) {
if (tokensToAnalyze == null) {
tokensToAnalyze = elementsQueueSupplier.get();
final IRobotLineElement firstToken = tokensToAnalyze.peekFirst();
if (firstToken != null) {
currentOffsetInToken = rangeOffset - firstToken.getStartOffset();
}
}
if (tokensToAnalyze.isEmpty()) {
lastTokenPosition = lastTokenPosition == null ? new Position(0, 0)
: new Position(getTokenOffset() + getTokenLength(), 0);
return Token.EOF;
}
final IRobotLineElement nextToken = tokensToAnalyze.poll();
for (final ISyntaxColouringRule rule : rules) {
if (!rule.isApplicable(nextToken)) {
continue;
}
final Optional<PositionedTextToken> tok = rule.evaluate(nextToken, currentOffsetInToken, analyzedTokens);
if (tok.isPresent()) {
final PositionedTextToken textToken = tok.get();
lastTokenPosition = textToken.getPosition();
if (lastTokenPosition.offset + lastTokenPosition.length >= nextToken.getStartOffset()
+ nextToken.getText().length()) {
// rule have consumed whole Robot Token
currentOffsetInToken = 0;
analyzedTokens.add(nextToken);
} else {
// the token needs more coloring, so return it to queue and shift the
// offset
currentOffsetInToken = lastTokenPosition.getOffset() + lastTokenPosition.getLength()
- nextToken.getStartOffset();
tokensToAnalyze.addFirst(nextToken);
}
return textToken.getToken();
}
}
lastTokenPosition = new Position(nextToken.getStartOffset() + currentOffsetInToken,
nextToken.getText().length() - currentOffsetInToken);
currentOffsetInToken = 0;
analyzedTokens.add(nextToken);
return ISyntaxColouringRule.DEFAULT_TOKEN;
}
@Override
public int getTokenOffset() {
return lastTokenPosition.getOffset();
}
@Override
public int getTokenLength() {
return lastTokenPosition.getLength();
}
public static class UnableToScanTokensException extends IllegalStateException {
private static final long serialVersionUID = 1L;
public UnableToScanTokensException(final String message, final Throwable cause) {
super(message, cause);
}
}
}