mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IJPL-218636 [psi] send all corresponding events on file move
GitOrigin-RevId: 745ad1d400aa37f763db6fbcf3fb41ece68bed4f
This commit is contained in:
committed by
intellij-monorepo-bot
parent
bf54663a43
commit
7e35a5675f
@@ -677,13 +677,13 @@ public final class FileManagerImpl implements FileManagerEx {
|
||||
myVFileToViewProviderMap.clear();
|
||||
}
|
||||
|
||||
// When some file is moved to another directory, its contexts might change
|
||||
// So, view providers associated with irrelevant contexts should be processed
|
||||
// When some file is moved to another directory, its contexts might change.
|
||||
// So, view providers associated with irrelevant contexts should be processed.
|
||||
// The best way to deal with such view providers is just to invalidate them.
|
||||
// But PSI clients are not usually ready for that.
|
||||
// So we try to preserve one of them, clearing the context of this lucky view provider and setting its context to `any`.
|
||||
// So we try to preserve one of the view providers, clearing the context of this lucky view provider and setting its context to `any`.
|
||||
// It is possible to do that if there are no other relevant view providers for this file left.
|
||||
// Also, if there's a view provider with `any` context, there can be no other view providers for this file, so we can silently accept them here.
|
||||
// Also, if there's a view provider with `any` context, there can be no other view providers for this file, so we don't need to do anything for this file.
|
||||
Set<VirtualFile> filesHavingRelevantViewProviders = new HashSet<>();
|
||||
Map<VirtualFile, Entry> irrelevantViewProviders = new LinkedHashMap<>();
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.psi.impl.file.impl
|
||||
|
||||
import com.intellij.codeInsight.multiverse.anyContext
|
||||
import com.intellij.ide.PsiCopyPasteManager
|
||||
import com.intellij.ide.impl.ProjectUtilCore
|
||||
import com.intellij.ide.plugins.DynamicPluginListener
|
||||
@@ -491,7 +490,7 @@ private class PsiVFSListener(private val project: Project) {
|
||||
|
||||
// optimization: call fileManager.removeInvalidFilesAndDirs() once for a group of move events, instead of once for each event
|
||||
private fun filesMoved(events: List<VFileEvent>) {
|
||||
val allOldElements = ArrayList<PsiElement?>(events.size)
|
||||
val allOldElements = ArrayList<List<PsiElement>>(events.size)
|
||||
val allOldParentDirs = ArrayList<PsiDirectory?>(events.size)
|
||||
val allNewParentDirs = ArrayList<PsiDirectory?>(events.size)
|
||||
|
||||
@@ -503,12 +502,11 @@ private class PsiVFSListener(private val project: Project) {
|
||||
var oldParentDir = fileManager.findDirectory(event.oldParent)
|
||||
var newParentDir = fileManager.findDirectory(event.newParent)
|
||||
|
||||
var oldElement: PsiElement? = if (vFile.isDirectory) {
|
||||
fileManager.getCachedDirectory(vFile)
|
||||
var oldElements = if (vFile.isDirectory) {
|
||||
listOfNotNull(fileManager.getCachedDirectory(vFile))
|
||||
}
|
||||
else {
|
||||
// todo IJPL-339 implement proper event for multiple files
|
||||
fileManager.getCachedPsiFileInner(vFile, anyContext())
|
||||
fileManager.getCachedPsiFilesInner(vFile)
|
||||
}
|
||||
val oldProject = ProjectLocator.getInstance().guessProjectForFile(vFile)
|
||||
if (oldProject != null && oldProject !== project) {
|
||||
@@ -516,11 +514,11 @@ private class PsiVFSListener(private val project: Project) {
|
||||
fileManager.removeFilesAndDirsRecursively(vFile)
|
||||
// avoiding crashes in filePointer.getElement()
|
||||
PsiCopyPasteManager.getInstance().fileMovedOutsideProject(vFile)
|
||||
oldElement = null
|
||||
oldElements = emptyList()
|
||||
oldParentDir = null
|
||||
newParentDir = null
|
||||
}
|
||||
allOldElements.add(oldElement)
|
||||
allOldElements.add(oldElements)
|
||||
allOldParentDirs.add(oldParentDir)
|
||||
allNewParentDirs.add(newParentDir)
|
||||
}
|
||||
@@ -535,7 +533,7 @@ private class PsiVFSListener(private val project: Project) {
|
||||
continue
|
||||
}
|
||||
|
||||
val oldElement = allOldElements[i]
|
||||
val oldElements = allOldElements[i]
|
||||
var newElement: PsiElement?
|
||||
var newViewProvider: FileViewProvider?
|
||||
if (vFile.isDirectory) {
|
||||
@@ -547,16 +545,16 @@ private class PsiVFSListener(private val project: Project) {
|
||||
newElement = newViewProvider.getPsi(fileManager.findViewProvider(vFile).baseLanguage)
|
||||
}
|
||||
|
||||
if (oldElement == null && newElement == null) {
|
||||
if (oldElements.isEmpty() && newElement == null) {
|
||||
continue
|
||||
}
|
||||
|
||||
ApplicationManager.getApplication().runWriteAction(ExternalChangeActionUtil.externalChangeAction {
|
||||
val treeEvent = PsiTreeChangeEventImpl(manager)
|
||||
if (oldElement == null) {
|
||||
if (oldElements.isEmpty()) {
|
||||
if (newViewProvider != null) {
|
||||
fileManager.setViewProvider(vFile, newViewProvider)
|
||||
}
|
||||
val treeEvent = PsiTreeChangeEventImpl(manager)
|
||||
treeEvent.parent = newParentDir
|
||||
treeEvent.child = newElement
|
||||
manager.childAdded(treeEvent)
|
||||
@@ -564,24 +562,41 @@ private class PsiVFSListener(private val project: Project) {
|
||||
else {
|
||||
if (newElement == null) {
|
||||
clearViewProvider(vFile, "PSI moved")
|
||||
treeEvent.parent = oldParentDir
|
||||
treeEvent.child = oldElement
|
||||
manager.childRemoved(treeEvent)
|
||||
|
||||
for (oldElement in oldElements) {
|
||||
val treeEvent = PsiTreeChangeEventImpl(manager)
|
||||
treeEvent.parent = oldParentDir
|
||||
treeEvent.child = oldElement
|
||||
manager.childRemoved(treeEvent)
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (newElement is PsiDirectory ||
|
||||
FileManagerImpl.areViewProvidersEquivalent(newViewProvider!!, (oldElement as PsiFile).viewProvider)) {
|
||||
treeEvent.oldParent = oldParentDir
|
||||
treeEvent.newParent = newParentDir
|
||||
treeEvent.child = oldElement
|
||||
manager.childMoved(treeEvent)
|
||||
FileManagerImpl.areViewProvidersEquivalent(newViewProvider!!, (oldElements.first() as PsiFile).viewProvider)) {
|
||||
|
||||
for (oldElement in oldElements) {
|
||||
val treeEvent = PsiTreeChangeEventImpl(manager)
|
||||
treeEvent.oldParent = oldParentDir
|
||||
treeEvent.newParent = newParentDir
|
||||
treeEvent.child = oldElement
|
||||
if (oldElement.isValid) { // fileManager.removeInvalidFilesAndDirs(true) must have already invalided all old elements that must die.
|
||||
manager.childMoved(treeEvent)
|
||||
}
|
||||
else {
|
||||
manager.childRemoved(treeEvent)
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
fileManager.setViewProvider(vFile, newViewProvider)
|
||||
val treeRemoveEvent = PsiTreeChangeEventImpl(manager)
|
||||
treeRemoveEvent.parent = oldParentDir
|
||||
treeRemoveEvent.child = oldElement
|
||||
manager.childRemoved(treeRemoveEvent)
|
||||
|
||||
for (oldElement in oldElements) {
|
||||
val treeRemoveEvent = PsiTreeChangeEventImpl(manager)
|
||||
treeRemoveEvent.parent = oldParentDir
|
||||
treeRemoveEvent.child = oldElement
|
||||
manager.childRemoved(treeRemoveEvent)
|
||||
}
|
||||
|
||||
val treeAddEvent = PsiTreeChangeEventImpl(manager)
|
||||
treeAddEvent.parent = newParentDir
|
||||
treeAddEvent.child = newElement
|
||||
|
||||
+46
@@ -41,6 +41,7 @@ internal class MultiversePsiEventTest {
|
||||
|
||||
private val sourceRoot = sharedSourceRootFixture(module1, module2)
|
||||
private val sourceRoot2 = sharedSourceRootFixture(module1, module2)
|
||||
private val m1sourceRoot = module1.sourceRootFixture()
|
||||
|
||||
@Suppress("unused")
|
||||
private val registerFakeLang = testFixture {
|
||||
@@ -282,6 +283,51 @@ internal class MultiversePsiEventTest {
|
||||
expectedEventNumber = 2
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `test we receive 2 child moved events on moving a file with 2 psi files`() = doChangeTest(
|
||||
listenerFactory = { counter ->
|
||||
object : PsiTreeChangeAdapter() {
|
||||
override fun childMoved(event: PsiTreeChangeEvent) {
|
||||
counter.incrementAndGet()
|
||||
}
|
||||
}
|
||||
},
|
||||
updateBlock = { file ->
|
||||
file.move(this, sourceRoot2.get().virtualFile)
|
||||
},
|
||||
expectedEventNumber = 2
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `test we receive 1 child moved event (and 1 child removed event) on moving a file with 2 psi files to a directory where only 1 context exists`() = doChangeTest(
|
||||
listenerFactory = { counter ->
|
||||
object : PsiTreeChangeAdapter() {
|
||||
override fun childMoved(event: PsiTreeChangeEvent) {
|
||||
counter.incrementAndGet()
|
||||
}
|
||||
}
|
||||
},
|
||||
updateBlock = { file ->
|
||||
file.move(this, m1sourceRoot.get().virtualFile)
|
||||
},
|
||||
expectedEventNumber = 1
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `test we receive (1 child moved event and) 1 child removed event on moving a file with 2 psi files to a directory where only 1 context exists`() = doChangeTest(
|
||||
listenerFactory = { counter ->
|
||||
object : PsiTreeChangeAdapter() {
|
||||
override fun childRemoved(event: PsiTreeChangeEvent) {
|
||||
counter.incrementAndGet()
|
||||
}
|
||||
}
|
||||
},
|
||||
updateBlock = { file ->
|
||||
file.move(this, m1sourceRoot.get().virtualFile)
|
||||
},
|
||||
expectedEventNumber = 1
|
||||
)
|
||||
|
||||
private fun doChangeTest(
|
||||
listenerFactory: (AtomicInteger) -> PsiTreeChangeListener,
|
||||
updateBlock: (file: VirtualFile) -> Unit,
|
||||
|
||||
Reference in New Issue
Block a user