com.intellij.embedding.IndentEatingLexer Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of xml-psi-impl Show documentation
Show all versions of xml-psi-impl Show documentation
A packaging of the IntelliJ Community Edition xml-psi-impl library.
This is release number 1 of trunk branch 142.
The newest version!
/*
* Copyright 2000-2014 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.embedding;
import com.intellij.lexer.Lexer;
import com.intellij.openapi.util.text.StringUtil;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* This masquerading lexer cuts out `myIndent` spaces/tabs after each newline in the text passed to this lexer.
* The string without these parts (indents) is then passed to a delegate lexer to lex correctly without these indents.
* The production of this lexer can also be effectively used in {@link com.intellij.embedding.MasqueradingPsiBuilderAdapter}
* to parse the text without these indents.
*/
public class IndentEatingLexer extends MasqueradingLexer.SmartDelegate {
private final int myIndent;
@NotNull
private CharSequence myBuffer;
@NotNull
private List myDeletions;
private int myCurrentDelta;
private int myTotalDelta;
private int myCurrentDelIndex;
public IndentEatingLexer(@NotNull Lexer delegate, int baseIndent) {
super(delegate);
myIndent = baseIndent;
myBuffer = "";
myDeletions = Collections.emptyList();
}
@Override
public void start(@NotNull CharSequence buffer, int startOffset, int endOffset, int initialState) {
myBuffer = buffer;
myDeletions = findAllDeletions(buffer, startOffset, endOffset);
myCurrentDelta = 0;
myTotalDelta = 0;
if (myDeletions.isEmpty()) {
super.start(buffer, startOffset, endOffset, initialState);
return;
}
CharSequence newSequence = deleteIndents(buffer, startOffset, endOffset);
super.start(newSequence, 0, newSequence.length(), initialState);
myTotalDelta = startOffset;
myCurrentDelIndex = 0;
updateDeltas();
}
private void updateDeltas() {
myTotalDelta += myCurrentDelta;
myCurrentDelta = 0;
while (myCurrentDelIndex < myDeletions.size()) {
final DeletedIndentInfo info = myDeletions.get(myCurrentDelIndex);
if (info.getShrunkPos() >= super.getTokenStart()
&& info.getShrunkPos() <= super.getTokenEnd()) {
myCurrentDelta += info.getLength();
myCurrentDelIndex++;
}
else {
break;
}
}
}
@Override
public void advance() {
super.advance();
updateDeltas();
}
@Override
public int getTokenStart() {
return super.getTokenStart() + myTotalDelta;
}
@Override
public int getTokenEnd() {
return super.getTokenEnd() + myTotalDelta + myCurrentDelta;
}
@NotNull
@Override
public String getTokenText() {
return myBuffer.subSequence(getTokenStart(), getTokenEnd()).toString();
}
@NotNull
private List findAllDeletions(@NotNull CharSequence buffer, int startOffset, int endOffset) {
List result = new ArrayList();
int offset = startOffset;
while (offset < endOffset) {
final int newline = StringUtil.indexOf(buffer, '\n', offset, endOffset);
if (newline < 0) {
break;
}
int charsToDelete = 0;
char c;
while (charsToDelete < myIndent
&& newline + charsToDelete + 1 < endOffset
&& (c = buffer.charAt(newline + charsToDelete + 1)) != '\n'
&& Character.isWhitespace(c)) {
charsToDelete++;
}
if (charsToDelete > 0) {
result.add(new DeletedIndentInfo(newline + 1, charsToDelete));
}
offset = newline + charsToDelete + 1;
}
return result;
}
@NotNull
private CharSequence deleteIndents(@NotNull CharSequence buffer, int startOffset, int endOffset) {
StringBuilder result = new StringBuilder();
int offset = startOffset;
for (DeletedIndentInfo deletion : myDeletions) {
result.append(buffer.subSequence(offset, deletion.getRealPos()));
deletion.setShrunkPos(result.length());
offset = deletion.getRealPos() + deletion.getLength();
}
result.append(buffer.subSequence(offset, endOffset));
return result.toString();
}
private static class DeletedIndentInfo {
private final int realPos;
private final int length;
private int shrunkPos;
public DeletedIndentInfo(int realPos, int length) {
this.realPos = realPos;
this.length = length;
shrunkPos = -1;
}
public void setShrunkPos(int shrunkPos) {
this.shrunkPos = shrunkPos;
}
public int getRealPos() {
return realPos;
}
public int getLength() {
return length;
}
public int getShrunkPos() {
return shrunkPos;
}
}
}