package jetbrains.mps.samples.ActionWithProgress.plugin; /*Generated by MPS */ import jetbrains.mps.workbench.action.BaseAction; import javax.swing.Icon; import com.intellij.openapi.actionSystem.AnActionEvent; import java.util.Map; import com.intellij.openapi.project.Project; import com.intellij.openapi.actionSystem.CommonDataKeys; import jetbrains.mps.project.MPSProject; import jetbrains.mps.ide.actions.MPSCommonDataKeys; import org.jetbrains.annotations.NotNull; import com.intellij.openapi.progress.PerformInBackgroundOption; import com.intellij.openapi.progress.Task; import com.intellij.openapi.progress.ProgressIndicator; import jetbrains.mps.progress.ProgressMonitorAdapter; import org.jetbrains.mps.openapi.module.SRepository; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.progress.ProgressManager; public class BackgroundableProgressAction_Action extends BaseAction { private static final Icon ICON = null; public BackgroundableProgressAction_Action() { super("BackgroundableProgressAction", "", ICON); this.setIsAlwaysVisible(false); this.setExecuteOutsideCommand(false); } @Override public boolean isDumbAware() { return true; } @Override protected boolean collectActionData(AnActionEvent event, final Map<String, Object> _params) { if (!(super.collectActionData(event, _params))) { return false; } { Project p = event.getData(CommonDataKeys.PROJECT); if (p == null) { return false; } } { MPSProject p = event.getData(MPSCommonDataKeys.MPS_PROJECT); if (p == null) { return false; } } return true; } @Override public void doExecute(@NotNull final AnActionEvent event, final Map<String, Object> _params) { // Indicates whether the progress dialog has the'Cancel' option boolean canBeCanceled = true; // Will be sent to the background with the flag PerformInBackgroundOption.ALWAYS_BACKGROUND PerformInBackgroundOption showProgress = PerformInBackgroundOption.DEAF; // This is a backgroundable task. It can be sent to the background and canceled // The PerformInBackgroundOption flag specifies if the progress is shown to the user // or if should be in the background from the start // ALWAYS_BACKGROUND is probably the best solution for quick background tasks - // the user will not get a blinking, hardly noticable progress bar visible for only a few fractions of a second // Important thing - you need to implement the onCacel() method // Your code needs to frequently check if the process has been canceled (between every calculation steps) // and handle yourself all steps to revert the action final Task.Backgroundable backgroundable = new Task.Backgroundable(event.getData(CommonDataKeys.PROJECT), "Backgroundable cancelable task", canBeCanceled, showProgress) { @Override public void run(@NotNull final ProgressIndicator indicator) { final ProgressMonitorAdapter adapter = new ProgressMonitorAdapter(indicator); SRepository repository = event.getData(MPSCommonDataKeys.MPS_PROJECT).getRepository(); adapter.start("Progress in progress...", 4); int stepValue = 1; // a normal step adapter.step("Do simple work..."); BackgroundableProgressAction_Action.this.doWork(event); adapter.advance(stepValue); // Check if the progress is canceled after each step if (adapter.isCanceled()) { return; } // ReadAction in step is ok repository.getModelAccess().runReadAction(new Runnable() { public void run() { adapter.step("Do some work with Read Lock..."); BackgroundableProgressAction_Action.this.doWork(event); } }); adapter.advance(stepValue); if (adapter.isCanceled()) { return; } // WriteAction in step is ok repository.getModelAccess().runWriteAction(new Runnable() { public void run() { adapter.step("Do some work with Write Lock..."); BackgroundableProgressAction_Action.this.doWork(event); } }); adapter.advance(stepValue); if (adapter.isCanceled()) { return; } adapter.step("Finishing..."); BackgroundableProgressAction_Action.this.doWork(event); adapter.advance(stepValue); if (adapter.isCanceled()) { return; } adapter.done(); } @Override public void onCancel() { super.onCancel(); // Needs to handle reverting changes for all the finished steps // This method does not interrupt the steps - steps must be either short or have such interruption capability } }; // The execute() method of actions must be very quick // so every long calculation must be invoked outside of this method like this: ApplicationManager.getApplication().invokeLater(new Runnable() { public void run() { ProgressManager.getInstance().run(backgroundable); } }); } private void doWork(final AnActionEvent event) { // 42 because it is ultimate answer to everything =) BackgroundableProgressAction_Action.this.fib(44, event); } private int fib(int n, final AnActionEvent event) { // Very ineffective implementation with exponential time complexity if (n < 1) { throw new IllegalArgumentException(); } if (n == 1) { return 0; } else if (n == 2 || n == 3) { return 1; } return BackgroundableProgressAction_Action.this.fib(n - 1, event) + BackgroundableProgressAction_Action.this.fib(n - 2, event); } }