mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IJPL-218636 [psi] send property-change events on write access update
GitOrigin-RevId: 7847dace0cd31e31a4b05af5171f3e925cb8f56b
This commit is contained in:
committed by
intellij-monorepo-bot
parent
7a7ead7ff0
commit
d3e3f2815c
@@ -247,15 +247,19 @@ private class PsiVFSListener(private val project: Project) {
|
||||
}
|
||||
}
|
||||
else if (propertyName == VirtualFile.PROP_WRITABLE) {
|
||||
// todo IJPL-339 implement proper event for multiple files
|
||||
val psiFile = fileManager.getCachedPsiFileInner(vFile, anyContext()) ?: return@externalChangeAction
|
||||
val treeEvent = PsiTreeChangeEventImpl(manager)
|
||||
treeEvent.parent = parentDir
|
||||
treeEvent.element = psiFile
|
||||
treeEvent.propertyName = PsiTreeChangeEvent.PROP_WRITABLE
|
||||
treeEvent.oldValue = event.oldValue
|
||||
treeEvent.newValue = event.newValue
|
||||
manager.beforePropertyChange(treeEvent)
|
||||
val psiFiles = fileManager.getCachedPsiFilesInner(vFile).ifEmpty {
|
||||
return@externalChangeAction
|
||||
}
|
||||
|
||||
for (psiFile in psiFiles) {
|
||||
val treeEvent = PsiTreeChangeEventImpl(manager)
|
||||
treeEvent.parent = parentDir
|
||||
treeEvent.element = psiFile
|
||||
treeEvent.propertyName = PsiTreeChangeEvent.PROP_WRITABLE
|
||||
treeEvent.oldValue = event.oldValue
|
||||
treeEvent.newValue = event.newValue
|
||||
manager.beforePropertyChange(treeEvent)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -271,17 +275,17 @@ private class PsiVFSListener(private val project: Project) {
|
||||
val propertyName = event.propertyName
|
||||
val vFile = event.file
|
||||
|
||||
val oldFileViewProvider = fileManager.findCachedViewProvider(vFile)
|
||||
if (oldFileViewProvider != null && FileContentUtilCore.FORCE_RELOAD_REQUESTOR == event.requestor) {
|
||||
val oldFileViewProviders = fileManager.findCachedViewProviders(vFile)
|
||||
if (oldFileViewProviders.isNotEmpty() && FileContentUtilCore.FORCE_RELOAD_REQUESTOR == event.requestor) {
|
||||
// there is no need to rebuild if there were no PSI in the first place
|
||||
fileManager.forceReload(vFile)
|
||||
return
|
||||
}
|
||||
|
||||
val oldPsiFile = fileManager.getCachedPsiFile(vFile)
|
||||
val oldPsiFiles = fileManager.getCachedPsiFiles(vFile)
|
||||
val parentDir = run {
|
||||
val parent = vFile.parent
|
||||
if (oldPsiFile != null && parent != null)
|
||||
if (oldPsiFiles.isNotEmpty() && parent != null)
|
||||
fileManager.findDirectory(parent)
|
||||
else
|
||||
getCachedDirectory(parent)
|
||||
@@ -302,8 +306,6 @@ private class PsiVFSListener(private val project: Project) {
|
||||
|
||||
val fileTypeManager = FileTypeManager.getInstance()
|
||||
ApplicationManager.getApplication().runWriteAction(ExternalChangeActionUtil.externalChangeAction {
|
||||
val treeEvent = PsiTreeChangeEventImpl(manager)
|
||||
treeEvent.parent = parentDir
|
||||
when (propertyName) {
|
||||
VirtualFile.PROP_NAME -> {
|
||||
if (vFile.isDirectory) {
|
||||
@@ -312,10 +314,14 @@ private class PsiVFSListener(private val project: Project) {
|
||||
if (fileTypeManager.isFileIgnored(vFile)) {
|
||||
fileManager.removeFilesAndDirsRecursively(vFile)
|
||||
|
||||
val treeEvent = PsiTreeChangeEventImpl(manager)
|
||||
treeEvent.parent = parentDir
|
||||
treeEvent.child = psiDir
|
||||
manager.childRemoved(treeEvent)
|
||||
}
|
||||
else {
|
||||
val treeEvent = PsiTreeChangeEventImpl(manager)
|
||||
treeEvent.parent = parentDir
|
||||
treeEvent.element = psiDir
|
||||
treeEvent.propertyName = PsiTreeChangeEvent.PROP_DIRECTORY_NAME
|
||||
treeEvent.oldValue = event.oldValue
|
||||
@@ -326,41 +332,75 @@ private class PsiVFSListener(private val project: Project) {
|
||||
else {
|
||||
val psiDir1 = fileManager.findDirectory(vFile)
|
||||
if (psiDir1 != null) {
|
||||
val treeEvent = PsiTreeChangeEventImpl(manager)
|
||||
treeEvent.parent = parentDir
|
||||
treeEvent.child = psiDir1
|
||||
manager.childAdded(treeEvent)
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
val fileViewProvider = fileManager.createFileViewProvider(vFile, true)
|
||||
val newPsiFile = fileViewProvider.getPsi(fileViewProvider.baseLanguage)
|
||||
if (oldPsiFile != null) {
|
||||
val newFileViewProvider = fileManager.createFileViewProvider(vFile, true)
|
||||
val newPsiFile = newFileViewProvider.getPsi(newFileViewProvider.baseLanguage)
|
||||
if (oldPsiFiles.isNotEmpty()) {
|
||||
if (newPsiFile == null) {
|
||||
clearViewProvider(vFile, "PSI renamed")
|
||||
|
||||
treeEvent.child = oldPsiFile
|
||||
manager.childRemoved(treeEvent)
|
||||
}
|
||||
else if (!FileManagerImpl.areViewProvidersEquivalent(fileViewProvider, oldFileViewProvider!!)) {
|
||||
fileManager.setViewProvider(vFile, fileViewProvider)
|
||||
|
||||
treeEvent.oldChild = oldPsiFile
|
||||
treeEvent.newChild = newPsiFile
|
||||
manager.childReplaced(treeEvent)
|
||||
for (oldPsiFile in oldPsiFiles) {
|
||||
val treeEvent = PsiTreeChangeEventImpl(manager)
|
||||
treeEvent.parent = parentDir
|
||||
treeEvent.child = oldPsiFile
|
||||
manager.childRemoved(treeEvent)
|
||||
}
|
||||
}
|
||||
else {
|
||||
FileManagerImpl.clearPsiCaches(oldFileViewProvider)
|
||||
val firstOldViewProvider = oldFileViewProviders.first() // todo IJPL-339 do we want to select a preferred view provider instead of the first one???
|
||||
if (!FileManagerImpl.areViewProvidersEquivalent(newFileViewProvider, firstOldViewProvider)) {
|
||||
// the file has changed its view provider factory
|
||||
// we need to delete all old providers and create one new provider.
|
||||
|
||||
treeEvent.element = oldPsiFile
|
||||
treeEvent.propertyName = PsiTreeChangeEvent.PROP_FILE_NAME
|
||||
treeEvent.oldValue = event.oldValue
|
||||
treeEvent.newValue = event.newValue
|
||||
manager.propertyChanged(treeEvent)
|
||||
fileManager.setViewProvider(vFile, newFileViewProvider)
|
||||
|
||||
run {
|
||||
val treeEvent = PsiTreeChangeEventImpl(manager)
|
||||
treeEvent.parent = parentDir
|
||||
treeEvent.oldChild = firstOldViewProvider.getPsi(firstOldViewProvider.baseLanguage)
|
||||
treeEvent.newChild = newPsiFile
|
||||
manager.childReplaced(treeEvent)
|
||||
}
|
||||
|
||||
for (oldFileViewProvider in oldFileViewProviders.drop(1)) {
|
||||
val treeEvent = PsiTreeChangeEventImpl(manager)
|
||||
treeEvent.parent = parentDir
|
||||
treeEvent.oldChild = oldFileViewProvider.getPsi(oldFileViewProvider.baseLanguage)
|
||||
manager.childRemoved(treeEvent)
|
||||
}
|
||||
}
|
||||
else {
|
||||
// the file keeps the same view provider factory
|
||||
// let's reuse all old view providers
|
||||
|
||||
for (oldFileViewProvider in oldFileViewProviders) {
|
||||
FileManagerImpl.clearPsiCaches(oldFileViewProvider)
|
||||
}
|
||||
|
||||
for (oldPsiFile in oldPsiFiles) {
|
||||
val treeEvent = PsiTreeChangeEventImpl(manager)
|
||||
treeEvent.parent = parentDir
|
||||
treeEvent.element = oldPsiFile
|
||||
treeEvent.propertyName = PsiTreeChangeEvent.PROP_FILE_NAME
|
||||
treeEvent.oldValue = event.oldValue
|
||||
treeEvent.newValue = event.newValue
|
||||
manager.propertyChanged(treeEvent)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (newPsiFile != null) {
|
||||
fileManager.setViewProvider(vFile, fileViewProvider)
|
||||
fileManager.setViewProvider(vFile, newFileViewProvider)
|
||||
if (parentDir != null) {
|
||||
val treeEvent = PsiTreeChangeEventImpl(manager)
|
||||
treeEvent.parent = parentDir
|
||||
treeEvent.child = newPsiFile
|
||||
manager.childAdded(treeEvent)
|
||||
}
|
||||
@@ -368,22 +408,28 @@ private class PsiVFSListener(private val project: Project) {
|
||||
}
|
||||
}
|
||||
VirtualFile.PROP_WRITABLE -> {
|
||||
if (oldPsiFile == null) {
|
||||
if (oldPsiFiles.isEmpty()) {
|
||||
return@externalChangeAction
|
||||
}
|
||||
|
||||
treeEvent.element = oldPsiFile
|
||||
treeEvent.propertyName = PsiTreeChangeEvent.PROP_WRITABLE
|
||||
treeEvent.oldValue = event.oldValue
|
||||
treeEvent.newValue = event.newValue
|
||||
manager.propertyChanged(treeEvent)
|
||||
for (oldPsiFile in oldPsiFiles) {
|
||||
val treeEvent = PsiTreeChangeEventImpl(manager)
|
||||
treeEvent.parent = parentDir
|
||||
treeEvent.element = oldPsiFile
|
||||
treeEvent.propertyName = PsiTreeChangeEvent.PROP_WRITABLE
|
||||
treeEvent.oldValue = event.oldValue
|
||||
treeEvent.newValue = event.newValue
|
||||
manager.propertyChanged(treeEvent)
|
||||
}
|
||||
}
|
||||
VirtualFile.PROP_ENCODING -> {
|
||||
if (oldPsiFile == null) {
|
||||
if (oldPsiFiles.isEmpty()) {
|
||||
return@externalChangeAction
|
||||
}
|
||||
|
||||
treeEvent.element = oldPsiFile
|
||||
val treeEvent = PsiTreeChangeEventImpl(manager)
|
||||
treeEvent.parent = parentDir
|
||||
treeEvent.element = oldPsiFiles.first() // todo IJPL-339 update me
|
||||
treeEvent.propertyName = VirtualFile.PROP_ENCODING
|
||||
treeEvent.oldValue = event.oldValue
|
||||
treeEvent.newValue = event.newValue
|
||||
|
||||
+82
-5
@@ -3,9 +3,12 @@ package com.intellij.psi.impl.file.impl
|
||||
|
||||
import com.intellij.codeInsight.multiverse.CodeInsightContext
|
||||
import com.intellij.codeInsight.multiverse.ProjectModelContextBridge
|
||||
import com.intellij.lang.fakeLang.registerFakeLanguage
|
||||
import com.intellij.openapi.application.EDT
|
||||
import com.intellij.openapi.application.readAction
|
||||
import com.intellij.openapi.application.writeAction
|
||||
import com.intellij.openapi.module.ModuleManager
|
||||
import com.intellij.openapi.util.Disposer
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.platform.testFramework.junit5.projectStructure.fixture.withSharedSourceEnabled
|
||||
import com.intellij.psi.PsiFile
|
||||
@@ -16,10 +19,9 @@ import com.intellij.psi.impl.PsiManagerEx
|
||||
import com.intellij.testFramework.IndexingTestUtil
|
||||
import com.intellij.testFramework.common.timeoutRunBlocking
|
||||
import com.intellij.testFramework.junit5.TestApplication
|
||||
import com.intellij.testFramework.junit5.fixture.disposableFixture
|
||||
import com.intellij.testFramework.junit5.fixture.moduleFixture
|
||||
import com.intellij.testFramework.junit5.fixture.projectFixture
|
||||
import com.intellij.testFramework.junit5.fixture.virtualFileFixture
|
||||
import com.intellij.testFramework.junit5.fixture.*
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.Test
|
||||
import java.lang.ref.Reference
|
||||
@@ -34,6 +36,19 @@ internal class MultiversePsiEventTest {
|
||||
private val module2 = projectFixture.moduleFixture("m2")
|
||||
|
||||
private val sourceRoot = sharedSourceRootFixture(module1, module2)
|
||||
|
||||
@Suppress("unused")
|
||||
private val registerFakeLang = testFixture {
|
||||
val newDisposable = Disposer.newDisposable()
|
||||
withContext(Dispatchers.EDT) {
|
||||
registerFakeLanguage(newDisposable)
|
||||
}
|
||||
initialized(Unit) {
|
||||
withContext(Dispatchers.EDT) {
|
||||
Disposer.dispose(newDisposable)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val testDisposable by disposableFixture()
|
||||
@@ -108,10 +123,72 @@ internal class MultiversePsiEventTest {
|
||||
expectedEventNumber = 2
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `test we receive 1 delete event on renaming file to another file type with 2 psi files`() = doChangeTest(
|
||||
listenerFactory = { counter ->
|
||||
object : PsiTreeChangeAdapter() {
|
||||
override fun childRemoved(event: PsiTreeChangeEvent) {
|
||||
counter.incrementAndGet()
|
||||
}
|
||||
}
|
||||
},
|
||||
updateBlock = { file -> file.rename(this, "A.fake") },
|
||||
expectedEventNumber = 1
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `test we receive 1 replaced event on renaming file to another file type with 2 psi files`() = doChangeTest(
|
||||
listenerFactory = { counter ->
|
||||
object : PsiTreeChangeAdapter() {
|
||||
override fun childReplaced(event: PsiTreeChangeEvent) {
|
||||
counter.incrementAndGet()
|
||||
}
|
||||
}
|
||||
},
|
||||
updateBlock = { file ->
|
||||
file.rename(this, "A.fake")
|
||||
},
|
||||
expectedEventNumber = 1
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `test we receive 2 writeable event on changing writable status of a file with 2 psi files`() = doChangeTest(
|
||||
listenerFactory = { counter ->
|
||||
object : PsiTreeChangeAdapter() {
|
||||
override fun propertyChanged(event: PsiTreeChangeEvent) {
|
||||
if (event.propertyName == PsiTreeChangeEvent.PROP_WRITABLE) {
|
||||
counter.incrementAndGet()
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
updateBlock = { file ->
|
||||
file.isWritable = false
|
||||
},
|
||||
expectedEventNumber = 2
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `test we receive 2 before-writeable event on changing writable status of a file with 2 psi files`() = doChangeTest(
|
||||
listenerFactory = { counter ->
|
||||
object : PsiTreeChangeAdapter() {
|
||||
override fun beforePropertyChange(event: PsiTreeChangeEvent) {
|
||||
if (event.propertyName == PsiTreeChangeEvent.PROP_WRITABLE) {
|
||||
counter.incrementAndGet()
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
updateBlock = { file ->
|
||||
file.isWritable = false
|
||||
},
|
||||
expectedEventNumber = 2
|
||||
)
|
||||
|
||||
private fun doChangeTest(
|
||||
listenerFactory: (AtomicInteger) -> PsiTreeChangeListener,
|
||||
updateBlock: (file: VirtualFile) -> Unit,
|
||||
@Suppress("SameParameterValue") expectedEventNumber: Int
|
||||
@Suppress("SameParameterValue") expectedEventNumber: Int,
|
||||
) = runTest {
|
||||
val counter = AtomicInteger(0)
|
||||
val listener = listenerFactory(counter)
|
||||
|
||||
Reference in New Issue
Block a user