@@ -3,37 +3,23 @@ package org.digma.intellij.plugin.idea.navigation
33import com.intellij.openapi.Disposable
44import com.intellij.openapi.components.service
55import com.intellij.openapi.diagnostic.Logger
6- import com.intellij.openapi.editor.Document
7- import com.intellij.openapi.fileEditor.FileDocumentManager
86import com.intellij.openapi.project.DumbService
97import com.intellij.openapi.project.Project
108import 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
159import com.intellij.psi.search.GlobalSearchScope
16- import com.intellij.util.Alarm
1710import com.intellij.util.concurrency.AppExecutorUtil
1811import 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
2212import org.digma.intellij.plugin.common.SearchScopeProvider
2313import org.digma.intellij.plugin.common.isProjectValid
2414import org.digma.intellij.plugin.common.isValidVirtualFile
25- import org.digma.intellij.plugin.common.runInReadAccessWithResult
2615import org.digma.intellij.plugin.errorreporting.ErrorReporter
2716import org.digma.intellij.plugin.idea.navigation.model.NavigationDiscoveryTrigger
2817import org.digma.intellij.plugin.idea.navigation.model.NavigationProcessContext
29- import org.digma.intellij.plugin.idea.psi.isJvmSupportedFile
3018import org.digma.intellij.plugin.log.Log
3119import org.digma.intellij.plugin.posthog.ActivityMonitor
3220import org.digma.intellij.plugin.process.ProcessManager
33- import org.digma.intellij.plugin.psi.PsiUtils
3421import org.digma.intellij.plugin.session.SessionMetadataProperties
3522import org.digma.intellij.plugin.session.getPluginLoadedKey
36- import java.util.concurrent.ConcurrentHashMap
3723import java.util.concurrent.ScheduledExecutorService
3824import java.util.concurrent.TimeUnit
3925import 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}
0 commit comments