mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
add NonBlockingReadAction API for easier running of bg read actions
This commit is contained in:
@@ -7,9 +7,6 @@ import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.application.ReadAction;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.progress.ProcessCanceledException;
|
||||
import com.intellij.openapi.progress.ProgressIndicator;
|
||||
import com.intellij.openapi.progress.util.ProgressIndicatorUtils;
|
||||
import com.intellij.openapi.progress.util.ReadTask;
|
||||
import com.intellij.openapi.project.DumbService;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
@@ -83,20 +80,7 @@ public abstract class DefaultMessageHandler implements BuilderMessageHandler {
|
||||
protected abstract void handleBuildEvent(UUID sessionId, CmdlineRemoteProto.Message.BuilderMessage.BuildEvent event);
|
||||
|
||||
private void handleConstantSearchTask(final Channel channel, final UUID sessionId, final CmdlineRemoteProto.Message.BuilderMessage.ConstantSearchTask task) {
|
||||
ProgressIndicatorUtils.scheduleWithWriteActionPriority(myTaskExecutor, new ReadTask() {
|
||||
@Override
|
||||
public Continuation runBackgroundProcess(@NotNull ProgressIndicator indicator) throws ProcessCanceledException {
|
||||
return DumbService.getInstance(myProject).runReadActionInSmartMode(() -> {
|
||||
doHandleConstantSearchTask(channel, sessionId, task);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCanceled(@NotNull ProgressIndicator indicator) {
|
||||
DumbService.getInstance(myProject).runWhenSmart(() -> handleConstantSearchTask(channel, sessionId, task));
|
||||
}
|
||||
});
|
||||
ReadAction.nonBlocking(() -> doHandleConstantSearchTask(channel, sessionId, task)).inSmartMode(myProject).submit(myTaskExecutor);
|
||||
}
|
||||
|
||||
private void doHandleConstantSearchTask(Channel channel, UUID sessionId, CmdlineRemoteProto.Message.BuilderMessage.ConstantSearchTask task) {
|
||||
|
||||
@@ -25,7 +25,7 @@ public interface AppUIExecutor extends Executor {
|
||||
*/
|
||||
@NotNull
|
||||
static AppUIExecutor onUiThread(@NotNull ModalityState modality) {
|
||||
return ApplicationManager.getApplication().createUIExecutor(modality);
|
||||
return AsyncExecutionService.getService().createUIExecutor(modality);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -435,9 +435,4 @@ public interface Application extends ComponentManager {
|
||||
|
||||
boolean isEAP();
|
||||
|
||||
/** Use {@link AppUIExecutor#onUiThread} */
|
||||
@NotNull
|
||||
default AppUIExecutor createUIExecutor(@NotNull ModalityState modalityState) {
|
||||
throw new UnsupportedOperationException("createUIExecutor is not implemented in " + getClass());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.openapi.application;
|
||||
|
||||
import com.intellij.openapi.components.ServiceManager;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
/**
|
||||
* An internal service not supposed to be used directly
|
||||
*/
|
||||
public abstract class AsyncExecutionService {
|
||||
@NotNull
|
||||
protected abstract AppUIExecutor createUIExecutor(@NotNull ModalityState modalityState);
|
||||
|
||||
@NotNull
|
||||
protected abstract <T> NonBlockingReadAction<T> buildNonBlockingReadAction(@NotNull Callable<T> computation);
|
||||
|
||||
@NotNull
|
||||
static AsyncExecutionService getService() {
|
||||
return ServiceManager.getService(AsyncExecutionService.class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.openapi.application;
|
||||
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.util.concurrency.AppExecutorUtil;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.concurrency.CancellablePromise;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.function.BooleanSupplier;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* An utility for running non-blocking read actions in background thread.
|
||||
* "Interruptible" means to prevent UI freezes, when a write action is about to occur, a read action can be interrupted by a
|
||||
* {@link com.intellij.openapi.progress.ProcessCanceledException} and then restarted.
|
||||
*/
|
||||
public interface NonBlockingReadAction<T> {
|
||||
|
||||
/**
|
||||
* @return a copy of this builder that runs read actions only when index is available.
|
||||
* @see com.intellij.openapi.project.DumbService
|
||||
*/
|
||||
@Contract(pure=true)
|
||||
NonBlockingReadAction<T> inSmartMode(@NotNull Project project);
|
||||
|
||||
/**
|
||||
* @return a copy of this builder that cancels submitted read actions after they become obsolete (i.e. when the provided condition returns true). If {@code expireWhen} is called several times, any of the corresponding conditions being {@code true} is sufficient for cancelling
|
||||
* the activity. The conditions are checked inside a read action, either on background or on UI thread.
|
||||
*/
|
||||
@Contract(pure=true)
|
||||
NonBlockingReadAction<T> expireWhen(@NotNull BooleanSupplier expireCondition);
|
||||
|
||||
/**
|
||||
* @return a copy of this builder that completes submitted read actions on UI thread with the given modality state.
|
||||
* The read actions are still executed on background thread, but the callbacks on their completion
|
||||
* are invoked on UI thread, and no write action is allowed to interfere before that and possibly invalidate the result.
|
||||
*/
|
||||
@Contract(pure=true)
|
||||
NonBlockingReadAction<T> finishOnUiThread(@NotNull ModalityState modality, @NotNull Consumer<T> uiThreadAction);
|
||||
|
||||
/**
|
||||
* Submit this computation to be performed in a non-blocking read action on background thread. The returned promise
|
||||
* is completed on the same thread (in the same read action), or on UI thread if {@link #finishOnUiThread} has been called.
|
||||
* @param backgroundThreadExecutor an executor to actually run the computation. Common examples are
|
||||
* {@link AppExecutorUtil#getAppExecutorService()} or
|
||||
* {@link com.intellij.util.concurrency.BoundedTaskExecutor} on top of that.
|
||||
*/
|
||||
CancellablePromise<T> submit(@NotNull Executor backgroundThreadExecutor);
|
||||
|
||||
}
|
||||
@@ -17,8 +17,11 @@ package com.intellij.openapi.application;
|
||||
|
||||
import com.intellij.openapi.util.ThrowableComputable;
|
||||
import com.intellij.util.ThrowableRunnable;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
public abstract class ReadAction<T> extends BaseActionRunnable<T> {
|
||||
/**
|
||||
* @deprecated use {@link #run(ThrowableRunnable)} or {@link #compute(ThrowableComputable)} instead
|
||||
@@ -53,4 +56,26 @@ public abstract class ReadAction<T> extends BaseActionRunnable<T> {
|
||||
public static <T, E extends Throwable> T compute(@NotNull ThrowableComputable<T, E> action) throws E {
|
||||
return ApplicationManager.getApplication().runReadAction(action);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an {@link NonBlockingReadAction} builder to run the given Runnable in non-blocking read action on a background thread.
|
||||
*/
|
||||
@NotNull
|
||||
@Contract(pure=true)
|
||||
public static NonBlockingReadAction<Void> nonBlocking(@NotNull Runnable task) {
|
||||
return nonBlocking(() -> {
|
||||
task.run();
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an {@link NonBlockingReadAction} builder to run the given Callable in a non-blocking read action on a background thread.
|
||||
*/
|
||||
@NotNull
|
||||
@Contract(pure=true)
|
||||
public static <T> NonBlockingReadAction<T> nonBlocking(@NotNull Callable<T> task) {
|
||||
return AsyncExecutionService.getService().buildNonBlockingReadAction(task);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -18,22 +18,26 @@ package com.intellij.execution.impl;
|
||||
import com.intellij.execution.filters.Filter;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.application.ModalityState;
|
||||
import com.intellij.openapi.application.ReadAction;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.RangeMarker;
|
||||
import com.intellij.openapi.editor.impl.DocumentImpl;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.progress.util.ProgressIndicatorUtils;
|
||||
import com.intellij.util.TimeoutUtil;
|
||||
import com.intellij.util.concurrency.SequentialTaskExecutor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.concurrency.Promise;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
@@ -59,25 +63,22 @@ class AsyncFilterRunner {
|
||||
if (ApplicationManager.getApplication().isWriteAccessAllowed()) {
|
||||
runTasks();
|
||||
highlightAvailableResults();
|
||||
} else if (isQuick(ourExecutor.submit(this::runFiltersInBackground))) {
|
||||
return;
|
||||
}
|
||||
|
||||
Promise<?> promise = ReadAction
|
||||
.nonBlocking(this::runTasks)
|
||||
.finishOnUiThread(ModalityState.any(), __ -> highlightAvailableResults())
|
||||
.submit(ourExecutor);
|
||||
|
||||
if (isQuick(promise)) {
|
||||
highlightAvailableResults();
|
||||
}
|
||||
}
|
||||
|
||||
private void runFiltersInBackground() {
|
||||
while (true) {
|
||||
boolean finished = ProgressIndicatorUtils.runInReadActionWithWriteActionPriority(this::runTasks);
|
||||
if (hasResults()) {
|
||||
ApplicationManager.getApplication().invokeLater(this::highlightAvailableResults, ModalityState.any());
|
||||
}
|
||||
if (finished) return;
|
||||
ProgressIndicatorUtils.yieldToPendingWriteActions();
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isQuick(Future<?> future) {
|
||||
private static boolean isQuick(Promise<?> future) {
|
||||
try {
|
||||
future.get(5, TimeUnit.MILLISECONDS);
|
||||
future.blockingGet(5, TimeUnit.MILLISECONDS);
|
||||
return true;
|
||||
}
|
||||
catch (TimeoutException ignored) {
|
||||
|
||||
@@ -1502,9 +1502,4 @@ public class ApplicationImpl extends PlatformComponentManagerImpl implements App
|
||||
Disposer.register(disposable, () -> myDispatcher.getListeners().addAll(listeners));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public AppUIExecutor createUIExecutor(@NotNull ModalityState modalityState) {
|
||||
return new AppUIExecutorImpl(modalityState);
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.openapi.application.impl;
|
||||
|
||||
import com.intellij.openapi.application.AppUIExecutor;
|
||||
import com.intellij.openapi.application.AsyncExecutionService;
|
||||
import com.intellij.openapi.application.ModalityState;
|
||||
import com.intellij.openapi.application.NonBlockingReadAction;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
/**
|
||||
* @author peter
|
||||
*/
|
||||
public class AsyncExecutionServiceImpl extends AsyncExecutionService {
|
||||
@NotNull
|
||||
@Override
|
||||
public AppUIExecutor createUIExecutor(@NotNull ModalityState modalityState) {
|
||||
return new AppUIExecutorImpl(modalityState);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public <T> NonBlockingReadAction<T> buildNonBlockingReadAction(Callable<T> computation) {
|
||||
return new NonBlockingReadActionImpl<>(null, null, () -> false, computation);
|
||||
}
|
||||
}
|
||||
+168
@@ -0,0 +1,168 @@
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.openapi.application.impl;
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.application.NonBlockingReadAction;
|
||||
import com.intellij.openapi.application.ModalityState;
|
||||
import com.intellij.openapi.progress.EmptyProgressIndicator;
|
||||
import com.intellij.openapi.progress.ProgressIndicator;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.progress.util.ProgressIndicatorUtils;
|
||||
import com.intellij.openapi.project.DumbService;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.util.concurrency.Semaphore;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.concurrency.AsyncPromise;
|
||||
import org.jetbrains.concurrency.CancellablePromise;
|
||||
import org.jetbrains.concurrency.Promises;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.function.BooleanSupplier;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* @author peter
|
||||
*/
|
||||
class NonBlockingReadActionImpl<T> implements NonBlockingReadAction<T> {
|
||||
private final @Nullable Pair<ModalityState, Consumer<T>> myEdtFinish;
|
||||
private final @Nullable DumbService myRequireSmartMode; //todo a more pluggable constraint API
|
||||
private final BooleanSupplier myExpireCondition;
|
||||
private final Callable<T> myComputation;
|
||||
|
||||
NonBlockingReadActionImpl(@Nullable Pair<ModalityState, Consumer<T>> edtFinish,
|
||||
@Nullable DumbService requireSmartMode,
|
||||
@NotNull BooleanSupplier expireCondition,
|
||||
@NotNull Callable<T> computation) {
|
||||
myEdtFinish = edtFinish;
|
||||
myRequireSmartMode = requireSmartMode;
|
||||
myExpireCondition = expireCondition;
|
||||
myComputation = computation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NonBlockingReadAction<T> inSmartMode(@NotNull Project project) {
|
||||
return new NonBlockingReadActionImpl<>(myEdtFinish, DumbService.getInstance(project), myExpireCondition, myComputation).expireWhen(project::isDisposed);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NonBlockingReadAction<T> expireWhen(@NotNull BooleanSupplier expireCondition) {
|
||||
return new NonBlockingReadActionImpl<>(myEdtFinish, myRequireSmartMode, () -> myExpireCondition.getAsBoolean() || expireCondition.getAsBoolean(), myComputation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NonBlockingReadAction<T> finishOnUiThread(@NotNull ModalityState modality, @NotNull Consumer<T> uiThreadAction) {
|
||||
return new NonBlockingReadActionImpl<>(Pair.create(modality, uiThreadAction), myRequireSmartMode, myExpireCondition, myComputation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CancellablePromise<T> submit(@NotNull Executor backgroundThreadExecutor) {
|
||||
AsyncPromise<T> promise = new AsyncPromise<>();
|
||||
new Submission(promise, backgroundThreadExecutor).transferToBgThread();
|
||||
return promise;
|
||||
}
|
||||
|
||||
private class Submission {
|
||||
private final AsyncPromise<T> promise;
|
||||
@NotNull private final Executor backendExecutor;
|
||||
private volatile ProgressIndicator currentIndicator;
|
||||
private final ModalityState creationModality = ModalityState.defaultModalityState();
|
||||
|
||||
Submission(AsyncPromise<T> promise, @NotNull Executor backgroundThreadExecutor) {
|
||||
this.promise = promise;
|
||||
backendExecutor = backgroundThreadExecutor;
|
||||
promise.onError(__ -> {
|
||||
ProgressIndicator indicator = currentIndicator;
|
||||
if (indicator != null) {
|
||||
indicator.cancel();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void transferToBgThread() {
|
||||
backendExecutor.execute(() -> {
|
||||
try {
|
||||
ProgressIndicator indicator = new EmptyProgressIndicator(creationModality);
|
||||
currentIndicator = indicator;
|
||||
ProgressIndicatorUtils.runInReadActionWithWriteActionPriority(() -> insideReadAction(indicator), indicator);
|
||||
}
|
||||
finally {
|
||||
currentIndicator = null;
|
||||
}
|
||||
|
||||
if (Promises.isPending(promise)) {
|
||||
rescheduleLater();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void rescheduleLater() {
|
||||
if (myRequireSmartMode != null) {
|
||||
myRequireSmartMode.runWhenSmart(this::transferToBgThread);
|
||||
} else {
|
||||
ApplicationManager.getApplication().invokeLater(this::transferToBgThread, ModalityState.any());
|
||||
}
|
||||
}
|
||||
|
||||
void insideReadAction(ProgressIndicator indicator) {
|
||||
try {
|
||||
if (checkObsolete() || !constraintsAreSatisfied()) return;
|
||||
|
||||
T result = myComputation.call();
|
||||
|
||||
if (myEdtFinish != null) {
|
||||
safeTransferToEdt(result, myEdtFinish);
|
||||
} else {
|
||||
promise.setResult(result);
|
||||
}
|
||||
}
|
||||
catch (Throwable e) {
|
||||
if (!indicator.isCanceled()) {
|
||||
promise.setError(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean constraintsAreSatisfied() {
|
||||
return myRequireSmartMode == null || !myRequireSmartMode.isDumb();
|
||||
}
|
||||
|
||||
private boolean checkObsolete() {
|
||||
if (myExpireCondition.getAsBoolean()) {
|
||||
promise.cancel();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void safeTransferToEdt(T result, Pair<ModalityState, Consumer<T>> edtFinish) {
|
||||
if (Promises.isRejected(promise)) return;
|
||||
|
||||
Semaphore semaphore = new Semaphore(1);
|
||||
ApplicationManager.getApplication().invokeLater(() -> {
|
||||
if (checkObsolete()) {
|
||||
semaphore.up();
|
||||
return;
|
||||
}
|
||||
|
||||
// complete the promise now to prevent write actions inside custom callback from cancelling it
|
||||
promise.setResult(result);
|
||||
|
||||
// now background thread may release its read lock, and we continue on EDT, invoking custom callback
|
||||
semaphore.up();
|
||||
|
||||
if (Promises.isFulfilled(promise)) { // in case another thread managed to cancel it just before `setResult`
|
||||
edtFinish.second.accept(result);
|
||||
}
|
||||
}, edtFinish.first);
|
||||
|
||||
// don't release read action until we're on EDT, to avoid result invalidation in between
|
||||
while (!semaphore.waitFor(10)) {
|
||||
ProgressManager.checkCanceled();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+27
-44
@@ -16,14 +16,12 @@
|
||||
package com.intellij.openapi.fileEditor.impl.text;
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.application.ModalityState;
|
||||
import com.intellij.openapi.application.ReadAction;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
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.progress.ProcessCanceledException;
|
||||
import com.intellij.openapi.progress.ProgressIndicator;
|
||||
import com.intellij.openapi.progress.util.ProgressIndicatorUtils;
|
||||
import com.intellij.openapi.progress.util.ReadTask;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.util.concurrency.AppExecutorUtil;
|
||||
@@ -31,54 +29,39 @@ import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.TestOnly;
|
||||
import org.jetbrains.concurrency.CancellablePromise;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
/**
|
||||
* @author peter
|
||||
*/
|
||||
public class AsyncHighlighterUpdater extends ReadTask {
|
||||
public class AsyncHighlighterUpdater {
|
||||
private static final ExecutorService ourExecutor = AppExecutorUtil.createBoundedApplicationPoolExecutor("AsyncEditorLoader Pool", 2);
|
||||
private static final Map<Editor, Future<?>> ourHighlighterFutures = ContainerUtil.newConcurrentMap();
|
||||
private final Project myProject;
|
||||
private final Editor myEditor;
|
||||
private final VirtualFile myFile;
|
||||
|
||||
private AsyncHighlighterUpdater(Project project, Editor editor, VirtualFile file) {
|
||||
myProject = project;
|
||||
myEditor = editor;
|
||||
myFile = file;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Continuation performInReadAction(@NotNull ProgressIndicator indicator) throws ProcessCanceledException {
|
||||
if (!isEverythingValid()) return null;
|
||||
|
||||
EditorHighlighter highlighter = EditorHighlighterFactory.getInstance().createEditorHighlighter(myProject, myFile);
|
||||
highlighter.setText(myEditor.getDocument().getImmutableCharSequence());
|
||||
return new Continuation(() -> ((EditorEx)myEditor).setHighlighter(highlighter));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCanceled(@NotNull ProgressIndicator indicator) {
|
||||
updateHighlighters(myProject, myEditor, myFile);
|
||||
}
|
||||
|
||||
private boolean isEverythingValid() {
|
||||
return !myProject.isDisposed() && !myEditor.isDisposed() && myFile.isValid();
|
||||
}
|
||||
private static final Map<Editor, CancellablePromise<?>> ourHighlighterFutures = ContainerUtil.newConcurrentMap();
|
||||
|
||||
public static void updateHighlighters(@NotNull Project project, @NotNull Editor editor, @NotNull VirtualFile file) {
|
||||
AsyncHighlighterUpdater task = new AsyncHighlighterUpdater(project, editor, file);
|
||||
if (task.isEverythingValid()) {
|
||||
CompletableFuture<?> future = ProgressIndicatorUtils.scheduleWithWriteActionPriority(ourExecutor, task);
|
||||
Future<?> prev = ourHighlighterFutures.put(editor, future);
|
||||
if (prev != null) {
|
||||
prev.cancel(false);
|
||||
}
|
||||
future.whenComplete((a, b) -> ourHighlighterFutures.remove(editor, future));
|
||||
CancellablePromise<EditorHighlighter> promise = ReadAction
|
||||
.nonBlocking(() -> updateHighlighter(project, editor, file))
|
||||
.expireWhen(() -> !file.isValid() || editor.isDisposed() || project.isDisposed())
|
||||
.finishOnUiThread(ModalityState.any(), highlighter -> ((EditorEx)editor).setHighlighter(highlighter))
|
||||
.submit(ourExecutor);
|
||||
|
||||
CancellablePromise<?> prev = ourHighlighterFutures.put(editor, promise);
|
||||
if (prev != null) {
|
||||
prev.cancel();
|
||||
}
|
||||
promise.onProcessed(__ -> ourHighlighterFutures.remove(editor, promise));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static EditorHighlighter updateHighlighter(@NotNull Project project, @NotNull Editor editor, @NotNull VirtualFile file) {
|
||||
EditorHighlighter highlighter = EditorHighlighterFactory.getInstance().createEditorHighlighter(project, file);
|
||||
highlighter.setText(editor.getDocument().getImmutableCharSequence());
|
||||
return highlighter;
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
@@ -89,12 +72,12 @@ public class AsyncHighlighterUpdater extends ReadTask {
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
private static void waitForFuture(Future<?> future) {
|
||||
private static void waitForFuture(CancellablePromise<?> future) {
|
||||
int iteration = 0;
|
||||
while (!future.isDone() && iteration++ < 1000) {
|
||||
UIUtil.dispatchAllInvocationEvents();
|
||||
try {
|
||||
future.get(10, TimeUnit.MILLISECONDS);
|
||||
future.blockingGet(10, TimeUnit.MILLISECONDS);
|
||||
return;
|
||||
}
|
||||
catch (TimeoutException ignore) {
|
||||
|
||||
@@ -480,6 +480,9 @@
|
||||
<applicationService serviceInterface="com.intellij.ide.TypePresentationService"
|
||||
serviceImplementation="com.intellij.ide.TypePresentationServiceImpl"/>
|
||||
|
||||
<applicationService serviceInterface="com.intellij.openapi.application.AsyncExecutionService"
|
||||
serviceImplementation="com.intellij.openapi.application.impl.AsyncExecutionServiceImpl"/>
|
||||
|
||||
<preloadingActivity implementation="com.intellij.ide.ui.OptionsTopHitProvider$Activity"/>
|
||||
<postStartupActivity implementation="com.intellij.ide.ui.OptionsTopHitProvider$Activity"/>
|
||||
|
||||
|
||||
+1
-2
@@ -18,7 +18,6 @@ package org.jetbrains.plugins.gradle.integrations.maven;
|
||||
import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskId;
|
||||
import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskNotificationListenerAdapter;
|
||||
import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskType;
|
||||
import com.intellij.openapi.progress.util.ProgressIndicatorUtils;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.plugins.gradle.util.GradleConstants;
|
||||
@@ -37,7 +36,7 @@ public class GradleMavenProjectImportNotificationListener extends ExternalSystem
|
||||
&& id.getType() == ExternalSystemTaskType.RESOLVE_PROJECT) {
|
||||
final Project project = id.findProject();
|
||||
if (project == null) return;
|
||||
ProgressIndicatorUtils.scheduleWithWriteActionPriority(new ImportMavenRepositoriesTask(project));
|
||||
new ImportMavenRepositoriesTask(project).schedule();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
*/
|
||||
package org.jetbrains.plugins.gradle.integrations.maven;
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.progress.util.ProgressIndicatorUtils;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.startup.StartupActivity;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -29,7 +27,6 @@ public class GradleProjectStartupActivity implements StartupActivity {
|
||||
|
||||
@Override
|
||||
public void runActivity(@NotNull final Project project) {
|
||||
if (ApplicationManager.getApplication().isUnitTestMode()) return;
|
||||
ProgressIndicatorUtils.scheduleWithWriteActionPriority(new ImportMavenRepositoriesTask(project));
|
||||
new ImportMavenRepositoriesTask(project).schedule();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,11 +20,6 @@ import com.intellij.openapi.application.ReadAction;
|
||||
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil;
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.module.ModuleManager;
|
||||
import com.intellij.openapi.progress.ProcessCanceledException;
|
||||
import com.intellij.openapi.progress.ProgressIndicator;
|
||||
import com.intellij.openapi.progress.util.ProgressIndicatorUtils;
|
||||
import com.intellij.openapi.progress.util.ReadTask;
|
||||
import com.intellij.openapi.project.DumbService;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
@@ -33,6 +28,7 @@ import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.concurrency.AppExecutorUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -60,32 +56,24 @@ import java.util.stream.Collectors;
|
||||
* @author Vladislav.Soroka
|
||||
* @since 10/29/13
|
||||
*/
|
||||
public class ImportMavenRepositoriesTask extends ReadTask {
|
||||
class ImportMavenRepositoriesTask {
|
||||
|
||||
@NotNull
|
||||
private final MavenRemoteRepository mavenCentralRemoteRepository;
|
||||
|
||||
private final Project myProject;
|
||||
private final DumbService myDumbService;
|
||||
|
||||
public ImportMavenRepositoriesTask(Project project) {
|
||||
ImportMavenRepositoriesTask(Project project) {
|
||||
myProject = project;
|
||||
myDumbService = DumbService.getInstance(myProject);
|
||||
mavenCentralRemoteRepository = new MavenRemoteRepository("central", null, "https://repo1.maven.org/maven2/", null, null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Continuation runBackgroundProcess(@NotNull ProgressIndicator indicator) throws ProcessCanceledException {
|
||||
return myDumbService.runReadActionInSmartMode(() -> {
|
||||
performTask();
|
||||
return null;
|
||||
});
|
||||
void schedule() {
|
||||
if (ApplicationManager.getApplication().isUnitTestMode()) return;
|
||||
ReadAction.nonBlocking(this::performTask).inSmartMode(myProject).submit(AppExecutorUtil.getAppExecutorService());
|
||||
}
|
||||
|
||||
private void performTask() {
|
||||
if(myProject.isDisposed()) return;
|
||||
if (ApplicationManager.getApplication().isUnitTestMode()) return;
|
||||
|
||||
final LocalFileSystem localFileSystem = LocalFileSystem.getInstance();
|
||||
final List<PsiFile> psiFileList = ContainerUtil.newArrayList();
|
||||
|
||||
@@ -146,13 +134,6 @@ public class ImportMavenRepositoriesTask extends ReadTask {
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCanceled(@NotNull ProgressIndicator indicator) {
|
||||
if (!myProject.isDisposed()) {
|
||||
ProgressIndicatorUtils.scheduleWithWriteActionPriority(this);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static Collection<? extends GrClosableBlock> findClosableBlocks(@NotNull final PsiElement element,
|
||||
@NotNull final String... blockNames) {
|
||||
|
||||
+5
-22
@@ -19,18 +19,16 @@ import com.intellij.notification.Notification;
|
||||
import com.intellij.notification.NotificationListener;
|
||||
import com.intellij.notification.NotificationType;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.application.ReadAction;
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.module.ModuleManager;
|
||||
import com.intellij.openapi.progress.ProgressIndicator;
|
||||
import com.intellij.openapi.progress.util.ProgressIndicatorUtils;
|
||||
import com.intellij.openapi.progress.util.ReadTask;
|
||||
import com.intellij.openapi.project.DumbAware;
|
||||
import com.intellij.openapi.project.DumbService;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.startup.StartupActivity;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.VfsUtil;
|
||||
import com.intellij.util.concurrency.AppExecutorUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -44,10 +42,8 @@ public class MvcProjectWithoutLibraryNotificator implements StartupActivity, Dum
|
||||
@Override
|
||||
public void runActivity(@NotNull final Project project) {
|
||||
if (ApplicationManager.getApplication().isUnitTestMode()) return;
|
||||
ProgressIndicatorUtils.scheduleWithWriteActionPriority(new ReadTask() {
|
||||
@Override
|
||||
public void computeInReadAction(@NotNull ProgressIndicator indicator) {
|
||||
if (project.isDisposed()) return;
|
||||
|
||||
ReadAction.nonBlocking(() -> {
|
||||
final Pair<Module, MvcFramework> pair = findModuleWithoutLibrary(project);
|
||||
if (pair == null) return;
|
||||
|
||||
@@ -76,20 +72,7 @@ public class MvcProjectWithoutLibraryNotificator implements StartupActivity, Dum
|
||||
}
|
||||
}
|
||||
).notify(project);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Continuation runBackgroundProcess(@NotNull final ProgressIndicator indicator) {
|
||||
return DumbService.getInstance(project).runReadActionInSmartMode(() -> performInReadAction(indicator));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCanceled(@NotNull ProgressIndicator indicator) {
|
||||
if (!project.isDisposed()) {
|
||||
ProgressIndicatorUtils.scheduleWithWriteActionPriority(this);
|
||||
}
|
||||
}
|
||||
});
|
||||
}).inSmartMode(project).submit(AppExecutorUtil.getAppExecutorService());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
||||
Reference in New Issue
Block a user