mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-189793 refresh files in the project view on VFS change
This commit is contained in:
@@ -27,7 +27,9 @@ import com.intellij.psi.PsiManager;
|
||||
import com.intellij.ui.tree.AsyncTreeModel;
|
||||
import com.intellij.ui.tree.RestoreSelectionListener;
|
||||
import com.intellij.ui.tree.StructureTreeModel;
|
||||
import com.intellij.ui.tree.TreeCollector;
|
||||
import com.intellij.ui.tree.TreeVisitor;
|
||||
import com.intellij.ui.tree.ProjectFileChangeListener;
|
||||
import com.intellij.util.SmartList;
|
||||
import com.intellij.util.messages.MessageBusConnection;
|
||||
import com.intellij.util.ui.tree.TreeUtil;
|
||||
@@ -41,9 +43,12 @@ import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import static com.intellij.ide.util.treeView.TreeState.expand;
|
||||
import static com.intellij.openapi.vfs.VirtualFileManager.VFS_CHANGES;
|
||||
|
||||
class AsyncProjectViewSupport {
|
||||
private static final Logger LOG = Logger.getInstance(AsyncProjectViewSupport.class);
|
||||
private final TreeCollector<VirtualFile> myFileRoots = TreeCollector.createFileRootsCollector();
|
||||
private final ProjectFileChangeListener myChangeListener;
|
||||
private final StructureTreeModel myStructureTreeModel;
|
||||
private final AsyncTreeModel myAsyncTreeModel;
|
||||
|
||||
@@ -57,8 +62,14 @@ class AsyncProjectViewSupport {
|
||||
myStructureTreeModel.setComparator(comparator);
|
||||
myAsyncTreeModel = new AsyncTreeModel(myStructureTreeModel, true, parent);
|
||||
myAsyncTreeModel.setRootImmediately(myStructureTreeModel.getRootImmediately());
|
||||
myChangeListener = new ProjectFileChangeListener(project, (module, file) -> {
|
||||
if (myFileRoots.add(file)) {
|
||||
myFileRoots.processLater(myStructureTreeModel.getInvoker(), roots -> roots.forEach(root -> updateByFile(root, true)));
|
||||
}
|
||||
});
|
||||
setModel(tree, myAsyncTreeModel);
|
||||
MessageBusConnection connection = project.getMessageBus().connect(parent);
|
||||
connection.subscribe(VFS_CHANGES, myChangeListener);
|
||||
connection.subscribe(ProjectTopics.PROJECT_ROOTS, new ModuleRootListener() {
|
||||
@Override
|
||||
public void rootsChanged(ModuleRootEvent event) {
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.ui.tree;
|
||||
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.roots.ProjectFileIndex;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.openapi.vfs.newvfs.BulkFileListener;
|
||||
import com.intellij.openapi.vfs.newvfs.events.VFileCopyEvent;
|
||||
import com.intellij.openapi.vfs.newvfs.events.VFileCreateEvent;
|
||||
import com.intellij.openapi.vfs.newvfs.events.VFileDeleteEvent;
|
||||
import com.intellij.openapi.vfs.newvfs.events.VFileEvent;
|
||||
import com.intellij.openapi.vfs.newvfs.events.VFileMoveEvent;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
public final class ProjectFileChangeListener implements BulkFileListener {
|
||||
private final BiConsumer<Module, VirtualFile> consumer;
|
||||
private final Project project;
|
||||
|
||||
public ProjectFileChangeListener(@NotNull Project project, @NotNull BiConsumer<Module, VirtualFile> consumer) {
|
||||
this.consumer = consumer;
|
||||
this.project = project;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void after(@NotNull List<? extends VFileEvent> events) {
|
||||
for (VFileEvent event : events) {
|
||||
if (event instanceof VFileCreateEvent) {
|
||||
VFileCreateEvent create = (VFileCreateEvent)event;
|
||||
invalidate(create.getParent());
|
||||
}
|
||||
else if (event instanceof VFileCopyEvent) {
|
||||
VFileCopyEvent copy = (VFileCopyEvent)event;
|
||||
invalidate(copy.getNewParent());
|
||||
}
|
||||
else if (event instanceof VFileMoveEvent) {
|
||||
VFileMoveEvent move = (VFileMoveEvent)event;
|
||||
VirtualFile parent = move.getOldParent();
|
||||
if (parent != null) invalidate(parent);
|
||||
invalidate(move.getNewParent());
|
||||
invalidate(move.getFile());
|
||||
}
|
||||
else {
|
||||
VirtualFile file = event.getFile();
|
||||
if (file != null) {
|
||||
if (event instanceof VFileDeleteEvent) {
|
||||
VirtualFile parent = file.getParent();
|
||||
if (parent != null) invalidate(parent);
|
||||
}
|
||||
invalidate(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void invalidate(@NotNull VirtualFile file) {
|
||||
ProjectFileIndex index = project.isDisposed() ? null : ProjectFileIndex.getInstance(project);
|
||||
Module module = index == null ? null : index.getModuleForFile(file);
|
||||
if (module != null) consumer.accept(module, file);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.ui.tree;
|
||||
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.util.SmartList;
|
||||
import com.intellij.util.concurrency.Invoker;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.swing.tree.TreePath;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.BiPredicate;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import static com.intellij.openapi.vfs.VfsUtilCore.isAncestor;
|
||||
|
||||
public final class TreeCollector<T> {
|
||||
private final AtomicReference<List<T>> reference = new AtomicReference<>();
|
||||
private final AtomicLong counter = new AtomicLong();
|
||||
private final BiPredicate<T, T> predicate;
|
||||
|
||||
private TreeCollector(@NotNull BiPredicate<T, T> predicate) {
|
||||
this.predicate = predicate;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<T> get() {
|
||||
synchronized (reference) {
|
||||
List<T> list = reference.getAndSet(null);
|
||||
return list != null ? list : Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean add(@NotNull T object) {
|
||||
synchronized (reference) {
|
||||
List<T> list = reference.get();
|
||||
if (list == null) {
|
||||
reference.set(new SmartList<>(object));
|
||||
}
|
||||
else {
|
||||
for (T parent : list) {
|
||||
if (predicate.test(parent, object)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Iterator<T> iterator = list.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
if (predicate.test(object, iterator.next())) {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
list.add(object);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public void processLater(@NotNull Invoker invoker, @NotNull Consumer<List<T>> consumer) {
|
||||
long count = counter.incrementAndGet();
|
||||
invoker.invokeLater(() -> {
|
||||
// is this request still actual after 10 ms?
|
||||
if (count == counter.get()) consumer.accept(get());
|
||||
}, 10);
|
||||
}
|
||||
|
||||
|
||||
public static TreeCollector<VirtualFile> createFileLeafsCollector() {
|
||||
return new TreeCollector<>((child, parent) -> isAncestor(parent, child, false));
|
||||
}
|
||||
|
||||
public static TreeCollector<VirtualFile> createFileRootsCollector() {
|
||||
return new TreeCollector<>((parent, child) -> isAncestor(parent, child, false));
|
||||
}
|
||||
|
||||
|
||||
public static TreeCollector<TreePath> createPathLeafsCollector() {
|
||||
return new TreeCollector<>((child, parent) -> parent.isDescendant(child));
|
||||
}
|
||||
|
||||
public static TreeCollector<TreePath> createPathRootsCollector() {
|
||||
return new TreeCollector<>((parent, child) -> parent.isDescendant(child));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user