Skip to content

Commit 05018be

Browse files
committed
Show Open/Close Project for mixed selections in Project Explorer
The ResourceMgmtActionProvider enablement expression required ALL selected elements to adapt to IResource or IWorkingSet. This hid Open/Close Project actions when the selection contained non-resource elements (e.g., working set headers from Ctrl+A). Changed the enablement to always activate the provider. The existing fillContextMenu() logic via selectionToProjects() already filters to applicable projects and only shows relevant actions. Fixes #3790
1 parent f22f75c commit 05018be

3 files changed

Lines changed: 110 additions & 10 deletions

File tree

bundles/org.eclipse.ui.navigator.resources/plugin.xml

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -237,13 +237,8 @@
237237
class="org.eclipse.ui.internal.navigator.resources.actions.ResourceMgmtActionProvider"
238238
id="org.eclipse.ui.navigator.resources.ResourceMgmtActions">
239239
<enablement>
240-
<or>
241-
<adapt type="org.eclipse.core.resources.IResource" />
242-
<adapt type="java.util.Collection">
243-
<count value="0" />
244-
</adapt>
245-
<adapt type="org.eclipse.ui.IWorkingSet" />
246-
</or>
240+
<!-- Always enabled: fillContextMenu() filters to applicable projects -->
241+
<or/>
247242
</enablement>
248243
</actionProvider>
249244

