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

com.intellij.lexer.BaseHtmlLexer 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.lexer;

import com.intellij.codeInsight.completion.CompletionUtilCore;
import com.intellij.lang.HtmlScriptContentProvider;
import com.intellij.lang.Language;
import com.intellij.lang.LanguageHtmlScriptContentProvider;
import com.intellij.lang.html.HTMLLanguage;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.impl.source.tree.TreeUtil;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.tree.TokenSet;
import com.intellij.psi.xml.XmlTokenType;
import com.intellij.util.text.CharArrayUtil;
import com.intellij.xml.util.documentation.HtmlDescriptorsTable;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;

/**
 * @author Maxim.Mossienko
 */
abstract class BaseHtmlLexer extends DelegateLexer {
  protected static final int BASE_STATE_MASK = 0x3F;
  private static final int SEEN_STYLE = 0x40;
  private static final int SEEN_TAG = 0x80;
  private static final int SEEN_SCRIPT = 0x100;
  private static final int SEEN_ATTRIBUTE = 0x200;
  private static final int SEEN_CONTENT_TYPE = 0x400;
  protected static final int BASE_STATE_SHIFT = 11;
  @Nullable
  protected static final Language ourDefaultLanguage = Language.findLanguageByID("JavaScript");

  private boolean seenTag;
  private boolean seenAttribute;
  private boolean seenStyle;
  private boolean seenScript;

  @Nullable
  protected String scriptType = null;

  private final boolean caseInsensitive;
  private boolean seenContentType;
  private CharSequence cachedBufferSequence;
  private Lexer lexerOfCacheBufferSequence;

  static final TokenSet TOKENS_TO_MERGE = TokenSet.create(XmlTokenType.XML_COMMENT_CHARACTERS, XmlTokenType.XML_WHITE_SPACE, XmlTokenType.XML_REAL_WHITE_SPACE,
                                                          XmlTokenType.XML_ATTRIBUTE_VALUE_TOKEN, XmlTokenType.XML_DATA_CHARACTERS,
                                                          XmlTokenType.XML_TAG_CHARACTERS);

  public interface TokenHandler {
    void handleElement(Lexer lexer);
  }

  public class XmlNameHandler implements TokenHandler {
    @NonNls private static final String TOKEN_SCRIPT = "script";
    @NonNls private static final String TOKEN_STYLE = "style";
    @NonNls private static final String TOKEN_ON = "on";

    @Override
    public void handleElement(Lexer lexer) {
      final CharSequence buffer;
      if (lexerOfCacheBufferSequence == lexer) {
        buffer = cachedBufferSequence;
      } else {
        cachedBufferSequence = lexer.getBufferSequence();
        buffer = cachedBufferSequence;
        lexerOfCacheBufferSequence = lexer;
      }
      final char firstCh = buffer.charAt(lexer.getTokenStart());

      if (seenScript && !seenTag) {
        seenContentType = false;

        if (((firstCh == 'l' || firstCh == 't') || (caseInsensitive && (firstCh == 'L' || firstCh == 'T')))) {
          @NonNls String name = TreeUtil.getTokenText(lexer);
          if (caseInsensitive) name = name.toLowerCase();

          if ("language".equals(name) || "type".equals(name)) {
            seenContentType = true;
          }
        }

        return;
      }

      if (firstCh !='o' && firstCh !='s' &&
          (!caseInsensitive || (firstCh !='S' && firstCh !='O') )
          ) {
        return; // optimization
      }

      String name = TreeUtil.getTokenText(lexer);
      if (caseInsensitive) name = name.toLowerCase();

      final boolean style = name.equals(TOKEN_STYLE);
      final int state = getState() & BASE_STATE_MASK;
      final boolean script = name.equals(TOKEN_SCRIPT) ||
                       ((name.startsWith(TOKEN_ON) && name.indexOf(':') == -1 && !isHtmlTagState(state) &&
                         HtmlDescriptorsTable.getAttributeDescriptor(name) != null));

      if (style || script) {
        // encountered tag name in end of tag
        if (seenTag) {
          if (isHtmlTagState(state)) {
            seenTag = false;
          }
          return;
        }

        seenStyle = style;
        seenScript = script;

        if (!isHtmlTagState(state)) {
          seenAttribute=true;
        }
      }
    }
  }

  class XmlAttributeValueEndHandler implements TokenHandler {
    @Override
    public void handleElement(Lexer lexer) {
      if (seenAttribute) {
        seenStyle = false;
        seenScript = false;
        seenAttribute = false;
      }
      seenContentType = false;
    }
  }

  class XmlAttributeValueHandler implements TokenHandler {
    @Override
    public void handleElement(Lexer lexer) {
      if (seenContentType) {
        if(!seenScript || seenAttribute) {
          return; // something invalid
        }

        @NonNls String mimeType = TreeUtil.getTokenText(lexer);
        if (caseInsensitive) mimeType = mimeType.toLowerCase();
        scriptType = mimeType;
      }
    }
  }

