Skip to content

Commit 5f1b343

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 f9dd240 commit 5f1b343

6 files changed

Lines changed: 199 additions & 4 deletions

File tree

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

Lines changed: 7 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
@@ -464,4 +464,10 @@ private ActionMessages() {
464464
public static String GenerateToStringAction_tostring;
465465
public static String GenerateToStringAction_error_caption;
466466

467+
public static String OpenMethodAction_ToolTipText;
468+
public static String OpenMethodAction_InfoDialogTitle;
469+
public static String OpenMethodAction_ActionName;
470+
public static String OpenMethodAction_NoElementToQualify;
471+
472+
467473
}

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

Lines changed: 6 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,8 @@ 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_InfoDialogTitle=Open Method
497+
OpenMethodAction_ActionName=Open Method
498+
OpenMethodAction_NoElementToQualify=Please select a valid method.
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2025 IBM Corporation and others.
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.dialogs.MessageDialog;
19+
import org.eclipse.jface.viewers.IStructuredSelection;
20+
21+
import org.eclipse.jface.text.source.ISourceViewer;
22+
23+
import org.eclipse.ui.IEditorPart;
24+
import org.eclipse.ui.IWorkbenchPage;
25+
import org.eclipse.ui.IWorkbenchSite;
26+
import org.eclipse.ui.IWorkbenchWindow;
27+
import org.eclipse.ui.PartInitException;
28+
import org.eclipse.ui.PlatformUI;
29+
30+
import org.eclipse.jdt.core.IJavaElement;
31+
import org.eclipse.jdt.core.IMethod;
32+
import org.eclipse.jdt.core.ITypeRoot;
33+
import org.eclipse.jdt.core.JavaModelException;
34+
import org.eclipse.jdt.core.dom.ASTNode;
35+
import org.eclipse.jdt.core.dom.CompilationUnit;
36+
import org.eclipse.jdt.core.dom.IBinding;
37+
import org.eclipse.jdt.core.dom.Name;
38+
import org.eclipse.jdt.core.dom.NodeFinder;
39+
import org.eclipse.jdt.core.manipulation.SharedASTProviderCore;
40+
41+
import org.eclipse.jdt.ui.JavaUI;
42+
import org.eclipse.jdt.ui.actions.SelectionDispatchAction;
43+
44+
import org.eclipse.jdt.internal.ui.JavaPlugin;
45+
import org.eclipse.jdt.internal.ui.javaeditor.EditorUtility;
46+
import org.eclipse.jdt.internal.ui.javaeditor.JavaEditor;
47+
48+
49+
public class OpenMethod extends SelectionDispatchAction {
50+
51+
public static final String ACTION_DEFINITION_ID= "org.eclipse.jdt.ui.edit.text.java.open.method"; //$NON-NLS-1$
52+
53+
public static final String ACTION_HANDLER_ID= "org.eclipse.jdt.ui.actions.OpenMethod"; //$NON-NLS-1$
54+
55+
private JavaEditor fEditor;
56+
57+
public OpenMethod(JavaEditor editor) {
58+
this(editor.getSite());
59+
fEditor= editor;
60+
setEnabled(true);
61+
}
62+
63+
public OpenMethod(IWorkbenchSite site) {
64+
super(site);
65+
setText(ActionMessages.OpenMethodAction_ActionName);
66+
setToolTipText(ActionMessages.OpenMethodAction_ToolTipText);
67+
}
68+
69+
@Override
70+
public void selectionChanged(IStructuredSelection selection) {
71+
setEnabled(canEnable(selection.toArray()));
72+
}
73+
74+
private boolean canEnable(Object[] objects) {
75+
for (Object element : objects) {
76+
if (isValidElement(element))
77+
return true;
78+
}
79+
return false;
80+
}
81+
82+
private boolean isValidElement(Object element) {
83+
if(element instanceof IMethod) {
84+
return true;
85+
}
86+
return false;
87+
}
88+
89+
@Override
90+
public void run() {
91+
92+
Object[] elements= getSelectedElements();
93+
if (elements == null) {
94+
return;
95+
}
96+
if (elements.length == 1) {
97+
Object element= elements[0];
98+
if (element instanceof IMethod method) {
99+
openMethodInClonedEditor(method);
100+
return;
101+
}
102+
103+
}
104+
}
105+
106+
private Object[] getSelectedElements() {
107+
if (fEditor != null) {
108+
Object element= getSelectedElement(fEditor);
109+
if (element!= null) {
110+
if(isValidElement(element)){
111+
return new Object[] { element };
112+
}
113+
}
114+
}
115+
MessageDialog.openInformation(getShell(), ActionMessages.OpenMethodAction_InfoDialogTitle, ActionMessages.OpenMethodAction_NoElementToQualify);
116+
return null;
117+
}
118+
119+
private Object getSelectedElement(JavaEditor editor) {
120+
ISourceViewer viewer= editor.getViewer();
121+
if (viewer == null)
122+
return null;
123+
124+
Point selectedRange= viewer.getSelectedRange();
125+
int length= selectedRange.y;
126+
int offset= selectedRange.x;
127+
128+
ITypeRoot element= JavaUI.getEditorInputTypeRoot(editor.getEditorInput());
129+
if (element == null)
130+
return null;
131+
132+
CompilationUnit ast= SharedASTProviderCore.getAST(element, SharedASTProviderCore.WAIT_YES, null);
133+
if (ast == null)
134+
return null;
135+
136+
NodeFinder finder= new NodeFinder(ast, offset, length);
137+
ASTNode node= finder.getCoveringNode();
138+
IBinding binding= null;
139+
if (node instanceof Name nameNode) {
140+
binding=nameNode.resolveBinding();
141+
return binding.getJavaElement();
142+
}
143+
return null;
144+
}
145+
146+
protected void openMethodInClonedEditor(IMethod method) {
147+
try {
148+
IEditorPart target = null;
149+
IEditorPart existingEditr = EditorUtility.isOpenInEditor(method);
150+
IJavaElement methodElement= method;
151+
if(existingEditr!=null) { // Cloning
152+
IWorkbenchWindow window= PlatformUI.getWorkbench().getActiveWorkbenchWindow();
153+
IWorkbenchPage page= window.getActivePage();
154+
target = page.openEditor(
155+
existingEditr.getEditorInput(),
156+
existingEditr.getSite().getId(),
157+
true,
158+
IWorkbenchPage.MATCH_NONE);
159+
} else {
160+
target = JavaUI.openInEditor(method, true, false);
161+
}
162+
JavaUI.revealInEditor(target, methodElement);
163+
} catch (PartInitException | JavaModelException e) {
164+
JavaPlugin.log(e);
165+
}
166+
}
167+
168+
}

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: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,7 @@
219219
import org.eclipse.jdt.internal.ui.actions.CompositeActionGroup;
220220
import org.eclipse.jdt.internal.ui.actions.CopyQualifiedNameAction;
221221
import org.eclipse.jdt.internal.ui.actions.FoldingActionGroup;
222+
import org.eclipse.jdt.internal.ui.actions.OpenMethod;
222223
import org.eclipse.jdt.internal.ui.actions.SelectionConverter;
223224
import org.eclipse.jdt.internal.ui.javaeditor.breadcrumb.EditorBreadcrumb;
224225
import org.eclipse.jdt.internal.ui.javaeditor.breadcrumb.IBreadcrumb;
@@ -2126,6 +2127,8 @@ public void editorContextMenuAboutToShow(IMenuManager menu) {
21262127
else
21272128
addAction(menu, ITextEditorActionConstants.GROUP_COPY, IJavaEditorActionConstants.RAW_PASTE);
21282129

2130+
action = getAction(IJavaEditorActionConstants.OPEN_METHOD);
2131+
menu.insertAfter(IJavaEditorActionDefinitionIds.OPEN_HIERARCHY, action);
21292132
}
21302133

