Skip to content

Commit 87e0d39

Browse files
authored
Merge pull request #2424 from digma-ai/alternative-to-bulkFileListener
refactore change listener for span and endpoint navigation Closes #2408
2 parents 3a27b8a + 8d27971 commit 87e0d39

File tree

9 files changed

+425
-407
lines changed

9 files changed

+425
-407
lines changed

ide-common/src/main/kotlin/org/digma/intellij/plugin/bulklistener/AbstractBulkFileChangeListener.kt

Lines changed: 0 additions & 32 deletions
This file was deleted.

jvm-common/src/main/kotlin/org/digma/intellij/plugin/idea/navigation/AbstractNavigationDiscovery.kt

Lines changed: 22 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -3,37 +3,23 @@ package org.digma.intellij.plugin.idea.navigation
33
import com.intellij.openapi.Disposable
44
import com.intellij.openapi.components.service
55
import com.intellij.openapi.diagnostic.Logger
6-
import com.intellij.openapi.editor.Document
7-
import com.intellij.openapi.fileEditor.FileDocumentManager
86
import com.intellij.openapi.project.DumbService
97
import com.intellij.openapi.project.Project
108
import com.intellij.openapi.vfs.VirtualFile
11-
import com.intellij.psi.PsiDocumentManager
12-
import com.intellij.psi.PsiFile
13-
import com.intellij.psi.PsiManager
14-
import com.intellij.psi.PsiTreeAnyChangeAbstractAdapter
159
import com.intellij.psi.search.GlobalSearchScope
16-
import com.intellij.util.Alarm
1710
import com.intellij.util.concurrency.AppExecutorUtil
1811
import kotlinx.datetime.Clock
19-
import org.digma.intellij.plugin.common.Backgroundable
20-
import org.digma.intellij.plugin.common.EDT
21-
import org.digma.intellij.plugin.common.ReadActions
2212
import org.digma.intellij.plugin.common.SearchScopeProvider
2313
import org.digma.intellij.plugin.common.isProjectValid
2414
import org.digma.intellij.plugin.common.isValidVirtualFile
25-
import org.digma.intellij.plugin.common.runInReadAccessWithResult
2615
import org.digma.intellij.plugin.errorreporting.ErrorReporter
2716
import org.digma.intellij.plugin.idea.navigation.model.NavigationDiscoveryTrigger
2817
import org.digma.intellij.plugin.idea.navigation.model.NavigationProcessContext
29-
import org.digma.intellij.plugin.idea.psi.isJvmSupportedFile
3018
import org.digma.intellij.plugin.log.Log
3119
import org.digma.intellij.plugin.posthog.ActivityMonitor
3220
import org.digma.intellij.plugin.process.ProcessManager
33-
import org.digma.intellij.plugin.psi.PsiUtils
3421
import org.digma.intellij.plugin.session.SessionMetadataProperties
3522
import org.digma.intellij.plugin.session.getPluginLoadedKey
36-
import java.util.concurrent.ConcurrentHashMap
3723
import java.util.concurrent.ScheduledExecutorService
3824
import java.util.concurrent.TimeUnit
3925
import java.util.concurrent.locks.ReentrantLock
@@ -50,22 +36,12 @@ abstract class AbstractNavigationDiscovery(protected val project: Project) : Dis
5036
get() = AppExecutorUtil.createBoundedScheduledExecutorService("${this.type}Nav", 1)
5137

5238

53-
init {
54-
//we need PsiTreeChange because bulk file listener does not always notify on time on changes, it will notify all changes only
55-
// when saving files. for example if refactoring name for class A that requires changes on class B, bulk file listener will notify on A,
56-
// but not on B, only when the system decides to save changes it will notify on B.
57-
//psi change events will be fired immediately on all changed classes.
58-
//on the other hand psi change events will not be fired for file deletion, but bulk listener will.
59-
//so we use a combination of both.
60-
//many times we will be notified for the same file more than once, and we will run a process more than once for the same file, while its better
61-
// not to the performance and resource consumption is not much more.
62-
@Suppress("LeakingThis")
63-
PsiManager.getInstance(project).addPsiTreeChangeListener(MyPsiTreeAnyChangeListener(this), this)
64-
}
65-
66-
6739
override fun dispose() {
68-
scheduledExecutorService.shutdownNow()
40+
try {
41+
scheduledExecutorService.shutdownNow()
42+
} catch (e: Throwable) {
43+
//ignore
44+
}
6945
}
7046

7147
abstract val type: String
@@ -113,14 +89,18 @@ abstract class AbstractNavigationDiscovery(protected val project: Project) : Dis
11389
scheduledExecutorService.schedule({
11490

11591
try {
116-
Log.log(logger::trace, "Starting navigation discovery process in smart mode")
92+
Log.log(logger::trace, "Starting navigation discovery process for {}", type)
93+
Log.log(logger::trace, "waiting for smart mode")
11794
DumbService.getInstance(project).waitForSmartMode()
95+
Log.log(logger::trace, "processing in smart mode")
11896
//run the preTask on same thread before the main task
11997
preTask?.run()
12098
buildNavigationUnderProgress(searchScopeProvider, navigationDiscoveryTrigger, retry)
12199
} catch (e: Throwable) {
122100
Log.warnWithException(logger, project, e, "Error in navigation discovery process")
123101
ErrorReporter.getInstance().reportError(project, "${this::class.simpleName}.schedule", e)
102+
} finally {
103+
Log.log(logger::trace, "Navigation discovery process completed for {}", type)
124104
}
125105

126106
}, delayMillis, TimeUnit.MILLISECONDS)
@@ -173,47 +153,13 @@ abstract class AbstractNavigationDiscovery(protected val project: Project) : Dis
173153

