@Override
 public boolean flip(PsiElement left, PsiElement right) {
   if (left instanceof PsiVariable && right instanceof PsiVariable) {
     final PsiElement first = left.getFirstChild();
     if (!(first instanceof PsiModifierList)) {
       return false;
     }
     final PsiElement child = PsiTreeUtil.skipSiblingsForward(first, PsiWhiteSpace.class);
     if (!(child instanceof PsiTypeElement)) {
       return false;
     }
     final PsiElement last = child.getNextSibling();
     if (!(last instanceof PsiWhiteSpace)) {
       return false;
     }
     final PsiElement anchor = right.getFirstChild();
     if (!(anchor instanceof PsiIdentifier)) {
       return false;
     }
     final PsiElement semiColon = right.getLastChild();
     if (!(semiColon instanceof PsiJavaToken)) {
       return false;
     }
     right.addRangeBefore(first, last, anchor);
     left.deleteChildRange(first, last);
     left.add(semiColon);
     semiColon.delete();
     final PsiElement copy = left.copy();
     left.replace(right);
     right.replace(copy);
     return true;
   }
   return false;
 }
Beispiel #2
0
    @Override
    public void visitElement(PsiElement element) {
      super.visitElement(element);

      final EquivalenceDescriptorProvider descriptorProvider =
          EquivalenceDescriptorProvider.getInstance(element);

      if (descriptorProvider != null) {
        final EquivalenceDescriptor descriptor1 = descriptorProvider.buildDescriptor(element);
        final EquivalenceDescriptor descriptor2 =
            descriptorProvider.buildDescriptor(myGlobalVisitor.getElement());

        if (descriptor1 != null && descriptor2 != null) {
          final boolean result =
              DuplocatorUtil.match(
                  descriptor1,
                  descriptor2,
                  myGlobalVisitor,
                  Collections.<PsiElementRole>emptySet(),
                  null);
          myGlobalVisitor.setResult(result);
          return;
        }
      }

      if (isLiteral(element)) {
        visitLiteral(element);
        return;
      }

      if (canBePatternVariable(element)
          && myGlobalVisitor.getMatchContext().getPattern().isRealTypedVar(element)
          && !shouldIgnoreVarNode(element)) {

        PsiElement matchedElement = myGlobalVisitor.getElement();
        PsiElement newElement = SkippingHandler.skipNodeIfNeccessary(matchedElement);
        while (newElement != matchedElement) {
          matchedElement = newElement;
          newElement = SkippingHandler.skipNodeIfNeccessary(matchedElement);
        }

        myGlobalVisitor.setResult(myGlobalVisitor.handleTypedElement(element, matchedElement));
      } else if (element instanceof LeafElement) {
        myGlobalVisitor.setResult(element.getText().equals(myGlobalVisitor.getElement().getText()));
      } else if (element.getFirstChild() == null && element.getTextLength() == 0) {
        myGlobalVisitor.setResult(true);
      } else {
        PsiElement patternChild = element.getFirstChild();
        PsiElement matchedChild = myGlobalVisitor.getElement().getFirstChild();

        FilteringNodeIterator patternIterator = new SsrFilteringNodeIterator(patternChild);
        FilteringNodeIterator matchedIterator = new SsrFilteringNodeIterator(matchedChild);

        boolean matched = myGlobalVisitor.matchSequentially(patternIterator, matchedIterator);
        myGlobalVisitor.setResult(matched);
      }
    }
 public boolean matchSonsOptionally(final PsiElement element, final PsiElement element2) {
   if (element == null && isLeftLooseMatching()) {
     return true;
   }
   if (element2 == null && isRightLooseMatching()) {
     return true;
   }
   if (element == null || element2 == null) {
     return element == element2;
   }
   return matchSequentiallyOptionally(element.getFirstChild(), element2.getFirstChild());
 }
 @NotNull
 private static Collection<PsiLanguageInjectionHost> collectInjectionHosts(
     @NotNull PsiFile file, @NotNull TextRange range) {
   Stack<PsiElement> toProcess = new Stack<PsiElement>();
   for (PsiElement e = file.findElementAt(range.getStartOffset());
       e != null;
       e = e.getNextSibling()) {
     if (e.getTextRange().getStartOffset() >= range.getEndOffset()) {
       break;
     }
     toProcess.push(e);
   }
   if (toProcess.isEmpty()) {
     return Collections.emptySet();
   }
   Set<PsiLanguageInjectionHost> result = null;
   while (!toProcess.isEmpty()) {
     PsiElement e = toProcess.pop();
     if (e instanceof PsiLanguageInjectionHost) {
       if (result == null) {
         result = ContainerUtilRt.newHashSet();
       }
       result.add((PsiLanguageInjectionHost) e);
     } else {
       for (PsiElement child = e.getFirstChild(); child != null; child = child.getNextSibling()) {
         if (e.getTextRange().getStartOffset() >= range.getEndOffset()) {
           break;
         }
         toProcess.push(child);
       }
     }
   }
   return result == null ? Collections.<PsiLanguageInjectionHost>emptySet() : result;
 }
  /**
   * Get array string values mapped with their PsiElements
   *
   * <p>["value", "value2"]
   */
  @NotNull
  public static Map<String, PsiElement> getArrayValuesAsMap(
      @NotNull ArrayCreationExpression arrayCreationExpression) {

    List<PsiElement> arrayValues =
        PhpPsiUtil.getChildren(
            arrayCreationExpression,
            new Condition<PsiElement>() {
              @Override
              public boolean value(PsiElement psiElement) {
                return psiElement.getNode().getElementType() == PhpElementTypes.ARRAY_VALUE;
              }
            });

    if (arrayValues == null) {
      return Collections.emptyMap();
    }

    Map<String, PsiElement> keys = new HashMap<String, PsiElement>();
    for (PsiElement child : arrayValues) {
      String stringValue = PhpElementsUtil.getStringValue(child.getFirstChild());
      if (stringValue != null && StringUtils.isNotBlank(stringValue)) {
        keys.put(stringValue, child);
      }
    }

    return keys;
  }
 private static boolean isPossibleEmptyAttrs(PsiElement attrs) {
   if (!(attrs instanceof BnfParenExpression)) return false;
   if (attrs.getFirstChild().getNode().getElementType() != BnfTypes.BNF_LEFT_BRACE) return false;
   if (!(((BnfParenExpression) attrs).getExpression() instanceof BnfReferenceOrToken))
     return false;
   return isLastInRuleOrFree(attrs);
 }
 public static PsiElement getChild(
     final PsiElement parent,
     int position,
     boolean ignoreWhiteSpaces,
     boolean ignoreComments,
     boolean ignoreErrors) {
   PsiElement curChild = parent.getFirstChild();
   int i = 0;
   while (i <= position && curChild != null) {
     if (WHITE_SPACES.contains(curChild.getNode().getElementType())) {
       if (!ignoreWhiteSpaces) i++;
       curChild = curChild.getNextSibling();
     } else if (COMMENTS.contains(curChild.getNode().getElementType())) {
       if (!ignoreComments) i++;
       curChild = curChild.getNextSibling();
     } else if (curChild instanceof PsiErrorElement) {
       if (!ignoreErrors) {
         return null;
       }
       i++;
       curChild = curChild.getNextSibling();
     } else {
       if (i == position) return curChild;
       i++;
       curChild = curChild.getNextSibling();
     }
   }
   return null;
 }
 private static boolean isBlockElement(@NotNull PsiElement element) {
   PsiElement firstChild = element.getFirstChild();
   PsiElement lastChild = element.getLastChild();
   return firstChild != null
       && "{".equals(firstChild.getText())
       && lastChild != null
       && "}".equals(lastChild.getText());
 }
 private static void addFoldingDescriptorsFromChildren(
     List<FoldingDescriptor> descriptors, PsiElement tag, @NotNull Document document) {
   for (PsiElement child = tag.getFirstChild(); child != null; child = child.getNextSibling()) {
     if (child instanceof CfmlCompositeElement || child instanceof PsiComment) {
       addFoldingDescriptors(descriptors, child, document);
     }
   }
 }
 public static void changeModifier(
     PsiElement element,
     @Nullable JetModifierList modifierList,
     @Nullable PsiElement insertAnchor,
     JetToken[] modifiersThatCanBeReplaced,
     Project project,
     boolean toBeginning,
     JetModifierList listWithModifier) {
   PsiElement whiteSpace = JetPsiFactory.createWhiteSpace(project);
   if (modifierList == null) {
     if (listWithModifier != null) {
       if (insertAnchor != null) {
         listWithModifier = (JetModifierList) element.addBefore(listWithModifier, insertAnchor);
         element.addBefore(whiteSpace, insertAnchor);
         element.addBefore(whiteSpace, listWithModifier);
       } else {
         PsiElement firstChild = element.getFirstChild();
         element.addBefore(listWithModifier, firstChild);
         element.addBefore(whiteSpace, firstChild);
       }
     }
   } else {
     boolean replaced = false;
     if (modifiersThatCanBeReplaced != null) {
       PsiElement toBeReplaced = null;
       PsiElement toReplace = null;
       for (JetToken modifierThatCanBeReplaced : modifiersThatCanBeReplaced) {
         if (modifierList.hasModifier(modifierThatCanBeReplaced)) {
           PsiElement modifierElement =
               modifierList.getModifierNode(modifierThatCanBeReplaced).getPsi();
           assert modifierElement != null;
           if (!replaced && listWithModifier != null) {
             toBeReplaced = modifierElement;
             toReplace = listWithModifier.getFirstChild();
             // modifierElement.replace(listWithModifier.getFirstChild());
             replaced = true;
           } else {
             modifierList.deleteChildInternal(modifierElement.getNode());
           }
         }
       }
       if (toBeReplaced != null && toReplace != null) {
         toBeReplaced.replace(toReplace);
       }
     }
     if (!replaced && listWithModifier != null) {
       if (toBeginning) {
         PsiElement firstChild = modifierList.getFirstChild();
         modifierList.addBefore(listWithModifier.getFirstChild(), firstChild);
         modifierList.addBefore(whiteSpace, firstChild);
       } else {
         PsiElement lastChild = modifierList.getLastChild();
         modifierList.addAfter(listWithModifier.getFirstChild(), lastChild);
         modifierList.addAfter(whiteSpace, lastChild);
       }
     }
   }
 }
 /** Depth first traversal to find all PsiCodeBlock children. */
 private static void collectFollowingBlocks(PsiElement element, List<PsiCodeBlock> out) {
   while (element != null) {
     if (element instanceof PsiCodeBlock) {
       out.add((PsiCodeBlock) element);
     }
     collectFollowingBlocks(element.getFirstChild(), out);
     element = element.getNextSibling();
   }
 }
 public final boolean matchSonsInAnyOrder(PsiElement element1, PsiElement element2) {
   if (element1 == null && isLeftLooseMatching()) {
     return true;
   }
   if (element2 == null && isRightLooseMatching()) {
     return true;
   }
   if (element1 == null || element2 == null) {
     return element1 == element2;
   }
   PsiElement e = element1.getFirstChild();
   PsiElement e2 = element2.getFirstChild();
   return (e == null && isLeftLooseMatching())
       || (e2 == null && isRightLooseMatching())
       || matchInAnyOrder(
           new FilteringNodeIterator(new SiblingNodeIterator(e), getNodeFilter()),
           new FilteringNodeIterator(new SiblingNodeIterator(e2), getNodeFilter()));
 }
 /** @return leading regex or null if it does not exist */
 @Nullable
 private static GrLiteral getRegexAtTheBeginning(PsiElement expr) {
   PsiElement fchild = expr;
   while (fchild != null) {
     if (fchild instanceof GrLiteral && GrStringUtil.isRegex((GrLiteral) fchild))
       return (GrLiteral) fchild;
     fchild = fchild.getFirstChild();
   }
   return null;
 }
 @Nullable
 public static String getUnescapedLeafText(PsiElement element, boolean strict) {
   String unescaped = element.getCopyableUserData(LeafPatcher.UNESCAPED_TEXT);
   if (unescaped != null) {
     return unescaped;
   }
   if (!strict && element.getFirstChild() == null) {
     return element.getText();
   }
   return null;
 }
  private static PsiElement[] getChildren(PsiElement element) {
    PsiElement psiChild = element.getFirstChild();
    if (psiChild == null) return new PsiElement[0];

    List<PsiElement> result = new ArrayList<PsiElement>();
    while (psiChild != null) {
      result.add(psiChild);

      psiChild = psiChild.getNextSibling();
    }
    return result.toArray(new PsiElement[result.size()]);
  }
  @Nullable
  public static String getNameByReference(@Nullable PsiElement expression) {
    if (!(expression instanceof GrReferenceExpression)) return null;

    PsiElement firstChild = expression.getFirstChild();
    if (firstChild != expression.getLastChild()
        || !PsiImplUtil.isLeafElementOfType(firstChild, GroovyTokenTypes.mIDENT)) return null;

    GrReferenceExpression ref = (GrReferenceExpression) expression;
    if (ref.isQualified()) return null;

    return ref.getReferenceName();
  }
  @Nullable
  private static String getDocumentationText(final DartComponent dartComponent) {
    // PSI is not perfect currently, doc comment may be not part of the corresponding DartComponent
    // element, so docs are searched for in several places:
    // - direct child of this DartComponent
    // - previous sibling (or previous sibling of parent element if this element is first child of
    // its parent DartClassMembers)
    // Consequent line doc comments (///) are joined

    // 1. Look for multiline doc comment as direct child
    final DartDocComment multilineComment =
        PsiTreeUtil.getChildOfType(dartComponent, DartDocComment.class);
    if (multilineComment != null) return getMultilineDocCommentText(multilineComment);

    // 2. Look for single line doc comments as direct children
    final PsiComment[] childComments =
        PsiTreeUtil.getChildrenOfType(dartComponent, PsiComment.class);
    if (childComments != null) {
      //
      final String docText = getSingleLineDocCommentsText(childComments);
      if (docText != null) return docText;
    }

    PsiElement anchorElement = dartComponent;

    final PsiElement parent = dartComponent.getParent();
    if (parent instanceof DartClassMembers && parent.getFirstChild() == dartComponent
        || dartComponent instanceof DartVarAccessDeclaration) {
      anchorElement = parent;
    }

    // 3. Look for multiline doc comment or line doc comments as previous siblings
    final List<PsiComment> siblingComments = new ArrayList<PsiComment>();
    PsiElement previous = anchorElement;
    while ((previous = UsefulPsiTreeUtil.getPrevSiblingSkipWhiteSpaces(previous, true))
        instanceof PsiComment) {
      if (previous instanceof DartDocComment) {
        return getMultilineDocCommentText((DartDocComment) previous);
      }
      siblingComments.add(0, (PsiComment) previous);
    }

    if (!siblingComments.isEmpty()) {
      return getSingleLineDocCommentsText(
          siblingComments.toArray(new PsiComment[siblingComments.size()]));
    }

    return null;
  }
 /**
  * Allows to answer if it's possible to use {@link #TOP_LEVEL_CHILD_MARKER} for the given element.
  *
  * @param element element to check
  * @return <code>true</code> if {@link #TOP_LEVEL_CHILD_MARKER} can be used for the given element;
  *     <code>false</code> otherwise
  */
 private static boolean canResolveTopLevelChild(@NotNull PsiElement element) {
   final PsiElement parent = element.getParent();
   if (parent == null) {
     return false;
   }
   for (PsiElement child = parent.getFirstChild(); child != null; child = child.getNextSibling()) {
     if (child instanceof PsiWhiteSpace) {
       continue;
     }
     if (child != element) {
       return false;
     }
   }
   return true;
 }
  @Nullable
  public static <T extends PsiElement> T getChildrenOfType(
      @Nullable PsiElement element, ElementPattern<T> pattern) {
    if (element == null) return null;

    for (PsiElement child = element.getFirstChild();
        child != null;
        child = child.getNextSibling()) {
      if (pattern.accepts(child)) {
        //noinspection unchecked
        return (T) child;
      }
    }

    return null;
  }
  protected int getCloseQuoteOffset() {
    PsiElement lastChild = getLastChild();

    if (lastChild instanceof PerlParsableStringWrapperlImpl) {
      PsiElement realString = lastChild.getFirstChild();
      assert realString instanceof PerlStringImplMixin;
      return ((PerlStringImplMixin) realString).getCloseQuoteOffset();
    }

    ASTNode currentNode = lastChild.getNode();

    if (PerlParserUtil.CLOSE_QUOTES.contains(currentNode.getElementType()))
      return currentNode.getStartOffset();

    // unclosed string
    return lastChild.getTextOffset() + lastChild.getTextLength();
  }
