11package org .digma .intellij .plugin .editor ;
22
33import com .intellij .openapi .Disposable ;
4+ import com .intellij .openapi .application .ModalityState ;
5+ import com .intellij .openapi .application .ReadAction ;
46import com .intellij .openapi .diagnostic .Logger ;
57import com .intellij .openapi .editor .Document ;
68import com .intellij .openapi .editor .Editor ;
79import com .intellij .openapi .editor .event .DocumentEvent ;
810import com .intellij .openapi .editor .event .DocumentListener ;
11+ import com .intellij .openapi .fileEditor .FileEditorManager ;
912import com .intellij .openapi .project .IndexNotReadyException ;
1013import com .intellij .openapi .project .Project ;
1114import com .intellij .openapi .util .Disposer ;
1518import com .intellij .psi .PsiInvalidElementAccessException ;
1619import com .intellij .util .Alarm ;
1720import com .intellij .util .AlarmFactory ;
21+ import com .intellij .util .RunnableCallable ;
22+ import com .intellij .util .concurrency .NonUrgentExecutor ;
1823import com .intellij .util .indexing .FileBasedIndex ;
19- import org .digma .intellij .plugin .common .Backgroundable ;
2024import org .digma .intellij .plugin .document .DocumentInfoService ;
2125import org .digma .intellij .plugin .index .DocumentInfoIndex ;
2226import org .digma .intellij .plugin .log .Log ;
@@ -97,18 +101,35 @@ public void documentChanged(@NotNull DocumentEvent event) {
97101 documentChangeAlarm .cancelAllRequests ();
98102 documentChangeAlarm .addRequest (() ->
99103 {
100- //this code is always executed in smart mode because the listener is installed only in smart mode
101- PsiFile fileToQuery = PsiDocumentManager .getInstance (project ).getPsiFile (event .getDocument ());
104+ //this code is always executed in smart mode because the document listener is installed only in smart mode
105+ PsiFile changedPsiFile = PsiDocumentManager .getInstance (project ).getPsiFile (event .getDocument ());
102106 //probably should never happen
103- if (fileToQuery == null ) {
107+ if (changedPsiFile == null ) {
104108 return ;
105109 }
106- try {
107- Log .log (LOGGER ::debug , "Processing documentChanged event for {}" , fileToQuery .getVirtualFile ());
108- processDocumentChanged (editor , fileToQuery );
109- } catch (PsiInvalidElementAccessException e ) {
110- Log .debugWithException (LOGGER , e , "exception while processing file: {}, {}" , fileToQuery .getVirtualFile (), e .getMessage ());
111- }
110+
111+ ReadAction .nonBlocking (new RunnableCallable (() -> {
112+ try {
113+ Log .log (LOGGER ::debug , "Processing documentChanged event for {}" , changedPsiFile .getVirtualFile ());
114+ processDocumentChanged (changedPsiFile );
115+ } catch (PsiInvalidElementAccessException e ) {
116+ Log .debugWithException (LOGGER , e , "exception while processing documentChanged event for file: {}, {}" , changedPsiFile .getVirtualFile (), e .getMessage ());
117+ }
118+ })).inSmartMode (project ).withDocumentsCommitted (project ).finishOnUiThread (ModalityState .defaultModalityState (), unused -> {
119+
120+ var selectedTextEditor = FileEditorManager .getInstance (project ).getSelectedTextEditor ();
121+ if (selectedTextEditor != null ) {
122+ PsiFile selectedPsiFile = PsiDocumentManager .getInstance (project ).getPsiFile (selectedTextEditor .getDocument ());
123+ //if the selected editor is still the file that was changed in this event then call contextChanged
124+ // otherwise do nothing
125+ if (selectedPsiFile != null && selectedPsiFile .equals (changedPsiFile )) {
126+ LanguageService languageService = LanguageServiceLocator .getInstance (project ).locate (selectedPsiFile .getLanguage ());
127+ MethodUnderCaret methodUnderCaret = languageService .detectMethodUnderCaret (project , selectedPsiFile , selectedTextEditor .getCaretModel ().getOffset ());
128+ caretContextService .contextChanged (methodUnderCaret );
129+ }
130+ }
131+
132+ }).submit (NonUrgentExecutor .getInstance ());
112133
113134 }, 500 );
114135
@@ -117,64 +138,44 @@ public void documentChanged(@NotNull DocumentEvent event) {
117138 }
118139
119140
120- private void processDocumentChanged (@ NotNull Editor editor , @ NotNull PsiFile psiFile ) {
141+ private void processDocumentChanged (@ NotNull PsiFile psiFile ) {
121142
122143 if (project .isDisposed ()) {
123144 return ;
124145 }
125146
147+ LanguageService languageService = LanguageServiceLocator .getInstance (project ).locate (psiFile .getLanguage ());
126148
127- PsiDocumentManager .getInstance (project ).performLaterWhenAllCommitted (() -> {
128-
129- LanguageService languageService = LanguageServiceLocator .getInstance (project ).locate (psiFile .getLanguage ());
130-
131- DocumentInfo documentInfo ;
132- try {
133- Map <Integer , DocumentInfo > documentInfoMap =
134- FileBasedIndex .getInstance ().getFileData (DocumentInfoIndex .DOCUMENT_INFO_INDEX_ID , psiFile .getVirtualFile (), project );
135- //there is only one DocumentInfo per file in the index.
136- //all relevant files must be indexed, so if we are here then DocumentInfo must be found in the index is ready,
137- // or we have a mistake somewhere else. java interfaces,enums and annotations are indexed but the DocumentInfo
138- // object is empty of methods, that's because currently we have no way to exclude those types from indexing.
139- documentInfo = documentInfoMap .values ().stream ().findFirst ().orElse (null );
140-
141- //usually we should find the document info in the index. on extreme cases, maybe is the index is corrupted
142- // the document info will not be found, try again to build it
143- if (documentInfo == null ) {
144- documentInfo = languageService .buildDocumentInfo (psiFile );
145- }
146-
147- } catch (IndexNotReadyException e ) {
148- //IndexNotReadyException will be thrown on dumb mode, when indexing is still in progress.
149- //usually it should not happen because the listener is installed only in smart mode.
150- documentInfo = languageService .buildDocumentInfo (psiFile );
151- }
149+ DocumentInfo documentInfo ;
150+ try {
151+ Map <Integer , DocumentInfo > documentInfoMap =
152+ FileBasedIndex .getInstance ().getFileData (DocumentInfoIndex .DOCUMENT_INFO_INDEX_ID , psiFile .getVirtualFile (), project );
153+ //there is only one DocumentInfo per file in the index.
154+ //all relevant files must be indexed, so if we are here then DocumentInfo must be found in the index is ready,
155+ // or we have a mistake somewhere else. java interfaces,enums and annotations are indexed but the DocumentInfo
156+ // object is empty of methods, that's because currently we have no way to exclude those types from indexing.
157+ documentInfo = documentInfoMap .values ().stream ().findFirst ().orElse (null );
152158
159+ //usually we should find the document info in the index. on extreme cases, maybe if the index is corrupted
160+ // the document info will not be found, try again to build it
153161 if (documentInfo == null ) {
154- Log .log (LOGGER ::error , "Could not find DocumentInfo for file {}" , psiFile .getVirtualFile ());
155- throw new DocumentInfoIndexNotFoundException ("Could not find DocumentInfo index for " + psiFile .getVirtualFile ());
162+ documentInfo = languageService .buildDocumentInfo (psiFile );
156163 }
157- Log .log (LOGGER ::debug , "Found DocumentInfo index for {},'{}'" , psiFile .getVirtualFile (), documentInfo );
158-
159- MethodUnderCaret methodUnderCaret = languageService .detectMethodUnderCaret (project , psiFile , editor .getCaretModel ().getOffset ());
160-
161- update (languageService , psiFile , documentInfo , methodUnderCaret );
162164
163- });
164- }
165+ } catch (IndexNotReadyException e ) {
166+ //IndexNotReadyException will be thrown on dumb mode, when indexing is still in progress.
167+ //usually it should not happen because the document listener is installed only in smart mode.
168+ documentInfo = languageService .buildDocumentInfo (psiFile );
169+ }
165170
171+ if (documentInfo == null ) {
172+ Log .log (LOGGER ::error , "Could not find DocumentInfo for file {}" , psiFile .getVirtualFile ());
173+ throw new DocumentInfoIndexNotFoundException ("Could not find DocumentInfo index for " + psiFile .getVirtualFile ());
174+ }
175+ Log .log (LOGGER ::debug , "Found DocumentInfo index for {},'{}'" , psiFile .getVirtualFile (), documentInfo );
166176
167- private void update (@ NotNull LanguageService languageService , @ NotNull PsiFile psiFile , DocumentInfo documentInfo , MethodUnderCaret methodUnderCaret ) {
168- Backgroundable .ensureBackground (project , "Document changed" , () -> {
169- //documentInfoService will update the discovery code objects, refresh backend data and call some event
170- //so that code lens will be installed.
171- //contextChanged will update the UI and must run after documentInfoService.addCodeObjects is finished
172- //enrichDocumentInfo is meant mainly to discover spans. the DocumentInfoIndex can
173- // not discover spans because there is no reference resolving during file based index.
174- languageService .enrichDocumentInfo (documentInfo , psiFile );
175- documentInfoService .addCodeObjects (psiFile , documentInfo );
176- caretContextService .contextChanged (methodUnderCaret );
177- });
177+ languageService .enrichDocumentInfo (documentInfo , psiFile );
178+ documentInfoService .addCodeObjects (psiFile , documentInfo );
178179 }
179180
180181
0 commit comments