public static <T> void diffTrees(
     @NotNull final ASTNode oldRoot,
     @NotNull final DiffTreeChangeBuilder<ASTNode, T> builder,
     @NotNull final ShallowNodeComparator<ASTNode, T> comparator,
     @NotNull final FlyweightCapableTreeStructure<T> newTreeStructure,
     @NotNull ProgressIndicator indicator) {
   TreeUtil.ensureParsedRecursivelyCheckingProgress(oldRoot, indicator);
   DiffTree.diff(
       createInterruptibleASTStructure(oldRoot, indicator), newTreeStructure, comparator, builder);
 }
예제 #2
0
  public StubTree calcStubTree() {
    FileElement fileElement = calcTreeElement();
    synchronized (myStubFromTreeLock) {
      SoftReference<StubTree> ref = fileElement.getUserData(STUB_TREE_IN_PARSED_TREE);
      StubTree tree = SoftReference.dereference(ref);

      if (tree == null) {
        ApplicationManager.getApplication().assertReadAccessAllowed();
        IElementType contentElementType = getContentElementType();
        if (!(contentElementType instanceof IStubFileElementType)) {
          VirtualFile vFile = getVirtualFile();
          String message =
              "ContentElementType: "
                  + contentElementType
                  + "; file: "
                  + this
                  + "\n\t"
                  + "Boolean.TRUE.equals(getUserData(BUILDING_STUB)) = "
                  + Boolean.TRUE.equals(getUserData(BUILDING_STUB))
                  + "\n\t"
                  + "getTreeElement() = "
                  + getTreeElement()
                  + "\n\t"
                  + "vFile instanceof VirtualFileWithId = "
                  + (vFile instanceof VirtualFileWithId)
                  + "\n\t"
                  + "StubUpdatingIndex.canHaveStub(vFile) = "
                  + StubTreeLoader.getInstance().canHaveStub(vFile);
          rebuildStub();
          throw new AssertionError(message);
        }

        StubElement currentStubTree =
            ((IStubFileElementType) contentElementType).getBuilder().buildStubTree(this);
        if (currentStubTree == null) {
          throw new AssertionError(
              "Stub tree wasn't built for " + contentElementType + "; file: " + this);
        }

        tree = new StubTree((PsiFileStub) currentStubTree);
        tree.setDebugInfo("created in calcStubTree");
        try {
          TreeUtil.bindStubsToTree(this, tree);
        } catch (TreeUtil.StubBindingException e) {
          rebuildStub();
          throw new RuntimeException("Stub and PSI element type mismatch in " + getName(), e);
        }

        fileElement.putUserData(STUB_TREE_IN_PARSED_TREE, new SoftReference<StubTree>(tree));
      }

      return tree;
    }
  }
  @Override
  public TreeElement addInternal(TreeElement first, ASTNode last, ASTNode anchor, Boolean before) {
    boolean needToAddNewline = false;
    if (first == last && first.getElementType() == DOC_TAG) {
      if (anchor == null) {
        anchor = getLastChildNode(); // this is a '*/'
        final ASTNode prevBeforeWS =
            TreeUtil.skipElementsBack(anchor.getTreePrev(), ElementType.JAVA_WHITESPACE_BIT_SET);
        if (prevBeforeWS != null) {
          anchor = prevBeforeWS;
          before = Boolean.FALSE;
        } else {
          before = Boolean.TRUE;
        }
        needToAddNewline = true;
      }
      if (anchor.getElementType() != DOC_TAG) {
        final CharTable charTable = SharedImplUtil.findCharTableByTree(this);
        final TreeElement newLine =
            Factory.createSingleLeafElement(DOC_COMMENT_DATA, "\n", 0, 1, charTable, getManager());
        final TreeElement leadingAsterisk =
            Factory.createSingleLeafElement(
                DOC_COMMENT_LEADING_ASTERISKS, "*", 0, 1, charTable, getManager());
        final TreeElement commentData =
            Factory.createSingleLeafElement(DOC_COMMENT_DATA, " ", 0, 1, charTable, getManager());
        final TreeElement indentWS =
            Factory.createSingleLeafElement(DOC_COMMENT_DATA, " ", 0, 1, charTable, getManager());
        newLine.getTreeParent().addChild(indentWS);
        newLine.getTreeParent().addChild(leadingAsterisk);
        newLine.getTreeParent().addChild(commentData);
        super.addInternal(newLine, commentData, anchor, Boolean.FALSE);

        anchor = commentData;
        before = Boolean.FALSE;
      } else {
        needToAddNewline = true;
      }
    }
    if (before) anchor.getTreeParent().addChildren(first, last.getTreeNext(), anchor);
    else anchor.getTreeParent().addChildren(first, last.getTreeNext(), anchor.getTreeNext());

    if (needToAddNewline) {
      if (first.getTreePrev() != null && first.getTreePrev().getElementType() == DOC_TAG) {
        addNewLineToTag((CompositeElement) first.getTreePrev(), getProject());
      }
      if (first.getTreeNext() != null && first.getTreeNext().getElementType() == DOC_TAG) {
        addNewLineToTag((CompositeElement) first, getProject());
      } else {
        removeEndingAsterisksFromTag((CompositeElement) first);
      }
    }
    return first;
  }
 private static void removeEndingAsterisksFromTag(CompositeElement tag) {
   ASTNode current = tag.getLastChildNode();
   while (current != null && current.getElementType() == DOC_COMMENT_DATA) {
     current = current.getTreePrev();
   }
   if (current != null && current.getElementType() == DOC_COMMENT_LEADING_ASTERISKS) {
     final ASTNode prevWhiteSpace =
         TreeUtil.skipElementsBack(current.getTreePrev(), ElementType.JAVA_WHITESPACE_BIT_SET);
     ASTNode toBeDeleted = prevWhiteSpace.getTreeNext();
     while (toBeDeleted != null) {
       ASTNode next = toBeDeleted.getTreeNext();
       tag.deleteChildInternal(toBeDeleted);
       toBeDeleted = next;
     }
   }
 }
  public ASTNode findChildByRole(int role) {
    LOG.assertTrue(ChildRole.isUnique(role));
    switch (role) {
      default:
        return null;

      case ChildRole.PACKAGE_KEYWORD:
        return findChildByType(JavaTokenType.PACKAGE_KEYWORD);

      case ChildRole.PACKAGE_REFERENCE:
        return findChildByType(JavaElementType.JAVA_CODE_REFERENCE);

      case ChildRole.CLOSING_SEMICOLON:
        return TreeUtil.findChildBackward(this, JavaTokenType.SEMICOLON);

      case ChildRole.MODIFIER_LIST:
        return findChildByType(JavaElementType.MODIFIER_LIST);
    }
  }
  public TreeElement process(TreeElement element, boolean addImports, boolean uncompleteCode) {
    IElementType elementType = element.getElementType();
    if (elementType == JavaElementType.JAVA_CODE_REFERENCE
        || elementType == JavaElementType.REFERENCE_EXPRESSION) {
      final IElementType parentElementType = element.getTreeParent().getElementType();
      if (elementType == JavaElementType.JAVA_CODE_REFERENCE
          || parentElementType == JavaElementType.REFERENCE_EXPRESSION
          || parentElementType == JavaElementType.METHOD_REF_EXPRESSION
          || uncompleteCode) {
        final PsiJavaCodeReferenceElement ref =
            (PsiJavaCodeReferenceElement) SourceTreeToPsiMap.treeElementToPsi(element);
        final PsiReferenceParameterList parameterList = ref.getParameterList();
        if (parameterList != null) {
          final PsiTypeElement[] typeParameters = parameterList.getTypeParameterElements();
          for (PsiTypeElement typeParameter : typeParameters) {
            process(
                (TreeElement) SourceTreeToPsiMap.psiElementToTree(typeParameter),
                addImports,
                uncompleteCode);
          }
        }

        boolean rightKind = true;
        if (elementType == JavaElementType.JAVA_CODE_REFERENCE) {
          int kind = ((PsiJavaCodeReferenceElementImpl) element).getKind();
          rightKind =
              kind == PsiJavaCodeReferenceElementImpl.CLASS_NAME_KIND
                  || kind == PsiJavaCodeReferenceElementImpl.CLASS_OR_PACKAGE_NAME_KIND;
        }

        if (rightKind) {
          boolean isInsideDocComment =
              TreeUtil.findParent(element, JavaDocElementType.DOC_COMMENT) != null;
          boolean isShort = !((SourceJavaCodeReference) element).isQualified();
          if (!makeFQ(isInsideDocComment)) {
            if (isShort) return element; // short name already, no need to change
          }
          PsiElement refElement;
          if (!uncompleteCode) {
            refElement = ref.resolve();
          } else {
            PsiResolveHelper helper =
                JavaPsiFacade.getInstance(element.getManager().getProject()).getResolveHelper();
            refElement =
                helper.resolveReferencedClass(
                    ((SourceJavaCodeReference) element).getClassNameText(),
                    SourceTreeToPsiMap.treeElementToPsi(element));
          }
          if (refElement instanceof PsiClass) {
            if (makeFQ(isInsideDocComment)) {
              String qName = ((PsiClass) refElement).getQualifiedName();
              if (qName == null) return element;
              PsiImportHolder file =
                  (PsiImportHolder)
                      SourceTreeToPsiMap.treeElementToPsi(element).getContainingFile();
              if (file instanceof PsiJavaFile
                  && ImportHelper.isImplicitlyImported(qName, (PsiJavaFile) file)) {
                if (isShort) return element;
                return (TreeElement)
                    makeShortReference(
                        (CompositeElement) element, (PsiClass) refElement, addImports);
              }
              if (file instanceof PsiJavaFile) {
                String thisPackageName = ((PsiJavaFile) file).getPackageName();
                if (ImportHelper.hasPackage(qName, thisPackageName)) {
                  if (!isShort) {
                    return (TreeElement)
                        makeShortReference(
                            (CompositeElement) element, (PsiClass) refElement, addImports);
                  }
                }
              }
              return (TreeElement) replaceReferenceWithFQ(element, (PsiClass) refElement);
            } else {
              int oldLength = element.getTextLength();
              TreeElement treeElement =
                  (TreeElement)
                      makeShortReference(
                          (CompositeElement) element, (PsiClass) refElement, addImports);
              if (treeElement.getTextLength() == oldLength
                  && ((PsiClass) refElement).getContainingClass() != null) {
                PsiElement qualifier = ref.getQualifier();
                if (qualifier instanceof PsiJavaCodeReferenceElement
                    && ((PsiJavaCodeReferenceElement) qualifier).resolve() instanceof PsiClass) {
                  process((TreeElement) qualifier.getNode(), addImports, uncompleteCode);
                }
              }
              return treeElement;
            }
          }
        }
      }
    }

    for (TreeElement child = element.getFirstChildNode();
        child != null;
        child = child.getTreeNext()) {
      child = process(child, addImports, uncompleteCode);
    }

    return element;
  }
  /**
   * This method searches ast node that could be reparsed incrementally and returns pair of target
   * reparseable node and new replacement node. Returns null if there is no any chance to make
   * incremental parsing.
   */
  @Nullable
  public Couple<ASTNode> findReparseableRoots(
      @NotNull PsiFileImpl file,
      @NotNull TextRange changedPsiRange,
      @NotNull CharSequence newFileText) {
    Project project = file.getProject();
    final FileElement fileElement = file.getTreeElement();
    final CharTable charTable = fileElement.getCharTable();
    int lengthShift = newFileText.length() - fileElement.getTextLength();

    if (fileElement.getElementType() instanceof ITemplateDataElementType || isTooDeep(file)) {
      // unable to perform incremental reparse for template data in JSP, or in exceptionally deep
      // trees
      return null;
    }

    final ASTNode leafAtStart =
        fileElement.findLeafElementAt(Math.max(0, changedPsiRange.getStartOffset() - 1));
    final ASTNode leafAtEnd =
        fileElement.findLeafElementAt(
            Math.min(changedPsiRange.getEndOffset(), fileElement.getTextLength() - 1));
    ASTNode node =
        leafAtStart != null && leafAtEnd != null
            ? TreeUtil.findCommonParent(leafAtStart, leafAtEnd)
            : fileElement;
    Language baseLanguage = file.getViewProvider().getBaseLanguage();

    while (node != null && !(node instanceof FileElement)) {
      IElementType elementType = node.getElementType();
      if (elementType instanceof IReparseableElementType) {
        final TextRange textRange = node.getTextRange();
        final IReparseableElementType reparseable = (IReparseableElementType) elementType;

        if (baseLanguage.isKindOf(reparseable.getLanguage())
            && textRange.getLength() + lengthShift > 0) {
          final int start = textRange.getStartOffset();
          final int end = start + textRange.getLength() + lengthShift;
          if (end > newFileText.length()) {
            reportInconsistentLength(file, newFileText, node, start, end);
            break;
          }

          CharSequence newTextStr = newFileText.subSequence(start, end);

          if (reparseable.isParsable(node.getTreeParent(), newTextStr, baseLanguage, project)) {
            ASTNode chameleon = reparseable.createNode(newTextStr);
            if (chameleon != null) {
              DummyHolder holder =
                  DummyHolderFactory.createHolder(
                      file.getManager(), null, node.getPsi(), charTable);
              holder.getTreeElement().rawAddChildren((TreeElement) chameleon);

              if (holder.getTextLength() != newTextStr.length()) {
                String details =
                    ApplicationManager.getApplication().isInternal()
                        ? "text=" + newTextStr + "; treeText=" + holder.getText() + ";"
                        : "";
                LOG.error("Inconsistent reparse: " + details + " type=" + elementType);
              }

              return Couple.of(node, chameleon);
            }
          }
        }
      }
      node = node.getTreeParent();
    }
    return null;
  }
예제 #8
0
  @Override
  public ASTNode process(
      @NotNull ASTNode element,
      boolean addImports,
      boolean incompleteCode,
      boolean useFqInJavadoc,
      boolean useFqInCode) {
    IElementType elementType = element.getElementType();
    if ((elementType == JavaElementType.JAVA_CODE_REFERENCE
            || elementType == JavaElementType.REFERENCE_EXPRESSION)
        && !isAnnotated(element)) {
      IElementType parentType = element.getTreeParent().getElementType();
      if (elementType == JavaElementType.JAVA_CODE_REFERENCE
          || incompleteCode
          || parentType == JavaElementType.REFERENCE_EXPRESSION
          || parentType == JavaElementType.METHOD_REF_EXPRESSION) {
        PsiJavaCodeReferenceElement ref = (PsiJavaCodeReferenceElement) element.getPsi();

        PsiReferenceParameterList parameterList = ref.getParameterList();
        if (parameterList != null) {
          PsiTypeElement[] typeParameters = parameterList.getTypeParameterElements();
          for (PsiTypeElement typeParameter : typeParameters) {
            process(
                typeParameter.getNode(), addImports, incompleteCode, useFqInJavadoc, useFqInCode);
          }
        }

        boolean rightKind = true;
        if (elementType == JavaElementType.JAVA_CODE_REFERENCE) {
          PsiJavaCodeReferenceElementImpl impl = (PsiJavaCodeReferenceElementImpl) element;
          int kind = impl.getKind(impl.getContainingFile());
          rightKind =
              kind == PsiJavaCodeReferenceElementImpl.CLASS_NAME_KIND
                  || kind == PsiJavaCodeReferenceElementImpl.CLASS_OR_PACKAGE_NAME_KIND;
        }

        if (rightKind) {
          // annotations may jump out of reference (see PsiJavaCodeReferenceImpl#setAnnotations())
          // so they should be processed first
          List<PsiAnnotation> annotations =
              PsiTreeUtil.getChildrenOfTypeAsList(ref, PsiAnnotation.class);
          for (PsiAnnotation annotation : annotations) {
            process(annotation.getNode(), addImports, incompleteCode, useFqInJavadoc, useFqInCode);
          }

          boolean isInsideDocComment =
              TreeUtil.findParent(element, JavaDocElementType.DOC_COMMENT) != null;
          boolean isShort = !ref.isQualified();
          if (isInsideDocComment ? !useFqInJavadoc : !useFqInCode) {
            if (isShort) return element; // short name already, no need to change
          }

          PsiElement refElement;
          if (!incompleteCode) {
            refElement = ref.resolve();
          } else {
            PsiResolveHelper helper =
                JavaPsiFacade.getInstance(ref.getManager().getProject()).getResolveHelper();
            final SourceJavaCodeReference reference = (SourceJavaCodeReference) element;
            refElement = helper.resolveReferencedClass(reference.getClassNameText(), ref);
          }

          if (refElement instanceof PsiClass) {
            PsiClass psiClass = (PsiClass) refElement;
            if (isInsideDocComment ? useFqInJavadoc : useFqInCode) {
              String qName = psiClass.getQualifiedName();
              if (qName == null) return element;

              PsiFile file = ref.getContainingFile();
              if (file instanceof PsiJavaFile) {
                if (ImportHelper.isImplicitlyImported(qName, (PsiJavaFile) file)) {
                  if (isShort) return element;
                  return makeShortReference((CompositeElement) element, psiClass, addImports);
                }

                String thisPackageName = ((PsiJavaFile) file).getPackageName();
                if (ImportHelper.hasPackage(qName, thisPackageName)) {
                  if (!isShort) {
                    return makeShortReference((CompositeElement) element, psiClass, addImports);
                  }
                }
              }

              return replaceReferenceWithFQ(element, psiClass);
            } else {
              int oldLength = element.getTextLength();
              ASTNode treeElement =
                  makeShortReference((CompositeElement) element, psiClass, addImports);
              if (treeElement.getTextLength() == oldLength
                  && psiClass.getContainingClass() != null) {
                PsiElement qualifier = ref.getQualifier();
                if (qualifier instanceof PsiJavaCodeReferenceElement
                    && ((PsiJavaCodeReferenceElement) qualifier).resolve() instanceof PsiClass) {
                  process(
                      qualifier.getNode(), addImports, incompleteCode, useFqInJavadoc, useFqInCode);
                }
              }
              return treeElement;
            }
          }
        }
      }
    }

    for (ASTNode child = element.getFirstChildNode(); child != null; child = child.getTreeNext()) {
      //noinspection AssignmentToForLoopParameter
      child = process(child, addImports, incompleteCode, useFqInJavadoc, useFqInCode);
    }

    return element;
  }
  private static void createActionsMap(
      final List<ASTNode> astNodes,
      final FileViewProvider provider,
      final TreeSet<PostprocessFormattingTask> rangesToProcess) {
    final Set<ASTNode> nodesToProcess = new HashSet<ASTNode>(astNodes);
    final Document document = provider.getDocument();
    for (final ASTNode node : astNodes) {
      nodesToProcess.remove(node);
      final FileElement fileElement = TreeUtil.getFileElement((TreeElement) node);
      if (fileElement == null || ((PsiFile) fileElement.getPsi()).getViewProvider() != provider)
        continue;
      final boolean isGenerated = CodeEditUtil.isNodeGenerated(node);

      ((TreeElement) node)
          .acceptTree(
              new RecursiveTreeElementVisitor() {
                boolean inGeneratedContext = !isGenerated;

                protected boolean visitNode(TreeElement element) {
                  if (nodesToProcess.contains(element)) return false;
                  if (CodeEditUtil.isPostponedFormattingDisabled(element)) return false;

                  final boolean currentNodeGenerated = CodeEditUtil.isNodeGenerated(element);
                  CodeEditUtil.setNodeGenerated(element, false);
                  if (currentNodeGenerated && !inGeneratedContext) {
                    rangesToProcess.add(
                        new ReformatTask(document.createRangeMarker(element.getTextRange())));
                    inGeneratedContext = true;
                  }
                  if (!currentNodeGenerated && inGeneratedContext) {
                    if (element.getElementType() == TokenType.WHITE_SPACE) return false;
                    final int oldIndent = CodeEditUtil.getOldIndentation(element);
                    LOG.assertTrue(
                        oldIndent >= 0,
                        "for not generated items old indentation must be defined: element "
                            + element);
                    rangesToProcess.add(
                        new ReindentTask(
                            document.createRangeMarker(element.getTextRange()), oldIndent));
                    inGeneratedContext = false;
                  }
                  return true;
                }

                @Override
                public void visitComposite(CompositeElement composite) {
                  boolean oldGeneratedContext = inGeneratedContext;
                  super.visitComposite(composite);
                  inGeneratedContext = oldGeneratedContext;
                }

                @Override
                public void visitLeaf(LeafElement leaf) {
                  boolean oldGeneratedContext = inGeneratedContext;
                  super.visitLeaf(leaf);
                  inGeneratedContext = oldGeneratedContext;
                }
              });

      CodeEditUtil.enablePostponedFormattingInTree(node);
    }
  }
  private static void createActionsMap(
      @NotNull List<ASTNode> astNodes,
      @NotNull FileViewProvider provider,
      @NotNull final TreeSet<PostprocessFormattingTask> rangesToProcess) {
    final Set<ASTNode> nodesToProcess = new HashSet<ASTNode>(astNodes);
    final Document document = provider.getDocument();
    if (document == null) {
      return;
    }
    for (final ASTNode node : astNodes) {
      nodesToProcess.remove(node);
      final FileElement fileElement = TreeUtil.getFileElement((TreeElement) node);
      if (fileElement == null || ((PsiFile) fileElement.getPsi()).getViewProvider() != provider)
        continue;
      final boolean isGenerated = CodeEditUtil.isNodeGenerated(node);

      ((TreeElement) node)
          .acceptTree(
              new RecursiveTreeElementVisitor() {
                boolean inGeneratedContext = !isGenerated;

                @Override
                protected boolean visitNode(TreeElement element) {
                  if (nodesToProcess.contains(element)) return false;

                  final boolean currentNodeGenerated = CodeEditUtil.isNodeGenerated(element);
                  CodeEditUtil.setNodeGenerated(element, false);
                  if (currentNodeGenerated && !inGeneratedContext) {
                    rangesToProcess.add(
                        new ReformatTask(document.createRangeMarker(element.getTextRange())));
                    inGeneratedContext = true;
                  }
                  if (!currentNodeGenerated && inGeneratedContext) {
                    if (element.getElementType() == TokenType.WHITE_SPACE) return false;
                    final int oldIndent = CodeEditUtil.getOldIndentation(element);
                    CodeEditUtil.setOldIndentation(element, -1);
                    LOG.assertTrue(
                        oldIndent >= 0,
                        "for not generated items old indentation must be defined: element "
                            + element);
                    for (TextRange indentRange : getEnabledRanges(element.getPsi())) {
                      rangesToProcess.add(
                          new ReindentTask(document.createRangeMarker(indentRange), oldIndent));
                    }
                    inGeneratedContext = false;
                  }
                  return true;
                }

                private Iterable<TextRange> getEnabledRanges(@NotNull PsiElement element) {
                  List<TextRange> disabledRanges = new ArrayList<TextRange>();
                  for (DisabledIndentRangesProvider rangesProvider :
                      DisabledIndentRangesProvider.EP_NAME.getExtensions()) {
                    Collection<TextRange> providedDisabledRanges =
                        rangesProvider.getDisabledIndentRanges(element);
                    if (providedDisabledRanges != null) {
                      disabledRanges.addAll(providedDisabledRanges);
                    }
                  }
                  return TextRangeUtil.excludeRanges(element.getTextRange(), disabledRanges);
                }

                @Override
                public void visitComposite(CompositeElement composite) {
                  boolean oldGeneratedContext = inGeneratedContext;
                  super.visitComposite(composite);
                  inGeneratedContext = oldGeneratedContext;
                }

                @Override
                public void visitLeaf(LeafElement leaf) {
                  boolean oldGeneratedContext = inGeneratedContext;
                  super.visitLeaf(leaf);
                  inGeneratedContext = oldGeneratedContext;
                }
              });
    }
  }