Beispiel #21
0
  @Nullable
  private static PsiElement getMovableElement(@NotNull PsiElement element, boolean lookRight) {
    //noinspection unchecked
    PsiElement movableElement =
        PsiUtilPackage.getParentByTypesAndPredicate(
            element, false, MOVABLE_ELEMENT_CLASSES, MOVABLE_ELEMENT_CONSTRAINT);
    if (movableElement == null) return null;

    if (isBracelessBlock(movableElement)) {
      movableElement =
          firstNonWhiteElement(
              lookRight ? movableElement.getLastChild() : movableElement.getFirstChild(),
              !lookRight);
    }

    return movableElement;
  }
Beispiel #22
0
    private void initTopLevelElement(PsiElement element) {
      CompiledPattern pattern = myGlobalVisitor.getContext().getPattern();

      PsiElement newElement = SkippingHandler.skipNodeIfNeccessary(element);

      if (element != newElement && newElement != null) {
        // way to support partial matching (ex. if ($condition$) )
        newElement.accept(this);
        pattern.setHandler(element, new LightTopLevelMatchingHandler(pattern.getHandler(element)));
      } else {
        myGlobalVisitor.setCodeBlockLevel(myGlobalVisitor.getCodeBlockLevel() + 1);

        for (PsiElement el = element.getFirstChild(); el != null; el = el.getNextSibling()) {
          if (GlobalCompilingVisitor.getFilter().accepts(el)) {
            if (el instanceof PsiWhiteSpace) {
              myGlobalVisitor.addLexicalNode(el);
            }
          } else {
            el.accept(this);

            MatchingHandler matchingHandler = pattern.getHandler(el);
            pattern.setHandler(
                el,
                element == myTopElement
                    ? new TopLevelMatchingHandler(matchingHandler)
                    : new LightTopLevelMatchingHandler(matchingHandler));

            /*
             do not assign light-top-level handlers through skipping, because it is incorrect;
             src: if (...) { st1; st2; }
             pattern: if (...) {$a$;}

             $a$ will have top-level handler, so matching will be considered as correct, although "st2;" is left!
            */
          }
        }

        myGlobalVisitor.setCodeBlockLevel(myGlobalVisitor.getCodeBlockLevel() - 1);
        pattern.setHandler(element, new TopLevelMatchingHandler(pattern.getHandler(element)));
      }
    }
  private void appendDescriptors(
      ASTNode node, Document document, List<FoldingDescriptor> descriptors) {
    if (node == null) {
      return;
    }
    IElementType type = node.getElementType();
    if (type == SparqlElementTypes.GROUP_GRAPH_PATTERN && isMultiline(node)) {
      descriptors.add(new FoldingDescriptor(node, node.getTextRange()));
    } else if (type == SparqlElementTypes.PREFIX_DECLS && isMultiline(node)) {
      descriptors.add(new FoldingDescriptor(node, node.getTextRange()));
    }

    final PsiElement psi = node.getPsi();
    if (psi != null) {
      PsiElement child = psi.getFirstChild();
      while (child != null) {
        appendDescriptors(child.getNode(), document, descriptors);
        child = child.getNextSibling();
      }
    }
  }
  protected int getOpenQuoteOffset() {
    PsiElement firstChild = getFirstChild();

    if (firstChild instanceof PerlParsableStringWrapperlImpl) {
      PsiElement realString = firstChild.getFirstChild();
      assert realString instanceof PerlStringImplMixin;
      return ((PerlStringImplMixin) realString).getOpenQuoteOffset();
    }

    ASTNode currentNode = firstChild.getNode();

    while (currentNode != null) {
      if (PerlParserUtil.OPEN_QUOTES.contains(currentNode.getElementType()))
        return currentNode.getStartOffset();
      currentNode = currentNode.getTreeNext();
    }
    throw new RuntimeException(
        "Unable to find opening quote inside: "
            + getText()
            + " "
            + getContainingFile().getVirtualFile());
  }
  private void highlightUsages() {
    Editor e = getEditor();
    if (e == null) return;
    boolean foundVisibleElement = false;
    int offset = Integer.MAX_VALUE;
    for (SearchResult sr : searchResults.values()) {
      Map<Node, PsiElement> match = sr.getMatch();
      TextAttributes textAttributes = createUniqueTextAttrsFor(sr);
      PsiElement targetElement = match.get(pattern.getTheOne());

      if (!foundVisibleElement && insideVisibleArea(e, targetElement)) {
        foundVisibleElement = true;
      }
      if (!foundVisibleElement && targetElement.getTextOffset() < offset) {
        offset = targetElement.getTextOffset();
      }
      for (PsiElement element : match.values()) {
        TextAttributes attributes = textAttributes;
        if (element == targetElement) {
          attributes = MAIN_TARGET_ATTRIBUTES;
        }
        if (element instanceof XmlTag) {
          element = element.getFirstChild().getNextSibling();
        }
        TextRange textRange = element.getTextRange();

        e.getMarkupModel()
            .addRangeHighlighter(
                textRange.getStartOffset(),
                textRange.getEndOffset(),
                HighlighterLayer.SELECTION + 1,
                attributes,
                HighlighterTargetArea.EXACT_RANGE);
      }
    }
    if (!foundVisibleElement && offset != Integer.MAX_VALUE) {
      e.getScrollingModel().scrollTo(e.offsetToLogicalPosition(offset), ScrollType.CENTER);
    }
  }
        @Override
        protected Boolean compute(PsiElement parent, Object p) {
          OuterLanguageElement element =
              PsiTreeUtil.getChildOfType(parent, OuterLanguageElement.class);

          if (element == null) {
            // JspOuterLanguageElement is located under XmlText
            for (PsiElement child = parent.getFirstChild();
                child != null;
                child = child.getNextSibling()) {
              if (child instanceof XmlText) {
                element = PsiTreeUtil.getChildOfType(child, OuterLanguageElement.class);
                if (element != null) {
                  break;
                }
              }
            }
          }
          if (element == null) return false;
          PsiFile containingFile = parent.getContainingFile();
          return containingFile.getViewProvider().getBaseLanguage() != containingFile.getLanguage();
        }
