Reload and select new created PsiElement

已回答

I write an action to create my PSI item (a directory physically). After VirtualFile.createChildDirectory, I call VirtualFile.refresh and IdeView.selectElement, but it neither reload nor select the new created item in project structure tree. Any thing I missed?

The action code:

public class MyDirectoryCreateAction extends AnAction implements WriteActionAware {

    @Override
    public void actionPerformed(@NotNull AnActionEvent event) {
        Object[] selectedItems = PlatformCoreDataKeys.SELECTED_ITEMS.getData(event.getDataContext());
        if (selectedItems == null || selectedItems.length != 1) {
            return;
        }
        Object selectedItem = selectedItems[0];
        if (selectedItem instanceof MyTreeNode selectedTreeNode) {
            MyPsiDirectory selectedPsiDirectory = selectedTreeNode.getValue();
            Random random = new Random();
            String dirName = "my_dir_" + random.nextInt(0, 100000000);
            try {
                WriteAction.run(() -> {
                // create new item
                    VirtualFile childVirtualFile = selectedPsiDirectory.getVirtualFile().createChildDirectory(this, dirName);
                    // select the new item
                    selectedPsiDirectory.getVirtualFile().refresh(false, true);
                    PsiElement[] childPsiElements = selectedPsiDirectory.getChildren();
                    for (PsiElement childPsiElement : childPsiElements) {
                        if (childPsiElement instanceof MyPsiDirectory childPsiDirectory) {
                            if (childPsiDirectory.getName().equals(childVirtualFile.getName())) {
                                IdeView ideView = ActionHelper.getIdeView(event);
                                // select the new item
                                ideView.selectElement(childPsiDirectory);
                                break;
                            }
                        }
                    }
                });
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }

    @Override
    public void update(@NotNull AnActionEvent e) {
        super.update(e);
        e.getPresentation().setText("Create My Directory");
        e.getPresentation().setEnabledAndVisible(true);
    }

    @Override
    public @NotNull ActionUpdateThread getActionUpdateThread() {
        return ActionUpdateThread.BGT;
    }
}

 

My PSI item:


public class MyPsiDirectory implements PsiFileSystemItem {

    private final VirtualFile virtualFile;
    private final PsiFileSystemItem parentPsiElement;
    private ItemPresentation itemPresentation;

    public MyPsiDirectory(VirtualFile virtualFile, PsiFileSystemItem parentPsiElement) {
        this.virtualFile = virtualFile;
        this.parentPsiElement = parentPsiElement;
    }

    public @NotNull VirtualFile getVirtualFile() {
        return virtualFile;
    }

    @Override
    public @NotNull String getName() {
        return virtualFile.getName();
    }

    @Override
    public PsiElement setName(@NotNull String name) throws IncorrectOperationException {
        throw new IncorrectOperationException();
    }

    @Override
    public boolean processChildren(@NotNull PsiElementProcessor<? super PsiFileSystemItem> processor) {
        return true;
    }

    @Override
    public @Nullable ItemPresentation getPresentation() {
        if (itemPresentation == null) {
            itemPresentation = createItemPresentation();
        }
        return itemPresentation;
    }

    @Override
    public @NotNull Project getProject() throws PsiInvalidElementAccessException {
        return parentPsiElement.getProject();
    }

    @Override
    public PsiManager getManager() {
        return parentPsiElement.getManager();
    }

    @Override
    public @NotNull PsiElement @NotNull [] getChildren() {
        VirtualFile[] subDirs = getVirtualFile().getChildren();
        MyPsiDirectory[] subPsiDirs = new  MyPsiDirectory[subDirs.length];
        for (int i = 0; i < subDirs.length; i++) {
            MyPsiDirectory subPsiDir = new MyPsiDirectory(subDirs[i], this);
            subPsiDirs[i] = subPsiDir;
        }
        return subPsiDirs;
    }

    @Override
    public boolean isDirectory() {
        return true;
    }

    @Override
    public @Nullable PsiFileSystemItem getParent() {
        return new PsiDirectoryImpl((PsiManagerImpl) getManager(), parentPsiElement.getVirtualFile());
    }

    @Override
    public boolean isEquivalentTo(PsiElement another) {
        return another instanceof MyPsiDirectory && getName().equals(((MyPsiDirectory) another).getName()) && Objects.equals(getProject(), another.getProject());
    }

    @Override
    public Icon getIcon(int flags) {
        return AllIcons.Nodes.Package;
    }

    @Override
    public PsiFile getContainingFile() throws PsiInvalidElementAccessException {
        return null;
    }

    @Override
    public void checkSetName(String name) throws IncorrectOperationException {
        throw new IncorrectOperationException();
    }


    @Override
    public @NotNull Language getLanguage() {
        return Language.ANY;
    }

    @Override
    public PsiElement getFirstChild() {
        PsiElement[] children = getChildren();
        if (children.length == 0) return null;
        return children[0];
    }

    @Override
    public PsiElement getLastChild() {
        PsiElement[] children = getChildren();
        if (children.length == 0) return null;
        return children[children.length - 1];
    }

    @Override
    public PsiElement getNextSibling() {
        return SharedPsiElementImplUtil.getNextSibling(this);
    }

    @Override
    public PsiElement getPrevSibling() {
        return SharedPsiElementImplUtil.getPrevSibling(this);
    }


    @Override
    public TextRange getTextRange() {
        return null;
    }

    @Override
    public int getStartOffsetInParent() {
        return -1;
    }

    @Override
    public int getTextLength() {
        return -1;
    }

    @Override
    public @Nullable PsiElement findElementAt(int offset) {
        return null;
    }

    @Override
    public @Nullable PsiReference findReferenceAt(int offset) {
        return SharedPsiElementImplUtil.findReferenceAt(this, offset);
    }

    @Override
    public int getTextOffset() {
        return -1;
    }

    @Override
    public String getText() {
        return "";
    }

    public ItemPresentation createItemPresentation() {
        return new ItemPresentation() {
            @Override
            public @NotNull String getPresentableText() {
                //noinspection
                return getName();
            }

            @Override
            public @NotNull Icon getIcon(boolean unused) {
                return MyPsiDirectory.this.getIcon(0);
            }
        };
    }



    @Override
    public char @NotNull [] textToCharArray() {
        return ArrayUtilRt.EMPTY_CHAR_ARRAY;
    }

    @Override
    public PsiElement getNavigationElement() {
        return this;
    }

    @Override
    public PsiElement getOriginalElement() {
        return this;
    }

    @Override
    public boolean textMatches(@NotNull @NonNls CharSequence text) {
        return false;
    }

    @Override
    public boolean textMatches(@NotNull PsiElement element) {
        return false;
    }

    @Override
    public boolean textContains(char c) {
        return false;
    }

    @Override
    public void accept(@NotNull PsiElementVisitor visitor) {
    }

    @Override
    public void acceptChildren(@NotNull PsiElementVisitor visitor) {
        PsiElement child = getFirstChild();
        while (child != null) {
            child.accept(visitor);
            child = child.getNextSibling();
        }
    }

    @Override
    public PsiElement copy() {
        throw new IncorrectOperationException();
    }

    @Override
    public PsiElement add(@NotNull PsiElement element) throws IncorrectOperationException {
        throw new IncorrectOperationException();
    }

    @Override
    public PsiElement addBefore(@NotNull PsiElement element, @Nullable PsiElement anchor) throws IncorrectOperationException {
        throw new IncorrectOperationException();
    }

    @Override
    public PsiElement addAfter(@NotNull PsiElement element, @Nullable PsiElement anchor) throws IncorrectOperationException {
        throw new IncorrectOperationException();
    }

    @Override
    public ASTNode getNode() {
        return null;
    }


    @Override
    public boolean canNavigate() {
        return true;
    }

    @Override
    public boolean isWritable() {
        return false;
    }

    @Override
    public @Nullable PsiReference getReference() {
        return null;
    }

    @Override
    public PsiReference @NotNull [] getReferences() {
        return SharedPsiElementImplUtil.getReferences(this);
    }

    @Override
    public  <T> @Nullable T getCopyableUserData(@NotNull Key<T> key) {
        return null;
    }

    @Override
    public <T> void putCopyableUserData(@NotNull Key<T> key, @Nullable T value) {

    }

    @Override
    public boolean processDeclarations(@NotNull PsiScopeProcessor processor, @NotNull ResolveState state, @Nullable PsiElement lastParent, @NotNull PsiElement place) {
        return true;
    }

    @Override
    public @Nullable PsiElement getContext() {
        return getParent();
    }

    @Override
    public boolean isPhysical() {
        return false;
    }

    @Override
    public @NotNull GlobalSearchScope getResolveScope() {
        return ResolveScopeManager.getElementResolveScope(this);
    }

    @Override
    public @NotNull SearchScope getUseScope() {
        return ResolveScopeManager.getElementUseScope(this);
    }

    @Override
    public void checkAdd(@NotNull PsiElement element) throws IncorrectOperationException {
        throw new IncorrectOperationException();
    }

    @Override
    public PsiElement addRange(PsiElement first, PsiElement last) throws IncorrectOperationException {
        throw new IncorrectOperationException();
    }

    @Override
    public PsiElement addRangeBefore(@NotNull PsiElement first, @NotNull PsiElement last, PsiElement anchor) throws IncorrectOperationException {
        throw new IncorrectOperationException();
    }

    @Override
    public PsiElement addRangeAfter(PsiElement first, PsiElement last, PsiElement anchor) throws IncorrectOperationException {
        throw new IncorrectOperationException();
    }

    @Override
    public void delete() throws IncorrectOperationException {
        throw new IncorrectOperationException("Cannot delete this directory");
    }

    @Override
    @Deprecated
    public void checkDelete() throws IncorrectOperationException {
        throw new IncorrectOperationException();
    }

    @Override
    public void deleteChildRange(PsiElement first, PsiElement last) throws IncorrectOperationException {
        throw new IncorrectOperationException();
    }

    @Override
    public PsiElement replace(@NotNull PsiElement newElement) throws IncorrectOperationException {
        throw new IncorrectOperationException();
    }

    @Override
    public boolean isValid() {
        return true;
    }

    @Override
    public <T> @Nullable T getUserData(@NotNull Key<T> key) {
        return null;
    }

    @Override
    public <T> void putUserData(@NotNull Key<T> key, @Nullable T value) {

    }
}

 

My tree node:

public class MyTreeNode extends AbstractTreeNode<MyPsiDirectory> {

    private List<MyTreeNode> children;

    public MyTreeNode(Project project, @NotNull MyPsiDirectory value) {
        super(project, value);
    }

    @Override
    public @NotNull Collection<? extends AbstractTreeNode<?>> getChildren() {
        if (children == null) {
            children = new ArrayList<>();
            for (PsiElement psiElement : getValue().getChildren()) {
                if (psiElement instanceof MyPsiDirectory child) {
                    MyTreeNode childNode = new MyTreeNode(getProject(), child);
                    children.add(childNode);
                }
            }
        }
        return children;
    }

    @Override
    protected void update(@NotNull PresentationData presentation) {
        presentation.setPresentableText(getValue().getName());
        presentation.setIcon(getValue().getIcon(0));
    }
}

 

My tree structure provider:

public class MyStructureProvider implements TreeStructureProvider {

    @Override
    public @NotNull Collection<AbstractTreeNode<?>> modify(@NotNull AbstractTreeNode<?> parent, @NotNull Collection<AbstractTreeNode<?>> children, ViewSettings settings) {

        Project project = parent.getProject();
        if (parent.getParent() != null &&
                parent instanceof PsiDirectoryNode &&
                parent.getParent() instanceof ProjectViewProjectNode &&
                project.getName().equals(parent.getName())) {
            PsiDirectory projectPsiElement = ((PsiDirectoryNode) parent).getValue();
            List<AbstractTreeNode<?>> replacedChildren = new ArrayList<>(children.size());
            for (AbstractTreeNode<?> child : children) {
                if (child instanceof PsiDirectoryNode psiDirectoryNode) {
                    VirtualFile virtualFile = psiDirectoryNode.getVirtualFile();
                    assert virtualFile != null;
                    if ("MyDirectory".equals(virtualFile.getName())) {
                        MyPsiDirectory myPsiDirectory = new MyPsiDirectory(virtualFile, projectPsiElement);
                        MyTreeNode myTreeNode = new MyTreeNode(project, myPsiDirectory);
                        replacedChildren.add(myTreeNode);
                    } else {
                        replacedChildren.add(child);
                    }
                } else {
                    replacedChildren.add(child);
                }
            }
            return replacedChildren;
        }
        return children;
    }
}

 

 

0

Hi,

Did you try to debug it? Do you have any observations like something is null? Which part of the code doesn't work?

0

It works when the MyPsiDirectory class implements ‘equals’ method correctly.

0

请先登录再写评论。