Bug 538952: [R-Editor] Improve context information assist

  - Improve selection of parsed region for embedded R chunks
  - Keep in sync with implementations in docmlet
  - CleanUp

Change-Id: Id48715fa4f181865f29d6aa53e6b9934084a5776
diff --git a/r/org.eclipse.statet.r.core/src/org/eclipse/statet/r/core/source/RPartitionNodeScanner.java b/r/org.eclipse.statet.r.core/src/org/eclipse/statet/r/core/source/RPartitionNodeScanner.java
index 80e7447..92329a6 100644
--- a/r/org.eclipse.statet.r.core/src/org/eclipse/statet/r/core/source/RPartitionNodeScanner.java
+++ b/r/org.eclipse.statet.r.core/src/org/eclipse/statet/r/core/source/RPartitionNodeScanner.java
@@ -20,6 +20,8 @@
 import org.eclipse.jface.text.IDocument;
 import org.eclipse.jface.text.rules.ICharacterScanner;
 
+import org.eclipse.statet.jcommons.lang.Nullable;
+
 import org.eclipse.statet.ecommons.text.CharacterScannerReader;
 import org.eclipse.statet.ecommons.text.core.rules.BufferedDocumentScanner;
 import org.eclipse.statet.ecommons.text.core.treepartitioner.ITreePartitionNode;