Beispiel #27
0
  private static void divideInsideAndOutside(
      @NotNull PsiFile root,
      int startOffset,
      int endOffset,
      @NotNull TextRange range,
      @NotNull List<PsiElement> inside,
      @NotNull List<PsiElement> outside,
      boolean includeParents) {
    final int currentOffset = root.getTextRange().getStartOffset();
    final Condition<PsiElement>[] filters = Extensions.getExtensions(CollectHighlightsUtil.EP_NAME);

    int offset = currentOffset;

    final TIntStack starts = new TIntStack(STARTING_TREE_HEIGHT);
    starts.push(startOffset);
    final Stack<PsiElement> elements = new Stack<PsiElement>(STARTING_TREE_HEIGHT);
    final Stack<PsiElement> children = new Stack<PsiElement>(STARTING_TREE_HEIGHT);
    PsiElement element = root;

    PsiElement child = PsiUtilBase.NULL_PSI_ELEMENT;
    while (true) {
      ProgressManager.checkCanceled();

      for (Condition<PsiElement> filter : filters) {
        if (!filter.value(element)) {
          assert child == PsiUtilBase.NULL_PSI_ELEMENT;
          child = null; // do not want to process children
          break;
        }
      }

      boolean startChildrenVisiting;
      if (child == PsiUtilBase.NULL_PSI_ELEMENT) {
        startChildrenVisiting = true;
        child = element.getFirstChild();
      } else {
        startChildrenVisiting = false;
      }

      if (child == null) {
        if (startChildrenVisiting) {
          // leaf element
          offset += element.getTextLength();
        }

        int start = starts.pop();
        if (startOffset <= start && offset <= endOffset) {
          if (range.containsRange(start, offset)) {
            inside.add(element);
          } else {
            outside.add(element);
          }
        }

        if (elements.isEmpty()) break;
        element = elements.pop();
        child = children.pop();
      } else {
        // composite element
        if (offset > endOffset) break;
        children.push(child.getNextSibling());
        starts.push(offset);
        elements.push(element);
        element = child;
        child = PsiUtilBase.NULL_PSI_ELEMENT;
      }
    }

    if (includeParents) {
      PsiElement parent =
          !outside.isEmpty()
              ? outside.get(outside.size() - 1)
              : !inside.isEmpty()
                  ? inside.get(inside.size() - 1)
                  : CollectHighlightsUtil.findCommonParent(root, startOffset, endOffset);
      while (parent != null && parent != root) {
        parent = parent.getParent();
        if (parent != null) outside.add(parent);
      }
    }
  }
  @Override
  protected PsiElement restoreBySignatureTokens(
      @NotNull PsiFile file,
      @NotNull PsiElement parent,
      @NotNull final String type,
      @NotNull StringTokenizer tokenizer,
      @Nullable StringBuilder processingInfoStorage) {
    if (!TYPE_MARKER.equals(type)) {
      if (processingInfoStorage != null) {
        processingInfoStorage.append(
            String.format(
                "Stopping '%s' provider because given signature doesn't have expected type - can work with '%s' but got '%s'%n",
                getClass().getName(), TYPE_MARKER, type));
      }
      return null;
    }
    String elementMarker = tokenizer.nextToken();
    if (TOP_LEVEL_CHILD_MARKER.equals(elementMarker)) {
      PsiElement result = null;
      for (PsiElement child = file.getFirstChild(); child != null; child = child.getNextSibling()) {
        if (child instanceof PsiWhiteSpace) {
          continue;
        }
        if (result == null) {
          result = child;
        } else {
          if (processingInfoStorage != null) {
            processingInfoStorage.append(
                String.format(
                    "Stopping '%s' provider because it has top level marker but more than one non white-space child: %s%n",
                    getClass().getName(), Arrays.toString(file.getChildren())));
          }
          // More than one top-level non-white space children. Can't match.
          return null;
        }
      }
      if (processingInfoStorage != null) {
        processingInfoStorage.append(
            String.format(
                "Finished processing of '%s' provider because all of its top-level children have been processed: %s%n",
                getClass().getName(), Arrays.toString(file.getChildren())));
      }
      return result;
    }
    if (DOC_COMMENT_MARKER.equals(elementMarker)) {
      PsiElement candidate = parent.getFirstChild();
      return candidate instanceof PsiComment ? candidate : null;
    }
    if (CODE_BLOCK_MARKER.equals(elementMarker)) {
      int index = 0;
      if (tokenizer.hasMoreTokens()) {
        String indexStr = tokenizer.nextToken();
        try {
          index = Integer.parseInt(indexStr);
        } catch (NumberFormatException e) {
          if (processingInfoStorage != null) {
            processingInfoStorage.append("Invalid block index: ").append(indexStr).append("\n");
          }
        }
      }
      for (PsiElement child = parent.getFirstChild();
          child != null;
          child = child.getNextSibling()) {
        if (isBlockElement(child)) {
          if (--index < 0) {
            return child;
          }
        }
      }
      return null;
    }

    if (!tokenizer.hasMoreTokens()) {
      if (processingInfoStorage != null) {
        processingInfoStorage.append(
            String.format(
                "Stopping '%s' provider because it has no more data to process%n",
                getClass().getName()));
      }
      return null;
    }
    try {
      int index = Integer.parseInt(tokenizer.nextToken());
      if (processingInfoStorage != null) {
        processingInfoStorage.append(
            String.format(
                "Looking for the child with a name '%s' # %d at the element '%s'%n",
                elementMarker, index, parent));
      }
      return restoreElementInternal(parent, unescape(elementMarker), index, PsiNamedElement.class);
    } catch (NumberFormatException e) {
      return null;
    }
  }
  /**
   * Tries to produce signature for the exact given PSI element.
   *
   * @param element target element
   * @param buffer buffer to store the signature in
   * @return buffer that contains signature of the given element if it was produced; <code>null
   *     </code> as an indication that signature for the given element was not produced
   */
  @SuppressWarnings("unchecked")
  @Nullable
  private static StringBuilder getSignature(
      @NotNull PsiElement element, @Nullable StringBuilder buffer) {
    if (element instanceof PsiNamedElement) {
      PsiNamedElement named = (PsiNamedElement) element;
      final String name = named.getName();
      if (StringUtil.isEmpty(name)) {
        return null;
      }
      int index = getChildIndex(named, element.getParent(), name, PsiNamedElement.class);
      StringBuilder bufferToUse = buffer;
      if (bufferToUse == null) {
        bufferToUse = new StringBuilder();
      }
      bufferToUse
          .append(TYPE_MARKER)
          .append(ELEMENT_TOKENS_SEPARATOR)
          .append(escape(name))
          .append(ELEMENT_TOKENS_SEPARATOR)
          .append(index);
      return bufferToUse;
    }
    if (element instanceof PsiComment) {
      PsiElement parent = element.getParent();
      boolean nestedComment = false;
      if (parent instanceof PsiComment && parent.getTextRange().equals(element.getTextRange())) {
        parent = parent.getParent();
        nestedComment = true;
      }
      if (parent instanceof PsiNamedElement
          && (nestedComment || parent.getFirstChild() == element)) {
        // Consider doc comment element that is the first child of named element to be a doc
        // comment.
        StringBuilder bufferToUse = buffer;
        if (bufferToUse == null) {
          bufferToUse = new StringBuilder();
        }
        bufferToUse.append(TYPE_MARKER).append(ELEMENT_TOKENS_SEPARATOR).append(DOC_COMMENT_MARKER);
        return bufferToUse;
      }
    }

    PsiElement parent = element.getParent();
    if (parent instanceof PsiNamedElement && !(parent instanceof PsiFile)) {
      if (isBlockElement(element)) {
        int index = getBlockElementIndex(element);
        StringBuilder bufferToUse = buffer;
        if (bufferToUse == null) {
          bufferToUse = new StringBuilder();
        }
        bufferToUse.append(TYPE_MARKER).append(ELEMENT_TOKENS_SEPARATOR).append(CODE_BLOCK_MARKER);
        if (index > 0) {
          bufferToUse.append(ELEMENT_TOKENS_SEPARATOR).append(index);
        }
        return bufferToUse;
      }
    }

    return null;
  }
  @Nullable
  public static GrExpression replaceExpression(
      GrExpression oldExpr, GrExpression newExpr, boolean removeUnnecessaryParentheses) {
    PsiElement oldParent = oldExpr.getParent();
    if (oldParent == null) throw new PsiInvalidElementAccessException(oldExpr);

    if (!(oldExpr instanceof GrApplicationStatement)) {
      newExpr = ApplicationStatementUtil.convertToMethodCallExpression(newExpr);
    }

    // Remove unnecessary parentheses
    if (removeUnnecessaryParentheses
        && oldParent instanceof GrParenthesizedExpression
        && !(oldParent.getParent() instanceof GrArgumentLabel)) {
      return ((GrExpression) oldParent)
          .replaceWithExpression(newExpr, removeUnnecessaryParentheses);
    }

    // regexes cannot be after identifier , try to replace it with simple string
    if (getRegexAtTheBeginning(newExpr) != null && isAfterIdentifier(oldExpr)) {
      final PsiElement copy = newExpr.copy();
      final GrLiteral regex = getRegexAtTheBeginning(copy);
      LOG.assertTrue(regex != null);
      final GrLiteral stringLiteral = GrStringUtil.createStringFromRegex(regex);
      if (regex == copy) {
        return oldExpr.replaceWithExpression(stringLiteral, removeUnnecessaryParentheses);
      } else {
        regex.replace(stringLiteral);
        return oldExpr.replaceWithExpression((GrExpression) copy, removeUnnecessaryParentheses);
      }
    }

    GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(oldExpr.getProject());
    if (oldParent instanceof GrStringInjection) {
      if (newExpr instanceof GrString
          || newExpr instanceof GrLiteral && ((GrLiteral) newExpr).getValue() instanceof String) {
        return GrStringUtil.replaceStringInjectionByLiteral(
            (GrStringInjection) oldParent, (GrLiteral) newExpr);
      } else {
        newExpr = factory.createExpressionFromText("{" + newExpr.getText() + "}");
        oldParent.getNode().replaceChild(oldExpr.getNode(), newExpr.getNode());
        return newExpr;
      }
    }

    if (PsiTreeUtil.getParentOfType(oldExpr, GrStringInjection.class, false, GrCodeBlock.class)
        != null) {
      final PsiElement replaced = oldExpr.replace(newExpr);
      final GrStringInjection stringInjection =
          PsiTreeUtil.getParentOfType(replaced, GrStringInjection.class);
      GrStringUtil.wrapInjection(stringInjection);
      assert stringInjection != null;
      return stringInjection.getClosableBlock();
    }

    // check priorities
    if (oldParent instanceof GrExpression && !(oldParent instanceof GrParenthesizedExpression)) {
      GrExpression addedParenth =
          addParenthesesIfNeeded(newExpr, oldExpr, (GrExpression) oldParent);
      if (newExpr != addedParenth) {
        return oldExpr.replaceWithExpression(addedParenth, removeUnnecessaryParentheses);
      }
    }

    // if replace closure argument with expression
    // we should add the expression in arg list
    if (oldExpr instanceof GrClosableBlock
        && !(newExpr instanceof GrClosableBlock)
        && oldParent instanceof GrMethodCallExpression
        && ArrayUtil.contains(
            oldExpr, ((GrMethodCallExpression) oldParent).getClosureArguments())) {
      return ((GrMethodCallExpression) oldParent)
          .replaceClosureArgument((GrClosableBlock) oldExpr, newExpr);
    }

    newExpr = (GrExpression) oldExpr.replace(newExpr);

    // if newExpr is the first grand child of command argument list we should replace command arg
    // list with parenthesised arg list.
    // In other case the code will be broken. So we try to find wrapping command arg list counting
    // levels. After arg list replace we go inside it
    // to find target parenthesised expression.
    if (newExpr instanceof GrParenthesizedExpression && isFirstChild(newExpr)) {
      int parentCount = 0;

      PsiElement element = oldParent;
      while (element != null && !(element instanceof GrCommandArgumentList)) {
        if (element instanceof GrCodeBlock || element instanceof GrParenthesizedExpression) break;
        if (element instanceof PsiFile) break;

        final PsiElement parent = element.getParent();
        if (parent == null) break;
        if (!isFirstChild(element)) break;

        element = parent;
        parentCount++;
      }

      if (element instanceof GrCommandArgumentList) {
        final GrCommandArgumentList commandArgList = (GrCommandArgumentList) element;

        final PsiElement parent = commandArgList.getParent();
        LOG.assertTrue(parent instanceof GrApplicationStatement);

        final GrMethodCall methodCall =
            factory.createMethodCallByAppCall((GrApplicationStatement) parent);
        final GrMethodCall newCall = (GrMethodCall) parent.replace(methodCall);

        PsiElement result = newCall.getArgumentList().getAllArguments()[0];

        for (int i = 0; i < parentCount; i++) {
          result = PsiUtil.skipWhitespacesAndComments(result.getFirstChild(), true);
        }

        LOG.assertTrue(result instanceof GrParenthesizedExpression);
        return (GrExpression) result;
      }
    }
    return newExpr;
  }