  @Nullable
  protected Language getScriptLanguage() {
    Collection instancesByMimeType = Language.findInstancesByMimeType(scriptType != null ? scriptType.trim() : null);
    return instancesByMimeType.isEmpty() ? null : instancesByMimeType.iterator().next();
  }

  @Nullable
  protected IElementType getCurrentScriptElementType() {
    HtmlScriptContentProvider scriptContentProvider = findScriptContentProvider(scriptType);
    return scriptContentProvider == null ? null : scriptContentProvider.getScriptElementType();
  }

  @Nullable
  protected static HtmlScriptContentProvider findScriptContentProvider(@Nullable String mimeType) {
    if (StringUtil.isEmpty(mimeType)) {
      return ourDefaultLanguage != null ? LanguageHtmlScriptContentProvider.getScriptContentProvider(ourDefaultLanguage) : null;
    }
    Collection instancesByMimeType = Language.findInstancesByMimeType(mimeType.trim());
    if (instancesByMimeType.isEmpty() && mimeType.contains("template")) {
      instancesByMimeType = Collections.singletonList(HTMLLanguage.INSTANCE);
    }
    for (Language language : instancesByMimeType) {
      HtmlScriptContentProvider scriptContentProvider = LanguageHtmlScriptContentProvider.getScriptContentProvider(language);
      if (scriptContentProvider != null) {
        return scriptContentProvider;
      }
    }
    return null;
  }

  class XmlTagClosedHandler implements TokenHandler {
    @Override
    public void handleElement(Lexer lexer) {
      if (seenAttribute) {
        seenScript=false;
        seenStyle=false;

        seenAttribute=false;
      } else {
        if (seenStyle || seenScript) {
          seenTag=true;
        }
      }
    }
  }

  class XmlTagEndHandler implements TokenHandler {
    @Override
    public void handleElement(Lexer lexer) {
      seenStyle=false;
      seenScript=false;
      seenAttribute=false;
      seenContentType=false;
      scriptType = null;
    }
  }

  private final HashMap tokenHandlers = new HashMap();

  protected BaseHtmlLexer(Lexer _baseLexer, boolean _caseInsensitive)  {
    super(_baseLexer);
    caseInsensitive = _caseInsensitive;

    XmlNameHandler value = new XmlNameHandler();
    tokenHandlers.put(XmlTokenType.XML_NAME,value);
    tokenHandlers.put(XmlTokenType.XML_TAG_NAME,value);
    tokenHandlers.put(XmlTokenType.XML_TAG_END,new XmlTagClosedHandler());
    tokenHandlers.put(XmlTokenType.XML_END_TAG_START,new XmlTagEndHandler());
    tokenHandlers.put(XmlTokenType.XML_EMPTY_ELEMENT_END,new XmlTagEndHandler());
    tokenHandlers.put(XmlTokenType.XML_ATTRIBUTE_VALUE_END_DELIMITER,new XmlAttributeValueEndHandler());
    tokenHandlers.put(XmlTokenType.XML_ATTRIBUTE_VALUE_TOKEN,new XmlAttributeValueHandler());
  }

  protected void registerHandler(IElementType elementType, TokenHandler value) {
    final TokenHandler tokenHandler = tokenHandlers.get(elementType);

    if (tokenHandler != null) {
      final TokenHandler newHandler = value;
      value = new TokenHandler() {
        @Override
        public void handleElement(final Lexer lexer) {
          tokenHandler.handleElement(lexer);
          newHandler.handleElement(lexer);
        }
      };
    }

    tokenHandlers.put(elementType,value);
  }

  @Override
  public void start(@NotNull final CharSequence buffer, final int startOffset, final int endOffset, final int initialState) {
    initState(initialState);
    super.start(buffer, startOffset, endOffset, initialState & BASE_STATE_MASK);
  }

  private void initState(final int initialState) {
    seenScript = (initialState & SEEN_SCRIPT)!=0;
    seenStyle = (initialState & SEEN_STYLE)!=0;
    seenTag = (initialState & SEEN_TAG)!=0;
    seenAttribute = (initialState & SEEN_ATTRIBUTE)!=0;
    seenContentType = (initialState & SEEN_CONTENT_TYPE) != 0;
    lexerOfCacheBufferSequence = null;
    cachedBufferSequence = null;
  }

  protected int skipToTheEndOfTheEmbeddment() {
    Lexer base = getDelegate();
    int tokenEnd = base.getTokenEnd();
    int lastState = 0;
    int lastStart = 0;

    final CharSequence buf = base.getBufferSequence();
    final char[] bufArray = CharArrayUtil.fromSequenceWithoutCopying(buf);

    if (seenTag) {
      FoundEnd:
      while(true) {
        FoundEndOfTag:
        while(base.getTokenType() != XmlTokenType.XML_END_TAG_START) {
          if (base.getTokenType() == XmlTokenType.XML_COMMENT_CHARACTERS) {
            // we should terminate on first occurence of 




© 2015 - 2024 Weber Informatics LLC | Privacy Policy