package com.intellij.tokenindex;

import com.intellij.lang.javascript.JSTokenTypes;
import com.intellij.lang.javascript.psi.*;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiComment;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiWhiteSpace;
import com.intellij.psi.impl.source.tree.LeafPsiElement;
import org.jetbrains.annotations.NotNull;

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

/**
 * @author Eugene.Kudelevsky
 */
public class JavaScriptTokenizer implements Tokenizer {
  private static final byte EXPRESSION_TYPE = 0;
  private static final byte IDENTIFIER_TYPE = 1;
  private static final byte PARAM_LIST_TYPE = 2;

  @NotNull
  public List<Token> tokenize(Collection<? extends PsiElement> roots) {
    ArrayList<Token> tokens = new ArrayList<Token>();
    for (PsiElement root : roots) {
      tokenize(root, tokens);
    }
    return tokens;
  }

  private static void tokenize(PsiElement root, List<Token> tokens) {
    root.accept(new MyVisitor(tokens));
  }

  private static void tokenizeChildren(PsiElement root, List<Token> tokens) {
    root.acceptChildren(new MyVisitor(tokens));
  }

  private static class MyVisitor extends JSRecursiveWalkingElementVisitor {
    private final List<Token> myTokens;

    private MyVisitor(List<Token> tokens) {
      myTokens = tokens;
    }

    @Override
    public void visitElement(PsiElement element) {
      super.visitElement(element);
      if (element instanceof LeafPsiElement && !(element instanceof PsiWhiteSpace) && !(element instanceof PsiComment)) {
        visitLeafElement((LeafPsiElement)element);
      }
    }

    @Override
    public void visitJSExpression(JSExpression expression) {
      TextRange range = expression.getTextRange();
      myTokens.add(new AnonymToken(EXPRESSION_TYPE, range.getStartOffset(), range.getEndOffset()));
    }

    @Override
    public void visitJSParameterList(JSParameterList node) {
      TextRange range = node.getTextRange();
      myTokens.add(new AnonymToken(PARAM_LIST_TYPE, range.getStartOffset(), range.getEndOffset()));
    }

    @Override
    public void visitJSStatement(JSStatement node) {
      PsiElement parent = node.getParent();
      if (parent instanceof JSIfStatement || parent instanceof JSLoopStatement) {
        if (node instanceof JSBlockStatement) {
          for (JSStatement statement : ((JSBlockStatement)node).getStatements()) {
            tokenize(statement, myTokens);
          }
        }
        else {
          tokenizeChildren(node, myTokens);
        }
        myTokens.add(new IndentToken(-1, node.getTextRange().getEndOffset()));
      }
      else {
        super.visitJSStatement(node);
      }
    }

    private void visitLeafElement(LeafPsiElement element) {
      if (element.getTextLength() == 0) {
        return;
      }
      if (element.getElementType() == JSTokenTypes.IDENTIFIER) {
        TextRange range = element.getTextRange();
        myTokens.add(new AnonymToken(IDENTIFIER_TYPE, range.getStartOffset(), range.getEndOffset()));
      }
      else {
        TextRange range = element.getTextRange();
        myTokens.add(new TextToken(element.getText().hashCode(), range.getStartOffset(), range.getEndOffset()));
      }
    }
  }
}

@@ -328,24 +328,67 @@
 PsiElement;%0A
+import com.intellij.psi.tree.IElementType;%0A
 import org.j
@@ -923,301 +923,67 @@
     
-tokenize(root, tokens);%0A    %7D%0A    return tokens;%0A  %7D%0A%0A  private static void tokenize(PsiElement root, List%3CToken%3E tokens) %7B%0A    root.accept(new MyVisitor(tokens));%0A  %7D%0A%0A  private static void tokenizeChildren(PsiElement root, List%3CToken%3E tokens) %7B%0A    root.acceptChildren(new MyVisitor(
+root.accept(new MyVisitor(tokens));%0A    %7D%0A    return 
 tokens
-))
 ;%0A  
@@ -1722,263 +1722,98 @@
    p
-ublic void visitJSParameterList(JSParameterList node) %7B%0A      TextRange range = node.getTextRange();%0A      myTokens.add(new AnonymToken(PARAM_LIST_TYPE, range.getStartOffset(), range.getEndOffset()));%0A    %7D%0A%0A    @Override%0A    public void visitJSStatement(
+rotected void elementFinished(@NotNull PsiElement element) %7B%0A      if (element instanceof 
 JSSt
@@ -1823,17 +1823,14 @@
 ment
- node
 ) %7B%0A
+  
     
@@ -1851,20 +1851,23 @@
 arent = 
-node
+element
 .getPare
@@ -1868,24 +1868,26 @@
 etParent();%0A
+  
       if (pa
@@ -1968,130 +1968,83 @@
     
-if (node instanceof JSBlockStatement) %7B%0A          for (JSStatement statement : ((JSBlockStatement)node).getStatements()) %7B
+  myTokens.add(new IndentToken(-1, element.getTextRange().getEndOffset()));
 %0A   
@@ -2052,194 +2052,226 @@
     
-    tokenize(statement, myTokens);
+%7D
 %0A      
+%7D%0A
     %7D%0A
+%0A
     
-    %7D%0A        else %7B%0A          tokenizeChildren(node, myTokens);%0A        %7D%0A        myTokens.add(new IndentToken(-1, node.getTextR
+@Override%0A    public void visitJSParameterList(JSParameterList node) %7B%0A      TextRange range = node.getTextRange();%0A      myTokens.add(new AnonymToken(PARAM_LIST_TYPE, range.getStartOffset(), r
 ange
-()
 .get
@@ -2289,75 +2289,8 @@
 ));%0A
-      %7D%0A      else %7B%0A        super.visitJSStatement(node);%0A      %7D%0A
     
@@ -2420,28 +2420,44 @@
     %7D%0A      
-if (
+IElementType type = 
 element.getE
@@ -2468,16 +2468,32 @@
 ntType()
+;%0A      if (type
  == JSTo
@@ -2677,25 +2677,372 @@
     
-%7D%0A      else %7B%0A  
+  return;%0A      %7D%0A      if (type == JSTokenTypes.LBRACE %7C%7C type == JSTokenTypes.RBRACE) %7B%0A        PsiElement parent = element.getParent();%0A        if (parent instanceof JSBlockStatement) %7B%0A          parent = parent.getParent();%0A          if (parent instanceof JSIfStatement %7C%7C parent instanceof JSLoopStatement) %7B%0A            return;%0A          %7D%0A        %7D%0A      %7D%0A
     
@@ -3077,34 +3077,32 @@
 getTextRange();%0A
-  
       myTokens.a
@@ -3196,24 +3196,16 @@
 et()));%0A
-      %7D%0A
     %7D%0A  

fix StackOverflow