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

com.intellij.embedding.MasqueradingPsiBuilderAdapter 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-2015 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.lang.ASTNode;
import com.intellij.lang.LighterLazyParseableNode;
import com.intellij.lang.ParserDefinition;
import com.intellij.lang.PsiBuilder;
import com.intellij.lang.impl.DelegateMarker;
import com.intellij.lang.impl.PsiBuilderAdapter;
import com.intellij.lang.impl.PsiBuilderImpl;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.psi.TokenType;
import com.intellij.psi.tree.IElementType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.ArrayList;
import java.util.List;

/**
 * A delegate PsiBuilder that hides or substitutes some tokens (namely, the ones provided by {@link MasqueradingLexer})
 * from a parser, however, _still inserting_ them into a production tree in their initial appearance.
 * @see MasqueradingLexer
 */
public class MasqueradingPsiBuilderAdapter extends PsiBuilderAdapter {
  private final static Logger LOG = Logger.getInstance(MasqueradingPsiBuilderAdapter.class);

  private List myShrunkSequence;

  private CharSequence myShrunkCharSequence;

  private int myLexPosition;

  private final PsiBuilderImpl myBuilderDelegate;

  private final MasqueradingLexer myLexer;

  public MasqueradingPsiBuilderAdapter(@NotNull final Project project,
                        @NotNull final ParserDefinition parserDefinition,
                        @NotNull final MasqueradingLexer lexer,
                        @NotNull final ASTNode chameleon,
                        @NotNull final CharSequence text) {
    this(new PsiBuilderImpl(project, parserDefinition, lexer, chameleon, text));
  }

  public MasqueradingPsiBuilderAdapter(@NotNull final Project project,
                        @NotNull final ParserDefinition parserDefinition,
                        @NotNull final MasqueradingLexer lexer,
                        @NotNull final LighterLazyParseableNode chameleon,
                        @NotNull final CharSequence text) {
    this(new PsiBuilderImpl(project, parserDefinition, lexer, chameleon, text));
  }

  private MasqueradingPsiBuilderAdapter(PsiBuilderImpl builder) {
    super(builder);

    LOG.assertTrue(myDelegate instanceof PsiBuilderImpl);
    myBuilderDelegate = ((PsiBuilderImpl)myDelegate);

    LOG.assertTrue(myBuilderDelegate.getLexer() instanceof MasqueradingLexer);
    myLexer = ((MasqueradingLexer)myBuilderDelegate.getLexer());

    initShrunkSequence();
  }

  @Override
  public CharSequence getOriginalText() {
    return myShrunkCharSequence;
  }

  @Override
  public void advanceLexer() {
    myLexPosition++;
    skipWhitespace();

    synchronizePositions(false);
  }

  /**
   * @param exact if true then positions should be equal;
   *              else delegate should be behind, not including exactly all foreign (skipped) or whitespace tokens
   */
  private void synchronizePositions(boolean exact) {
    final PsiBuilder delegate = getDelegate();

    if (myLexPosition >= myShrunkSequence.size() || delegate.eof()) {
      myLexPosition = myShrunkSequence.size();
      while (!delegate.eof()) {
        delegate.advanceLexer();
      }
      return;
    }

    if (delegate.getCurrentOffset() > myShrunkSequence.get(myLexPosition).realStart) {
      LOG.error("delegate is ahead of my builder!");
      return;
    }

    final int keepUpPosition = getKeepUpPosition(exact);

    while (!delegate.eof()) {
      final int delegatePosition = delegate.getCurrentOffset();

      if (delegatePosition < keepUpPosition) {
        delegate.advanceLexer();
      }
      else {
        break;
      }
    }
  }

  private int getKeepUpPosition(boolean exact) {
    if (exact) {
      return myShrunkSequence.get(myLexPosition).realStart;
    }

    int lexPosition = myLexPosition;
    while (lexPosition > 0 && (myShrunkSequence.get(lexPosition - 1).shrunkStart == myShrunkSequence.get(lexPosition).shrunkStart
                               || isWhiteSpaceOnPos(lexPosition - 1))) {
      lexPosition--;
    }
    if (lexPosition == 0) {
      return myShrunkSequence.get(lexPosition).realStart;
    }
    return myShrunkSequence.get(lexPosition - 1).realStart + 1;
  }

  @Override
  public IElementType lookAhead(int steps) {
    if (eof()) {    // ensure we skip over whitespace if it's needed
      return null;
    }
    int cur = myLexPosition;

    while (steps > 0) {
      ++cur;
      while (cur < myShrunkSequence.size() && isWhiteSpaceOnPos(cur)) {
        cur++;
      }

      steps--;
    }

    return cur < myShrunkSequence.size() ? myShrunkSequence.get(cur).elementType : null;
  }

  @Override
  public IElementType rawLookup(int steps) {
    int cur = myLexPosition + steps;
    return cur >= 0 && cur < myShrunkSequence.size() ? myShrunkSequence.get(cur).elementType : null;
  }

  @Override
  public int rawTokenTypeStart(int steps) {
    int cur = myLexPosition + steps;
    if (cur < 0) return -1;
    if (cur >= myShrunkSequence.size()) return getOriginalText().length();
    return myShrunkSequence.get(cur).shrunkStart;
  }

  @Override
  public int rawTokenIndex() {
    return myLexPosition;
  }

  @Override
  public int getCurrentOffset() {
    return myLexPosition < myShrunkSequence.size() ? myShrunkSequence.get(myLexPosition).shrunkStart : myShrunkCharSequence.length();
  }