@@ -34,23 +36,24 @@
 public class RPartitionNodeScanner implements ITreePartitionNodeScanner {
 	
 	
-	public static final ITreePartitionNode findRRootNode(ITreePartitionNode node) {
+	public static final @Nullable ITreePartitionNode findRRootNode(@Nullable ITreePartitionNode node) {
+		ITreePartitionNode rNode;
 		while (true) {
 			if (node == null) {
 				return null;
 			}
 			if (node.getType() instanceof RPartitionNodeType) {
+				rNode= node;
 				break;
 			}
 			node= node.getParent();
 		}
-		// (node.getType() instanceof RPartitionNodeType)
 		ITreePartitionNode parentNode;
-		while ((parentNode= node.getParent()) != null
+		while ((parentNode= rNode.getParent()) != null
 				&& parentNode.getType() instanceof RPartitionNodeType) {
-			node= parentNode;
+			rNode= parentNode;
 		}
-		return node;
+		return rNode;
 	}
 	
 	
diff --git a/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/internal/r/ui/editors/RArgumentListContextInformation.java b/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/internal/r/ui/editors/RArgumentListContextInformation.java
index 3476ecc..d19b73f 100644
--- a/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/internal/r/ui/editors/RArgumentListContextInformation.java
+++ b/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/internal/r/ui/editors/RArgumentListContextInformation.java
@@ -38,7 +38,7 @@
 	private final @Nullable ArgsDefinition args;
 	
 	private final String information;
-	private final int[] informationIndexes;
+	private final int[] informationArgumentIdxs;
 	
 	
 	public RArgumentListContextInformation(final int offset, final IRMethod method) {
@@ -48,7 +48,7 @@
 		final IntList idxs= new IntArrayList();
 		new RLabelProvider().appendArgumentInformation(sb, idxs, this.args);
 		this.information= sb.toString();
-		this.informationIndexes= idxs.toArray();
+		this.informationArgumentIdxs= idxs.toArray();
 	}
 	
 	
@@ -56,21 +56,22 @@
 		return this.offset;
 	}
 	
-	public @Nullable ArgsDefinition getArguments() {
+	public @Nullable ArgsDefinition getArgsDefinition() {
 		return this.args;
 	}
 	
+	
+	@Override
+	public String getContextDisplayString() {
+		return getInformationDisplayString();
+	}
+	
 	@Override
 	public @Nullable Image getImage() {
 		return null;
 	}
 	
 	@Override
-	public String getContextDisplayString() {
-		return getInformationDisplayString();
-	}
-	
-	@Override
 	public int getContextInformationPosition() {
 		return Math.max(this.offset, 0);
 	}
@@ -81,9 +82,10 @@
 	}
 	
 	public int[] getInformationDisplayStringArgumentIdxs() {
-		return this.informationIndexes;
+		return this.informationArgumentIdxs;
 	}
 	
+	
 	@Override
 	public boolean equals(final @Nullable Object obj) {
 		// prevent stacking of context information at the same position
diff --git a/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/internal/r/ui/editors/RContextInformationValidator.java b/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/internal/r/ui/editors/RContextInformationValidator.java
index da294e4..ce6424c 100644
--- a/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/internal/r/ui/editors/RContextInformationValidator.java
+++ b/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/internal/r/ui/editors/RContextInformationValidator.java
@@ -14,8 +14,9 @@
 
 package org.eclipse.statet.internal.r.ui.editors;
 
+import static org.eclipse.statet.jcommons.lang.ObjectUtils.nonNullAssert;
+
 import org.eclipse.jface.text.AbstractDocument;
-import org.eclipse.jface.text.IDocument;
 import org.eclipse.jface.text.ITextViewer;
 import org.eclipse.jface.text.TextPresentation;
 import org.eclipse.jface.text.contentassist.IContextInformation;
@@ -29,53 +30,57 @@
 import org.eclipse.statet.jcommons.text.core.input.OffsetStringParserInput;
 
 import org.eclipse.statet.ecommons.text.core.FragmentDocument;
+import org.eclipse.statet.ecommons.text.core.treepartitioner.ITreePartitionNode;
+import org.eclipse.statet.ecommons.text.core.treepartitioner.TreePartitionUtils;
 
 import org.eclipse.statet.ltk.ast.core.AstInfo;
 import org.eclipse.statet.ltk.ui.sourceediting.assist.AssistCompletionInformationProposalWrapper;
 import org.eclipse.statet.r.core.model.ArgsDefinition;
+import org.eclipse.statet.r.core.rsource.ast.FCall;
 import org.eclipse.statet.r.core.rsource.ast.FCall.Args;
 import org.eclipse.statet.r.core.rsource.ast.RAst;
 import org.eclipse.statet.r.core.rsource.ast.RAst.FCallArgMatch;
 import org.eclipse.statet.r.core.rsource.ast.RScanner;
+import org.eclipse.statet.r.core.source.RPartitionNodeScanner;
+import org.eclipse.statet.r.ui.editors.IRSourceEditor;
 
 
 @NonNullByDefault
 public class RContextInformationValidator implements IContextInformationValidator, IContextInformationPresenter {
 	
 	
-	private ITextViewer viewer;
-	private int startOffset;
+	private final IRSourceEditor sourceEditor;
 	
 	private @Nullable RArgumentListContextInformation info;
-	private int currentParameter;
+	
 	private long scannedArgsStamp;
 	private @Nullable Args scannedArgs;
 	
 	private int lastPresentation= -2;
 	
 	
-	public RContextInformationValidator() {
+	public RContextInformationValidator(final IRSourceEditor sourceEditor) {
+		this.sourceEditor= sourceEditor;
 	}
 	
 	
 	@Override
 	public void install(IContextInformation info, final ITextViewer viewer, final int offset) {
 		if (info instanceof AssistCompletionInformationProposalWrapper) {
-			info= ((AssistCompletionInformationProposalWrapper) info).getContextInformation();
+			info= ((AssistCompletionInformationProposalWrapper)info).getContextInformation();
 		}
 		
 		this.scannedArgs= null;
 		this.lastPresentation= -2;
-		if (info instanceof RArgumentListContextInformation) {
-			this.info= (RArgumentListContextInformation) info;
-			this.currentParameter= -1;
+		
+		if (info instanceof RArgumentListContextInformation
+				&& viewer == this.sourceEditor.getViewer()) {
+			this.info= (RArgumentListContextInformation)info;
 		}
 		else {
 			this.info= null;
 			return;
 		}
-		this.viewer= viewer;
-		this.startOffset= offset;
 	}
 	
 	@Override
@@ -84,15 +89,15 @@
 		if (info == null) {
 			return false;
 		}
-		final IDocument document= this.viewer.getDocument();
-		if (offset < this.startOffset || offset > document.getLength()) {
+		if (offset < info.getContextInformationPosition()
+				|| offset > this.sourceEditor.getViewer().getDocument().getLength()) {
 			return false;
 		}
 		final Args args= getScannedArgs();
 		if (args != null) {
 			return (offset <= args.getEndOffset());
 		}
-		return (offset == this.startOffset);
+		return (offset == info.getContextInformationPosition());
 	}
 	
 	@Override
@@ -101,19 +106,19 @@
 		if (info == null) {
 			return false;
 		}
-		final ArgsDefinition args= info.getArguments();
+		final ArgsDefinition args= info.getArgsDefinition();
 		if (args != null && args.size() > 0) {
-			final int argIndex= getCurrentArgInFDef(offset);
+			final int argIdx= getCurrentArgInFDef(offset);
 			final int[] idxs= info.getInformationDisplayStringArgumentIdxs();
-			if (argIndex >= 0 && argIndex < idxs.length) {
-				if (argIndex == this.lastPresentation) {
+			if (argIdx >= 0 && argIdx < idxs.length) {
+				if (argIdx == this.lastPresentation) {
 					return false;
 				}
-				final int start= idxs[argIndex];
-				final int stop= (argIndex+1 < idxs.length) ? idxs[argIndex+1] : info.getInformationDisplayString().length();
+				final int start= idxs[argIdx];
+				final int stop= (argIdx + 1 < idxs.length) ? idxs[argIdx + 1] : info.getInformationDisplayString().length();
 				presentation.clear();
-				presentation.addStyleRange(new StyleRange(start, stop-start, null, null, SWT.BOLD));
-				this.lastPresentation= argIndex;
+				presentation.addStyleRange(new StyleRange(start, stop - start, null, null, SWT.BOLD));
+				this.lastPresentation= argIdx;
 				return true;
 			}
 		}
@@ -125,9 +130,11 @@
 		return false;
 	}
 	
+	
 	private @Nullable Args getScannedArgs() {
-		AbstractDocument document= (AbstractDocument)this.viewer.getDocument();
-		final int startOffset= this.info.getCallArgsOffset();
+		final RArgumentListContextInformation info= nonNullAssert(this.info);
+		final int startOffset= info.getCallArgsOffset();
+		AbstractDocument document= (AbstractDocument)this.sourceEditor.getViewer().getDocument();
 		int docStartOffset= startOffset;
 		if (document instanceof FragmentDocument) {
 			final FragmentDocument fragmentDoc= (FragmentDocument)document;
@@ -140,12 +147,19 @@
 		final long stamp= document.getModificationStamp();
 		if (this.scannedArgs == null || this.scannedArgsStamp != stamp) {
 			try {
-				final String text= document.get(docStartOffset, Math.min(0x800, document.getLength() - docStartOffset));
-				final RScanner scanner= new RScanner(AstInfo.LEVEL_MODEL_DEFAULT);
-				this.scannedArgs= scanner.scanFCallArgs(
-						new OffsetStringParserInput(text, startOffset)
-								.init(startOffset, startOffset + text.length()),
-						true );
+				FCall.Args args= null;
+				final ITreePartitionNode rRootNode= RPartitionNodeScanner.findRRootNode(
+						TreePartitionUtils.getNode(document, this.sourceEditor.getDocumentContentInfo().getPartitioning(),
+								docStartOffset, true));
+				if (rRootNode != null) {
+					final int docEndOffset= Math.min(0x800, rRootNode.getEndOffset() - docStartOffset);
+					final String text= document.get(docStartOffset, docEndOffset);
+					final RScanner scanner= new RScanner(AstInfo.LEVEL_MODEL_DEFAULT);
+					args= scanner.scanFCallArgs(new OffsetStringParserInput(text, startOffset)
+									.init(startOffset, startOffset + text.length()),
+							true );
+				}
+				this.scannedArgs= args;
 				this.scannedArgsStamp= stamp;
 			}
 			catch (final Exception e) {
@@ -162,7 +176,7 @@
 		if (args != null) {
 			final int callArgIdx= getCurrentArgInFCall(args, offset);
 			if (callArgIdx >= 0) {
-				final FCallArgMatch match= RAst.matchArgs(args, this.info.getArguments());
+				final FCallArgMatch match= RAst.matchArgs(args, this.info.getArgsDefinition());
 				final ArgsDefinition.Arg argDef= match.getArgDef(callArgIdx);
 				if (argDef != null) {
 					return argDef.index;
diff --git a/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/r/ui/sourceediting/RAssistInvocationContext.java b/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/r/ui/sourceediting/RAssistInvocationContext.java
index 6136a7d..b1666af 100644
--- a/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/r/ui/sourceediting/RAssistInvocationContext.java
+++ b/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/r/ui/sourceediting/RAssistInvocationContext.java
@@ -74,19 +74,30 @@
 		
 		private final RElementAccess access;
 		
+		private final int invocationArgIdx;
+		
 		private @Nullable RFrameSearchPath searchPath;
 		
 		
 		public FCallInfo(final FCall node, final RElementAccess access) {
 			this.node= node;
 			this.access= access;
+			this.invocationArgIdx= getArgIdx(getInvocationOffset());
 		}
 		
 		
+		/**
+		 * Returns the node of the function call.
+		 * @return the ast node
+		 */
 		public FCall getNode() {
 			return this.node;
 		}
 		
+		/**
+		 * Returns the element access of the function belonging to the function call.
+		 * @return the element access
+		 */
 		public RElementAccess getAccess() {
 			return this.access;
 		}
@@ -151,14 +162,20 @@
 			return (argIdx < args.getChildCount()) ? args.getChild(argIdx) : null;
 		}
 		
+		/**
+		 * Returns the index of the argument at the {@link RAssistInvocationContext#getInvocationOffset() invocation offset}.
+		 * @return the index of the argument
+		 */
+		public final int getInvocationArgIdx() {
+			return this.invocationArgIdx;
+		}
+		
 	}
 	
 	
 	private static final byte PARSE_OPERATOR=               1 << 0;
 	private static final byte PARSE_SYMBOL=                 1 << 1;
 	
-	private static final char[] F_BRACKETS= new char[] { '(', ')' };
-	
 	
 	private @Nullable RHeuristicTokenScanner scanner;
 	private @Nullable RLexer lexer;
@@ -214,7 +231,7 @@
 	@Override
 	protected boolean reuse(final ISourceEditor editor, final int offset) {
 		if (super.reuse(editor, offset)) {
-			LoadReferencesUtil toolReferencesUtil= this.toolReferencesUtil;
+			final LoadReferencesUtil toolReferencesUtil= this.toolReferencesUtil;
 			if (toolReferencesUtil != null) {
 				toolReferencesUtil.setWaitTimeout(getToolReferencesWaitTimeout());
 			}
diff --git a/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/r/ui/sourceediting/RContentAssistProcessor.java b/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/r/ui/sourceediting/RContentAssistProcessor.java
index 44d7987..34a44fe 100644
--- a/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/r/ui/sourceediting/RContentAssistProcessor.java
+++ b/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/r/ui/sourceediting/RContentAssistProcessor.java
@@ -22,9 +22,9 @@
 import org.eclipse.jface.text.contentassist.IContextInformationValidator;
 
 import org.eclipse.statet.jcommons.collections.ImList;
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
 
 import org.eclipse.statet.ecommons.text.core.FragmentDocument;
-import org.eclipse.statet.ecommons.text.core.PartitionConstraint;
 
 import org.eclipse.statet.internal.r.ui.editors.RContextInformationValidator;
 import org.eclipse.statet.ltk.ui.sourceediting.assist.AssistInvocationContext;
@@ -38,23 +38,16 @@
 import org.eclipse.statet.r.ui.editors.IRSourceEditor;
 
 
+@NonNullByDefault
 public class RContentAssistProcessor extends ContentAssistProcessor {
 	
 	
-	private static PartitionConstraint NO_R_COMMENT_CONSTRAINT = new PartitionConstraint() {
-		@Override
-		public boolean matches(final String partitionType) {
-			return (partitionType != IRDocumentConstants.R_COMMENT_CONTENT_TYPE);
-		}
-	};
-	
-	
 	private class Context extends RAssistInvocationContext {
 		
 		
 		public Context(final int offset, final boolean isProposal,
 				final IProgressMonitor monitor) {
-			super((IRSourceEditor) RContentAssistProcessor.this.getEditor(),
+			super(RContentAssistProcessor.this.getEditor(),
 					offset, getContentType(),
 					isProposal,
 					RContentAssistProcessor.this.scanner,
@@ -89,6 +82,11 @@
 	
 	
 	@Override
+	protected IRSourceEditor getEditor() {
+		return (IRSourceEditor)super.getEditor();
+	}
+	
+	@Override
 	protected AssistInvocationContext createCompletionProposalContext(final int offset,
 			final IProgressMonitor monitor) {
 		return new Context(offset, true, monitor);
@@ -119,7 +117,7 @@
 	
 	@Override
 	protected IContextInformationValidator createContextInformationValidator() {
-		return new RContextInformationValidator();
+		return new RContextInformationValidator(getEditor());
 	}
 	
 	@Override
@@ -139,7 +137,7 @@
 			if (offset < 2) {
 				return false;
 			}
-			this.scanner.configure(document, NO_R_COMMENT_CONSTRAINT);
+			this.scanner.configure(document, IRDocumentConstants.R_NO_COMMENT_CONTENT_CONSTRAINT);
 			final int previousOffset = this.scanner.findNonBlankBackward(offset, RHeuristicTokenScanner.UNBOUND, true);
 			if (previousOffset > 0) {
 				final char c = document.getChar(previousOffset);
diff --git a/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/r/ui/sourceediting/RElementCompletionComputer.java b/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/r/ui/sourceediting/RElementCompletionComputer.java
index 984cc3b..cfddd7e 100644
--- a/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/r/ui/sourceediting/RElementCompletionComputer.java
+++ b/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/r/ui/sourceediting/RElementCompletionComputer.java
@@ -339,7 +339,7 @@
 		if (fCallInfo != null) {
 			boolean argName= false;
 			boolean argValue= false;
-			final int argIdx= fCallInfo.getArgIdx(context.getInvocationOffset());
+			final int argIdx= fCallInfo.getInvocationArgIdx();
 			if (argIdx >= 0) {
 				final FCall.Arg arg= fCallInfo.getArg(argIdx);
 				final int argBeginOffset= fCallInfo.getArgBeginOffset(argIdx);