diff --git a/bundles/org.eclipse.ui.ide/extensions/org/eclipse/ui/actions/RefreshAction.java b/bundles/org.eclipse.ui.ide/extensions/org/eclipse/ui/actions/RefreshAction.java index 94707e8c36d2..5981e6fb6fab 100644 --- a/bundles/org.eclipse.ui.ide/extensions/org/eclipse/ui/actions/RefreshAction.java +++ b/bundles/org.eclipse.ui.ide/extensions/org/eclipse/ui/actions/RefreshAction.java @@ -18,14 +18,14 @@ import java.lang.reflect.InvocationTargetException; import java.net.URI; import java.util.ArrayList; -import java.util.Iterator; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import org.eclipse.core.filesystem.IFileInfo; import org.eclipse.core.filesystem.IFileStore; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; -import org.eclipse.core.resources.IResourceRuleFactory; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.resources.WorkspaceJob; @@ -49,6 +49,7 @@ import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.internal.ide.IDEWorkbenchMessages; +import org.eclipse.ui.internal.ide.IDEWorkbenchPlugin; import org.eclipse.ui.internal.ide.IIDEHelpContextIds; import org.eclipse.ui.internal.ide.StatusUtil; import org.eclipse.ui.internal.ide.dialogs.IDEResourceInfoUtils; @@ -217,41 +218,111 @@ final public void refreshAll() { @Override final protected IRunnableWithProgress createOperation(final IStatus[] errorStatus) { - ISchedulingRule rule = null; - IResourceRuleFactory factory = ResourcesPlugin.getWorkspace().getRuleFactory(); - List actionResources = new ArrayList<>(getActionResources()); if (shouldPerformResourcePruning()) { actionResources = pruneResources(actionResources); } final List resources = actionResources; - Iterator res = resources.iterator(); - while (res.hasNext()) { - rule = MultiRule.combine(rule, factory.refreshRule(res.next())); + ISchedulingRule rule = null; + for (IResource resource : resources) { + ISchedulingRule newRule = (resource.getType() == IResource.ROOT) ? resource : resource.getProject(); + rule = MultiRule.combine(rule, newRule); } + return new WorkspaceModifyOperation(rule) { @Override public void execute(IProgressMonitor mon) { SubMonitor subMonitor = SubMonitor.convert(mon, resources.size()); - MultiStatus errors = null; subMonitor.setTaskName(getOperationMessage()); - Iterator resourcesEnum = resources.iterator(); - while (resourcesEnum.hasNext()) { + List errors = new ArrayList<>(); + for (IResource resource : resources) { try { - IResource resource = resourcesEnum.next(); refreshResource(resource, subMonitor.split(1)); } catch (CoreException e) { - errors = recordError(errors, e); + errors.add(e.getStatus()); } } - if (errors != null) { - errorStatus[0] = errors; + if (!errors.isEmpty()) { + MultiStatus multiStatus = new MultiStatus(IDEWorkbenchPlugin.IDE_WORKBENCH, IStatus.ERROR, + getProblemsMessage(), null); + for (IStatus s : errors) { + multiStatus.merge(s); + } + errorStatus[0] = multiStatus; } } }; } + /** + * Creates a {@link WorkspaceJob} that refreshes the given resources under the + * given scheduling rule. The job is not yet scheduled when returned, allowing + * callers to attach listeners before scheduling. + * + * @param resources resources to refresh; must not be null + * @param rule scheduling rule for the job (a project or workspace root) + * @return the created but unscheduled job + * @since 3.23 + */ + protected WorkspaceJob createRefreshJob(List resources, ISchedulingRule rule) { + final IStatus[] errorStatus = { Status.OK_STATUS }; + WorkspaceModifyOperation op = new WorkspaceModifyOperation(rule) { + @Override + public void execute(IProgressMonitor mon) { + SubMonitor subMonitor = SubMonitor.convert(mon, resources.size()); + subMonitor.setTaskName(getOperationMessage()); + List errors = new ArrayList<>(); + for (IResource resource : resources) { + try { + refreshResource(resource, subMonitor.split(1)); + } catch (CoreException e) { + errors.add(e.getStatus()); + } + } + if (!errors.isEmpty()) { + MultiStatus multiStatus = new MultiStatus(IDEWorkbenchPlugin.IDE_WORKBENCH, IStatus.ERROR, + getProblemsMessage(), null); + for (IStatus s : errors) { + multiStatus.merge(s); + } + errorStatus[0] = multiStatus; + } + } + }; + String jobName = (rule instanceof IProject p) ? "refresh (" + p.getName() + ")" : "refresh"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + WorkspaceJob job = new WorkspaceJob(jobName) { + @Override + public IStatus runInWorkspace(IProgressMonitor monitor) throws CoreException { + try { + op.run(monitor); + } catch (InvocationTargetException e) { + String msg = NLS.bind(IDEWorkbenchMessages.WorkspaceAction_logTitle, getClass().getName(), + e.getTargetException()); + return StatusUtil.newStatus(IStatus.ERROR, msg, e.getTargetException()); + } catch (InterruptedException e) { + return Status.CANCEL_STATUS; + } + return errorStatus[0]; + } + }; + job.setRule(op.getRule()); + job.setUser(true); + return job; + } + + /** + * Creates and schedules a {@link WorkspaceJob} for the given resources. + * Subclasses may override to attach listeners before the job is scheduled. + * + * @param resources resources to refresh; must not be null + * @param rule scheduling rule for the job (a project or workspace root) + * @since 3.23 + */ + protected void scheduleRefreshJob(List resources, ISchedulingRule rule) { + createRefreshJob(resources, rule).schedule(); + } + /** * Refresh the resource (with a check for deleted projects). *

@@ -285,32 +356,19 @@ protected void refreshResource(IResource resource, IProgressMonitor monitor) thr @Override public void run() { - final IStatus[] errorStatus = new IStatus[1]; - errorStatus[0] = Status.OK_STATUS; - final WorkspaceModifyOperation op = (WorkspaceModifyOperation) createOperation(errorStatus); - WorkspaceJob job = new WorkspaceJob("refresh") { //$NON-NLS-1$ - - @Override - public IStatus runInWorkspace(IProgressMonitor monitor) throws CoreException { - try { - op.run(monitor); - } catch (InvocationTargetException e) { - String msg = NLS.bind( - IDEWorkbenchMessages.WorkspaceAction_logTitle, getClass() - .getName(), e.getTargetException()); - throw new CoreException(StatusUtil.newStatus(IStatus.ERROR, msg, e.getTargetException())); - } catch (InterruptedException e) { - return Status.CANCEL_STATUS; - } - return errorStatus[0]; - } - - }; - ISchedulingRule rule = op.getRule(); - if (rule != null) { - job.setRule(rule); + List actionResources = new ArrayList<>(getActionResources()); + if (shouldPerformResourcePruning()) { + actionResources = pruneResources(actionResources); + } + // Group resources by scheduling rule so each project can be refreshed in + // parallel while still holding a project-level rule during its refresh. + Map> byRule = new LinkedHashMap<>(); + for (IResource resource : actionResources) { + ISchedulingRule rule = (resource.getType() == IResource.ROOT) ? resource : resource.getProject(); + byRule.computeIfAbsent(rule, r -> new ArrayList<>()).add(resource); + } + for (Map.Entry> entry : byRule.entrySet()) { + scheduleRefreshJob(entry.getValue(), entry.getKey()); } - job.setUser(true); - job.schedule(); } } diff --git a/bundles/org.eclipse.ui.navigator.resources/src/org/eclipse/ui/internal/navigator/resources/actions/ResourceMgmtActionProvider.java b/bundles/org.eclipse.ui.navigator.resources/src/org/eclipse/ui/internal/navigator/resources/actions/ResourceMgmtActionProvider.java index 281115512933..f80a884797a1 100644 --- a/bundles/org.eclipse.ui.navigator.resources/src/org/eclipse/ui/internal/navigator/resources/actions/ResourceMgmtActionProvider.java +++ b/bundles/org.eclipse.ui.navigator.resources/src/org/eclipse/ui/internal/navigator/resources/actions/ResourceMgmtActionProvider.java @@ -15,7 +15,6 @@ package org.eclipse.ui.internal.navigator.resources.actions; -import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -23,21 +22,20 @@ import org.eclipse.core.resources.ICommand; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IncrementalProjectBuilder; +import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.resources.WorkspaceJob; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IAdaptable; -import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.core.runtime.IStatus; -import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.IJobChangeEvent; import org.eclipse.core.runtime.jobs.ISchedulingRule; +import org.eclipse.core.runtime.jobs.JobChangeAdapter; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.action.Separator; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.StructuredViewer; import org.eclipse.jface.window.IShellProvider; -import org.eclipse.osgi.util.NLS; import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.IActionBars; import org.eclipse.ui.IWorkbenchCommandConstants; @@ -48,11 +46,8 @@ import org.eclipse.ui.actions.CloseUnrelatedProjectsAction; import org.eclipse.ui.actions.OpenResourceAction; import org.eclipse.ui.actions.RefreshAction; -import org.eclipse.ui.actions.WorkspaceModifyOperation; import org.eclipse.ui.ide.IDEActionFactory; import org.eclipse.ui.internal.ide.IDEWorkbenchPlugin; -import org.eclipse.ui.internal.navigator.NavigatorPlugin; -import org.eclipse.ui.internal.navigator.resources.plugin.WorkbenchNavigatorMessages; import org.eclipse.ui.navigator.CommonActionProvider; import org.eclipse.ui.navigator.ICommonActionExtensionSite; import org.eclipse.ui.navigator.ICommonMenuConstants; @@ -215,40 +210,22 @@ protected void makeActions() { refreshAction = new RefreshAction(sp) { @Override - public void run() { - final IStatus[] errorStatus = new IStatus[1]; - errorStatus[0] = Status.OK_STATUS; - final WorkspaceModifyOperation op = (WorkspaceModifyOperation) createOperation(errorStatus); - WorkspaceJob job = new WorkspaceJob("refresh") { //$NON-NLS-1$ - + protected void scheduleRefreshJob(List resources, ISchedulingRule rule) { + WorkspaceJob job = createRefreshJob(resources, rule); + job.addJobChangeListener(new JobChangeAdapter() { @Override - public IStatus runInWorkspace(IProgressMonitor monitor) throws CoreException { - try { - op.run(monitor); - if (shell != null && !shell.isDisposed()) { - shell.getDisplay().asyncExec(() -> { - StructuredViewer viewer = getActionSite().getStructuredViewer(); - if (viewer != null && viewer.getControl() != null - && !viewer.getControl().isDisposed()) { - viewer.refresh(); - } - }); - } - } catch (InvocationTargetException e) { - String msg = NLS.bind(WorkbenchNavigatorMessages.ResourceMgmtActionProvider_logTitle, getClass().getName(), e.getTargetException()); - throw new CoreException(new Status(IStatus.ERROR, NavigatorPlugin.PLUGIN_ID, IStatus.ERROR, msg, e.getTargetException())); - } catch (InterruptedException e) { - return Status.CANCEL_STATUS; + public void done(IJobChangeEvent event) { + if (shell != null && !shell.isDisposed()) { + shell.getDisplay().asyncExec(() -> { + StructuredViewer viewer = getActionSite().getStructuredViewer(); + if (viewer != null && viewer.getControl() != null + && !viewer.getControl().isDisposed()) { + viewer.refresh(); + } + }); } - return errorStatus[0]; } - - }; - ISchedulingRule rule = op.getRule(); - if (rule != null) { - job.setRule(rule); - } - job.setUser(true); + }); job.schedule(); } };