  @Nullable
  @Override
  public IElementType getTokenType() {
    if (allIsEmpty()) {
      return TokenType.DUMMY_HOLDER;
    }
    skipWhitespace();

    return myLexPosition < myShrunkSequence.size() ? myShrunkSequence.get(myLexPosition).elementType : null;
  }

  @Nullable
  @Override
  public String getTokenText() {
    if (allIsEmpty()) {
      return getDelegate().getOriginalText().toString();
    }
    skipWhitespace();

    if (myLexPosition >= myShrunkSequence.size()) {
      return null;
    }

    final MyShiftedToken token = myShrunkSequence.get(myLexPosition);
    return myShrunkCharSequence.subSequence(token.shrunkStart, token.shrunkEnd).toString();
  }

  @Override
  public boolean eof() {
    boolean isEof = myLexPosition >= myShrunkSequence.size();
    if (!isEof) {
      return false;
    }

    synchronizePositions(true);
    return true;
  }

  @NotNull
  @Override
  public Marker mark() {
    // In the case of the topmost node all should be inserted
    if (myLexPosition != 0) {
      synchronizePositions(true);
    }

    final Marker mark = super.mark();
    return new MyMarker(mark, myLexPosition);
  }

  private boolean allIsEmpty() {
    return myShrunkSequence.isEmpty() && getDelegate().getOriginalText().length() != 0;
  }

  private void skipWhitespace() {
    while (myLexPosition < myShrunkSequence.size() && isWhiteSpaceOnPos(myLexPosition)) {
      myLexPosition++;
    }
  }

  private boolean isWhiteSpaceOnPos(int pos) {
    return myBuilderDelegate.whitespaceOrComment(myShrunkSequence.get(pos).elementType);
  }

  protected void initShrunkSequence() {
    initTokenListAndCharSequence(myLexer);
    myLexPosition = 0;
  }

  private void initTokenListAndCharSequence(MasqueradingLexer lexer) {
    lexer.start(getDelegate().getOriginalText());
    myShrunkSequence = new ArrayList();
    StringBuilder charSequenceBuilder = new StringBuilder();

    int realPos = 0;
    int shrunkPos = 0;
    while (lexer.getTokenType() != null) {
      final IElementType masqueTokenType = lexer.getMasqueTokenType();
      final String masqueTokenText = lexer.getMasqueTokenText();

      final int realLength = lexer.getTokenEnd() - lexer.getTokenStart();
      if (masqueTokenType != null) {
        assert masqueTokenText != null;

        final int masqueLength = masqueTokenText.length();
        myShrunkSequence.add(new MyShiftedToken(masqueTokenType,
                                                realPos, realPos + realLength,
                                                shrunkPos, shrunkPos + masqueLength));
        charSequenceBuilder.append(masqueTokenText);

        shrunkPos += masqueLength;
      }
      realPos += realLength;

      lexer.advance();
    }

    myShrunkCharSequence = charSequenceBuilder.toString();
  }

  @SuppressWarnings({"StringConcatenationInsideStringBufferAppend", "UnusedDeclaration"})
  private void logPos() {
    StringBuilder sb = new StringBuilder();
    sb.append("\nmyLexPosition=" + myLexPosition + "/" + myShrunkSequence.size());
    if (myLexPosition < myShrunkSequence.size()) {
      final MyShiftedToken token = myShrunkSequence.get(myLexPosition);
      sb.append("\nshrunk:" + token.shrunkStart + "," + token.shrunkEnd);
      sb.append("\nreal:" + token.realStart + "," + token.realEnd);
      sb.append("\nTT:" + getTokenText());
    }
    sb.append("\ndelegate:");
    sb.append("eof=" + myDelegate.eof());
    if (!myDelegate.eof()) {
      //noinspection ConstantConditions
      sb.append("\nposition:" + myDelegate.getCurrentOffset() + "," + (myDelegate.getCurrentOffset() + myDelegate.getTokenText().length()));
      sb.append("\nTT:" + myDelegate.getTokenText());
    }
    LOG.info(sb.toString());
  }


  private static class MyShiftedToken {
    public final IElementType elementType;

    public final int realStart;
    public final int realEnd;

    public final int shrunkStart;
    public final int shrunkEnd;

    public MyShiftedToken(IElementType elementType, int realStart, int realEnd, int shrunkStart, int shrunkEnd) {
      this.elementType = elementType;
      this.realStart = realStart;
      this.realEnd = realEnd;
      this.shrunkStart = shrunkStart;
      this.shrunkEnd = shrunkEnd;
    }

    @Override
    public String toString() {
      return "MSTk: [" + realStart + ", " + realEnd + "] -> [" + shrunkStart + ", " + shrunkEnd + "]: " + elementType.toString();
    }
  }

  private class MyMarker extends DelegateMarker {

    private final int myBuilderPosition;

    public MyMarker(Marker delegate, int builderPosition) {
      super(delegate);

      myBuilderPosition = builderPosition;
    }

    @Override
    public void rollbackTo() {
      super.rollbackTo();
      myLexPosition = myBuilderPosition;
    }

    @Override
    public void doneBefore(@NotNull IElementType type, @NotNull Marker before) {
      super.doneBefore(type, getDelegateOrThis(before));
    }

    @Override
    public void doneBefore(@NotNull IElementType type, @NotNull Marker before, String errorMessage) {
      super.doneBefore(type, getDelegateOrThis(before), errorMessage);
    }

    @NotNull
    private Marker getDelegateOrThis(@NotNull Marker marker) {
      if (marker instanceof DelegateMarker) {
        return ((DelegateMarker)marker).getDelegate();
      }
      else {
        return marker;
      }
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy