private static void handleReformatMarkers(
     final FileViewProvider key, final TreeSet<PostprocessFormattingTask> rangesToProcess) {
   final Document document = key.getDocument();
   if (document == null) {
     return;
   }
   for (final FileElement fileElement : ((SingleRootFileViewProvider) key).getKnownTreeRoots()) {
     fileElement.acceptTree(
         new RecursiveTreeElementWalkingVisitor() {
           protected void visitNode(TreeElement element) {
             if (CodeEditUtil.isMarkedToReformatBefore(element)) {
               CodeEditUtil.markToReformatBefore(element, false);
               rangesToProcess.add(
                   new ReformatWithHeadingWhitespaceTask(
                       document.createRangeMarker(
                           element.getStartOffset(), element.getStartOffset())));
             } else if (CodeEditUtil.isMarkedToReformat(element)) {
               CodeEditUtil.markToReformat(element, false);
               rangesToProcess.add(
                   new ReformatWithHeadingWhitespaceTask(
                       document.createRangeMarker(
                           element.getStartOffset(),
                           element.getStartOffset() + element.getTextLength())));
             }
             super.visitNode(element);
           }
         });
   }
 }
Пример #2
0
  @Override
  @SuppressWarnings({"CloneDoesntDeclareCloneNotSupportedException", "CloneDoesntCallSuperClone"})
  protected PsiFileImpl clone() {
    FileViewProvider viewProvider = getViewProvider();
    FileViewProvider providerCopy = viewProvider.clone();
    final Language language = getLanguage();
    if (providerCopy == null) {
      throw new AssertionError(
          "Unable to clone the view provider: " + viewProvider + "; " + language);
    }
    PsiFileImpl clone = BlockSupportImpl.getFileCopy(this, providerCopy);
    copyCopyableDataTo(clone);

    if (getTreeElement() != null) {
      // not set by provider in clone
      final FileElement treeClone = (FileElement) calcTreeElement().clone();
      clone.setTreeElementPointer(
          treeClone); // should not use setTreeElement here because cloned file still have
                      // VirtualFile (SCR17963)
      treeClone.setPsi(clone);
    }

    if (viewProvider.isEventSystemEnabled()) {
      clone.myOriginalFile = this;
    } else if (myOriginalFile != null) {
      clone.myOriginalFile = myOriginalFile;
    }

    return clone;
  }
  @NotNull
  private static DiffLog makeFullParse(
      ASTNode parent,
      @NotNull CharSequence newFileText,
      int textLength,
      @NotNull PsiFileImpl fileImpl,
      @NotNull ProgressIndicator indicator) {
    if (fileImpl instanceof PsiCodeFragment) {
      final FileElement holderElement =
          new DummyHolder(fileImpl.getManager(), null).getTreeElement();
      holderElement.rawAddChildren(
          fileImpl.createContentLeafElement(
              holderElement.getCharTable().intern(newFileText, 0, textLength)));
      DiffLog diffLog = new DiffLog();
      diffLog.appendReplaceFileElement(
          (FileElement) parent, (FileElement) holderElement.getFirstChildNode());

      return diffLog;
    } else {
      FileViewProvider viewProvider = fileImpl.getViewProvider();
      viewProvider.getLanguages();
      FileType fileType = viewProvider.getVirtualFile().getFileType();
      String fileName = fileImpl.getName();
      final LightVirtualFile lightFile =
          new LightVirtualFile(
              fileName,
              fileType,
              newFileText,
              viewProvider.getVirtualFile().getCharset(),
              fileImpl.getViewProvider().getModificationStamp());
      lightFile.setOriginalFile(viewProvider.getVirtualFile());

      FileViewProvider copy = viewProvider.createCopy(lightFile);
      if (copy.isEventSystemEnabled()) {
        throw new AssertionError(
            "Copied view provider must be non-physical for reparse to deliver correct events: "
                + viewProvider);
      }
      copy.getLanguages();
      SingleRootFileViewProvider.doNotCheckFileSizeLimit(
          lightFile); // optimization: do not convert file contents to bytes to determine if we
      // should codeinsight it
      PsiFileImpl newFile = getFileCopy(fileImpl, copy);

      newFile.setOriginalFile(fileImpl);

      final FileElement newFileElement = (FileElement) newFile.getNode();
      final FileElement oldFileElement = (FileElement) fileImpl.getNode();

      DiffLog diffLog = mergeTrees(fileImpl, oldFileElement, newFileElement, indicator);

      ((PsiManagerEx) fileImpl.getManager()).getFileManager().setViewProvider(lightFile, null);
      return diffLog;
    }
  }
Пример #4
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;
    }
  }
