/**
* Copyright (C) 2015 drrb
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.github.drrb.rust.netbeans.parsing;
import com.github.drrb.rust.netbeans.util.Option;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import org.netbeans.api.lexer.TokenHierarchy;
import org.netbeans.api.lexer.TokenId;
import org.netbeans.api.lexer.TokenSequence;
import org.netbeans.modules.csl.api.OffsetRange;
import org.netbeans.modules.csl.spi.ParserResult;
public class RustLexUtils {
private static final Logger LOG = Logger.getLogger(RustLexUtils.class.getName());
public TokenSequence<RustTokenId> getRustTokenSequence(Document doc, int offset) {
TokenHierarchy<?> tokenHierarchy = TokenHierarchy.get(doc);
TokenSequence<RustTokenId> topLevelTokenSequence = tokenHierarchy.tokenSequence(RustTokenId.language());
if (topLevelTokenSequence != null) {
return topLevelTokenSequence;
}
TokenSequence<RustTokenId> embeddedRustTokenSequence = getEmbeddedRustTokenSequence(tokenHierarchy, offset, true);
if (embeddedRustTokenSequence != null) {
return embeddedRustTokenSequence;
}
TokenSequence<RustTokenId> embeddedRustTokenSequenceForwards = getEmbeddedRustTokenSequence(tokenHierarchy, offset, false);
if (embeddedRustTokenSequenceForwards != null) {
return embeddedRustTokenSequenceForwards;
}
try {
LOG.warning("Couldn't get Rust token sequence for document. Falling back to lexing it ourselves.");
tokenHierarchy = TokenHierarchy.create(doc.getText(0, doc.getLength()), RustTokenId.language());
return tokenHierarchy.tokenSequence(RustTokenId.language());
} catch (BadLocationException ex) {
LOG.log(Level.WARNING, "Couldn't get Rust token sequence for document at all!", ex);
return null;
}
}
private TokenSequence<RustTokenId> getEmbeddedRustTokenSequence(TokenHierarchy<?> tokenHierarchy, int offset, boolean backwardBias) {
for (TokenSequence<? extends TokenId> tokenSequence : tokenHierarchy.embeddedTokenSequences(offset, backwardBias)) {
if (tokenSequence.language() == RustTokenId.language()) {
@SuppressWarnings("unchecked")
TokenSequence<RustTokenId> embeddedTokenSequence = (TokenSequence<RustTokenId>) tokenSequence;
return embeddedTokenSequence;
}
}
return null;
}
public static Option<OffsetRustToken> getIdentifierAt(int caretOffset, ParserResult info) {
TokenHierarchy<?> tokenHierarchy = info.getSnapshot().getTokenHierarchy();
return getIdentifierAt(caretOffset, tokenHierarchy);
}
public static Option<OffsetRustToken> getIdentifierAt(int caretOffset, TokenHierarchy<?> tokenHierarchy) {
TokenSequence<RustTokenId> tokenSequence = tokenHierarchy.tokenSequence(RustTokenId.language());
Option<OffsetRustToken> tokenAtOffset = offsetTokenAt(caretOffset, tokenSequence);
if (tokenAtOffset.isNot()) {
return Option.none();
}
if (tokenAtOffset.value().id() == RustTokenId.IDENT) {
return tokenAtOffset;
} else if (caretOffset > 0) {
Option<OffsetRustToken> tokenBeforeOffset = offsetTokenAt(caretOffset - 1, tokenSequence);
if (tokenBeforeOffset.is() && tokenBeforeOffset.value().id() == RustTokenId.IDENT) {
return tokenBeforeOffset;
} else {
return Option.none();
}
} else {
return Option.none();
}
}
private static Option<OffsetRustToken> offsetTokenAt(int caretPosition, TokenSequence<RustTokenId> tokenSequence) {
tokenSequence.move(caretPosition);
if (tokenSequence.moveNext()) {
return Option.is(OffsetRustToken.atCurrentLocation(tokenSequence));
} else {
return Option.none();
}
}
public static OffsetRange range(int start, int end) {
return new OffsetRange(start, end);
}
}