preload actions in parallel with application loading for the menus to open faster the first time; add PreloadingActivity extension API for other similar clients

This commit is contained in:
peter
2015-11-23 19:55:01 +01:00
parent c9df424194
commit 283775864e
11 changed files with 228 additions and 114 deletions
@@ -18,7 +18,6 @@ package com.intellij.codeInsight.generation.actions;
import com.intellij.codeInsight.CodeInsightBundle;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.actionSystem.impl.ActionManagerImpl;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.DumbAwareAction;
import com.intellij.openapi.project.DumbService;
@@ -29,7 +28,7 @@ import com.intellij.util.ObjectUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class GenerateAction extends DumbAwareAction implements PreloadableAction {
public class GenerateAction extends DumbAwareAction {
@Override
public void actionPerformed(final AnActionEvent e) {
DataContext dataContext = e.getDataContext();
@@ -94,11 +93,6 @@ public class GenerateAction extends DumbAwareAction implements PreloadableAction
return copy;
}
@Override
public void preload() {
((ActionManagerImpl) ActionManager.getInstance()).preloadActionGroup(IdeActions.GROUP_GENERATE);
}
private static class GenerateWrappingGroup extends ActionGroup {
private final AnAction myAction;
@@ -0,0 +1,52 @@
/*
* Copyright 2000-2015 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.ide.ui.search;
import com.intellij.openapi.application.PreloadingActivity;
import com.intellij.openapi.progress.ProgressIndicator;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Set;
/**
* @author peter
*/
public class SearchableOptionPreloader extends PreloadingActivity {
@Override
public void preload(@NotNull ProgressIndicator indicator) {
final SearchableOptionsRegistrar registrar = SearchableOptionsRegistrar.getInstance();
final SearchableOptionProcessor processor = new SearchableOptionProcessor() {
@Override
public void addOptions(@NotNull String text,
@Nullable String path,
@Nullable String hit,
@NotNull String configurableId,
@Nullable String configurableDisplayName,
boolean applyStemming) {
Set<String> words = applyStemming ? registrar.getProcessedWords(text) : registrar.getProcessedWordsWithoutStemming(text);
for (String word : words) {
registrar.addOption(word, path, hit, configurableId, configurableDisplayName);
}
}
};
for (SearchableOptionContributor contributor : SearchableOptionContributor.EP_NAME.getExtensions()) {
indicator.checkCanceled();
contributor.processOptions(processor);
}
}
}
@@ -93,36 +93,6 @@ public class SearchableOptionsRegistrarImpl extends SearchableOptionsRegistrar {
catch (IOException e) {
LOG.error(e);
}
loadExtensions();
}
private void loadExtensions() {
final SearchableOptionProcessor processor = new SearchableOptionProcessor() {
@Override
public void addOptions(@NotNull String text,
@Nullable String path,
@Nullable String hit,
@NotNull String configurableId,
@Nullable String configurableDisplayName,
boolean applyStemming) {
Set<String> words = applyStemming ? getProcessedWords(text) : getProcessedWordsWithoutStemming(text);
for (String word : words) {
addOption(word, path, hit, configurableId, configurableDisplayName);
}
}
};
ApplicationManager.getApplication().executeOnPooledThread(new Runnable() {
@Override
public void run() {
//todo what if application is disposed?
for (SearchableOptionContributor contributor : SearchableOptionContributor.EP_NAME.getExtensions()) {
contributor.processOptions(processor);
}
}
});
}
private void loadHugeFilesIfNecessary() {
@@ -23,14 +23,16 @@ import com.intellij.ide.DataManager;
import com.intellij.ide.plugins.IdeaPluginDescriptor;
import com.intellij.ide.plugins.PluginManager;
import com.intellij.ide.plugins.PluginManagerCore;
import com.intellij.ide.ui.search.SearchableOptionsRegistrar;
import com.intellij.idea.IdeaLogger;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.actionSystem.ex.ActionManagerEx;
import com.intellij.openapi.actionSystem.ex.ActionUtil;
import com.intellij.openapi.actionSystem.ex.AnActionListener;
import com.intellij.openapi.application.*;
import com.intellij.openapi.application.Application;
import com.intellij.openapi.application.ApplicationActivationListener;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.application.ex.ApplicationManagerEx;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.extensions.PluginId;
@@ -39,9 +41,9 @@ import com.intellij.openapi.keymap.KeymapManager;
import com.intellij.openapi.keymap.KeymapUtil;
import com.intellij.openapi.keymap.ex.KeymapManagerEx;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.project.ProjectType;
import com.intellij.openapi.util.ActionCallback;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.IconLoader;
import com.intellij.openapi.util.registry.Registry;
@@ -70,7 +72,6 @@ import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.util.List;
import java.util.concurrent.Future;
public final class ActionManagerImpl extends ActionManagerEx implements Disposable {
@NonNls public static final String ACTION_ELEMENT_NAME = "action";
@@ -133,7 +134,6 @@ public final class ActionManagerImpl extends ActionManagerEx implements Disposab
private String myLastPreformedActionId;
private String myPrevPerformedActionId;
private long myLastTimeEditorWasTypedIn = 0;
private Runnable myPreloadActionsRunnable;
private boolean myTransparentOnlyUpdate;
private int myActionsPreloaded = 0;
@@ -1255,76 +1255,27 @@ public final class ActionManagerImpl extends ActionManagerEx implements Disposab
}
}
public Future<?> preloadActions() {
if (myPreloadActionsRunnable == null) {
myPreloadActionsRunnable = new Runnable() {
@Override
public void run() {
try {
SearchableOptionsRegistrar.getInstance(); // load inspection descriptions etc. to be used in Goto Action, Search Everywhere
doPreloadActions();
} catch (RuntimeInterruptedException ignore) {
}
}
};
return ApplicationManager.getApplication().executeOnPooledThread(myPreloadActionsRunnable);
}
return null;
}
private void doPreloadActions() {
pausePreloading(5000); // wait for project initialization to complete
preloadActionGroup(IdeActions.GROUP_EDITOR_POPUP);
preloadActionGroup(IdeActions.GROUP_EDITOR_TAB_POPUP);
preloadActionGroup(IdeActions.GROUP_PROJECT_VIEW_POPUP);
preloadActionGroup(IdeActions.GROUP_MAIN_MENU);
preloadActionGroup(IdeActions.GROUP_NEW);
// TODO anything else?
LOG.debug("Actions preloading completed");
}
public void preloadActionGroup(final String groupId) {
final AnAction action = getAction(groupId);
if (action instanceof ActionGroup) {
preloadActionGroup((ActionGroup) action);
}
}
private void preloadActionGroup(final ActionGroup group) {
public void preloadActions(ProgressIndicator indicator) {
final Application application = ApplicationManager.getApplication();
final AnAction[] children = application.runReadAction(new Computable<AnAction[]>() {
@Override
public AnAction[] compute() {
if (application.isDisposed()) {
return AnAction.EMPTY_ARRAY;
}
return group.getChildren(null);
}
});
for (AnAction action : children) {
for (String id : getActionIds()) {
indicator.checkCanceled();
if (application.isDisposed()) return;
final AnAction action = getAction(id);
if (action instanceof PreloadableAction) {
((PreloadableAction)action).preload();
}
else if (action instanceof ActionGroup) {
preloadActionGroup((ActionGroup)action);
application.runReadAction(new Runnable() {
@Override
public void run() {
if (!application.isDisposed()) {
((ActionGroup)action).getChildren(null);
}
}
});
}
myActionsPreloaded++;
if (myActionsPreloaded % 10 == 0) {
pausePreloading(300);
}
}
}
private static void pausePreloading(int millis) {
if (ApplicationManager.getApplication().isUnitTestMode()) return;
try {
Thread.sleep(millis);
}
catch (InterruptedException ignored) {
throw new RuntimeInterruptedException(ignored);
}
}
@@ -17,20 +17,18 @@ package com.intellij.openapi.actionSystem.impl;
import com.intellij.openapi.actionSystem.ActionManager;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.project.DumbAware;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.startup.StartupActivity;
import com.intellij.openapi.application.PreloadingActivity;
import com.intellij.openapi.progress.ProgressIndicator;
import org.jetbrains.annotations.NotNull;
/**
* @author yole
*/
public class ActionPreloader implements StartupActivity, DumbAware {
public class ActionPreloader extends PreloadingActivity {
@Override
public void runActivity(@NotNull Project project) {
public void preload(@NotNull ProgressIndicator indicator) {
if (!ApplicationManager.getApplication().isUnitTestMode() && !ApplicationManager.getApplication().isHeadlessEnvironment()) {
((ActionManagerImpl)ActionManager.getInstance()).preloadActions();
((ActionManagerImpl)ActionManager.getInstance()).preloadActions(indicator);
}
}
}
@@ -0,0 +1,100 @@
/*
* Copyright 2000-2015 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.application;
import com.intellij.openapi.components.ApplicationComponent;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.progress.util.AbstractProgressIndicatorBase;
import com.intellij.openapi.progress.util.ProgressIndicatorBase;
import com.intellij.util.TimeoutUtil;
import com.intellij.util.concurrency.SequentialTaskExecutor;
import com.intellij.util.io.storage.HeavyProcessLatch;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.ide.PooledThreadExecutor;
import java.util.concurrent.Executor;
/**
* @author peter
*/
public class Preloader implements ApplicationComponent {
private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.application.Preloader");
private final Executor myExecutor = new SequentialTaskExecutor(PooledThreadExecutor.INSTANCE);
private final ProgressIndicator myIndicator = new ProgressIndicatorBase();
private final ProgressIndicator myWrappingIndicator = new AbstractProgressIndicatorBase() {
@Override
public void checkCanceled() {
checkHeavyProcessRunning();
myIndicator.checkCanceled();
}
@Override
public boolean isCanceled() {
return myIndicator.isCanceled();
}
};
private static void checkHeavyProcessRunning() {
if (HeavyProcessLatch.INSTANCE.isRunning()) {
TimeoutUtil.sleep(1);
}
}
@Override
public void initComponent() {
if (ApplicationManager.getApplication().isUnitTestMode() || ApplicationManager.getApplication().isHeadlessEnvironment()) {
return;
}
for (final PreloadingActivity activity : PreloadingActivity.EP_NAME.getExtensions()) {
myExecutor.execute(new Runnable() {
@Override
public void run() {
if (myIndicator.isCanceled()) return;
checkHeavyProcessRunning();
if (myIndicator.isCanceled()) return;
ProgressManager.getInstance().runProcess(new Runnable() {
@Override
public void run() {
try {
activity.preload(myWrappingIndicator);
}
catch (ProcessCanceledException ignore) {
}
LOG.info("Finished preloading " + activity);
}
}, myIndicator);
}
});
}
}
@Override
public void disposeComponent() {
myIndicator.cancel();
}
@NotNull
@Override
public String getComponentName() {
return "Preloader";
}
}
@@ -0,0 +1,41 @@
/*
* Copyright 2000-2015 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.application;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.openapi.progress.ProgressIndicator;
import org.jetbrains.annotations.NotNull;
/**
* An activity to be executed in background on IDE startup. It may load some classes or other configuration
* so that when something (e.g. an action) is invoked the first time in the UI, there's no visible pause
* while required stuff is being lazily loaded.
*
* @author peter
* @since 144
*/
public abstract class PreloadingActivity {
public static final ExtensionPointName<PreloadingActivity> EP_NAME = ExtensionPointName.create("com.intellij.preloadingActivity");
/**
* Perform the preloading
* @param indicator a progress indicator for the background preloading process.
* Canceled if the application has exited.
* Long actions should periodically perform <code>indicator.checkCanceled()</code>.
*/
public abstract void preload(@NotNull ProgressIndicator indicator);
}
@@ -23,6 +23,9 @@
<extensionPoint name="codeInsight.containerProvider"
interface="com.intellij.codeInsight.ContainerProvider"/>
<extensionPoint name="preloadingActivity"
interface="com.intellij.openapi.application.PreloadingActivity"/>
<extensionPoint name="projectConfigurable"
beanClass="com.intellij.openapi.options.ConfigurableEP"
area="IDEA_PROJECT">
@@ -369,9 +369,11 @@
<postStartupActivity implementation="com.intellij.ide.TipOfTheDayManager"/>
<postStartupActivity implementation="com.intellij.ide.actions.PowerSaveModeNotifier"/>
<postStartupActivity implementation="com.intellij.openapi.actionSystem.impl.ActionPreloader"/>
<postStartupActivity id="OpenFilesActivity" implementation="com.intellij.openapi.fileEditor.impl.OpenFilesActivity"/>
<preloadingActivity implementation="com.intellij.openapi.actionSystem.impl.ActionPreloader"/>
<preloadingActivity implementation="com.intellij.ide.ui.search.SearchableOptionPreloader"/>
<writingAccessProvider implementation="com.intellij.openapi.fileEditor.impl.NonProjectFileWritingAccessProvider" order="first"/>
<patch.extension implementation="com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP"/>
@@ -73,6 +73,9 @@
<component>
<implementation-class>com.intellij.diagnostic.PerformanceWatcher</implementation-class>
</component>
<component>
<implementation-class>com.intellij.openapi.application.Preloader</implementation-class>
</component>
<component>
<interface-class>com.intellij.notification.NotificationsConfiguration</interface-class>
@@ -101297,7 +101297,7 @@ preliminary
prelims
preliterate
preliterates
preloaded
preload
prelude
prelude's
preludes