diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/NavigateToDefinitionHandler.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/NavigateToDefinitionHandler.java index de42c82beb..be41196c1e 100644 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/NavigateToDefinitionHandler.java +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/NavigateToDefinitionHandler.java @@ -15,6 +15,7 @@ import java.util.List; import org.apache.commons.lang3.StringUtils; +import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.jdt.core.IClassFile; import org.eclipse.jdt.core.ICompilationUnit; @@ -24,6 +25,23 @@ import org.eclipse.jdt.core.ITypeRoot; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.JavaModelException; +import org.eclipse.jdt.core.dom.ASTNode; +import org.eclipse.jdt.core.dom.BreakStatement; +import org.eclipse.jdt.core.dom.CompilationUnit; +import org.eclipse.jdt.core.dom.ContinueStatement; +import org.eclipse.jdt.core.dom.DoStatement; +import org.eclipse.jdt.core.dom.EnhancedForStatement; +import org.eclipse.jdt.core.dom.ForStatement; +import org.eclipse.jdt.core.dom.Initializer; +import org.eclipse.jdt.core.dom.LabeledStatement; +import org.eclipse.jdt.core.dom.MethodDeclaration; +import org.eclipse.jdt.core.dom.NodeFinder; +import org.eclipse.jdt.core.dom.SimpleName; +import org.eclipse.jdt.core.dom.SwitchExpression; +import org.eclipse.jdt.core.dom.SwitchStatement; +import org.eclipse.jdt.core.dom.WhileStatement; +import org.eclipse.jdt.core.manipulation.SharedASTProviderCore; +import org.eclipse.jdt.internal.corext.dom.TokenScanner; import org.eclipse.jdt.ls.core.internal.JDTUtils; import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin; import org.eclipse.jdt.ls.core.internal.managers.ProjectsManager; @@ -52,14 +70,83 @@ public List definition(TextDocumentPositionParams position, private Location computeDefinitionNavigation(ITypeRoot unit, int line, int column, IProgressMonitor monitor) { try { IJavaElement element = JDTUtils.findElementAtSelection(unit, line, column, this.preferenceManager, monitor); + if (element == null) { + return computeBreakContinue(unit, line, column); + } return computeDefinitionNavigation(element, unit.getJavaProject()); - } catch (JavaModelException e) { + } catch (CoreException e) { JavaLanguageServerPlugin.logException("Problem computing definition for" + unit.getElementName(), e); } return null; } + private Location computeBreakContinue(ITypeRoot typeRoot, int line, int column) throws CoreException { + int offset = JsonRpcHelpers.toOffset(typeRoot.getBuffer(), line, column); + if (offset >= 0) { + CompilationUnit unit = SharedASTProviderCore.getAST(typeRoot, SharedASTProviderCore.WAIT_YES, null); + if (unit == null) { + return null; + } + ASTNode selectedNode = NodeFinder.perform(unit, offset, 0); + ASTNode node = null; + SimpleName label = null; + if (selectedNode instanceof BreakStatement) { + node = selectedNode; + label = ((BreakStatement) node).getLabel(); + } else if (selectedNode instanceof ContinueStatement) { + node = selectedNode; + label = ((ContinueStatement) node).getLabel(); + } else if (selectedNode instanceof SimpleName && selectedNode.getParent() instanceof BreakStatement) { + node = selectedNode.getParent(); + label = ((BreakStatement) node).getLabel(); + } else if (selectedNode instanceof SimpleName && selectedNode.getParent() instanceof ContinueStatement) { + node = selectedNode.getParent(); + label = ((ContinueStatement) node).getLabel(); + } + if (node != null) { + ASTNode parent = node.getParent(); + ASTNode target = null; + while (parent != null) { + if (parent instanceof MethodDeclaration || parent instanceof Initializer) { + break; + } + if (label == null) { + if (parent instanceof ForStatement || parent instanceof EnhancedForStatement || parent instanceof WhileStatement || parent instanceof DoStatement) { + target = parent; + break; + } + if (node instanceof BreakStatement) { + if (parent instanceof SwitchStatement || parent instanceof SwitchExpression) { + target = parent; + break; + } + } + if (node instanceof LabeledStatement) { + target = parent; + break; + } + } else if (LabeledStatement.class.isInstance(parent)) { + LabeledStatement ls = (LabeledStatement) parent; + if (ls.getLabel().getIdentifier().equals(label.getIdentifier())) { + target = ls; + break; + } + } + parent = parent.getParent(); + } + if (target != null) { + int start = target.getStartPosition(); + int end = new TokenScanner(unit.getTypeRoot()).getNextEndOffset(node.getStartPosition(), true) - start; + if (start >= 0 && end >= 0) { + return JDTUtils.toLocation((ICompilationUnit) typeRoot, start, end); + } + } + } + } + return null; + } + public static Location computeDefinitionNavigation(IJavaElement element, IJavaProject javaProject) throws JavaModelException { if (element == null) { return null; diff --git a/org.eclipse.jdt.ls.tests/projects/maven/salut/src/main/java/org/sample/TestBreakContinue.java b/org.eclipse.jdt.ls.tests/projects/maven/salut/src/main/java/org/sample/TestBreakContinue.java new file mode 100644 index 0000000000..7f123075e2 --- /dev/null +++ b/org.eclipse.jdt.ls.tests/projects/maven/salut/src/main/java/org/sample/TestBreakContinue.java @@ -0,0 +1,26 @@ +package org.sample; + +public class TestBreakContinue { + + public static void main(String[] args) { + int i = 0; + outer: while (true) { + System.out.println(i); + while (true) { + i++; + if (i == 1) { + continue; // inner + } + if (i == 3) { + continue outer; + } + if (i == 5) { + break; //inner + } + if (i == 7) { + break outer; + } + } + } + } +} \ No newline at end of file diff --git a/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/handlers/NavigateToDefinitionHandlerTest.java b/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/handlers/NavigateToDefinitionHandlerTest.java index 55a56495dd..ec6d88b4ff 100644 --- a/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/handlers/NavigateToDefinitionHandlerTest.java +++ b/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/handlers/NavigateToDefinitionHandlerTest.java @@ -85,6 +85,40 @@ public void testJdkClasses() throws Exception { testClass("org.apache.commons.lang3.stringutils", 6579, 20); } + @Test + public void testBreakContinue() throws JavaModelException { + String uri = ClassFileUtil.getURI(project, "org.sample.TestBreakContinue"); + TextDocumentIdentifier identifier = new TextDocumentIdentifier(uri); + // continue + List definitions = handler.definition(new TextDocumentPositionParams(identifier, new Position(11, 5)), monitor); + assertNotNull(definitions); + assertEquals(1, definitions.size()); + Location definition = definitions.get(0); + assertEquals(8, definition.getRange().getStart().getLine()); + assertEquals(3, definition.getRange().getStart().getCharacter()); + // outer continue + definitions = handler.definition(new TextDocumentPositionParams(identifier, new Position(14, 5)), monitor); + assertNotNull(definitions); + assertEquals(1, definitions.size()); + definition = definitions.get(0); + assertEquals(6, definition.getRange().getStart().getLine()); + assertEquals(2, definition.getRange().getStart().getCharacter()); + // break + definitions = handler.definition(new TextDocumentPositionParams(identifier, new Position(17, 5)), monitor); + assertNotNull(definitions); + assertEquals(1, definitions.size()); + definition = definitions.get(0); + assertEquals(8, definition.getRange().getStart().getLine()); + assertEquals(3, definition.getRange().getStart().getCharacter()); + // outer break + definitions = handler.definition(new TextDocumentPositionParams(identifier, new Position(20, 5)), monitor); + assertNotNull(definitions); + assertEquals(1, definitions.size()); + definition = definitions.get(0); + assertEquals(6, definition.getRange().getStart().getLine()); + assertEquals(2, definition.getRange().getStart().getCharacter()); + } + private void testClass(String className, int line, int column) throws JavaModelException { String uri = ClassFileUtil.getURI(project, className); TextDocumentIdentifier identifier = new TextDocumentIdentifier(uri);