remove allowStartingDumbModeInside implementation
[idea/community.git] / platform / core-api / src / com / intellij / openapi / project / DumbService.java
1 /*
2  * Copyright 2000-2014 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.intellij.openapi.project;
17
18 import com.intellij.openapi.Disposable;
19 import com.intellij.openapi.application.ApplicationManager;
20 import com.intellij.openapi.application.ModalityState;
21 import com.intellij.openapi.components.ServiceManager;
22 import com.intellij.openapi.diagnostic.Logger;
23 import com.intellij.openapi.progress.ProgressManager;
24 import com.intellij.openapi.util.Computable;
25 import com.intellij.openapi.util.ModificationTracker;
26 import com.intellij.openapi.util.NotNullLazyKey;
27 import com.intellij.openapi.util.Ref;
28 import com.intellij.util.messages.Topic;
29 import org.jetbrains.annotations.NotNull;
30 import org.jetbrains.annotations.Nullable;
31
32 import javax.swing.*;
33 import java.util.ArrayList;
34 import java.util.Arrays;
35 import java.util.Collection;
36 import java.util.List;
37
38 /**
39  * A service managing IDEA's 'dumb' mode: when indices are updated in background and the functionality is very much limited.
40  * Only the explicitly allowed functionality is available. Usually it's allowed by implementing {@link DumbAware} interface.
41  *
42  * @author peter
43  */
44 public abstract class DumbService {
45   private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.project.DumbService");
46
47   /**
48    * @see Project#getMessageBus()
49    */
50   public static final Topic<DumbModeListener> DUMB_MODE = new Topic<DumbModeListener>("dumb mode", DumbModeListener.class);
51
52   /**
53    * The tracker is advanced each time we enter/exit from dumb mode.
54    */
55   public abstract ModificationTracker getModificationTracker();
56
57   /**
58    * @return whether IntelliJ IDEA is in dumb mode, which means that right now indices are updated in background.
59    * IDEA offers only limited functionality at such times, e.g. plain text file editing and version control operations.
60    */
61   public abstract boolean isDumb();
62
63   public static boolean isDumb(@NotNull Project project) {
64     return getInstance(project).isDumb();
65   }
66
67   /**
68    * Executes the runnable as soon as possible on AWT Event Dispatch when:
69    * <ul>
70    *   <li>project is initialized</li>
71    *   <li>and there's no dumb mode in progress</li>
72    * </ul>
73    * This may also happen immediately if these conditions are already met.<p/>
74    * Note that it's not guaranteed that the dumb mode won't start again during this runnable execution, it should manage that situation explicitly.
75    * @param runnable runnable to run
76    */
77   public abstract void runWhenSmart(@NotNull Runnable runnable);
78
79   /**
80    * Pause the current thread until dumb mode ends and then continue execution.
81    * NOTE: there are no guarantees that a new dumb mode won't begin before the next statement.
82    * Hence: use with care. Consider using {@link #runWhenSmart(Runnable)}, {@link #runReadActionInSmartMode(Runnable)} or {@link #repeatUntilPassesInSmartMode(Runnable)} instead
83    */
84   public abstract void waitForSmartMode();
85
86   /**
87    * Pause the current thread until dumb mode ends, and then run the read action. Index is guaranteed to be available inside that read action,
88    * unless this method is already called with read access allowed.
89    */
90   public <T> T runReadActionInSmartMode(@NotNull final Computable<T> r) {
91     final Ref<T> result = new Ref<T>();
92     runReadActionInSmartMode(new Runnable() {
93       @Override
94       public void run() {
95         result.set(r.compute());
96       }
97     });
98     return result.get();
99   }
100
101   @Nullable
102   public <T> T tryRunReadActionInSmartMode(@NotNull Computable<T> task, @Nullable String notification) {
103     if (ApplicationManager.getApplication().isReadAccessAllowed()) {
104       try {
105         return task.compute();
106       }
107       catch (IndexNotReadyException e) {
108         if (notification != null) {
109           showDumbModeNotification(notification);
110         }
111         return null;
112       }
113     }
114     else {
115       return runReadActionInSmartMode(task);
116     }
117   }
118
119   /**
120    * Pause the current thread until dumb mode ends, and then run the read action. Index is guaranteed to be available inside that read action,
121    * unless this method is already called with read access allowed.
122    */
123   public void runReadActionInSmartMode(@NotNull final Runnable r) {
124     if (ApplicationManager.getApplication().isReadAccessAllowed()) {
125       r.run();
126       return;
127     }
128
129     while (true) {
130       waitForSmartMode();
131       boolean success = ApplicationManager.getApplication().runReadAction(new Computable<Boolean>() {
132         @Override
133         public Boolean compute() {
134           if (isDumb()) {
135             return false;
136           }
137           r.run();
138           return true;
139         }
140       });
141       if (success) break;
142     }
143   }
144
145   /**
146    * 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,
147    * try again until the runnable is able to complete successfully.
148    * 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
149    * use a single long read action in order to keep the IDE responsive.
150    * 
151    * @see #runReadActionInSmartMode(Runnable) 
152    */
153   public void repeatUntilPassesInSmartMode(@NotNull final Runnable r) {
154     while (true) {
155       waitForSmartMode();
156       try {
157         r.run();
158         return;
159       }
160       catch (IndexNotReadyException e) {
161         LOG.info(e);
162       }
163     }
164   }
165
166   /**
167    * Invoke the runnable later on EventDispatchThread AND when IDEA isn't in dumb mode
168    * @param runnable runnable
169    */
170   public abstract void smartInvokeLater(@NotNull Runnable runnable);
171
172   public abstract void smartInvokeLater(@NotNull Runnable runnable, @NotNull ModalityState modalityState);
173
174   private static final NotNullLazyKey<DumbService, Project> INSTANCE_KEY = ServiceManager.createLazyKey(DumbService.class);
175
176   public static DumbService getInstance(@NotNull Project project) {
177     return INSTANCE_KEY.getValue(project);
178   }
179
180   /**
181    * @return all the elements of the given array if there's no dumb mode currently, or the dumb-aware ones if {@link #isDumb()} is true.
182    * @see #isDumbAware(Object) 
183    */
184   @NotNull
185   public <T> List<T> filterByDumbAwareness(@NotNull T[] array) {
186     return filterByDumbAwareness(Arrays.asList(array));
187   }
188
189   /**
190    * @return all the elements of the given collection if there's no dumb mode currently, or the dumb-aware ones if {@link #isDumb()} is true. 
191    * @see #isDumbAware(Object)
192    */
193   @NotNull
194   public <T> List<T> filterByDumbAwareness(@NotNull Collection<T> collection) {
195     if (isDumb()) {
196       final ArrayList<T> result = new ArrayList<T>(collection.size());
197       for (T element : collection) {
198         if (isDumbAware(element)) {
199           result.add(element);
200         }
201       }
202       return result;
203     }
204
205     if (collection instanceof List) {
206       return (List<T>)collection;
207     }
208
209     return new ArrayList<T>(collection);
210   }
211
212   /**
213    * Queues a task to be executed in "dumb mode", where access to indices is forbidden. Tasks are executed sequentially
214    * in background unless {@link #completeJustSubmittedTasks()} is called in the same dispatch thread activity.<p/>
215    *
216    * Tasks can specify custom "equality" policy via their constructor. Calling this method has no effect if an "equal" task is already enqueued (but not yet running).
217    */
218   public abstract void queueTask(@NotNull DumbModeTask task);
219
220   /**
221    * Cancels the given task. If it's in the queue, it won't be executed. If it's already running, its {@link com.intellij.openapi.progress.ProgressIndicator} is canceled, so the next {@link ProgressManager#checkCanceled()} call
222    * will throw {@link com.intellij.openapi.progress.ProcessCanceledException}.
223    */
224   public abstract void cancelTask(@NotNull DumbModeTask task);
225
226   /**
227    * Runs the "just submitted" tasks under a modal dialog. "Just submitted" means that tasks were queued for execution
228    * earlier within the same Swing event dispatch thread event processing, and there were no other tasks already running at that moment. Otherwise this method does nothing.<p/>
229    *
230    * This functionality can be useful in refactorings (invoked in "smart mode"), when after VFS or root changes
231    * (which could start "dumb mode") some reference resolve is required (which again requires "smart mode").
232    */
233   public abstract void completeJustSubmittedTasks();
234
235   public abstract JComponent wrapGently(@NotNull JComponent dumbUnawareContent, @NotNull Disposable parentDisposable);
236
237   public void makeDumbAware(@NotNull final JComponent component, @NotNull Disposable disposable) {
238     component.setEnabled(!isDumb());
239     getProject().getMessageBus().connect(disposable).subscribe(DUMB_MODE, new DumbModeListener() {
240       @Override
241       public void enteredDumbMode() {
242         component.setEnabled(false);
243       }
244
245       @Override
246       public void exitDumbMode() {
247         component.setEnabled(true);
248       }
249     });
250   }
251
252   public abstract void showDumbModeNotification(@NotNull String message);
253
254   public abstract Project getProject();
255
256   public static boolean isDumbAware(Object o) {
257     if (o instanceof PossiblyDumbAware) {
258       return ((PossiblyDumbAware)o).isDumbAware();
259     }
260     return o instanceof DumbAware;
261   }
262
263   /**
264    * Enables or disables alternative resolve strategies for the current thread.<p/> 
265    * 
266    * Normally reference resolution uses index, and hence is not available in dumb mode. In some cases, alternative ways
267    * of performing resolve are available, although much slower. It's impractical to always use these ways because it'll
268    * lead to overloaded CPU (especially given there's also indexing in progress). But for some explicit user actions
269    * (e.g. explicit Goto Declaration) turning these slower methods is beneficial.<p/>
270    *
271    * NOTE: even with alternative resolution enabled, methods like resolve(), findClass() etc may still throw
272    * {@link IndexNotReadyException}. So alternative resolve is not a panacea, it might help provide navigation in some cases
273    * but not in all.<p/>
274    * 
275    * A typical usage would involve try-finally, where the alternative resolution is first enabled, then an action is performed,
276    * and then alternative resolution is turned off in the finally block.
277    */
278   public abstract void setAlternativeResolveEnabled(boolean enabled);
279
280   /**
281    * Invokes the given runnable with alternative resolve set to true.
282    * @see #setAlternativeResolveEnabled(boolean) 
283    */
284   public void withAlternativeResolveEnabled(@NotNull Runnable runnable) {
285     setAlternativeResolveEnabled(true);
286     try {
287       runnable.run();
288     }
289     finally {
290       setAlternativeResolveEnabled(false);
291     }
292   }
293
294   /**
295    * @return whether alternative resolution is enabled for the current thread.
296    * 
297    * @see #setAlternativeResolveEnabled(boolean) 
298    */
299   public abstract boolean isAlternativeResolveEnabled();
300
301   /**
302    * Obsolete, does nothing, just executes the passed runnable.
303    * @see #completeJustSubmittedTasks()
304    */
305   @SuppressWarnings({"deprecation", "unused"})
306   @Deprecated
307   public static void allowStartingDumbModeInside(@NotNull DumbModePermission permission, @NotNull Runnable runnable) {
308     runnable.run();
309   }
310
311   /**
312    * @see #DUMB_MODE
313    */
314   public interface DumbModeListener {
315
316     /**
317      * The event arrives on EDT
318      */
319     void enteredDumbMode();
320
321     /**
322      * The event arrives on EDT
323      */
324     void exitDumbMode();
325
326   }
327
328 }