Skip to content

Commit 732c412

Browse files
committed
Add context menu to open method in new tab
Allows opening a method in a new editor tab from the right-click menu, even if it's in the same file. Improves code navigation, especially when working with large files or understanding multiple methods in parallel. Fixes : #2265
1 parent b0bbeb0 commit 732c412

6 files changed

Lines changed: 194 additions & 4 deletions

File tree

org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/actions/ActionMessages.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2000, 2020 IBM Corporation and others.
2+
* Copyright (c) 2000, 2025 IBM Corporation and others.
33
*
44
* This program and the accompanying materials
55
* are made available under the terms of the Eclipse Public License 2.0
@@ -463,5 +463,7 @@ private ActionMessages() {
463463
public static String GenerateToStringAction_tooltip;
464464
public static String GenerateToStringAction_tostring;
465465
public static String GenerateToStringAction_error_caption;
466+
public static String OpenMethodAction_ToolTipText;
467+
public static String OpenMethodAction_ActionName;
466468

467469
}

org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/actions/ActionMessages.properties

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
###############################################################################
2-
# Copyright (c) 2000, 2020 IBM Corporation and others.
2+
# Copyright (c) 2000, 2025 IBM Corporation and others.
33
#
44
# This program and the accompanying materials
55
# are made available under the terms of the Eclipse Public License 2.0
@@ -491,3 +491,6 @@ OrganizeImportsAction_summary_added={0} import(s) added.
491491