bundles/org.eclipse.ui.navigator.resources/src/org/eclipse/ui/internal/navigator/resources/actions/ResourceMgmtActionProvider.java

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
import org.eclipse.core.resources.ICommand;
2424
import org.eclipse.core.resources.IProject;
25+
import org.eclipse.core.resources.IResource;
2526
import org.eclipse.core.resources.IncrementalProjectBuilder;
2627
import org.eclipse.core.resources.ResourcesPlugin;
2728
import org.eclipse.core.resources.WorkspaceJob;
@@ -207,11 +208,29 @@ boolean hasBuilder(IProject project) {
207208
protected void makeActions() {
208209
IShellProvider sp = () -> shell;
209210

210-
openProjectAction = new OpenResourceAction(sp);
211+
openProjectAction = new OpenResourceAction(sp) {
212+
@Override
213+
protected synchronized List<? extends IResource> getSelectedResources() {
214+
return super.getSelectedResources().stream()
215+
.filter(IProject.class::isInstance).toList();
216+
}
217+
};
211218

212-
closeProjectAction = new CloseResourceAction(sp);
219+
closeProjectAction = new CloseResourceAction(sp) {
220+
@Override
221+
protected synchronized List<? extends IResource> getSelectedResources() {
222+
return super.getSelectedResources().stream()
223+
.filter(IProject.class::isInstance).toList();
224+
}
225+
};
213226

214-
closeUnrelatedProjectsAction = new CloseUnrelatedProjectsAction(sp);
227+
closeUnrelatedProjectsAction = new CloseUnrelatedProjectsAction(sp) {
228+
@Override
229+
protected List<? extends IResource> getSelectedResources() {
230+
return super.getSelectedResources().stream()
231+
.filter(IProject.class::isInstance).toList();
232+
}
233+
};
215234

216235
refreshAction = new RefreshAction(sp) {
217236
@Override

tests/org.eclipse.ui.tests.navigator/src/org/eclipse/ui/tests/navigator/resources/ResourceMgmtActionProviderTests.java

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,67 @@ public void testFillContextMenu_openProjectNoBuilderSelection() throws CoreExcep
118118
}
119119
}
120120

121+
/**
122+
* Test for a file selected together with an open project: Close Project must
123+
* be both present and enabled. Regression test for the bug where
124+
* selectionIsOfType(PROJECT) disabled the action for any mixed selection.
125+
*
126+
* @throws CoreException
127+
*/
128+
@Test
129+
public void testFillContextMenu_fileAndOpenProjectSelection_closeProjectEnabled() throws CoreException {
130+
// _p1 is already open; _project has a known 'src' folder + files
131+
IProject openProj = ResourcesPlugin.getWorkspace().getRoot().getProject("Test");
132+
openProj.open(null);
133+
// Select a file alongside a project (the typical Ctrl+A expanded scenario)
134+
ResourceMgmtActionProvider provider = providerForObjects(_p1, openProj.getFile(".project"));
135+
provider.fillContextMenu(manager);
136+
assertTrue(menuHasContribution("org.eclipse.ui.CloseResourceAction"),
137+
"Close Project should be in the menu");
138+
assertTrue(isMenuContributionEnabled("org.eclipse.ui.CloseResourceAction"),
139+
"Close Project should be enabled when open projects are in the selection");
140+
assertTrue(menuHasContribution("org.eclipse.ui.CloseUnrelatedProjectsAction"),
141+
"Close Unrelated Projects should be in the menu");
142+
assertTrue(isMenuContributionEnabled("org.eclipse.ui.CloseUnrelatedProjectsAction"),
143+
"Close Unrelated Projects should be enabled when open projects are in the selection");
144+
}
145+
146+
/**
147+
* Test for mixed selection: an open project alongside a non-adaptable element
148+
* (e.g. a working set header from Ctrl+A in Project Explorer). Close Project
149+
* and Refresh must still appear — regression test for issue #3790.
150+
*
151+
* @throws CoreException
152+
*/
153+
@Test
154+
public void testFillContextMenu_mixedSelectionOpenProjectAndNonAdaptableElement() throws CoreException {
155+
IProject openProj = ResourcesPlugin.getWorkspace().getRoot().getProject("Test");
156+
openProj.open(null);
157+
// Plain Object does not implement IAdaptable, so it is never resolved to a
158+
// project — it counts as a non-project element in the selection.
159+
Object nonResource = new Object();
160+
ResourceMgmtActionProvider provider = providerForObjects(openProj, nonResource);
161+
provider.fillContextMenu(manager);
162+
checkMenuHasCorrectContributions(false, true, false, true, true);
163+
}
164+
165+
/**
166+
* Test for a fully expanded selection: two open projects plus child resources
167+
* from both (simulating Ctrl+A when both projects are expanded). Close Project
168+
* must still appear for the open projects in the selection.
169+
*
170+
* @throws CoreException
171+
*/
172+
@Test
173+
public void testFillContextMenu_twoOpenProjectsWithChildResourcesSelection() throws CoreException {
174+
// _p1 and _p2 are already opened in setUp()
175+
IFolder srcFolder = _project.getFolder("src");
176+
IFolder binFolder = _project.getFolder("bin");
177+
ResourceMgmtActionProvider provider = providerForObjects(_p1, _p2, srcFolder, binFolder);
178+
provider.fillContextMenu(manager);
179+
checkMenuHasCorrectContributions(false, true, false, true, true);
180+
}
181+
121182
/**
122183
* Test for 'open project' that doesn't have a builder attached - only 'open
123184
* project' should be disabled
@@ -158,6 +219,19 @@ public void testFillContextMenu_openProjectWithBuilderSelection() throws CoreExc
158219
}
159220
}
160221

222+
/*
223+
* Return a provider for a mixed/arbitrary selection (Object[])
224+
*/
225+
private ResourceMgmtActionProvider providerForObjects(Object... selectedElements) {
226+
ICommonActionExtensionSite cfg = new CommonActionExtensionSite("NA", "NA",
227+
CommonViewerSiteFactory.createCommonViewerSite(_commonNavigator.getViewSite()),
228+
(NavigatorContentService) _contentService, _viewer);
229+
ResourceMgmtActionProvider provider = new ResourceMgmtActionProvider();
230+
provider.setContext(new ActionContext(new StructuredSelection(selectedElements)));
231+
provider.init(cfg);
232+
return provider;
233+
}
234+
161235
/*
162236
* Return a provider, given the selected navigator items
163237
*/
@@ -206,4 +280,16 @@ private boolean menuHasContribution(String contribution) {
206280
return false;
207281
}
208282

283+
/*
284+
* Check whether the named menu entry is enabled
285+
*/
286+
private boolean isMenuContributionEnabled(String contribution) {
287+
for (IContributionItem thisItem : manager.getItems()) {
288+
if (thisItem.getId() != null && thisItem.getId().equals(contribution)) {
289+
return thisItem.isEnabled();
290+
}
291+
}
292+
return false;
293+
}
294+
209295
}

0 commit comments

Comments
 (0)