Пример #5
0
  private void doClearCaches(String reason) {
    final FileElement tree = getTreeElement();
    if (tree != null) {
      tree.clearCaches();
    }

    synchronized (PsiLock.LOCK) {
      clearStub(reason);
    }
    if (tree != null) {
      tree.putUserData(STUB_TREE_IN_PARSED_TREE, null);
    }

    clearCaches();
  }
Пример #6
0
  @NotNull
  protected FileElement createFileElement(CharSequence docText) {
    final FileElement treeElement;
    final TreeElement contentLeaf = createContentLeafElement(docText);

    if (contentLeaf instanceof FileElement) {
      treeElement = (FileElement) contentLeaf;
    } else {
      final CompositeElement xxx = ASTFactory.composite(myElementType);
      assert xxx instanceof FileElement : "BUMM";
      treeElement = (FileElement) xxx;
      treeElement.rawAddChildrenWithoutNotifications(contentLeaf);
    }

    return treeElement;
  }
  @NotNull
  public static DiffLog mergeTrees(
      @NotNull final PsiFileImpl fileImpl,
      @NotNull final ASTNode oldRoot,
      @NotNull final ASTNode newRoot,
      @NotNull ProgressIndicator indicator) {
    if (newRoot instanceof FileElement) {
      ((FileElement) newRoot).setCharTable(fileImpl.getTreeElement().getCharTable());
    }

    try {
      newRoot.putUserData(TREE_TO_BE_REPARSED, oldRoot);
      if (isReplaceWholeNode(fileImpl, newRoot)) {
        DiffLog treeChangeEvent =
            replaceElementWithEvents((CompositeElement) oldRoot, (CompositeElement) newRoot);
        fileImpl.putUserData(TREE_DEPTH_LIMIT_EXCEEDED, Boolean.TRUE);

        return treeChangeEvent;
      }
      newRoot
          .getFirstChildNode(); // maybe reparsed in PsiBuilderImpl and have thrown exception here
    } catch (ReparsedSuccessfullyException e) {
      // reparsed in PsiBuilderImpl
      return e.getDiffLog();
    } finally {
      newRoot.putUserData(TREE_TO_BE_REPARSED, null);
    }

    final ASTShallowComparator comparator = new ASTShallowComparator(indicator);
    final ASTStructure treeStructure = createInterruptibleASTStructure(newRoot, indicator);

    DiffLog diffLog = new DiffLog();
    diffTrees(oldRoot, diffLog, comparator, treeStructure, indicator);
    return diffLog;
  }
Пример #8
0
 protected PsiFileImpl cloneImpl(FileElement treeElementClone) {
   PsiFileImpl clone = (PsiFileImpl) super.clone();
   clone.setTreeElementPointer(
       treeElementClone); // should not use setTreeElement here because cloned file still have
                          // VirtualFile (SCR17963)
   treeElementClone.setPsi(clone);
   return clone;
 }
Пример #9
0
 public void unloadContent() {
   ApplicationManager.getApplication().assertWriteAccessAllowed();
   clearCaches();
   myViewProvider.beforeContentsSynchronized();
   synchronized (PsiLock.LOCK) {
     FileElement treeElement = derefTreeElement();
     DebugUtil.startPsiModification("unloadContent");
     try {
       if (treeElement != null) {
         myTreeElementPointer = null;
         treeElement.detachFromFile();
         DebugUtil.onInvalidated(treeElement);
       }
       clearStub("unloadContent");
     } finally {
       DebugUtil.finishPsiModification();
     }
   }
 }
Пример #10
0
  @NotNull
  private FileElement loadTreeElement() {
    ApplicationManager.getApplication().assertReadAccessAllowed();

    final FileViewProvider viewProvider = getViewProvider();
    if (viewProvider.isPhysical()
        && myManager.isAssertOnFileLoading(viewProvider.getVirtualFile())) {
      LOG.error(
          "Access to tree elements not allowed in tests. path='"
              + viewProvider.getVirtualFile().getPresentableUrl()
              + "'");
    }

    Document cachedDocument =
        FileDocumentManager.getInstance().getCachedDocument(getViewProvider().getVirtualFile());

    FileElement treeElement = createFileElement(viewProvider.getContents());
    treeElement.setPsi(this);

    List<Pair<StubBasedPsiElementBase, CompositeElement>> bindings =
        calcStubAstBindings(treeElement, cachedDocument);

    synchronized (PsiLock.LOCK) {
      FileElement existing = derefTreeElement();
      if (existing != null) {
        return existing;
      }

      switchFromStubToAst(bindings);
      myStub = null;
      myTreeElementPointer = createTreeElementPointer(treeElement);

      if (LOG.isDebugEnabled() && viewProvider.isPhysical()) {
        LOG.debug("Loaded text for file " + viewProvider.getVirtualFile().getPresentableUrl());
      }

      return treeElement;
    }
  }
  /**
   * 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;
  }
  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;
                }
              });
    }
  }