blob: 59149ceb236ceba76b0c82b58763994d65afb761 [file] [log] [blame]
/*
* Copyright 2000-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.lexer;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Key;
import com.intellij.psi.tree.IElementType;
import com.intellij.util.containers.HashMap;
import org.jetbrains.annotations.NotNull;
import java.util.HashSet;
import java.util.Map;
/**
* @author max
*/
public class LayeredLexer extends DelegateLexer {
public static ThreadLocal<Boolean> ourDisableLayersFlag = new ThreadLocal<Boolean>();
private static final Logger LOG = Logger.getInstance("#com.intellij.lexer.LayeredLexer");
private static final int IN_LAYER_STATE = 1024; // TODO: Other value?
private static final int IN_LAYER_LEXER_FINISHED_STATE = 2048;
private int myState;
private final Map<IElementType, Lexer> myStartTokenToLayerLexer = new HashMap<IElementType, Lexer>();
private Lexer myCurrentLayerLexer;
// In some cases IDEA-57933 layered lexer is not able to parse all the token, that triggered this lexer,
// for this purposes we store left part of token in the following fields
private IElementType myCurrentBaseTokenType;
private int myLayerLeftPart = -1;
private int myBaseTokenEnd = -1;
private final HashSet<Lexer> mySelfStoppingLexers = new HashSet<Lexer>(1);
private final HashMap<Lexer, IElementType[]> myStopTokens = new HashMap<Lexer,IElementType[]>(1);
public LayeredLexer(Lexer baseLexer) {
super(baseLexer);
}
public void registerSelfStoppingLayer(Lexer lexer, IElementType[] startTokens, IElementType[] stopTokens) {
if (Boolean.TRUE.equals(ourDisableLayersFlag.get())) return;
registerLayer(lexer, startTokens);
mySelfStoppingLexers.add(lexer);
myStopTokens.put(lexer, stopTokens);
}
public void registerLayer(Lexer lexer, IElementType... startTokens) {
if (Boolean.TRUE.equals(ourDisableLayersFlag.get())) return;
for (IElementType startToken : startTokens) {
LOG.assertTrue(!myStartTokenToLayerLexer.containsKey(startToken));
myStartTokenToLayerLexer.put(startToken, lexer);
}
}
private void activateLayerIfNecessary() {
final IElementType baseTokenType = super.getTokenType();
myCurrentLayerLexer = myStartTokenToLayerLexer.get(baseTokenType);
if (myCurrentLayerLexer != null) {
myCurrentBaseTokenType = baseTokenType;
myBaseTokenEnd = super.getTokenEnd();
myCurrentLayerLexer.start(super.getBufferSequence(), super.getTokenStart(), super.getTokenEnd());
if (mySelfStoppingLexers.contains(myCurrentLayerLexer)) {
super.advance();
}
}
}
@Override
public void start(@NotNull CharSequence buffer, int startOffset, int endOffset, int initialState) {
LOG.assertTrue(initialState != IN_LAYER_STATE, "Restoring to layer is not supported.");
myState = initialState;
myCurrentLayerLexer = null;
super.start(buffer, startOffset, endOffset, initialState);
activateLayerIfNecessary();
}
@Override
public int getState() {
return myState;
}
@Override
public IElementType getTokenType() {
if (myState == IN_LAYER_LEXER_FINISHED_STATE) {
return myCurrentBaseTokenType;
}
return isLayerActive() ? myCurrentLayerLexer.getTokenType() : super.getTokenType();
}
@Override
public int getTokenStart() {
if (myState == IN_LAYER_LEXER_FINISHED_STATE) {
return myLayerLeftPart;
}
return isLayerActive() ? myCurrentLayerLexer.getTokenStart() : super.getTokenStart();
}
@Override
public int getTokenEnd() {
if (myState == IN_LAYER_LEXER_FINISHED_STATE) {
return myBaseTokenEnd;
}
return isLayerActive() ? myCurrentLayerLexer.getTokenEnd() : super.getTokenEnd();
}
@Override
public void advance() {
if (myState == IN_LAYER_LEXER_FINISHED_STATE){
myState = super.getState();
return;
}
if (isLayerActive()) {
final Lexer activeLayerLexer = myCurrentLayerLexer;
IElementType layerTokenType = activeLayerLexer.getTokenType();
if (!isStopToken(myCurrentLayerLexer, layerTokenType)) {
myCurrentLayerLexer.advance();
layerTokenType = myCurrentLayerLexer.getTokenType();
} else {
layerTokenType = null;
}
if (layerTokenType == null) {
int tokenEnd = myCurrentLayerLexer.getTokenEnd();
if (!mySelfStoppingLexers.contains(myCurrentLayerLexer)) {
myCurrentLayerLexer = null;
super.advance();
activateLayerIfNecessary();
} else {
myCurrentLayerLexer = null;
// In case when we have non-covered gap we should return left part as next token
if (tokenEnd != myBaseTokenEnd) {
if (LOG.isDebugEnabled()) {
LOG.debug("We've got not covered gap from layered lexer: " + activeLayerLexer +
"\n on token: " + getBufferSequence().subSequence(myLayerLeftPart, myBaseTokenEnd));
}
myState = IN_LAYER_LEXER_FINISHED_STATE;
myLayerLeftPart = tokenEnd;
return;
}
}
}
} else {
super.advance();
activateLayerIfNecessary();
}
myState = isLayerActive() ? IN_LAYER_STATE : super.getState();
}
@NotNull
@Override
public LexerPosition getCurrentPosition() {
return new LexerPositionImpl(getTokenStart(), getState());
}
@Override
public void restore(@NotNull LexerPosition position) {
start(getBufferSequence(), position.getOffset(), getBufferEnd(), position.getState());
}
private boolean isStopToken(Lexer lexer, IElementType tokenType) {
final IElementType[] stopTokens = myStopTokens.get(lexer);
if (stopTokens == null) return false;
for (IElementType stopToken : stopTokens) {
if (stopToken == tokenType) return true;
}
return false;
}
private boolean isLayerActive() {
return myCurrentLayerLexer != null;
}
}