diff options
author | Andrey Loskutov | 2015-02-11 20:10:54 +0000 |
---|---|---|
committer | Lars Vogel | 2015-02-24 20:04:54 +0000 |
commit | ea1e252c1a7f31b29af223acd8ce637d078728e0 (patch) | |
tree | 14b2bc6657ea959937874da1d4bfe35227600d63 | |
parent | bf270a574099351e2fa3eaa5ddeccaf28db5348a (diff) | |
download | eclipse.platform.ui-ea1e252c1a7f31b29af223acd8ce637d078728e0.tar.gz eclipse.platform.ui-ea1e252c1a7f31b29af223acd8ce637d078728e0.tar.xz eclipse.platform.ui-ea1e252c1a7f31b29af223acd8ce637d078728e0.zip |
Bug 41431 - [EditorMgmt] Affected editors should be closed before
executing project close or delete
The patch hooks into standard close/delete IDE actions and tries to find
and close opened editors matching closed/deleted resource roots. This is
the practical solution wich aims to cover 80% of user complains and
doesn't claim to fix all sophisticated ways of closing or deleting
resources in the IDE.
The matched editors will be closed without confirmation (which happens
already in Close/Delete actions), in case of Delete action only if the
editor resource does not exists anymore.
The editor matching strategy is pretty simple - it uses editor input
objects and hopes that they are either FileEditorInput's or can provide
adapters to IFile or IResource. This should cover the majority of
"standard" IDE editors and works fine with not yet instantiated editor
parts. Because real editor parts aren't instantiated there is no risk of
unexpected side effects.
3rd party editors can benefit from the provided patch too - all what
they need is to add IFile or IResource adapter to their input objects.
P.S:
The reason not to implement IResourceChangeListener is that this can
have permanent negative side effects on performance, because each time
such resource change happens the delta must be processed and each opened
editor has to be matched against each closed/deleted resource, including
extra (eventually redundant) confirmation dialog in case a dirty editor
matches.
Change-Id: I92af399bf0ceca3dfe8668a6c011c19c12d6ee51
Signed-off-by: Andrey Loskutov <loskutov@gmx.de>
4 files changed, 144 insertions, 7 deletions
diff --git a/bundles/org.eclipse.ui.ide/extensions/org/eclipse/ui/actions/CloseResourceAction.java b/bundles/org.eclipse.ui.ide/extensions/org/eclipse/ui/actions/CloseResourceAction.java index 50386d1fd4f..2967f2f9e9c 100644 --- a/bundles/org.eclipse.ui.ide/extensions/org/eclipse/ui/actions/CloseResourceAction.java +++ b/bundles/org.eclipse.ui.ide/extensions/org/eclipse/ui/actions/CloseResourceAction.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2010 IBM Corporation and others. + * Copyright (c) 2000, 2015 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -7,12 +7,15 @@ * * Contributors: * IBM Corporation - initial API and implementation + * Andrey Loskutov <loskutov@gmx.de> - Bug 41431 *******************************************************************************/ package org.eclipse.ui.actions; +import java.util.ArrayList; import java.util.Iterator; import java.util.List; +import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResourceChangeEvent; @@ -24,16 +27,25 @@ import org.eclipse.core.resources.mapping.IResourceChangeDescriptionFactory; import org.eclipse.core.resources.mapping.ResourceChangeValidator; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.SafeRunner; import org.eclipse.core.runtime.jobs.ISchedulingRule; import org.eclipse.core.runtime.jobs.MultiRule; +import org.eclipse.jface.util.SafeRunnable; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.window.IShellProvider; import org.eclipse.osgi.util.NLS; +import org.eclipse.swt.custom.BusyIndicator; import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.IEditorInput; +import org.eclipse.ui.IEditorReference; +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.IWorkbenchWindow; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.ide.IDE; import org.eclipse.ui.internal.ide.IDEWorkbenchMessages; import org.eclipse.ui.internal.ide.IIDEHelpContextIds; +import org.eclipse.ui.part.FileEditorInput; /** * Standard action for closing the currently selected project(s). @@ -140,8 +152,7 @@ public class CloseResourceAction extends WorkspaceAction implements return; } - IResource[] projectArray = (IResource[]) projects - .toArray(new IResource[projects.size()]); + final IResource[] projectArray = (IResource[]) projects.toArray(new IResource[projects.size()]); if (!IDE.saveAllEditors(projectArray, true)) { return; @@ -149,13 +160,15 @@ public class CloseResourceAction extends WorkspaceAction implements if (!validateClose()) { return; } + + closeMatchingEditors(projectArray, false); + //be conservative and include all projects in the selection - projects //can change state between now and when the job starts ISchedulingRule rule = null; IResourceRuleFactory factory = ResourcesPlugin.getWorkspace().getRuleFactory(); - Iterator resources = getSelectedResources().iterator(); - while (resources.hasNext()) { - IProject project = (IProject) resources.next(); + for (int i = 0; i < projectArray.length; i++) { + IProject project = (IProject) projectArray[i]; rule = MultiRule.combine(rule, factory.modifyRule(project)); } runInBackground(rule); @@ -274,4 +287,124 @@ public class CloseResourceAction extends WorkspaceAction implements } return IDE.promptToConfirm(getShell(), IDEWorkbenchMessages.CloseResourceAction_confirm, message, factory.getDelta(), getModelProviderIds(), false /* no need to syncExec */); } + + /** + * Tries to find opened editors matching given resource roots. The editors + * will be closed without confirmation and only if the editor resource does + * not exists anymore. + * + * @param resourceRoots + * non null array with deleted resource tree roots + * @param deletedOnly + * true to close only editors on resources which do not exist + */ + static void closeMatchingEditors(final IResource[] resourceRoots, final boolean deletedOnly) { + if (resourceRoots.length == 0) { + return; + } + Runnable runnable = new Runnable() { + @Override + public void run() { + SafeRunner.run(new SafeRunnable(IDEWorkbenchMessages.ErrorOnCloseEditors) { + @Override + public void run() throws CoreException { + IWorkbenchWindow w = getActiveWindow(); + if (w != null) { + List<IEditorReference> toClose = getMatchingEditors(resourceRoots, w, deletedOnly); + if (toClose.isEmpty()) { + return; + } + closeEditors(toClose, w); + } + } + }); + } + }; + BusyIndicator.showWhile(PlatformUI.getWorkbench().getDisplay(), runnable); + } + + private static IWorkbenchWindow getActiveWindow() { + IWorkbenchWindow w = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); + if (w == null) { + IWorkbenchWindow[] windows = PlatformUI.getWorkbench().getWorkbenchWindows(); + if (windows.length > 0) { + w = windows[0]; + } + } + return w; + } + + private static List<IEditorReference> getMatchingEditors(final IResource[] resourceRoots, IWorkbenchWindow w, + boolean deletedOnly) throws CoreException { + List<IEditorReference> toClose = new ArrayList<IEditorReference>(); + IEditorReference[] editors = getEditors(w); + for (int i = 0; i < editors.length; i++) { + IEditorReference ref = editors[i]; + IResource resource = getAdapter(ref); + // only collect editors for non existing resources + if (resource != null && belongsTo(resourceRoots, resource)) { + if (deletedOnly && resource.exists()) { + continue; + } + toClose.add(ref); + } + } + return toClose; + } + + private static IEditorReference[] getEditors(IWorkbenchWindow w) { + if (w != null) { + IWorkbenchPage page = w.getActivePage(); + if (page != null) { + return page.getEditorReferences(); + } + } + return new IEditorReference[0]; + } + + private static IResource getAdapter(IEditorReference ref) throws CoreException { + IEditorInput input = ref.getEditorInput(); + if (input instanceof FileEditorInput) { + FileEditorInput fi = (FileEditorInput) input; + IFile file = fi.getFile(); + if (file != null) { + return file; + } + } + // here we can only guess how the input might be related to a resource + Object adapter = input.getAdapter(IFile.class); + if (adapter != null) { + return (IResource) adapter; + } + adapter = input.getAdapter(IResource.class); + if (adapter != null) { + return (IResource) adapter; + } + adapter = Platform.getAdapterManager().getAdapter(input, IFile.class); + if (adapter != null) { + return (IResource) adapter; + } + adapter = Platform.getAdapterManager().getAdapter(input, IResource.class); + if (adapter != null) { + return (IResource) adapter; + } + return null; + } + + private static boolean belongsTo(IResource[] roots, IResource leaf) { + for (int i = 0; i < roots.length; i++) { + if (roots[i].contains(leaf)) { + return true; + } + } + return false; + } + + private static void closeEditors(List<IEditorReference> toClose, IWorkbenchWindow w) { + IWorkbenchPage page = w.getActivePage(); + if (page == null) { + return; + } + page.closeEditors(toClose.toArray(new IEditorReference[toClose.size()]), false); + } } diff --git a/bundles/org.eclipse.ui.ide/extensions/org/eclipse/ui/actions/DeleteResourceAction.java b/bundles/org.eclipse.ui.ide/extensions/org/eclipse/ui/actions/DeleteResourceAction.java index c7f7c753d95..33811277121 100644 --- a/bundles/org.eclipse.ui.ide/extensions/org/eclipse/ui/actions/DeleteResourceAction.java +++ b/bundles/org.eclipse.ui.ide/extensions/org/eclipse/ui/actions/DeleteResourceAction.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2013 IBM Corporation and others. + * Copyright (c) 2000, 2015 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -9,6 +9,7 @@ * IBM Corporation - initial API and implementation * Benjamin Muskalla <b.muskalla@gmx.net> * - Fix for bug 172574 - [IDE] DeleteProjectDialog inconsequent selection behavior + * Andrey Loskutov <loskutov@gmx.de> - Bug 41431 *******************************************************************************/ package org.eclipse.ui.actions; @@ -469,6 +470,7 @@ public class DeleteResourceAction extends SelectionListenerAction { if (!fTestingMode) { if (LTKLauncher.openDeleteWizard(getStructuredSelection())) { + CloseResourceAction.closeMatchingEditors(resources, true); return; } } diff --git a/bundles/org.eclipse.ui.ide/src/org/eclipse/ui/internal/ide/IDEWorkbenchMessages.java b/bundles/org.eclipse.ui.ide/src/org/eclipse/ui/internal/ide/IDEWorkbenchMessages.java index 9c6281f7b9d..fec0b82d221 100644 --- a/bundles/org.eclipse.ui.ide/src/org/eclipse/ui/internal/ide/IDEWorkbenchMessages.java +++ b/bundles/org.eclipse.ui.ide/src/org/eclipse/ui/internal/ide/IDEWorkbenchMessages.java @@ -51,6 +51,7 @@ public class IDEWorkbenchMessages extends NLS { public static String ErrorClosing; public static String ErrorOnSaveAll; + public static String ErrorOnCloseEditors; public static String ResourceInfoPage_noResource; diff --git a/bundles/org.eclipse.ui.ide/src/org/eclipse/ui/internal/ide/messages.properties b/bundles/org.eclipse.ui.ide/src/org/eclipse/ui/internal/ide/messages.properties index 35d8fa70fb7..a85f354c875 100644 --- a/bundles/org.eclipse.ui.ide/src/org/eclipse/ui/internal/ide/messages.properties +++ b/bundles/org.eclipse.ui.ide/src/org/eclipse/ui/internal/ide/messages.properties @@ -47,6 +47,7 @@ ConfigurationLogUpdateSection_timestamp=Profile timestamp\: {0} ErrorClosing = An error has occurred when closing the workbench. See error log for more details. ErrorOnSaveAll = An error has occurred while saving all editors. See error log for more details. +ErrorOnCloseEditors = An error has occurred while closing editors. See error log for more details. IDEIdleHelper_backgroundGC = Collecting garbage |