Skip to content

Commit 2aa00ee

Browse files
authored
improve document change listener (#216)
1 parent 2cf16ce commit 2aa00ee

File tree

2 files changed

+59
-98
lines changed

2 files changed

+59
-98
lines changed

ide-common/src/main/java/org/digma/intellij/plugin/editor/DocumentChangeListener.java

Lines changed: 57 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
package org.digma.intellij.plugin.editor;
22

33
import com.intellij.openapi.Disposable;
4+
import com.intellij.openapi.application.ModalityState;
5+
import com.intellij.openapi.application.ReadAction;
46
import com.intellij.openapi.diagnostic.Logger;
57
import com.intellij.openapi.editor.Document;
68
import com.intellij.openapi.editor.Editor;
79
import com.intellij.openapi.editor.event.DocumentEvent;
810
import com.intellij.openapi.editor.event.DocumentListener;
11+
import com.intellij.openapi.fileEditor.FileEditorManager;
912
import com.intellij.openapi.project.IndexNotReadyException;
1013
import com.intellij.openapi.project.Project;
1114
import com.intellij.openapi.util.Disposer;
@@ -15,8 +18,9 @@
1518
import com.intellij.psi.PsiInvalidElementAccessException;
1619
import com.intellij.util.Alarm;
1720
import com.intellij.util.AlarmFactory;
21+
import com.intellij.util.RunnableCallable;
22+
import com.intellij.util.concurrency.NonUrgentExecutor;
1823
import com.intellij.util.indexing.FileBasedIndex;
19-
import org.digma.intellij.plugin.common.Backgroundable;
2024
import org.digma.intellij.plugin.document.DocumentInfoService;
2125
import org.digma.intellij.plugin.index.DocumentInfoIndex;
2226
import 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

java/src/main/java/org/digma/intellij/plugin/idea/psi/java/JavaLanguageService.java

Lines changed: 2 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@
1919
import com.intellij.psi.search.searches.MethodReferencesSearch;
2020
import com.intellij.psi.util.PsiTreeUtil;
2121
import com.intellij.util.Query;
22-
import com.intellij.util.RunnableCallable;
23-
import com.intellij.util.concurrency.NonUrgentExecutor;
2422
import kotlin.Pair;
2523
import org.digma.intellij.plugin.document.DocumentInfoService;
2624
import org.digma.intellij.plugin.index.DocumentInfoIndex;
@@ -374,51 +372,13 @@ public void enrichDocumentInfo(@NotNull DocumentInfo documentInfo, @NotNull PsiF
374372
This method is called after loading the DocumentInfo from DocumentInfoIndex, and it is meant to
375373
enrich the DocumentInfo with discovery that can not be done in file based index or dumb mode.
376374
for example span discovery does not work in dumb mode, it must be done in smart mode.
377-
it may happen that this method is called in dumb mode when files are re-opened on project startup.
378-
in that case the code must wait for smart mode before trying to do span discovery and then do it in the
379-
background.
380-
if it's called in smart mode then span discovery will happen on the current thread blocking until its finished.
375+
This method must be called in smart mode inside s ReadAction or UI thread.
381376
*/
382377

383-
//todo: no need to wait for smart mode because enrichDocumentInfo should not be called in dumb mode
384-
// EditorEventsHandler and DocumentChangeListener will call this method only in smart mode
385-
// if (DumbService.getInstance(project).isDumb()) {
386-
// ReadAction.nonBlocking(new RunnableCallable(() -> {
387-
// spanDiscovery(psiFile, documentInfo);
388-
// refreshDocumentInfoAndNotifyContextChanged(psiFile);
389-
// })).inSmartMode(project).submit(NonUrgentExecutor.getInstance());
390-
// } else {
391-
ReadAction.nonBlocking(new RunnableCallable(() -> spanDiscovery(psiFile, documentInfo))).submit(NonUrgentExecutor.getInstance());
392-
// }
378+
spanDiscovery(psiFile, documentInfo);
393379
}
394380

395381

396-
// private void refreshDocumentInfoAndNotifyContextChanged(@NotNull PsiFile psiFile) {
397-
// //if documents are opened in dumb mode it may be that they don't have span infos in time because span discovery
398-
// // waits for smart mode,in that case they will not have span summaries and span insights.
399-
// // So after span discovery in smart mode ,refresh the document data and fire contextChange for the selected editor.
400-
//
401-
// Backgroundable.ensureBackground(project,"Refresh document",() -> {
402-
//
403-
// DocumentInfoService.getInstance(project).refreshIfExists(psiFile);
404-
//
405-
// ApplicationManager.getApplication().invokeLater(() -> {
406-
// var editor = FileEditorManager.getInstance(project).getSelectedTextEditor();
407-
// if (editor != null){
408-
// var virtualFile = FileDocumentManager.getInstance().getFile(editor.getDocument());
409-
// if (virtualFile != null) {
410-
// var selectedPsiFile = PsiManager.getInstance(project).findFile(virtualFile);
411-
// if (selectedPsiFile != null && selectedPsiFile.equals(psiFile)){
412-
// var offset = editor.getCaretModel().getOffset();
413-
// var methodUnderCaret = detectMethodUnderCaret(project,psiFile,offset);
414-
// caretContextService.contextChanged(methodUnderCaret);
415-
// }
416-
// }
417-
// }
418-
// });
419-
// });
420-
// }
421-
422382

423383
private void spanDiscovery(PsiFile psiFile, DocumentInfo documentInfo) {
424384
Log.log(LOGGER::debug, "Building spans for file {}", psiFile);

0 commit comments

Comments
 (0)