All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.intellij.embedding.IndentEatingLexer Maven / Gradle / Ivy

Go to download

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;
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy