Bug 540325: [R-Debug] Improve support for breakpoints in nested functions

Change-Id: I3b490a0158db574ef49be2bc32b6704ab58dc4df
diff --git a/r/org.eclipse.statet.r.core/src/org/eclipse/statet/r/core/rsource/ast/RAst.java b/r/org.eclipse.statet.r.core/src/org/eclipse/statet/r/core/rsource/ast/RAst.java
index 91599d2..574de23 100644
--- a/r/org.eclipse.statet.r.core/src/org/eclipse/statet/r/core/rsource/ast/RAst.java
+++ b/r/org.eclipse.statet.r.core/src/org/eclipse/statet/r/core/rsource/ast/RAst.java
@@ -17,6 +17,7 @@
 import java.lang.reflect.InvocationTargetException;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.List;
 
 import org.eclipse.core.runtime.OperationCanceledException;
@@ -707,23 +708,23 @@
 			// part of parent element
 			case SUB_INDEXED_ARGS:
 				if (parent == baseNode) {
-					break;
+					continue;
 				}
 				topdown.add(parent.getChildIndex(node) + 3);
-				node= parent.getRParent(); 
-				break;
+				node= parent.getRParent();
+				continue;
 			case C_IN:
 			case F_CALL_ARGS:
 				if (parent == baseNode) {
-					break;
+					continue;
 				}
 				topdown.add(parent.getChildIndex(node) + 2);
-				node= parent.getRParent(); 
-				break;
+				node= parent.getRParent();
+				continue;
 			
-			case SUB_INDEXED_ARG:
-			case F_DEF_ARG:
-			case F_CALL_ARG:
+			case SUB_INDEXED_ARG: // -> ARGS
+			case F_DEF_ARG: // -> ARGS
+			case F_CALL_ARG: // -> ARGS
 				node= parent;
 				continue;
 			
@@ -744,6 +745,75 @@
 		return path;
 	}
 	
+	public static List<RAstNode> computeRExpressionNodes(RAstNode node, final RAstNode baseNode) {
+		final List<RAstNode> nodes= new ArrayList<>();
+		while (node != baseNode) {
+			switch (node.getNodeType()) {
+			
+			// list
+			case SOURCELINES:
+			// [[1]]= name
+			case F_CALL:
+			case BLOCK:
+			case GROUP:
+			case SUB_INDEXED_S:
+			case SUB_INDEXED_D:
+			case NS_GET:
+			case NS_GET_INT:
+			case SUB_NAMED_PART:
+			case SUB_NAMED_SLOT:
+			case POWER:
+			case SIGN:
+			case SEQ:
+			case SPECIAL:
+			case MULT:
+			case ADD:
+			case RELATIONAL:
+			case NOT:
+			case AND:
+			case OR:
+			case MODEL:
+			case A_LEFT:
+			case A_RIGHT:
+			case A_EQUALS:
+			case A_COLON:
+			case HELP:
+			case C_IF:
+			case C_FOR:
+			case C_WHILE:
+			case C_REPEAT:
+			case F_DEF:
+			case F_DEF_ARGS:
+				nodes.add(node);
+				node= node.getRParent();
+				continue;
+			
+			// part of parent element
+			case SUB_INDEXED_ARGS:
+			case C_IN:
+			case F_CALL_ARGS:
+			
+			case SUB_INDEXED_ARG: // -> ARGS
+			case F_DEF_ARG: // -> ARGS
+			case F_CALL_ARG: // -> ARGS
+				node= node.getRParent();
+				continue;
+			
+			case ERROR:
+			case ERROR_TERM:
+			case DUMMY:
+				return null;
+			
+			default:
+				throw new IllegalStateException("Unexpected parent");
+			}
+		}
+		if (nodes.size() > 1) {
+			Collections.reverse(nodes);
+		}
+		return nodes;
+	}
+	
 	public static RAstNode getRRootNode(RAstNode node, final IRegion region) {
 		if (region == null) {
 			return node.getRRoot();
diff --git a/r/org.eclipse.statet.r.debug.core/src/org/eclipse/statet/internal/r/debug/core/breakpoints/RControllerBreakpointAdapter.java b/r/org.eclipse.statet.r.debug.core/src/org/eclipse/statet/internal/r/debug/core/breakpoints/RControllerBreakpointAdapter.java
index eca23cb..c56409c 100644
--- a/r/org.eclipse.statet.r.debug.core/src/org/eclipse/statet/internal/r/debug/core/breakpoints/RControllerBreakpointAdapter.java
+++ b/r/org.eclipse.statet.r.debug.core/src/org/eclipse/statet/internal/r/debug/core/breakpoints/RControllerBreakpointAdapter.java
@@ -91,6 +91,7 @@
 import org.eclipse.statet.r.nico.AbstractRDbgController.IRControllerTracepointAdapter;
 import org.eclipse.statet.r.nico.IRModelSrcref;
 import org.eclipse.statet.r.nico.IRSrcref;
+import org.eclipse.statet.r.nico.RSrcref;
 import org.eclipse.statet.rj.data.REnvironment;
 import org.eclipse.statet.rj.server.dbg.DbgEnablement;
 import org.eclipse.statet.rj.server.dbg.ElementTracepointInstallationRequest;
@@ -117,14 +118,10 @@
 		
 		public Position(final int type, final long id, final int[] exprIndex,
 				final IRLineBreakpoint breakpoint) {
-			super(type, id, exprIndex, null);
+			super(type, id, exprIndex);
 			this.breakpoint= breakpoint;
 		}
 		
-		void setExprSrcref(final int @Nullable [] srcref) {
-			this.exprSrcref= srcref;
-		}
-		
 		public IRLineBreakpoint getBreakpoint() {
 			return this.breakpoint;
 		}
@@ -1298,10 +1295,16 @@
 		if (!elementPositions.getPositions().contains(position)) {
 			{	final int[] elementSrcref= elementPositions.getElementSrcref();
 				if (elementSrcref != null) {
-					final IRSrcref rExpressionSrcref= validator.computeRExpressionSrcref();
-					if (rExpressionSrcref != null) {
-						position.setExprSrcref(Srcref.diff(elementSrcref,
-								RDbg.createRJSrcref(rExpressionSrcref) ));
+					final RSrcref[] rExpressionSrcrefs= validator.computeRExpressionSrcrefs();
+					if (rExpressionSrcrefs != null) {
+						final int [] @Nullable [] srcrefs= position.getSrcrefs();
+						for (int i= 0; i < rExpressionSrcrefs.length; i++) {
+							if (rExpressionSrcrefs[i] != null) {
+								srcrefs[i]= Srcref.substract(
+										RDbg.createRJSrcref(rExpressionSrcrefs[i]),
+										elementSrcref );
+							}
+						}
 					}
 				}
 			}
diff --git a/r/org.eclipse.statet.r.debug.core/src/org/eclipse/statet/r/debug/core/breakpoints/RLineBreakpointValidator.java b/r/org.eclipse.statet.r.debug.core/src/org/eclipse/statet/r/debug/core/breakpoints/RLineBreakpointValidator.java
index 6005882..7391919 100644
--- a/r/org.eclipse.statet.r.debug.core/src/org/eclipse/statet/r/debug/core/breakpoints/RLineBreakpointValidator.java
+++ b/r/org.eclipse.statet.r.debug.core/src/org/eclipse/statet/r/debug/core/breakpoints/RLineBreakpointValidator.java
@@ -15,6 +15,7 @@
 package org.eclipse.statet.r.debug.core.breakpoints;
 
 import java.lang.reflect.InvocationTargetException;
+import java.util.List;
 import java.util.concurrent.atomic.AtomicReference;
 
 import org.eclipse.core.resources.IMarker;
@@ -597,13 +598,21 @@
 		}
 	}
 	
-	public IRSrcref computeRExpressionSrcref() throws CoreException {
+	public RSrcref @Nullable [] computeRExpressionSrcrefs() throws CoreException {
 		if (this.type == null) {
 			throw invalid();
 		}
 		if (this.astNode != null && this.baseExpressionRootNode != null) {
+			final List<RAstNode> nodes= RAst.computeRExpressionNodes(this.astNode, this.baseExpressionRootNode);
 			try {
-				return new RSrcref(this.document, this.astNode);
+				final RSrcref[] srcrefs= new RSrcref[nodes.size()];
+				for (int i= 0; i < srcrefs.length; i++) {
+					final RAstNode node= nodes.get(i);
+					if (i == srcrefs.length - 1 || node.getNodeType() == NodeType.BLOCK) {
+						srcrefs[i]= new RSrcref(this.document, node);
+					}
+				}
+				return srcrefs;
 			}
 			catch (final BadLocationException e) {
 				throw failedComputing(e);