diff --git a/org.eclipse.jdt.core.manipulation/common/org/eclipse/jdt/internal/ui/text/correction/CorrectionMessages.java b/org.eclipse.jdt.core.manipulation/common/org/eclipse/jdt/internal/ui/text/correction/CorrectionMessages.java index 4088df6af1d..7abf61c83e5 100644 --- a/org.eclipse.jdt.core.manipulation/common/org/eclipse/jdt/internal/ui/text/correction/CorrectionMessages.java +++ b/org.eclipse.jdt.core.manipulation/common/org/eclipse/jdt/internal/ui/text/correction/CorrectionMessages.java @@ -515,4 +515,5 @@ private CorrectionMessages() { public static String PreviewFeaturesSubProcessor_open_compliance_page_enable_preview_features_info; public static String PreviewFeaturesSubProcessor_open_compliance_properties_page_enable_preview_features; public static String PreviewFeaturesSubProcessor_open_compliance_properties_page_enable_preview_features_info; + public static String OpenExistingDeclaration; } diff --git a/org.eclipse.jdt.core.manipulation/common/org/eclipse/jdt/internal/ui/text/correction/CorrectionMessages.properties b/org.eclipse.jdt.core.manipulation/common/org/eclipse/jdt/internal/ui/text/correction/CorrectionMessages.properties index b5b682f967f..48ff066124f 100644 --- a/org.eclipse.jdt.core.manipulation/common/org/eclipse/jdt/internal/ui/text/correction/CorrectionMessages.properties +++ b/org.eclipse.jdt.core.manipulation/common/org/eclipse/jdt/internal/ui/text/correction/CorrectionMessages.properties @@ -157,6 +157,7 @@ TypeMismatchSubProcessor_insertnullcheck_description=Insert '!= null' check TypeMismatchSubProcessor_changetooptionalempty_description=Change to empty Optional TypeMismatchSubProcessor_changetooptionalof_description=Wrap with Optional TypeMismatchSubProcessor_changetooptionalofnullable_description=Wrap with nullable Optional +OpenExistingDeclaration=Open Declaration RemoveDeclarationCorrectionProposal_removeunusedfield_description=Remove declaration of ''{0}'' and assignments without possible side effects RemoveDeclarationCorrectionProposal_removeunusedmethod_description=Remove method ''{0}'' diff --git a/org.eclipse.jdt.core.manipulation/common/org/eclipse/jdt/internal/ui/text/correction/LocalCorrectionsBaseSubProcessor.java b/org.eclipse.jdt.core.manipulation/common/org/eclipse/jdt/internal/ui/text/correction/LocalCorrectionsBaseSubProcessor.java index a8601db58aa..f4aefdedca0 100644 --- a/org.eclipse.jdt.core.manipulation/common/org/eclipse/jdt/internal/ui/text/correction/LocalCorrectionsBaseSubProcessor.java +++ b/org.eclipse.jdt.core.manipulation/common/org/eclipse/jdt/internal/ui/text/correction/LocalCorrectionsBaseSubProcessor.java @@ -190,6 +190,7 @@ import org.eclipse.jdt.internal.ui.text.correction.proposals.GenerateForLoopAssistProposalCore; import org.eclipse.jdt.internal.ui.text.correction.proposals.LinkedCorrectionProposalCore; import org.eclipse.jdt.internal.ui.text.correction.proposals.LinkedNamesAssistProposalCore; +import org.eclipse.jdt.internal.ui.text.correction.proposals.LinkedOpenDeclarationProposalCore; import org.eclipse.jdt.internal.ui.text.correction.proposals.MissingAnnotationAttributesProposalCore; import org.eclipse.jdt.internal.ui.text.correction.proposals.ModifierChangeCorrectionProposalCore; import org.eclipse.jdt.internal.ui.text.correction.proposals.NewLocalVariableCorrectionProposalCore; @@ -1130,6 +1131,10 @@ public void getInvalidVariableNameProposals(IInvocationContext context, IProblem LinkedNamesAssistProposalCore proposalCore= new LinkedNamesAssistProposalCore(name, context, nameNode, valueSuggestion); proposals.add(linkedNamesAssistProposalToT(proposalCore)); + if(nameNode.getParent() instanceof VariableDeclaration) { + LinkedOpenDeclarationProposalCore showExisting= new LinkedOpenDeclarationProposalCore(CorrectionMessages.OpenExistingDeclaration,context, nameNode); + proposals.add(LinkedNamesAssistShowDuplicateProposalToT(showExisting)); + } } private void addRemoveProposal(IInvocationContext context, ASTNode selectedNode, Collection proposals) { @@ -3423,6 +3428,7 @@ protected LocalCorrectionsBaseSubProcessor() { protected abstract T replaceCorrectionProposalToT(ReplaceCorrectionProposalCore core, int uid); protected abstract T cuCorrectionProposalToT(CUCorrectionProposalCore core, int uid); protected abstract T linkedNamesAssistProposalToT(LinkedNamesAssistProposalCore core); + protected abstract T LinkedNamesAssistShowDuplicateProposalToT(LinkedOpenDeclarationProposalCore core); protected abstract T assignToVariableAssistProposalToT(AssignToVariableAssistProposalCore core); protected abstract T newVariableCorrectionProposalToT(NewVariableCorrectionProposalCore core, int uid); protected abstract T newLocalVariableCorrectionProposalToT(NewLocalVariableCorrectionProposalCore core, int uid); diff --git a/org.eclipse.jdt.core.manipulation/proposals/org/eclipse/jdt/internal/ui/text/correction/proposals/LinkedOpenDeclarationProposalCore.java b/org.eclipse.jdt.core.manipulation/proposals/org/eclipse/jdt/internal/ui/text/correction/proposals/LinkedOpenDeclarationProposalCore.java new file mode 100644 index 00000000000..c39f9769094 --- /dev/null +++ b/org.eclipse.jdt.core.manipulation/proposals/org/eclipse/jdt/internal/ui/text/correction/proposals/LinkedOpenDeclarationProposalCore.java @@ -0,0 +1,53 @@ +/******************************************************************************* + * Copyright (c) 2025 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.ui.text.correction.proposals; + +import org.eclipse.jdt.core.dom.SimpleName; +import org.eclipse.jdt.core.manipulation.CUCorrectionProposalCore; + +import org.eclipse.jdt.ui.text.java.IInvocationContext; + +import org.eclipse.jdt.internal.ui.text.correction.IProposalRelevance; + + +/** + * A template proposal. + */ +public class LinkedOpenDeclarationProposalCore extends CUCorrectionProposalCore { + + public static final String ASSIST_ID= "org.eclipse.jdt.ui.correction.showOriginalDeclaration.assist"; //$NON-NLS-1$ + + private SimpleName fNode; + private String fLabel; + + public LinkedOpenDeclarationProposalCore(String label, IInvocationContext context, SimpleName node) { + super(label, context.getCompilationUnit(), IProposalRelevance.LINKED_NAMES_ASSIST); + fLabel= label; + fNode= node; + } + + public String getLabel() { + return fLabel; + } + + public SimpleName getNode() { + return fNode; + } + + @Override + public String getCommandId() { + return ASSIST_ID; + } + +} diff --git a/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/LocalCorrectionsQuickFixTest.java b/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/LocalCorrectionsQuickFixTest.java index cb887b3fc47..1b4b31ff5c2 100644 --- a/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/LocalCorrectionsQuickFixTest.java +++ b/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/LocalCorrectionsQuickFixTest.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2024 IBM Corporation and others. + * Copyright (c) 2000, 2025 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -65,6 +65,7 @@ import org.eclipse.jdt.internal.ui.text.correction.CorrectionMessages; import org.eclipse.jdt.internal.ui.text.correction.JavaCorrectionProcessor; import org.eclipse.jdt.internal.ui.text.correction.proposals.LinkedNamesAssistProposal; +import org.eclipse.jdt.internal.ui.text.correction.proposals.LinkedOpenDeclarationProposal; public class LocalCorrectionsQuickFixTest extends QuickFixTest { @@ -7763,7 +7764,7 @@ public void foo() { CompilationUnit astRoot= getASTRoot(cu); ArrayList proposals= collectCorrections(cu, astRoot); - assertNumberOfProposals(proposals, 2); + assertNumberOfProposals(proposals, 3); assertCorrectLabels(proposals); assertTrue(proposals.get(0) instanceof LinkedNamesAssistProposal); } @@ -7788,7 +7789,7 @@ public void foo(int count) { CompilationUnit astRoot= getASTRoot(cu); ArrayList proposals= collectCorrections(cu, astRoot); - assertNumberOfProposals(proposals, 2); + assertNumberOfProposals(proposals, 3); assertCorrectLabels(proposals); assertTrue(proposals.get(0) instanceof LinkedNamesAssistProposal); } @@ -7815,7 +7816,7 @@ class Inner { CompilationUnit astRoot= getASTRoot(cu); ArrayList proposals= collectCorrections(cu, astRoot); - assertNumberOfProposals(proposals, 2); + assertNumberOfProposals(proposals, 3); assertCorrectLabels(proposals); assertTrue(proposals.get(0) instanceof LinkedNamesAssistProposal); } @@ -7843,7 +7844,7 @@ class Inner { CompilationUnit astRoot= getASTRoot(cu); ArrayList proposals= collectCorrections(cu, astRoot); - assertNumberOfProposals(proposals, 2); + assertNumberOfProposals(proposals, 3); assertCorrectLabels(proposals); assertTrue(proposals.get(0) instanceof LinkedNamesAssistProposal); } @@ -7872,7 +7873,7 @@ public void foo() { CompilationUnit astRoot= getASTRoot(cu); ArrayList proposals= collectCorrections(cu, astRoot); - assertNumberOfProposals(proposals, 2); + assertNumberOfProposals(proposals, 3); assertCorrectLabels(proposals); assertTrue(proposals.get(0) instanceof LinkedNamesAssistProposal); } @@ -7900,7 +7901,7 @@ public void foo(int count) { CompilationUnit astRoot= getASTRoot(cu); ArrayList proposals= collectCorrections(cu, astRoot); - assertNumberOfProposals(proposals, 2); + assertNumberOfProposals(proposals, 3); assertCorrectLabels(proposals); assertTrue(proposals.get(0) instanceof LinkedNamesAssistProposal); } @@ -13602,4 +13603,23 @@ public E() { assertExpectedExistInProposals(proposals, expected); } + + @Test + public void testNavigateToInitialVariableAssist() throws Exception { + IPackageFragment pack1= fSourceFolder.createPackageFragment("test1", false, null); + String str= """ + package test1; + public class E { + public void foo(int x) { + double x; + } + } + """; + ICompilationUnit cu= pack1.createCompilationUnit("E.java", str, false, null); + + CompilationUnit astRoot= getASTRoot(cu); + ArrayList proposals= collectCorrections(cu, astRoot); + assertNumberOfProposals(proposals, 2); + assertTrue(proposals.get(1) instanceof LinkedOpenDeclarationProposal); + } } diff --git a/org.eclipse.jdt.ui/icons/full/obj16/show_declaration.svg b/org.eclipse.jdt.ui/icons/full/obj16/show_declaration.svg new file mode 100644 index 00000000000..1015f7096b4 --- /dev/null +++ b/org.eclipse.jdt.ui/icons/full/obj16/show_declaration.svg @@ -0,0 +1,98 @@ + + + +image/svg+xml diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/JavaPluginImages.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/JavaPluginImages.java index d20e83963e9..116351fbb77 100644 --- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/JavaPluginImages.java +++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/JavaPluginImages.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2020 IBM Corporation and others. + * Copyright (c) 2000, 2025 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -530,6 +530,7 @@ public class JavaPluginImages { public static final String IMG_CORRECTION_ADD= NAME_PREFIX + "add_correction.gif"; //$NON-NLS-1$ public static final String IMG_CORRECTION_CAST= NAME_PREFIX + "correction_cast.gif"; //$NON-NLS-1$ public static final String IMG_CORRECTION_MULTI_FIX= NAME_PREFIX + "correction_multi_fix.gif"; //$NON-NLS-1$ + public static final String IMG_SHOW_DECLARATION = NAME_PREFIX + "show_declaration.svg"; //$NON-NLS-1$ static { createManagedFromKey(T_OBJ, IMG_CORRECTION_CHANGE); @@ -546,6 +547,7 @@ public class JavaPluginImages { createManagedFromKey(T_OBJ, IMG_OBJS_WARNING_ALT); createManagedFromKey(T_OBJ, IMG_OBJS_INFO_ALT); createManagedFromKey(T_OBJ, IMG_BLANK); + createManagedFromKey(T_OBJ, IMG_SHOW_DECLARATION); } private static class SmallIntMap { diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/LocalCorrectionsSubProcessor.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/LocalCorrectionsSubProcessor.java index 52981d87118..52bd2c822e8 100644 --- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/LocalCorrectionsSubProcessor.java +++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/LocalCorrectionsSubProcessor.java @@ -125,6 +125,8 @@ import org.eclipse.jdt.internal.ui.text.correction.proposals.LinkedCorrectionProposalCore; import org.eclipse.jdt.internal.ui.text.correction.proposals.LinkedNamesAssistProposal; import org.eclipse.jdt.internal.ui.text.correction.proposals.LinkedNamesAssistProposalCore; +import org.eclipse.jdt.internal.ui.text.correction.proposals.LinkedOpenDeclarationProposalCore; +import org.eclipse.jdt.internal.ui.text.correction.proposals.LinkedOpenDeclarationProposal; import org.eclipse.jdt.internal.ui.text.correction.proposals.MissingAnnotationAttributesProposal; import org.eclipse.jdt.internal.ui.text.correction.proposals.MissingAnnotationAttributesProposalCore; import org.eclipse.jdt.internal.ui.text.correction.proposals.ModifierChangeCorrectionProposal; @@ -815,4 +817,8 @@ protected ICommandAccess generateForLoopAssistProposalToT(GenerateForLoopAssistP protected ICommandAccess linkedNamesAssistProposalToT(LinkedNamesAssistProposalCore core) { return new LinkedNamesAssistProposal(core.getLabel(), core.getContext(), core.getNode(), core.getValueSuggestion()); } -} + @Override + protected ICommandAccess LinkedNamesAssistShowDuplicateProposalToT(LinkedOpenDeclarationProposalCore core) { + return new LinkedOpenDeclarationProposal(core.getLabel(), core.getNode()); + } +} \ No newline at end of file diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/QuickFixProcessor.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/QuickFixProcessor.java index d410b20c2a2..f139c2cc111 100644 --- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/QuickFixProcessor.java +++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/QuickFixProcessor.java @@ -691,6 +691,7 @@ private void process(IInvocationContext context, IProblemLocation problem, Colle case IProblem.DuplicateMethod: case IProblem.DuplicateTypeVariable: case IProblem.DuplicateNestedType: + case IProblem.PatternVariableRedeclared: LocalCorrectionsSubProcessor.addInvalidVariableNameProposals(context, problem, proposals); break; case IProblem.NoMessageSendOnArrayType: diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/proposals/LinkedOpenDeclarationProposal.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/proposals/LinkedOpenDeclarationProposal.java new file mode 100644 index 00000000000..b99f128998d --- /dev/null +++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/proposals/LinkedOpenDeclarationProposal.java @@ -0,0 +1,157 @@ +/******************************************************************************* + * Copyright (c) 2025 IBM Corporation. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.ui.text.correction.proposals; + +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.Point; + +import org.eclipse.jface.text.DocumentEvent; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.ITextViewer; +import org.eclipse.jface.text.contentassist.ICompletionProposalExtension2; +import org.eclipse.jface.text.contentassist.IContextInformation; + +import org.eclipse.ui.IWorkbenchPage; + +import org.eclipse.ui.texteditor.ITextEditor; + +import org.eclipse.jdt.core.dom.ASTNode; +import org.eclipse.jdt.core.dom.ASTVisitor; +import org.eclipse.jdt.core.dom.SimpleName; +import org.eclipse.jdt.core.dom.SingleVariableDeclaration; +import org.eclipse.jdt.core.dom.VariableDeclaration; +import org.eclipse.jdt.core.dom.VariableDeclarationFragment; + +import org.eclipse.jdt.ui.text.java.IJavaCompletionProposal; +import org.eclipse.jdt.ui.text.java.correction.ICommandAccess; + +import org.eclipse.jdt.internal.ui.JavaPlugin; +import org.eclipse.jdt.internal.ui.JavaPluginImages; + + +/** + * Quick Navigation proposal. + */ +public class LinkedOpenDeclarationProposal implements IJavaCompletionProposal, ICompletionProposalExtension2, ICommandAccess { + + public static final String ASSIST_ID= "org.eclipse.jdt.ui.correction.showOriginalDeclaration.assist"; //$NON-NLS-1$ + private SimpleName fNode; + private String fLabel; + + public LinkedOpenDeclarationProposal(String label, SimpleName node) { + fLabel= label; + fNode= node; + } + + @Override + public void apply(ITextViewer viewer, char trigger, int stateMask, int offset) { + IWorkbenchPage page= JavaPlugin.getActiveWorkbenchWindow().getActivePage(); + if (page == null) { + return; + } + if (page.getActiveEditor() instanceof ITextEditor textEditor) { + final ASTNode[] result = new ASTNode[1]; + String varName = fNode.getFullyQualifiedName(); + ASTNode scopeOwner = fNode; + boolean[] found= { false }; + while (scopeOwner != null && !found[0]) { + + scopeOwner.accept(new ASTVisitor() { + @Override + public boolean visit(VariableDeclarationFragment node) { + if (!found[0] && node.getName().getIdentifier().equals(varName) && node.getStartPosition() != fNode.getStartPosition()) { + result[0]= node; + found[0]= true; + return false; + } + return true; + } + + @Override + public boolean visit(SingleVariableDeclaration node) { + if (!found[0] && node.getName().getIdentifier().equals(varName) && node.getStartPosition() != fNode.getStartPosition()) { + result[0]= node; + found[0]= true; + return false; + } + return true; + } + }); + scopeOwner= scopeOwner.getParent(); + } + if (scopeOwner == null) { + return; + } + + ASTNode decl= result[0]; + SimpleName name= ((VariableDeclaration) decl).getName(); + int start= name.getStartPosition(); + int length= name.getLength(); + textEditor.selectAndReveal(start, length); + } + } + @Override + public void apply(IDocument document) { + } + + @Override + public Point getSelection(IDocument document) { + return null; + } + + @Override + public String getAdditionalProposalInfo() { + return null; + } + + @Override + public String getDisplayString() { + return fLabel; + } + + @Override + public Image getImage() { + return JavaPluginImages.get(JavaPluginImages.IMG_SHOW_DECLARATION); + } + + @Override + public IContextInformation getContextInformation() { + return null; + } + + + @Override + public void selected(ITextViewer textViewer, boolean smartToggle) { + } + + @Override + public void unselected(ITextViewer textViewer) { + } + + @Override + public boolean validate(IDocument document, int offset, DocumentEvent event) { + return false; + } + + @Override + public String getCommandId() { + return ASSIST_ID; + } + + @Override + public int getRelevance() { + return 0; + } + +} diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/java/hover/AbstractAnnotationHover.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/java/hover/AbstractAnnotationHover.java index 584fc54fa14..d270acd8815 100644 --- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/java/hover/AbstractAnnotationHover.java +++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/java/hover/AbstractAnnotationHover.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2023 IBM Corporation and others. + * Copyright (c) 2000, 2025 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -14,8 +14,10 @@ package org.eclipse.jdt.internal.ui.text.java.hover; import java.util.ArrayList; +import java.util.Arrays; import java.util.Iterator; import java.util.List; +import java.util.Optional; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.ScrolledComposite; @@ -94,6 +96,7 @@ import org.eclipse.jdt.internal.ui.JavaPluginImages; import org.eclipse.jdt.internal.ui.javaeditor.JavaAnnotationIterator; import org.eclipse.jdt.internal.ui.text.correction.proposals.FixCorrectionProposal; +import org.eclipse.jdt.internal.ui.text.correction.proposals.LinkedOpenDeclarationProposal; /** @@ -365,7 +368,9 @@ private void createCompletionProposalsControl(Composite parent, ICompletionPropo layoutData.horizontalIndent= 4; quickFixLabel.setLayoutData(layoutData); String text; - if (proposals.length == 1) { + Optional showDeclaration = Arrays.asList(proposals).stream().filter(e -> e instanceof LinkedOpenDeclarationProposal).findFirst(); + int proposalsLen = showDeclaration.isPresent() ? proposals.length - 1 : proposals.length; + if (proposalsLen == 1) { text= JavaHoverMessages.AbstractAnnotationHover_message_singleQuickFix; } else { text= Messages.format(JavaHoverMessages.AbstractAnnotationHover_message_multipleQuickFix, new Object[] { String.valueOf(proposals.length) });