174154
}
175155

176-
fun documentChanged(document: Document) {
177-
178-
//this method is called on background thread from documentChanged event so that the EDT is released quickly.
179-
180-
EDT.assertNonDispatchThread()
181-
ReadActions.assertNotInReadAccess()
182-
183-
try {
184-
if (!isProjectValid(project)) {
185-
return
186-
}
187-
188-
val psiFile = runInReadAccessWithResult {
189-
PsiDocumentManager.getInstance(project).getPsiFile(document)
190-
}
191-
192-
psiFile?.let {
193-
if (PsiUtils.isValidPsiFile(it) && isJvmSupportedFile(project, it)) {
194-
val virtualFile = FileDocumentManager.getInstance().getFile(document)
195-
virtualFile?.takeIf { isValidVirtualFile(virtualFile) }?.let { vf ->
196-
fileChanged(vf)
197-
}
198-
}
199-
}
200-
} catch (e: Throwable) {
201-
Log.warnWithException(logger, e, "Exception in documentChanged")
202-
ErrorReporter.getInstance().reportError(project, "${this::class.simpleName}.documentChanged", e)
203-
}
204-
}
205-
206-
207156
/*
208157
This method must be called with a file that is relevant for span discovery and span navigation.
209-
checking that should be done before calling this method.
158+
checking that it is should be done before calling this method.
210159
*/
211160
fun fileChanged(virtualFile: VirtualFile?) {
212161

213162
//this method should be very fast and only schedule a task for the changed file.
214-
//it's called on background from BulkFileChangeListenerForJvmNavigationDiscovery.processEvents.
215-
//it's called on background from documentChanged
216-
//it's called on background from MyPsiTreeAnyChangeListener
217163

218164
if (!isProjectValid(project) || !isValidVirtualFile(virtualFile)) {
219165
return
@@ -244,12 +190,12 @@ abstract class AbstractNavigationDiscovery(protected val project: Project) : Dis
244190
return
245191
}
246192

247-
Backgroundable.ensurePooledThreadWithoutReadAccess {
248-
if (virtualFile != null) {
249-
buildLock.lock()
250-
try {
251-
removeDiscoveryForFile(virtualFile)
252-
} finally {
193+
if (virtualFile != null) {
194+
buildLock.lock()
195+
try {
196+
removeDiscoveryForFile(virtualFile)
197+
} finally {
198+
if (buildLock.isHeldByCurrentThread) {
253199
buildLock.unlock()
254200
}
255201
}
@@ -262,11 +208,11 @@ abstract class AbstractNavigationDiscovery(protected val project: Project) : Dis
262208
return
263209
}
264210

265-
Backgroundable.ensurePooledThreadWithoutReadAccess {
266-
buildLock.lock()
267-
try {
268-
removeDiscoveryForPath(path)
269-
} finally {
211+
buildLock.lock()
212+
try {
213+
removeDiscoveryForPath(path)
214+
} finally {
215+
if (buildLock.isHeldByCurrentThread) {
270216
buildLock.unlock()
271217
}
272218
}
@@ -339,31 +285,4 @@ abstract class AbstractNavigationDiscovery(protected val project: Project) : Dis
339285
)
340286
}
341287

342-
343-
//PsiTreeChangeEvent are fired many times for the same file, actually for every psi change in the file.
344-
//this listener will make sure not to call fileChanged for every event.
345-
//it collects the changed files and calls fileChanged every 5 seconds for the collected files.
346-
private inner class MyPsiTreeAnyChangeListener(val abstractNavigationDiscovery: AbstractNavigationDiscovery) : PsiTreeAnyChangeAbstractAdapter() {
347-
348-
private val changeAlarm = Alarm(Alarm.ThreadToUse.POOLED_THREAD, abstractNavigationDiscovery)
349-
private val changedFiles = ConcurrentHashMap.newKeySet<VirtualFile>()
350-
351-
override fun onChange(file: PsiFile?) {
352-
file?.let {
353-
val vf = it.virtualFile
354-
if (isValidRelevantFile(project, vf) && PsiUtils.isValidPsiFile(it) && isJvmSupportedFile(project, it)) {
355-
changedFiles.add(vf)
356-
changeAlarm.cancelAllRequests()
357-
changeAlarm.addRequest({
358-
changedFiles.forEach { file ->
359-
Log.log(logger::trace, "got psi tree change for file {}", file)
360-
changedFiles.remove(file)
361-
abstractNavigationDiscovery.fileChanged(file)
362-
}
363-
}, 5000)
364-
}
365-
}
366-
}
367-
}
368-
369288
}

jvm-common/src/main/kotlin/org/digma/intellij/plugin/idea/navigation/BulkFileChangeListenerForJvmNavigationDiscovery.kt

Lines changed: 0 additions & 113 deletions
This file was deleted.

0 commit comments

Comments
 (0)