| /* |
| * Copyright 2000-2014 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.project; |
| |
| import com.intellij.openapi.Disposable; |
| import com.intellij.openapi.application.ApplicationManager; |
| import com.intellij.openapi.application.ModalityState; |
| import com.intellij.openapi.components.ServiceManager; |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.openapi.util.Computable; |
| import com.intellij.openapi.util.NotNullLazyKey; |
| import com.intellij.openapi.util.Ref; |
| import com.intellij.util.messages.Topic; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| import javax.swing.*; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.List; |
| |
| /** |
| * A service managing IDEA's 'dumb' mode: when indices are updated in background and the functionality is very much limited. |
| * Only the explicitly allowed functionality is available. Usually it's allowed by implementing {@link com.intellij.openapi.project.DumbAware} interface. |
| * |
| * If you want to register a toolwindow, which will be enabled during the dumb mode, please use {@link com.intellij.openapi.wm.ToolWindowManager}'s |
| * registration methods which have 'canWorkInDumMode' parameter. |
| * |
| * @author peter |
| */ |
| public abstract class DumbService { |
| private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.project.DumbService"); |
| |
| /** |
| * @see com.intellij.openapi.project.Project#getMessageBus() |
| */ |
| public static final Topic<DumbModeListener> DUMB_MODE = new Topic<DumbModeListener>("dumb mode", DumbModeListener.class); |
| |
| /** |
| * @return whether IntelliJ IDEA is in dumb mode, which means that right now indices are updated in background. |
| * IDEA offers only limited functionality at such times, e.g. plain text file editing and version control operations. |
| */ |
| public abstract boolean isDumb(); |
| |
| public static boolean isDumb(@NotNull Project project) { |
| return getInstance(project).isDumb(); |
| } |
| |
| /** |
| * Executes the runnable immediately if not in dumb mode, or on AWT Event Dispatch thread when the dumb mode ends. |
| * @param runnable runnable to run |
| */ |
| public abstract void runWhenSmart(@NotNull Runnable runnable); |
| |
| /** |
| * Pause the current thread until dumb mode ends and then continue execution. |
| * NOTE: there are no guarantees that a new dumb mode won't begin before the next statement. |
| * Hence: use with care. Consider using {@link #runWhenSmart(Runnable)}, {@link #runReadActionInSmartMode(Runnable)} or {@link #repeatUntilPassesInSmartMode(Runnable)} instead |
| */ |
| public abstract void waitForSmartMode(); |
| |
| /** |
| * Pause the current thread until dumb mode ends, and then run the read action. Index is guaranteed to be available inside that read action. |
| */ |
| public <T> T runReadActionInSmartMode(@NotNull final Computable<T> r) { |
| final Ref<T> result = new Ref<T>(); |
| runReadActionInSmartMode(new Runnable() { |
| @Override |
| public void run() { |
| result.set(r.compute()); |
| } |
| }); |
| return result.get(); |
| } |
| |
| @Nullable |
| public <T> T tryRunReadActionInSmartMode(@NotNull Computable<T> task, @Nullable String notification) { |
| if (ApplicationManager.getApplication().isReadAccessAllowed()) { |
| try { |
| return task.compute(); |
| } |
| catch (IndexNotReadyException e) { |
| if (notification != null) { |
| showDumbModeNotification(notification); |
| } |
| return null; |
| } |
| } |
| else { |
| return runReadActionInSmartMode(task); |
| } |
| } |
| |
| /** |
| * Pause the current thread until dumb mode ends, and then run the read action. Index is guaranteed to be available inside that read action. |
| */ |
| public void runReadActionInSmartMode(@NotNull final Runnable r) { |
| while (true) { |
| waitForSmartMode(); |
| boolean success = ApplicationManager.getApplication().runReadAction(new Computable<Boolean>() { |
| @Override |
| public Boolean compute() { |
| if (isDumb()) { |
| return false; |
| } |
| r.run(); |
| return true; |
| } |
| }); |
| if (success) break; |
| } |
| } |
| |
| /** |
| * Pause the current thread until dumb mode ends, and then attempt to execute the runnable. If it fails due to another dumb mode having started, |
| * try again until the runnable is able to complete successfully. |
| * It makes sense to use this method when you have a long-running activity consisting of many small read actions, and you don't want to |
| * use a single long read action in order to keep the IDE responsive. |
| * |
| * @see #runReadActionInSmartMode(Runnable) |
| */ |
| public void repeatUntilPassesInSmartMode(@NotNull final Runnable r) { |
| while (true) { |
| waitForSmartMode(); |
| try { |
| r.run(); |
| return; |
| } |
| catch (IndexNotReadyException e) { |
| LOG.info(e); |
| } |
| } |
| } |
| |
| /** |
| * Invoke the runnable later on EventDispatchThread AND when IDEA isn't in dumb mode |
| * @param runnable runnable |
| */ |
| public abstract void smartInvokeLater(@NotNull Runnable runnable); |
| |
| public abstract void smartInvokeLater(@NotNull Runnable runnable, @NotNull ModalityState modalityState); |
| |
| private static final NotNullLazyKey<DumbService, Project> INSTANCE_KEY = ServiceManager.createLazyKey(DumbService.class); |
| |
| public static DumbService getInstance(@NotNull Project project) { |
| return INSTANCE_KEY.getValue(project); |
| } |
| |
| @NotNull |
| public <T> List<T> filterByDumbAwareness(@NotNull Collection<T> collection) { |
| if (isDumb()) { |
| final ArrayList<T> result = new ArrayList<T>(collection.size()); |
| for (T element : collection) { |
| if (isDumbAware(element)) { |
| result.add(element); |
| } |
| } |
| return result; |
| } |
| |
| if (collection instanceof List) { |
| return (List<T>)collection; |
| } |
| |
| return new ArrayList<T>(collection); |
| } |
| |
| public abstract void queueTask(@NotNull DumbModeTask task); |
| |
| public abstract void cancelTask(@NotNull DumbModeTask task); |
| |
| public abstract JComponent wrapGently(@NotNull JComponent dumbUnawareContent, @NotNull Disposable parentDisposable); |
| |
| public void makeDumbAware(@NotNull final JComponent component, @NotNull Disposable disposable) { |
| component.setEnabled(!isDumb()); |
| getProject().getMessageBus().connect(disposable).subscribe(DUMB_MODE, new DumbModeListener() { |
| @Override |
| public void enteredDumbMode() { |
| component.setEnabled(false); |
| } |
| |
| @Override |
| public void exitDumbMode() { |
| component.setEnabled(true); |
| } |
| }); |
| } |
| |
| public abstract void showDumbModeNotification(@NotNull String message); |
| |
| public abstract Project getProject(); |
| |
| public static boolean isDumbAware(Object o) { |
| if (o instanceof PossiblyDumbAware) { |
| return ((PossiblyDumbAware)o).isDumbAware(); |
| } |
| return o instanceof DumbAware; |
| } |
| |
| /** |
| * @see #DUMB_MODE |
| */ |
| public interface DumbModeListener { |
| |
| /** |
| * The event arrives on EDT |
| */ |
| void enteredDumbMode(); |
| |
| /** |
| * The event arrives on EDT |
| */ |
| void exitDumbMode(); |
| |
| } |
| } |