From 1c70e25d2e82e231df3ab5326738f3bd272b7e06 Mon Sep 17 00:00:00 2001 From: peter Date: Tue, 24 May 2016 17:09:15 +0200 Subject: [PATCH] IDEA-154272 Editor opening should do heavy operations in background --- .../find/actions/ShowUsagesAction.java | 22 +- .../impl/text/PsiAwareTextEditorImpl.java | 31 +++ .../impl/text/PsiAwareTextEditorProvider.java | 41 +--- .../editor/impl/ScrollingModelImpl.java | 7 +- .../openapi/editor/impl/SettingsImpl.java | 5 +- .../impl/FileEditorManagerImpl.java | 6 +- .../impl/text/AsyncEditorLoader.java | 220 ++++++++++++++++++ .../impl/text/TextEditorComponent.java | 3 - .../fileEditor/impl/text/TextEditorImpl.java | 30 ++- .../intellij/ui/EditorNotificationsImpl.java | 12 +- 10 files changed, 310 insertions(+), 67 deletions(-) create mode 100644 platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/text/AsyncEditorLoader.java diff --git a/platform/lang-impl/src/com/intellij/find/actions/ShowUsagesAction.java b/platform/lang-impl/src/com/intellij/find/actions/ShowUsagesAction.java index bcc6e0147a99..9c60d9e1fa99 100644 --- a/platform/lang-impl/src/com/intellij/find/actions/ShowUsagesAction.java +++ b/platform/lang-impl/src/com/intellij/find/actions/ShowUsagesAction.java @@ -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); } } diff --git a/platform/lang-impl/src/com/intellij/openapi/fileEditor/impl/text/PsiAwareTextEditorImpl.java b/platform/lang-impl/src/com/intellij/openapi/fileEditor/impl/text/PsiAwareTextEditorImpl.java index 7c4ba49563f6..557e630d0a76 100644 --- a/platform/lang-impl/src/com/intellij/openapi/fileEditor/impl/text/PsiAwareTextEditorImpl.java +++ b/platform/lang-impl/src/com/intellij/openapi/fileEditor/impl/text/PsiAwareTextEditorImpl.java @@ -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()); } diff --git a/platform/lang-impl/src/com/intellij/openapi/fileEditor/impl/text/PsiAwareTextEditorProvider.java b/platform/lang-impl/src/com/intellij/openapi/fileEditor/impl/text/PsiAwareTextEditorProvider.java index a61fe9ae91fc..20d94b533c3d 100644 --- a/platform/lang-impl/src/com/intellij/openapi/fileEditor/impl/text/PsiAwareTextEditorProvider.java +++ b/platform/lang-impl/src/com/intellij/openapi/fileEditor/impl/text/PsiAwareTextEditorProvider.java @@ -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"); diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/ScrollingModelImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/ScrollingModelImpl.java index 085ac2d9b44f..2c49a3e32fb6 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/ScrollingModelImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/ScrollingModelImpl.java @@ -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() { diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/SettingsImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/SettingsImpl.java index 258bdd1ba0cc..766a37309087 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/SettingsImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/SettingsImpl.java @@ -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 { diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileEditorManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileEditorManagerImpl.java index 4f09ef9a710a..ab726e1081f4 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileEditorManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileEditorManagerImpl.java @@ -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(), diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/text/AsyncEditorLoader.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/text/AsyncEditorLoader.java new file mode 100644 index 000000000000..64d51d6c1cbd --- /dev/null +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/text/AsyncEditorLoader.java @@ -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 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 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 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 computable) { + ApplicationManager.getApplication().assertIsDispatchThread(); + assert !ourCreatingAsyncEditor; + ourCreatingAsyncEditor = true; + try { + return computable.compute(); + } + finally { + ourCreatingAsyncEditor = false; + } + } + +} \ No newline at end of file diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/text/TextEditorComponent.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/text/TextEditorComponent.java index d298268bc901..8293362aa8b0 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/text/TextEditorComponent.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/text/TextEditorComponent.java @@ -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); diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/text/TextEditorImpl.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/text/TextEditorImpl.java index 3da9961a8ca8..ecbdf438bf1d 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/text/TextEditorImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/text/TextEditorImpl.java @@ -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(); } } diff --git a/platform/platform-impl/src/com/intellij/ui/EditorNotificationsImpl.java b/platform/platform-impl/src/com/intellij/ui/EditorNotificationsImpl.java index 2fbb979e301d..6c0781e88792 100644 --- a/platform/platform-impl/src/com/intellij/ui/EditorNotificationsImpl.java +++ b/platform/platform-impl/src/com/intellij/ui/EditorNotificationsImpl.java @@ -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 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() {