IDEA-154272 Editor opening should do heavy operations in background

This commit is contained in:
peter
2016-05-24 17:09:46 +02:00
parent 10f57a3960
commit 1c70e25d2e
10 changed files with 310 additions and 67 deletions
@@ -32,6 +32,7 @@ import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.fileEditor.FileEditor;
import com.intellij.openapi.fileEditor.FileEditorLocation;
import com.intellij.openapi.fileEditor.TextEditor;
import com.intellij.openapi.fileEditor.impl.text.AsyncEditorLoader;
import com.intellij.openapi.keymap.KeymapUtil;
import com.intellij.openapi.preview.PreviewManager;
import com.intellij.openapi.progress.ProgressIndicator;
@@ -1067,13 +1068,20 @@ public class ShowUsagesAction extends AnAction implements PopupAction {
int maxUsages,
@NotNull FindUsagesOptions options,
boolean isWarning) {
JComponent label = createHintComponent(hint, handler, popupPosition, editor, ShowUsagesAction::hideHints, maxUsages, options, isWarning);
if (editor == null || editor.isDisposed() || !editor.getComponent().isShowing()) {
HintManager.getInstance().showHint(label, popupPosition, HintManager.HIDE_BY_ANY_KEY |
HintManager.HIDE_BY_TEXT_CHANGE | HintManager.HIDE_BY_SCROLLING, 0);
}
else {
HintManager.getInstance().showInformationHint(editor, label);
Runnable runnable = () -> {
JComponent label = createHintComponent(hint, handler, popupPosition, editor, ShowUsagesAction::hideHints, maxUsages, options, isWarning);
if (editor == null || editor.isDisposed() || !editor.getComponent().isShowing()) {
HintManager.getInstance().showHint(label, popupPosition, HintManager.HIDE_BY_ANY_KEY |
HintManager.HIDE_BY_TEXT_CHANGE | HintManager.HIDE_BY_SCROLLING, 0);
}
else {
HintManager.getInstance().showInformationHint(editor, label);
}
};
if (editor == null) {
runnable.run();
} else {
AsyncEditorLoader.performWhenLoaded(editor, runnable);
}
}
@@ -20,15 +20,21 @@
package com.intellij.openapi.fileEditor.impl.text;
import com.intellij.codeHighlighting.BackgroundEditorHighlighter;
import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer;
import com.intellij.codeInsight.daemon.impl.TextEditorBackgroundHighlighter;
import com.intellij.codeInsight.folding.CodeFoldingManager;
import com.intellij.codeInsight.lookup.LookupManager;
import com.intellij.codeInsight.lookup.impl.LookupImpl;
import com.intellij.openapi.actionSystem.LangDataKeys;
import com.intellij.openapi.actionSystem.PlatformDataKeys;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.module.ModuleUtilCore;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import com.intellij.ui.EditorNotifications;
import org.jetbrains.annotations.NotNull;
public class PsiAwareTextEditorImpl extends TextEditorImpl {
@@ -38,6 +44,27 @@ public class PsiAwareTextEditorImpl extends TextEditorImpl {
super(project, file, provider);
}
@NotNull
@Override
protected Runnable loadEditorInBackground() {
Runnable baseAction = super.loadEditorInBackground();
PsiFile psiFile = PsiManager.getInstance(myProject).findFile(myFile);
Document document = FileDocumentManager.getInstance().getDocument(myFile);
CodeFoldingState foldingState = document != null && !myProject.isDefault()
? CodeFoldingManager.getInstance(myProject).buildInitialFoldings(document)
: null;
return () -> {
baseAction.run();
if (foldingState != null) {
foldingState.setToEditor(getEditor());
}
if (psiFile != null) {
DaemonCodeAnalyzer.getInstance(myProject).restart(psiFile);
}
EditorNotifications.getInstance(myProject).updateNotifications(myFile);
};
}
@NotNull
@Override
protected TextEditorComponent createEditorComponent(final Project project, final VirtualFile file) {
@@ -46,6 +73,10 @@ public class PsiAwareTextEditorImpl extends TextEditorImpl {
@Override
public BackgroundEditorHighlighter getBackgroundHighlighter() {
if (!AsyncEditorLoader.isEditorLoaded(getEditor())) {
return null;
}
if (myBackgroundHighlighter == null) {
myBackgroundHighlighter = new TextEditorBackgroundHighlighter(myProject, getEditor());
}
@@ -26,7 +26,6 @@ import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.fileEditor.*;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.WriteExternalException;
import com.intellij.openapi.util.registry.Registry;
@@ -37,7 +36,7 @@ import org.jdom.Element;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
public class PsiAwareTextEditorProvider extends TextEditorProvider implements AsyncFileEditorProvider {
public class PsiAwareTextEditorProvider extends TextEditorProvider {
private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.fileEditor.impl.text.PsiAwareTextEditorProvider");
@NonNls
private static final String FOLDING_ELEMENT = "folding";
@@ -45,41 +44,7 @@ public class PsiAwareTextEditorProvider extends TextEditorProvider implements As
@Override
@NotNull
public FileEditor createEditor(@NotNull final Project project, @NotNull final VirtualFile file) {
return createEditorAsync(project, file).build();
}
@NotNull
@Override
public Builder createEditorAsync(@NotNull final Project project, @NotNull final VirtualFile file) {
if (!accept(project, file)) {
LOG.error("Cannot open text editor for " + file);
}
CodeFoldingState state = null;
if (!project.isDefault()) { // There's no CodeFoldingManager for default project (which is used in diff command-line application)
try {
Document document = FileDocumentManager.getInstance().getDocument(file);
if (document != null) {
state = CodeFoldingManager.getInstance(project).buildInitialFoldings(document);
}
}
catch (ProcessCanceledException e) {
throw e;
}
catch (Exception e) {
LOG.error("Error building initial foldings", e);
}
}
final CodeFoldingState finalState = state;
return new Builder() {
@Override
public FileEditor build() {
final PsiAwareTextEditorImpl editor = new PsiAwareTextEditorImpl(project, file, PsiAwareTextEditorProvider.this);
if (finalState != null) {
finalState.setToEditor(editor.getEditor());
}
return editor;
}
};
return new PsiAwareTextEditorImpl(project, file, this);
}
@Override
@@ -149,7 +114,7 @@ public class PsiAwareTextEditorProvider extends TextEditorProvider implements As
super.setStateImpl(project, editor, state);
// Folding
final CodeFoldingState foldState = state.getFoldingState();
if (project != null && foldState != null) {
if (project != null && foldState != null && AsyncEditorLoader.isEditorLoaded(editor)) {
if (!PsiDocumentManager.getInstance(project).isCommitted(editor.getDocument())) {
PsiDocumentManager.getInstance(project).commitDocument(editor.getDocument());
LOG.error("File should be parsed when changing editor state, otherwise UI might be frozen for a considerable time");
@@ -37,6 +37,7 @@ import com.intellij.openapi.editor.event.VisibleAreaEvent;
import com.intellij.openapi.editor.event.VisibleAreaListener;
import com.intellij.openapi.editor.ex.ScrollingModelEx;
import com.intellij.openapi.editor.ex.util.EditorUtil;
import com.intellij.openapi.fileEditor.impl.text.AsyncEditorLoader;
import com.intellij.openapi.util.Disposer;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.ui.Animator;
@@ -141,8 +142,7 @@ public class ScrollingModelImpl implements ScrollingModelEx {
assertIsDispatchThread();
myEditor.validateSize();
if (myEditor.myUseNewRendering) {
VisualPosition caretPosition = myEditor.getCaretModel().getVisualPosition();
scrollTo(caretPosition, scrollType);
AsyncEditorLoader.performWhenLoaded(myEditor, () -> scrollTo(myEditor.getCaretModel().getVisualPosition(), scrollType));
}
else {
LogicalPosition caretPosition = myEditor.getCaretModel().getLogicalPosition();
@@ -166,8 +166,7 @@ public class ScrollingModelImpl implements ScrollingModelEx {
public void scrollTo(@NotNull LogicalPosition pos, @NotNull ScrollType scrollType) {
assertIsDispatchThread();
Point targetLocation = myEditor.logicalPositionToXY(pos);
scrollTo(targetLocation, scrollType);
AsyncEditorLoader.performWhenLoaded(myEditor, () -> scrollTo(myEditor.logicalPositionToXY(pos), scrollType));
}
private static void assertIsDispatchThread() {
@@ -32,6 +32,7 @@ import com.intellij.openapi.editor.ex.DocumentEx;
import com.intellij.openapi.editor.ex.EditorEx;
import com.intellij.openapi.editor.ex.EditorSettingsExternalizable;
import com.intellij.openapi.editor.impl.softwrap.SoftWrapAppliancePlaces;
import com.intellij.openapi.fileEditor.impl.text.AsyncEditorLoader;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.registry.Registry;
@@ -336,7 +337,7 @@ public class SettingsImpl implements EditorSettings {
}
private void reinitDocumentIndentOptions() {
if (myEditor == null || myEditor.isViewer()) return;
if (myEditor == null || myEditor.isViewer() || AsyncEditorLoader.isCreatingAsyncEditor()) return;
final Project project = myEditor.getProject();
final DocumentEx document = myEditor.getDocument();
@@ -363,7 +364,7 @@ public class SettingsImpl implements EditorSettings {
if (myTabSize != null) return myTabSize.intValue();
if (myCachedTabSize != null) return myCachedTabSize.intValue();
int tabSize;
if (project == null || project.isDisposed()) {
if (project == null || project.isDisposed() || AsyncEditorLoader.isCreatingAsyncEditor()) {
tabSize = CodeStyleSettingsManager.getSettings(null).getTabSize(null);
}
else {
@@ -73,6 +73,7 @@ import com.intellij.ui.tabs.impl.JBTabsImpl;
import com.intellij.util.SmartList;
import com.intellij.util.concurrency.Semaphore;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.io.storage.HeavyProcessLatch;
import com.intellij.util.messages.MessageBusConnection;
import com.intellij.util.messages.impl.MessageListenerList;
import com.intellij.util.ui.JBUI;
@@ -854,6 +855,9 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Persis
if (myProject.isDisposed() || !file.isValid()) {
return;
}
HeavyProcessLatch.INSTANCE.prioritizeUiActivity();
compositeRef.set(window.findFileComposite(file));
boolean newEditor = compositeRef.isNull();
if (newEditor) {
@@ -964,7 +968,7 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Persis
}
};
commitAndInvoke(runnable);
UIUtil.invokeAndWaitIfNeeded(runnable);
EditorWithProviderComposite composite = compositeRef.get();
return Pair.create(composite == null ? EMPTY_EDITOR_ARRAY : composite.getEditors(),
@@ -0,0 +1,220 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.openapi.fileEditor.impl.text;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.fileEditor.*;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.util.ProgressIndicatorBase;
import com.intellij.openapi.progress.util.ProgressIndicatorUtils;
import com.intellij.openapi.progress.util.ReadTask;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.wm.IdeFocusManager;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.util.ObjectUtils;
import com.intellij.util.concurrency.AppExecutorUtil;
import com.intellij.util.concurrency.Semaphore;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
public class AsyncEditorLoader {
private static final ExecutorService ourExecutor = AppExecutorUtil.createBoundedApplicationPoolExecutor(2);
private static final Key<AsyncEditorLoader> ASYNC_LOADER = Key.create("ASYNC_LOADER");
private static boolean ourCreatingAsyncEditor;
@NotNull private final Editor myEditor;
@NotNull private final Project myProject;
@NotNull private final TextEditorImpl myTextEditor;
@NotNull private final TextEditorComponent myEditorComponent;
@NotNull private final TextEditorProvider myProvider;
private boolean myLoaded;
private final List<Runnable> myDelayedActions = new ArrayList<>();
private TextEditorState myDelayedState;
AsyncEditorLoader(@NotNull TextEditorImpl textEditor, @NotNull TextEditorComponent component, @NotNull TextEditorProvider provider) {
myProvider = provider;
myTextEditor = textEditor;
myProject = textEditor.myProject;
myEditorComponent = component;
myEditor = textEditor.getEditor();
myEditor.putUserData(ASYNC_LOADER, this);
myEditorComponent.getContentPanel().setVisible(false);
}
void scheduleBackgroundLoading(boolean firstTime) {
ReadTask task = new ReadTask() {
PsiDocumentManager pdm = PsiDocumentManager.getInstance(myProject);
long startStamp = myEditor.getDocument().getModificationStamp();
@Override
public Continuation runBackgroundProcess(@NotNull ProgressIndicator indicator) throws ProcessCanceledException {
return pdm.commitAndRunReadAction(() -> {
if (Disposer.isDisposed(myTextEditor)) return null;
Runnable applyResults = myTextEditor.loadEditorInBackground();
return new Continuation(() -> {
if (Disposer.isDisposed(myTextEditor)) return;
if (startStamp != myEditor.getDocument().getModificationStamp()) {
onCanceled(indicator);
return;
}
applyResults.run();
loadingFinished();
});
});
}
@Override
public void onCanceled(@NotNull ProgressIndicator indicator) {
scheduleBackgroundLoading(false);
}
};
if (!firstTime || !loadImmediately(task)) {
myEditorComponent.startLoading();
ProgressIndicatorUtils.scheduleWithWriteActionPriority(ourExecutor, task);
}
}
/**
* Possible alternatives:
* 1. show "Loading" from the beginning, then it'll be always noticeable at least in fade-out phase
* 2. show a gray screen for some time and then "Loading" if it's still loading; it'll produce quick background blinking for all editors
* 3. show non-highlighted and unfolded editor as "Loading" background and allow it to relayout at the end of loading phase
* 4. freeze EDT a bit and hope that for small editors it'll suffice and for big ones show "Loading" after that.
* This strategy seems to produce minimal blinking annoyance.
*/
private boolean loadImmediately(ReadTask task) {
if (PsiDocumentManager.getInstance(myProject).hasUncommitedDocuments() ||
ApplicationManager.getApplication().isWriteAccessAllowed()) {
return false; // cannot perform commitAndRunReadAction in parallel to EDT waiting
}
Semaphore semaphore = new Semaphore();
semaphore.down();
Ref<ReadTask.Continuation> continuationRef = Ref.create();
ProgressIndicatorBase indicator = new ProgressIndicatorBase();
ourExecutor.submit(() -> {
try {
ProgressIndicatorUtils.runWithWriteActionPriority(() -> continuationRef.set(task.runBackgroundProcess(indicator)),
indicator);
}
finally {
semaphore.up();
}
});
ReadTask.Continuation applyImmediately = semaphore.waitFor(200) ? continuationRef.get() : null;
if (applyImmediately != null) {
applyImmediately.getAction().run();
return true;
}
indicator.cancel();
return false;
}
private void loadingFinished() {
myLoaded = true;
myEditor.putUserData(ASYNC_LOADER, null);
myEditorComponent.stopLoading();
myEditorComponent.getContentPanel().setVisible(true);
if (myDelayedState != null) {
TextEditorState state = new TextEditorState();
state.setFoldingState(myDelayedState.getFoldingState());
myProvider.setStateImpl(myProject, myEditor, state);
myDelayedState = null;
}
for (Runnable runnable : ObjectUtils.assertNotNull(myDelayedActions)) {
myEditor.getScrollingModel().disableAnimation();
runnable.run();
}
myEditor.getScrollingModel().enableAnimation();
if (FileEditorManager.getInstance(myProject).getSelectedTextEditor() == myEditor) {
IdeFocusManager.getInstance(myProject).requestFocus(myTextEditor.getPreferredFocusedComponent(), true);
}
}
public static void performWhenLoaded(@NotNull Editor editor, @NotNull Runnable runnable) {
ApplicationManager.getApplication().assertIsDispatchThread();
AsyncEditorLoader loader = editor.getUserData(ASYNC_LOADER);
if (loader == null) {
runnable.run();
} else {
loader.myDelayedActions.add(runnable);
}
}
@NotNull
TextEditorState getEditorState(@NotNull FileEditorStateLevel level) {
ApplicationManager.getApplication().assertIsDispatchThread();
TextEditorState state = myProvider.getStateImpl(myProject, myEditor, level);
if (!myLoaded && myDelayedState != null) {
state.setDelayedFoldState(myDelayedState::getFoldingState);
}
return state;
}
void setEditorState(@NotNull final TextEditorState state) {
ApplicationManager.getApplication().assertIsDispatchThread();
if (!myLoaded) {
myDelayedState = state;
}
myProvider.setStateImpl(myProject, myEditor, state);
}
public static boolean isEditorLoaded(@NotNull Editor editor) {
return editor.getUserData(ASYNC_LOADER) == null;
}
//todo remove this with async indent inference
public static boolean isCreatingAsyncEditor() {
ApplicationManager.getApplication().assertIsDispatchThread();
return ourCreatingAsyncEditor;
}
@NotNull
static TextEditorComponent createAsyncEditor(@NotNull Computable<TextEditorComponent> computable) {
ApplicationManager.getApplication().assertIsDispatchThread();
assert !ourCreatingAsyncEditor;
ourCreatingAsyncEditor = true;
try {
return computable.compute();
}
finally {
ourCreatingAsyncEditor = false;
}
}
}
@@ -23,7 +23,6 @@ import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.EditorFactory;
import com.intellij.openapi.editor.colors.EditorColorsManager;
import com.intellij.openapi.editor.event.DocumentAdapter;
import com.intellij.openapi.editor.event.DocumentEvent;
import com.intellij.openapi.editor.ex.EditorEx;
@@ -166,8 +165,6 @@ class TextEditorComponent extends JBLoadingPanel implements DataProvider {
((EditorMarkupModel) editor.getMarkupModel()).setErrorStripeVisible(true);
((EditorEx) editor).getGutterComponentEx().setForceShowRightFreePaintersArea(true);
EditorHighlighter highlighter = EditorHighlighterFactory.getInstance().createEditorHighlighter(myFile, EditorColorsManager.getInstance().getGlobalScheme(), myProject);
((EditorEx) editor).setHighlighter(highlighter);
((EditorEx) editor).setFile(myFile);
((EditorEx)editor).setContextMenuGroupId(IdeActions.GROUP_EDITOR_POPUP);
@@ -19,6 +19,11 @@ import com.intellij.codeHighlighting.BackgroundEditorHighlighter;
import com.intellij.ide.structureView.StructureViewBuilder;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.colors.EditorColorsManager;
import com.intellij.openapi.editor.colors.EditorColorsScheme;
import com.intellij.openapi.editor.ex.EditorEx;
import com.intellij.openapi.editor.highlighter.EditorHighlighter;
import com.intellij.openapi.editor.highlighter.EditorHighlighterFactory;
import com.intellij.openapi.fileEditor.*;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.UserDataHolderBase;
@@ -37,13 +42,25 @@ public class TextEditorImpl extends UserDataHolderBase implements TextEditor {
protected final Project myProject;
private final PropertyChangeSupport myChangeSupport;
@NotNull private final TextEditorComponent myComponent;
private final TextEditorProvider myProvider;
@NotNull protected final VirtualFile myFile;
private final AsyncEditorLoader myAsyncLoader;
TextEditorImpl(@NotNull final Project project, @NotNull final VirtualFile file, final TextEditorProvider provider) {
myProject = project;
myProvider = provider;
myFile = file;
myChangeSupport = new PropertyChangeSupport(this);
myComponent = createEditorComponent(project, file);
myComponent = AsyncEditorLoader.createAsyncEditor(() -> createEditorComponent(project, file));
myAsyncLoader = new AsyncEditorLoader(this, myComponent, provider);
myAsyncLoader.scheduleBackgroundLoading(true);
}
@NotNull
protected Runnable loadEditorInBackground() {
EditorColorsScheme scheme = EditorColorsManager.getInstance().getGlobalScheme();
EditorHighlighter highlighter = EditorHighlighterFactory.getInstance().createEditorHighlighter(myFile, scheme, myProject);
EditorEx editor = (EditorEx)getEditor();
highlighter.setText(editor.getDocument().getImmutableCharSequence());
return () -> editor.setHighlighter(highlighter);
}
@NotNull
@@ -63,6 +80,7 @@ public class TextEditorImpl extends UserDataHolderBase implements TextEditor {
}
@Override
@NotNull
public JComponent getPreferredFocusedComponent(){
return getActiveEditor().getContentComponent();
}
@@ -90,12 +108,12 @@ public class TextEditorImpl extends UserDataHolderBase implements TextEditor {
@Override
@NotNull
public FileEditorState getState(@NotNull FileEditorStateLevel level) {
return myProvider.getStateImpl(myProject, getActiveEditor(), level);
return myAsyncLoader.getEditorState(level);
}
@Override
public void setState(@NotNull final FileEditorState state) {
myProvider.setStateImpl(myProject, getActiveEditor(), (TextEditorState)state);
myAsyncLoader.setEditorState((TextEditorState)state);
}
@Override
@@ -166,6 +184,6 @@ public class TextEditorImpl extends UserDataHolderBase implements TextEditor {
@Override
public String toString() {
return "Editor: "+getComponent().getFile();
return "Editor: "+myComponent.getFile();
}
}
@@ -18,10 +18,8 @@ package com.intellij.ui;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.openapi.fileEditor.FileEditor;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.fileEditor.FileEditorManagerAdapter;
import com.intellij.openapi.fileEditor.FileEditorManagerListener;
import com.intellij.openapi.fileEditor.*;
import com.intellij.openapi.fileEditor.impl.text.AsyncEditorLoader;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.util.ProgressIndicatorBase;
@@ -111,8 +109,10 @@ public class EditorNotificationsImpl extends EditorNotifications {
@Nullable
private ReadTask createTask(@NotNull final ProgressIndicator indicator, @NotNull final VirtualFile file) {
final FileEditor[] editors = FileEditorManager.getInstance(myProject).getAllEditors(file);
if (editors.length == 0) return null;
List<FileEditor> editors = ContainerUtil.filter(
FileEditorManager.getInstance(myProject).getAllEditors(file),
editor -> !(editor instanceof TextEditor) || AsyncEditorLoader.isEditorLoaded(((TextEditor) editor).getEditor()));
if (editors.isEmpty()) return null;
return new ReadTask() {
private boolean isOutdated() {