492492
# DO NOT REMOVE, used in a product, see https://bugs.eclipse.org/296836
493493
OrganizeImportsAction_summary_removed={0} import(s) removed.
494+
495+
OpenMethodAction_ToolTipText=Opens the method definition in a new tab
496+
OpenMethodAction_ActionName=Open Method in New Tab
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2025 IBM Corporation.
3+
*
4+
* This program and the accompanying materials
5+
* are made available under the terms of the Eclipse Public License 2.0
6+
* which accompanies this distribution, and is available at
7+
* https://www.eclipse.org/legal/epl-2.0/
8+
*
9+
* SPDX-License-Identifier: EPL-2.0
10+
*
11+
* Contributors:
12+
* IBM Corporation - initial API and implementation
13+
*******************************************************************************/
14+
package org.eclipse.jdt.internal.ui.actions;
15+
16+
import org.eclipse.swt.graphics.Point;
17+
18+
import org.eclipse.jface.viewers.IStructuredSelection;
19+
20+
import org.eclipse.jface.text.source.ISourceViewer;
21+
22+
import org.eclipse.ui.IEditorPart;
23+
import org.eclipse.ui.IWorkbenchPage;
24+
import org.eclipse.ui.IWorkbenchSite;
25+
import org.eclipse.ui.IWorkbenchWindow;
26+
import org.eclipse.ui.PartInitException;
27+
import org.eclipse.ui.PlatformUI;
28+
29+
import org.eclipse.jdt.core.IJavaElement;
30+
import org.eclipse.jdt.core.IMethod;
31+
import org.eclipse.jdt.core.ITypeRoot;
32+
import org.eclipse.jdt.core.JavaModelException;
33+
import org.eclipse.jdt.core.dom.ASTNode;
34+
import org.eclipse.jdt.core.dom.CompilationUnit;
35+
import org.eclipse.jdt.core.dom.IBinding;
36+
import org.eclipse.jdt.core.dom.Name;
37+
import org.eclipse.jdt.core.dom.NodeFinder;
38+
import org.eclipse.jdt.core.manipulation.SharedASTProviderCore;
39+
40+
import org.eclipse.jdt.ui.JavaUI;
41+
import org.eclipse.jdt.ui.actions.SelectionDispatchAction;
42+
43+
import org.eclipse.jdt.internal.ui.JavaPlugin;
44+
import org.eclipse.jdt.internal.ui.javaeditor.EditorUtility;
45+
import org.eclipse.jdt.internal.ui.javaeditor.JavaEditor;
46+
47+
48+
public class OpenMethod extends SelectionDispatchAction {
49+
50+
public static final String ACTION_HANDLER_ID= "org.eclipse.jdt.ui.actions.OpenMethod"; //$NON-NLS-1$
51+
52+
private JavaEditor fEditor;
53+
54+
public OpenMethod(JavaEditor editor) {
55+
this(editor.getSite());
56+
fEditor= editor;
57+
setEnabled(true);
58+
}
59+
60+
public OpenMethod(IWorkbenchSite site) {
61+
super(site);
62+
setText(ActionMessages.OpenMethodAction_ActionName);
63+
setToolTipText(ActionMessages.OpenMethodAction_ToolTipText);
64+
}
65+
66+
@Override
67+
public void selectionChanged(IStructuredSelection selection) {
68+
setEnabled(canEnable(selection.toArray()));
69+
}
70+
71+
private boolean canEnable(Object[] objects) {
72+
for (Object element : objects) {
73+
if (isValidElement(element))
74+
return true;
75+
}
76+
return false;
77+
}
78+
79+
private boolean isValidElement(Object element) {
80+
if (element instanceof IMethod) {
81+
return true;
82+
}
83+
return false;
84+
}
85+
86+
@Override
87+
public void run() {
88+
Object[] elements= getSelectedElements();
89+
Object element= elements[0];
90+
if (element instanceof IMethod method) {
91+
openMethodInClonedEditor(method);
92+
}
93+
}
94+
95+
private Object[] getSelectedElements() {
96+
if (fEditor != null) {
97+
Object element= getSelectedElement(fEditor);
98+
if (element != null) {
99+
if (isValidElement(element)) {
100+
return new Object[] { element };
101+
}
102+
}
103+
}
104+
return null;
105+
}
106+
107+
private Object getSelectedElement(JavaEditor editor) {
108+
ISourceViewer viewer= editor.getViewer();
109+
if (viewer == null)
110+
return null;
111+
112+
Point selectedRange= viewer.getSelectedRange();
113+
int length= selectedRange.y;
114+
int offset= selectedRange.x;
115+
116+
ITypeRoot element= JavaUI.getEditorInputTypeRoot(editor.getEditorInput());
117+
if (element == null)
118+
return null;
119+
120+
CompilationUnit ast= SharedASTProviderCore.getAST(element, SharedASTProviderCore.WAIT_YES, null);
121+
if (ast == null)
122+
return null;
123+
124+
NodeFinder finder= new NodeFinder(ast, offset, length);
125+
ASTNode node= finder.getCoveringNode();
126+
IBinding binding= null;
127+
if (node instanceof Name nameNode) {
128+
binding= nameNode.resolveBinding();
129+
return binding.getJavaElement();
130+
}
131+
return null;
132+
}
133+
134+
protected void openMethodInClonedEditor(IMethod method) {
135+
try {
136+
IEditorPart target= null;
137+
IEditorPart existingEditr= EditorUtility.isOpenInEditor(method);
138+
IJavaElement methodElement= method;
139+
if (existingEditr != null) { // Cloning
140+
IWorkbenchWindow window= PlatformUI.getWorkbench().getActiveWorkbenchWindow();
141+
IWorkbenchPage page= window.getActivePage();
142+
target= page.openEditor(
143+
existingEditr.getEditorInput(),
144+
existingEditr.getSite().getId(),
145+
true,
146+
IWorkbenchPage.MATCH_NONE);
147+
} else {
148+
target= JavaUI.openInEditor(method, true, false);
149+
}
150+
JavaUI.revealInEditor(target, methodElement);
151+
} catch (PartInitException | JavaModelException e) {
152+
JavaPlugin.log(e);
153+
}
154+
}
155+
156+
@Override
157+
public boolean isEnabled() {
158+
Object[] elements= getSelectedElements();
159+
if (elements != null && elements.length == 1) {
160+
Object element= elements[0];
161+
if (element instanceof IMethod) {
162+
setEnabled(true);
163+
return true;
164+
}
165+
}
166+
setEnabled(false);
167+
return false;
168+
}
169+
170+
}

org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/javaeditor/BasicJavaEditorActionContributor.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141

4242
import org.eclipse.jdt.internal.ui.actions.CopyQualifiedNameAction;
4343
import org.eclipse.jdt.internal.ui.actions.FoldingActionGroup;
44+
import org.eclipse.jdt.internal.ui.actions.OpenMethod;
4445
import org.eclipse.jdt.internal.ui.javaeditor.selectionactions.GoToNextPreviousMemberAction;
4546
import org.eclipse.jdt.internal.ui.javaeditor.selectionactions.StructureSelectionAction;
4647

