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;
}
}
请先登录再写评论。
Hi,
Did you try to debug it? Do you have any observations like something is null? Which part of the code doesn't work?
It works when the MyPsiDirectory class implements ‘equals’ method correctly.