CollectedEvents (@NotNull List<? extends VFileEvent> events) { if (!hasAnyPointers()) { // e.g., in some VFS stress tests return new CollectedEvents(new MultiMap<>(), List.of(), List.of(), 0, 0); } long start = System.currentTimeMillis(); MultiMap<VirtualFilePointerListener, VirtualFilePointerImpl> toFirePointers = MultiMap.create(); List<NodeToUpdate> toUpdateNodes = new ArrayList<>(); long startModCount; List<EventDescriptor> eventList; List<VirtualFilePointer> allPointersToFire; //noinspection SynchronizeOnThis synchronized (this) { startModCount = myPointerSetModCount; for (VFileEvent event : events) { ProgressManager.checkCanceled(); VirtualFileSystem vfs = event.getFileSystem(); if (!(vfs instanceof VirtualFilePointerCapableFileSystem) || !(vfs instanceof NewVirtualFileSystem fs)) continue; if (event instanceof VFileDeleteEvent deleteEvent) { VirtualFileSystemEntry file = (VirtualFileSystemEntry)deleteEvent.getFile(); VirtualFileSystemEntry parent = (VirtualFileSystemEntry)FilePartNode.getParentThroughJar(file, file.getFileSystem()); if (parent != null) { addRelevantPointers(file, parent, FilePartNode.getNameId(file), toFirePointers, toUpdateNodes, true, fs, event); } } else if (event instanceof VFileCreateEvent createEvent) { boolean fireSubdirectoryPointers; if (createEvent.isDirectory()) { // when a new empty directory "/a/b" is created, there's no need to fire any deeper pointers like "/a/b/c/d.txt" - they're not created yet // OTOH when refresh found a new directory "/a/b" which is non-empty, we must fire deeper pointers because they may exist already fireSubdirectoryPointers = !createEvent.isEmptyDirectory(); } else { String createdFileName = createEvent.getChildName(); // if the .jar file created, there may be many files hiding inside FileType fileType = FileTypeManager.getInstance().getFileTypeByExtension(FileUtilRt.getExtension(createdFileName)); fireSubdirectoryPointers = fileType instanceof ArchiveFileType; } addRelevantPointers(null, (VirtualFileSystemEntry)createEvent.getParent(), createEvent.getChildNameId(), toFirePointers, toUpdateNodes, fireSubdirectoryPointers, fs, event); } else if (event instanceof VFileCopyEvent copyEvent) { addRelevantPointers(null, (VirtualFileSystemEntry)copyEvent.getNewParent(), toNameId(copyEvent.getNewChildName()), toFirePointers, toUpdateNodes, true, fs, event); } else if (event instanceof VFileMoveEvent moveEvent) { VirtualFileSystemEntry eventFile = (VirtualFileSystemEntry)moveEvent.getFile(); int newNameId = FilePartNode.getNameId(eventFile); // files deleted from eventFile and created in moveEvent.getNewParent() addRelevantPointers(null, (VirtualFileSystemEntry)moveEvent.getNewParent(), newNameId, toFirePointers, toUpdateNodes, true, fs, event); VirtualFileSystemEntry parent = (VirtualFileSystemEntry)FilePartNode.getParentThroughJar(eventFile, eventFile.getFileSystem()); if (parent != null) { addRelevantPointers(eventFile, parent, newNameId, toFirePointers, toUpdateNodes, true, fs, event); } } else if (event instanceof VFilePropertyChangeEvent change) { if (VirtualFile.PROP_NAME.equals(change.getPropertyName()) && !Comparing.equal(change.getOldValue(), change.getNewValue())) { VirtualFileSystemEntry eventFile = (VirtualFileSystemEntry)change.getFile(); VirtualFileSystemEntry parent = (VirtualFileSystemEntry)FilePartNode.getParentThroughJar(eventFile, eventFile.getFileSystem()); // e.g., for LightVirtualFiles if (parent != null) { int newNameId = toNameId(change.getNewValue().toString()); addRelevantPointers(eventFile, parent, newNameId, toFirePointers, toUpdateNodes, true, fs, event); // old pointers remain valid after rename, no need to fire addRelevantPointers(parent, eventFile, FilePartNode.getNameId(eventFile), toFirePointers, toUpdateNodes, true, fs, false, event); } } } } eventList = new ArrayList<>(); allPointersToFire = new ArrayList<>(); groupPointersToFire(toFirePointers, eventList, allPointersToFire); } if (!allPointersToFire.isEmpty()) { VirtualFilePointer[] allPointers = allPointersToFire.toArray(VirtualFilePointer.EMPTY_ARRAY); eventList.add(new EventDescriptor(myPublisher, allPointers)); } long prepareElapsedMs = System.currentTimeMillis() - start; return new CollectedEvents(toFirePointers, toUpdateNodes, eventList, startModCount, prepareElapsedMs); }