@@ -227,6 +228,8 @@ public void setActiveEditor(IEditorPart part) {
227228
actionBars.setGlobalActionHandler(CopyQualifiedNameAction.ACTION_HANDLER_ID, action);
228229
action= getAction(textEditor, IJavaEditorActionConstants.RAW_PASTE);
229230
actionBars.setGlobalActionHandler(IJavaEditorActionDefinitionIds.RAW_PASTE_ACTION_HANDLE_ID, action);
231+
action= getAction(textEditor, IJavaEditorActionConstants.OPEN_METHOD);
232+
actionBars.setGlobalActionHandler(OpenMethod.ACTION_HANDLER_ID, action);
230233

231234
actionBars.setGlobalActionHandler(IJavaEditorActionDefinitionIds.SHOW_IN_BREADCRUMB, getAction(textEditor, IJavaEditorActionDefinitionIds.SHOW_IN_BREADCRUMB));
232235
actionBars.setGlobalActionHandler("org.eclipse.jdt.internal.ui.actions.OpenHyperlink", getAction(textEditor, ITextEditorActionConstants.OPEN_HYPERLINK)); //$NON-NLS-1$

org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/javaeditor/IJavaEditorActionConstants.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,4 +52,10 @@ public interface IJavaEditorActionConstants {
5252
* @since 4.21
5353
*/
5454
String RAW_PASTE= "rawPaste"; //$NON-NLS-1$
55+
56+
/**
57+
* ID of the action to open method in a new tab.
58+
* @since 4.37
59+
*/
60+
String OPEN_METHOD="openMethod"; //$NON-NLS-1$
5561
}

org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/javaeditor/JavaEditor.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,7 @@
222222
import org.eclipse.jdt.internal.ui.actions.CompositeActionGroup;
223223
import org.eclipse.jdt.internal.ui.actions.CopyQualifiedNameAction;
224224
import org.eclipse.jdt.internal.ui.actions.FoldingActionGroup;
225+
import org.eclipse.jdt.internal.ui.actions.OpenMethod;
225226
import org.eclipse.jdt.internal.ui.actions.SelectionConverter;
226227
import org.eclipse.jdt.internal.ui.javaeditor.breadcrumb.EditorBreadcrumb;
227228
import org.eclipse.jdt.internal.ui.javaeditor.breadcrumb.IBreadcrumb;
@@ -2128,6 +2129,8 @@ public void editorContextMenuAboutToShow(IMenuManager menu) {
21282129
else
21292130
addAction(menu, ITextEditorActionConstants.GROUP_COPY, IJavaEditorActionConstants.RAW_PASTE);
21302131

2132+
action = getAction(IJavaEditorActionConstants.OPEN_METHOD);
2133+
menu.insertAfter(IJavaEditorActionDefinitionIds.OPEN_HIERARCHY, action);
21312134
}
21322135

21332136
/**
@@ -2283,8 +2286,7 @@ protected void setSelection(ISourceReference reference, boolean moveCursor) {
22832286
return;
22842287

22852288
ISelection selection= getSelectionProvider().getSelection();
2286-
if (selection instanceof ITextSelection) {
2287-
ITextSelection textSelection= (ITextSelection) selection;
2289+
if (selection instanceof ITextSelection textSelection) {
22882290
// PR 39995: [navigation] Forward history cleared after going back in navigation history:
22892291
// mark only in navigation history if the cursor is being moved (which it isn't if
22902292
// this is called from a PostSelectionEvent that should only update the magnet)
@@ -2782,6 +2784,10 @@ protected void createActions() {
27822784
action.setActionDefinitionId(CopyQualifiedNameAction.ACTION_DEFINITION_ID);
27832785
action.setImageDescriptor(null);
27842786
setAction(IJavaEditorActionConstants.COPY_QUALIFIED_NAME, action);
2787+
2788+
action= new OpenMethod(this);
2789+
action.setImageDescriptor(null);
2790+
setAction(IJavaEditorActionConstants.OPEN_METHOD, action);
27852791
}
27862792

27872793
/**

0 commit comments

Comments
 (0)