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);