21312134
/**
@@ -2281,8 +2284,7 @@ protected void setSelection(ISourceReference reference, boolean moveCursor) {
22812284
return;
22822285

22832286
ISelection selection= getSelectionProvider().getSelection();
2284-
if (selection instanceof ITextSelection) {
2285-
ITextSelection textSelection= (ITextSelection) selection;
2287+
if (selection instanceof ITextSelection textSelection) {
22862288
// PR 39995: [navigation] Forward history cleared after going back in navigation history:
22872289
// mark only in navigation history if the cursor is being moved (which it isn't if
22882290
// this is called from a PostSelectionEvent that should only update the magnet)
@@ -2780,6 +2782,11 @@ protected void createActions() {
27802782
action.setActionDefinitionId(CopyQualifiedNameAction.ACTION_DEFINITION_ID);
27812783
action.setImageDescriptor(null);
27822784
setAction(IJavaEditorActionConstants.COPY_QUALIFIED_NAME, action);
2785+
2786+
action= new OpenMethod(this);
2787+
action.setActionDefinitionId(OpenMethod.ACTION_DEFINITION_ID);
2788+
action.setImageDescriptor(null);
2789+
setAction(IJavaEditorActionConstants.OPEN_METHOD, action);
27832790
}
27842791

27852792
/**

0 commit comments

Comments
 (0)