blob: 0c749e0b2b37a7e16dc081c5e605035221f14aaf [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2008, 2019 Stephan Wahlbrink and others.
#
# This program and the accompanying materials are made available under the
# terms of the Eclipse Public License 2.0 which is available at
# https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
# which is available at https://www.apache.org/licenses/LICENSE-2.0.
#
# SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
#
# Contributors:
# Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
#=============================================================================*/
package org.eclipse.statet.r.ui.sourceediting;
import static org.eclipse.statet.jcommons.lang.ObjectUtils.nonNullAssert;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.BiFunction;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.BadPartitioningException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.statet.jcommons.collections.CollectionUtils;
import org.eclipse.statet.jcommons.collections.ImCollections;
import org.eclipse.statet.jcommons.lang.Nullable;
import org.eclipse.statet.ecommons.text.core.IFragmentDocument;
import org.eclipse.statet.ecommons.text.core.IPartitionConstraint;
import org.eclipse.statet.internal.r.ui.FCallNamePattern;
import org.eclipse.statet.internal.r.ui.editors.RElementCompletionProposal;
import org.eclipse.statet.internal.r.ui.editors.RKeywordCompletionProposal;
import org.eclipse.statet.internal.r.ui.editors.RSimpleCompletionProposal;
import org.eclipse.statet.ltk.ast.core.AstNode;
import org.eclipse.statet.ltk.model.core.elements.IModelElement;
import org.eclipse.statet.ltk.ui.IElementLabelProvider;
import org.eclipse.statet.ltk.ui.sourceediting.ISourceEditor;
import org.eclipse.statet.ltk.ui.sourceediting.assist.AssistInvocationContext;
import org.eclipse.statet.ltk.ui.sourceediting.assist.AssistProposalCollector;
import org.eclipse.statet.ltk.ui.sourceediting.assist.ContentAssist;
import org.eclipse.statet.ltk.ui.sourceediting.assist.IAssistCompletionProposal;
import org.eclipse.statet.ltk.ui.sourceediting.assist.IContentAssistComputer;
import org.eclipse.statet.r.core.IRCoreAccess;
import org.eclipse.statet.r.core.RCore;
import org.eclipse.statet.r.core.RSymbolComparator;
import org.eclipse.statet.r.core.RSymbolComparator.PrefixPattern;
import org.eclipse.statet.r.core.data.CombinedRElement;
import org.eclipse.statet.r.core.model.ArgsDefinition;
import org.eclipse.statet.r.core.model.IRElement;
import org.eclipse.statet.r.core.model.IRFrame;
import org.eclipse.statet.r.core.model.IRFrameInSource;
import org.eclipse.statet.r.core.model.IRLangElement;
import org.eclipse.statet.r.core.model.IRMethod;
import org.eclipse.statet.r.core.model.RCoreFunctions;
import org.eclipse.statet.r.core.model.RElementAccess;
import org.eclipse.statet.r.core.model.RElementName;
import org.eclipse.statet.r.core.model.RModel;
import org.eclipse.statet.r.core.pkgmanager.IRPkgManager;
import org.eclipse.statet.r.core.rlang.RTokens;
import org.eclipse.statet.r.core.rsource.ast.FCall;
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.RAstNode;
import org.eclipse.statet.r.core.source.IRDocumentConstants;
import org.eclipse.statet.r.core.source.RHeuristicTokenScanner;
import org.eclipse.statet.r.ui.RLabelProvider;
import org.eclipse.statet.r.ui.editors.IRSourceEditor;
import org.eclipse.statet.r.ui.sourceediting.RAssistInvocationContext.FCallInfo;
import org.eclipse.statet.r.ui.sourceediting.RFrameSearchPath.RFrameIterator;
import org.eclipse.statet.rhelp.core.REnvHelp;
import org.eclipse.statet.rhelp.core.RHelpManager;
import org.eclipse.statet.rhelp.core.RHelpTopicEntry;
import org.eclipse.statet.rhelp.core.RPkgHelp;
import org.eclipse.statet.rj.data.RObject;
import org.eclipse.statet.rj.data.RReference;
import org.eclipse.statet.rj.renv.core.REnv;
import org.eclipse.statet.rj.renv.core.RPkgBuilt;
import org.eclipse.statet.rj.renv.core.RPkgCompilation;
import org.eclipse.statet.rj.renv.core.RPkgUtils;
import org.eclipse.statet.rj.renv.runtime.RPkgManagerDataset;
public class RElementCompletionComputer implements IContentAssistComputer {
private static final IPartitionConstraint NO_R_COMMENT_CONSTRAINT= new IPartitionConstraint() {
@Override
public boolean matches(final String partitionType) {
return (partitionType != IRDocumentConstants.R_COMMENT_CONTENT_TYPE);
};
};
private static final List<String> fgKeywords;
static {
final ArrayList<String> list= new ArrayList<>();
Collections.addAll(list, RTokens.CONSTANT_WORDS);
Collections.addAll(list, RTokens.FLOWCONTROL_WORDS);
Collections.sort(list, RSymbolComparator.R_NAMES_COLLATOR);
list.trimToSize();
fgKeywords= Collections.unmodifiableList(list);
}
private static String toString(final Object value) {
if (value instanceof List) {
final List<String> list= (List<String>) value;
Collections.sort(list, RPkgUtils.NAMES_COLLATOR);
return CollectionUtils.toString(list, ", "); //$NON-NLS-1$
}
return value.toString();
}
public static class CompleteRuntime extends RElementCompletionComputer {
public CompleteRuntime() {
super(RFrameSearchPath.ENGINE_MODE);
}
}
protected static final int NA_PRIO= Integer.MIN_VALUE;
protected static final int ARG_NAME_PRIO= 80;
protected static final int ARG_TYPE_PRIO= 40;
protected static final int ARG_TYPE_NO_PRIO= -40;
private final IElementLabelProvider labelProvider= new RLabelProvider(RLabelProvider.NAMESPACE);
private final int mode;
private IRSourceEditor editor;
private ContentAssist assist;
private final RFrameSearchPath searchPath= new RFrameSearchPath();
private boolean inDefault;
private boolean inString;
private int pkgNamePrio;
private int helpTopicPrio;
private IStatus resultStatus;
public RElementCompletionComputer() {
this(0);
}
protected RElementCompletionComputer(final int mode) {
this.mode= mode;
}
/**
* {@inheritDoc}
*/
@Override
public void sessionStarted(final ISourceEditor editor, final ContentAssist assist) {
this.editor= (editor instanceof IRSourceEditor) ? (IRSourceEditor) editor : null;
this.assist= assist;
}
/**
* {@inheritDoc}
*/
@Override
public void sessionEnded() {
this.searchPath.clear();
this.assist= null;
this.resultStatus= null;
}
protected final void setStatus(final IStatus status) {
this.resultStatus= status;
}
protected final int getSearchMode(final RAssistInvocationContext context) {
if (this.mode != 0) {
return this.mode;
}
return context.getDefaultRFrameSearchMode();
}
protected final boolean isSymbolCandidate(final String name) {
for (int i= 0; i < name.length(); i++) {
if (RTokens.isRobustSeparator(name.charAt(i))) {
return false;
}
}
return true;
}
private boolean isCompletable(RElementName elementName) {
if (elementName == null) {
return false;
}
do {
switch (elementName.getType()) {
case RElementName.SUB_INDEXED_S:
case RElementName.SUB_INDEXED_D:
return false;
}
if (elementName.getSegmentName() == null) {
return false;
}
elementName= elementName.getNextSegment();
}
while (elementName != null);
return true;
}
private RAstNode getRAstNode(final RAssistInvocationContext context) {
AstNode node= context.getInvocationAstSelection().getCovering();
if (node == null) {
node= context.getAstInfo().getRoot();
}
return (node instanceof RAstNode) ? (RAstNode) node : null;
}
@Override
public IStatus computeCompletionProposals(final AssistInvocationContext context,
final int mode, final AssistProposalCollector proposals,
final IProgressMonitor monitor) {
this.resultStatus= null;
if (context instanceof RAssistInvocationContext) {
computeCompletionProposals((RAssistInvocationContext) context, mode, proposals,
monitor );
}
return this.resultStatus;
}
@Override
public IStatus computeInformationProposals(final AssistInvocationContext context,
final AssistProposalCollector proposals, final IProgressMonitor monitor) {
this.resultStatus= null;
if (context instanceof RAssistInvocationContext) {
doComputeContextProposals((RAssistInvocationContext) context, proposals, monitor);
}
return this.resultStatus;
}
protected void computeCompletionProposals(final RAssistInvocationContext context, final int mode,
final AssistProposalCollector proposals, final IProgressMonitor monitor) {
if (context.getModelInfo() == null) {
return;
}
// Get node
final RAstNode node= getRAstNode(context);
// Get prefix
final RElementName prefixName= context.getIdentifierElementName();
if (prefixName == null) {
return;
}
this.inString= (context.getInvocationContentType() == IRDocumentConstants.R_STRING_CONTENT_TYPE);
this.inDefault= !this.inString;
this.pkgNamePrio= NA_PRIO;
this.helpTopicPrio= NA_PRIO;
if (prefixName.getNextSegment() == null) {
final String help= checkHelp(context);
if (help != null) {
doComputeHelpTopicProposals(context, help, node, ARG_TYPE_PRIO, proposals);
if (prefixName.getScope() == null) {
doComputePkgNameProposals(context, ARG_TYPE_NO_PRIO, proposals);
}
return;
}
doComputeArgumentProposals(context, proposals, monitor);
if (this.inDefault) {
doComputeMainProposals(context, node, proposals, monitor);
}
if (this.mode == 0 && this.inDefault) {
doComputeKeywordProposals(context, proposals, monitor);
}
if (this.mode == 0) {
if (isPackageName(prefixName)) {
if (this.pkgNamePrio > 0) {
doComputePkgNameProposals(context, this.pkgNamePrio, proposals);
}
else if (!prefixName.getSegmentName().isEmpty()) {
doComputePkgNameProposals(context, ARG_TYPE_NO_PRIO, proposals);
}
}
{ if (this.helpTopicPrio > 0) {
doComputeHelpTopicProposals(context, null, node, this.helpTopicPrio, proposals);
}
}
}
}
else {
doComputeSubProposals(context, node, proposals, monitor);
}
}
private String checkHelp(final RAssistInvocationContext context) {
try {
if (context.getIdentifierOffset() > 0
&& context.getDocument().getChar(context.getIdentifierOffset() - 1) == '?') {
final String prefix= context.computeIdentifierPrefix(context.getIdentifierOffset() - 1);
if (prefix != null && !prefix.isEmpty()) {
if (prefix.equals("class") || prefix.equals("methods")) { //$NON-NLS-1$ //$NON-NLS-2$
return prefix;
}
return null;
}
return ""; //$NON-NLS-1$
}
return null;
}
catch (final BadPartitioningException | BadLocationException e) {
return null;
}
}
protected List<? extends IRLangElement> getChildren(final RAssistInvocationContext context,
IRLangElement e) {
if (e instanceof RReference) {
final RReference ref= (RReference) e;
final RObject rObject= ref.getResolvedRObject();
if (rObject == null && e instanceof CombinedRElement && context.getTool() != null) {
context.getToolReferencesUtil().resolve(ref, 0);
}
if (rObject instanceof CombinedRElement) {
e= (CombinedRElement) rObject;
}
}
return e.getModelChildren(null);
}
protected boolean doComputeArgumentProposals(final RAssistInvocationContext context,
final AssistProposalCollector proposals,
final IProgressMonitor monitor) {
final FCallInfo fCallInfo= context.getFCallInfo();
if (fCallInfo != null) {
boolean argName= false;
boolean argValue= false;
final int argIdx= fCallInfo.getArgIdx(context.getInvocationOffset());
if (argIdx >= 0) {
final FCall.Arg arg= fCallInfo.getArg(argIdx);
final int argBeginOffset= fCallInfo.getArgBeginOffset(argIdx);
IDocument document= context.getDocument();
int offsetShift= 0;
if (document instanceof IFragmentDocument) {
final IFragmentDocument inputDoc= (IFragmentDocument) document;
document= inputDoc.getMasterDocument();
offsetShift= inputDoc.getOffsetInMasterDocument();
}
if (argBeginOffset != Integer.MIN_VALUE) {
final RHeuristicTokenScanner scanner= context.getRHeuristicTokenScanner();
scanner.configure(document, NO_R_COMMENT_CONSTRAINT);
final int offset= context.getIdentifierOffset();
if (argBeginOffset == offset
|| scanner.findNonBlankForward(
argBeginOffset + offsetShift, offset + offsetShift, true) < 0 ) {
argName= (this.inDefault
&& (context.getIdentifierElementName().getScope() == null) );
argValue= true;
}
}
if (!argValue && arg != null && arg.getAssignOffset() != AstNode.NA_OFFSET) {
final RHeuristicTokenScanner scanner= context.getRHeuristicTokenScanner();
scanner.configure(document, NO_R_COMMENT_CONSTRAINT);
final int offset= context.getIdentifierOffset();
if (argBeginOffset == offset
|| scanner.findNonBlankForward(
arg.getAssignOffset() + offsetShift + 1, offset + offsetShift, true) < 0 ) {
argValue= true;
}
}
}
if (argName || argValue) {
doComputeFCallArgumentProposals(context, fCallInfo, argIdx, argName, argValue,
proposals);
return true;
}
}
return false;
}
protected void doComputeMainProposals(final RAssistInvocationContext context,
final RAstNode node,
final AssistProposalCollector proposals,
final IProgressMonitor monitor) {
final RElementName prefixName= context.getIdentifierElementName();
final String prefixSegmentName= nonNullAssert(prefixName.getSegmentName());
this.searchPath.init(context, node, getSearchMode(context), prefixName.getScope());
final RSymbolComparator.PrefixPattern pattern= new RSymbolComparator.PrefixPattern(prefixSegmentName);
final int offset= context.getIdentifierLastSegmentOffset();
final Set<String> mainNames= new HashSet<>();
final List<String> methodNames= new ArrayList<>();
for (final RFrameIterator iter= this.searchPath.iterator(); iter.hasNext();) {
final IRFrame envir= iter.next();
final List<? extends IRElement> elements= envir.getModelChildren(null);
for (final IRElement element : elements) {
final RElementName elementName= element.getElementName();
final int c1type= (element.getElementType() & IModelElement.MASK_C1);
final boolean isRich= (c1type == IModelElement.C1_METHOD);
if ((isRich || c1type == IModelElement.C1_VARIABLE)
&& isCompletable(elementName)
&& pattern.matches(elementName.getSegmentName())) {
final int relevance= iter.getRelevance();
if ((relevance < 0) && !isRich
&& mainNames.contains(elementName.getSegmentName()) ) {
continue;
}
final IAssistCompletionProposal proposal= createProposal(context,
offset, elementName, element, relevance );
if (proposal != null) {
if (elementName.getNextSegment() == null) {
if (isRich) {
methodNames.add(elementName.getSegmentName());
}
else {
mainNames.add(elementName.getSegmentName());
}
}
proposals.add(proposal);
}
}
}
}
mainNames.addAll(methodNames);
for (final RFrameIterator iter= this.searchPath.iterator(); iter.hasNext();) {
final IRFrame envir= iter.next();
if (envir instanceof IRFrameInSource) {
final IRFrameInSource sframe= (IRFrameInSource) envir;
final Set<String> elementNames= sframe.getAllAccessNames();
for (final String candidate : elementNames) {
if (candidate != null
&& pattern.matches(candidate)
&& !mainNames.contains(candidate)
&& !(candidate.equals(prefixSegmentName)
&& (sframe.getAllAccessOf(candidate, false).size() <= 1) )) {
final IAssistCompletionProposal proposal= createProposal(context,
offset, candidate );
if (proposal != null) {
mainNames.add(candidate);
proposals.add(proposal);
}
}
}
}
}
}
private void doComputeKeywordProposals(final RAssistInvocationContext context,
final AssistProposalCollector proposals,
final IProgressMonitor monitor) {
final RElementName prefixName= context.getIdentifierElementName();
final String prefixSegmentName= nonNullAssert(prefixName.getSegmentName());
if (prefixName.getScope() != null) {
return;
}
final String prefixSource= context.getIdentifierPrefix();
if (!prefixSegmentName.isEmpty() && prefixSource.charAt(0) != '`') {
final int offset= context.getIdentifierOffset();
final List<String> keywords= fgKeywords;
for (final String keyword : keywords) {
if (keyword.regionMatches(true, 0, prefixSegmentName, 0, prefixSegmentName.length())) {
proposals.add(new RKeywordCompletionProposal(context, keyword, offset));
}
}
}
}
protected void doComputeSubProposals(final RAssistInvocationContext context,
final RAstNode node,
final AssistProposalCollector proposals,
final IProgressMonitor monitor) {
final RElementName prefixName= context.getIdentifierElementName();
this.searchPath.init(context, node, getSearchMode(context), prefixName.getScope());
int count= 0;
final String namePrefix;
{ RElementName prefixSegment= prefixName;
while (true) {
count++;
if (prefixSegment.getNextSegment() != null) {
prefixSegment= prefixSegment.getNextSegment();
continue;
}
else {
break;
}
}
namePrefix= (prefixSegment.getSegmentName() != null) ?
prefixSegment.getSegmentName() : ""; //$NON-NLS-1$
}
final RSymbolComparator.PrefixPattern pattern= new RSymbolComparator.PrefixPattern(namePrefix);
final int offset= context.getIdentifierLastSegmentOffset();
final Set<String> mainNames= new HashSet<>();
final List<String> methodNames= new ArrayList<>();
for (final RFrameIterator iter= this.searchPath.iterator(); iter.hasNext();) {
final IRFrame envir= iter.next();
final List<? extends IRLangElement> elements= envir.getModelChildren(null);
ITER_ELEMENTS: for (final IRLangElement rootElement : elements) {
final RElementName elementName= rootElement.getElementName();
final int c1type= (rootElement.getElementType() & IModelElement.MASK_C1);
final boolean isRich= (c1type == IModelElement.C1_METHOD);
if (isRich || c1type == IModelElement.C1_VARIABLE) {
IRLangElement element= rootElement;
RElementName prefixSegment= prefixName;
RElementName elementSegment= elementName;
ITER_SEGMENTS: for (int i= 0; i < count-1; i++) {
if (elementSegment == null) {
final List<? extends IRLangElement> children= getChildren(context, element);
for (final IRLangElement child : children) {
elementSegment= child.getElementName();
if (isCompletable(elementSegment)
&& elementSegment.getSegmentName().equals(prefixSegment.getSegmentName())) {
element= child;
prefixSegment= prefixSegment.getNextSegment();
elementSegment= elementSegment.getNextSegment();
continue ITER_SEGMENTS;
}
}
continue ITER_ELEMENTS;
}
else {
if (isCompletable(elementSegment)
&& elementSegment.getSegmentName().equals(prefixSegment.getSegmentName())) {
prefixSegment= prefixSegment.getNextSegment();
elementSegment= elementSegment.getNextSegment();
continue ITER_SEGMENTS;
}
continue ITER_ELEMENTS;
}
}
final boolean childMode;
final List<? extends IRLangElement> children;
if (elementSegment == null) {
childMode= true;
children= getChildren(context, element);
}
else {
childMode= false;
children= Collections.singletonList(element);
}
for (final IRLangElement child : children) {
if (childMode) {
elementSegment= child.getElementName();
}
final String candidate= elementSegment.getSegmentName();
final int relevance= iter.getRelevance();
if (isCompletable(elementSegment)
&& pattern.matches(candidate) ) {
if ((relevance > 0) && !isRich
&& mainNames.contains(candidate) ) {
continue ITER_ELEMENTS;
}
final IAssistCompletionProposal proposal= createProposal(context,
offset, elementSegment, child, relevance );
if (proposal != null) {
if (elementSegment.getNextSegment() == null) {
if (isRich) {
methodNames.add(candidate);
}
else {
mainNames.add(candidate);
}
}
proposals.add(proposal);
}
}
}
}
}
}
mainNames.addAll(methodNames);
for (final RFrameIterator iter= this.searchPath.iterator(); iter.hasNext();) {
final IRFrame envir= iter.next();
if (envir instanceof IRFrameInSource) {
final IRFrameInSource sframe= (IRFrameInSource) envir;
final List<? extends RElementAccess> allAccess= sframe.getAllAccessOf(
prefixName.getSegmentName(), true );
if (allAccess != null) {
ITER_ELEMENTS: for (final RElementAccess elementAccess : allAccess) {
RElementAccess elementSegment= elementAccess;
RElementName prefixSegment= prefixName;
ITER_SEGMENTS: for (int i= 0; i < count - 1; i++) {
if (isCompletable(elementSegment)
&& elementSegment.getSegmentName().equals(prefixSegment.getSegmentName())) {
prefixSegment= prefixSegment.getNextSegment();
elementSegment= elementSegment.getNextSegment();
continue ITER_SEGMENTS;
}
continue ITER_ELEMENTS;
}
if (elementSegment == null || elementSegment.isSlave()) {
continue ITER_ELEMENTS;
}
final String candidate= elementSegment.getSegmentName();
if (candidate != null && isCompletable(elementSegment)
&& pattern.matches(candidate)
&& !mainNames.contains(candidate)
&& !candidate.equals(namePrefix) ) {
final IAssistCompletionProposal proposal= createProposal(context,
offset, candidate );
if (proposal != null) {
mainNames.add(candidate);
proposals.add(proposal);
}
}
}
}
}
}
}
protected IAssistCompletionProposal createProposal(final RAssistInvocationContext context,
final int offset, final String name) {
return new RSimpleCompletionProposal(context, name, offset);
}
protected IAssistCompletionProposal createProposal(final RAssistInvocationContext context,
final int offset, final RElementName elementName, final IRElement element,
final int relevance) {
return new RElementCompletionProposal(context, elementName, offset, element, relevance,
this.labelProvider );
}
public void doComputeContextProposals(final RAssistInvocationContext context,
final AssistProposalCollector proposals, final IProgressMonitor monitor) {
if (context.getModelInfo() == null) {
return;
}
final RAssistInvocationContext.FCallInfo fCallInfo= context.getFCallInfo();
if (fCallInfo != null) {
final FCallNamePattern pattern= new FCallNamePattern(fCallInfo.getAccess()) {
final int infoOffset= Math.max(fCallInfo.getNode().getArgsOpenOffset() + 1, 0);
@Override
protected void handleMatch(final IRMethod element, final IRFrame frame,
final RFrameIterator iterator) {
proposals.add(new RElementCompletionProposal.ContextInformationProposal(context,
element.getElementName(), this.infoOffset, element,
iterator.getRelevance(),
RElementCompletionComputer.this.labelProvider ));
}
};
pattern.searchFDef(fCallInfo.getSearchPath(getSearchMode(context)));
}
}
protected final boolean isPackageArg(final String name) {
return ("package".equals(name));
}
private void doComputeFCallArgumentProposals(final RAssistInvocationContext context,
final FCallInfo fCallInfo, final int argIdx, final boolean argName, final boolean argValue,
final AssistProposalCollector proposals) {
final RElementName prefixName= context.getIdentifierElementName();
final String prefixSegmentName= nonNullAssert(prefixName.getSegmentName());
final int offset= context.getIdentifierOffset();
if (argValue && !argName) {
final FCall.Arg arg= fCallInfo.getArg(argIdx);
if (arg != null && arg.hasName()) {
if (this.inString && isPackageArg(arg.getNameChild().getText())) {
this.pkgNamePrio= ARG_TYPE_PRIO;
}
}
}
class FCallHandler extends FCallNamePattern {
private final RCoreFunctions coreFunction;
private final HashSet<String> argNames= new HashSet<>();
private int matchCount;
FCallHandler() {
super(fCallInfo.getAccess());
if (fCallInfo.getAccess().getNextSegment() == null) {
this.coreFunction= RCoreFunctions.DEFAULT;
}
else {
this.coreFunction= null;
}
}
private boolean isValidNameContext(final ArgsDefinition.Arg argDef) {
if (RElementCompletionComputer.this.inString) {
return ((argDef.type & ArgsDefinition.NAME_AS_STRING) != 0);
}
else {
return ((argDef.type & ArgsDefinition.NAME_AS_SYMBOL) != 0);
}
}
private boolean checkArgsDef(final FCallArgMatch args,
final boolean guess, final int relevance) {
final ArgsDefinition.Arg argDef= args.getArgDef(argIdx);
if (argDef != null) {
final boolean typedDef= (argDef.type != 0);
if (RElementCompletionComputer.this.pkgNamePrio == NA_PRIO) {
if (typedDef) {
if ((argDef.type & ArgsDefinition.PACKAGE_NAME) != 0
&& isValidNameContext(argDef) ) {
RElementCompletionComputer.this.pkgNamePrio= ARG_TYPE_PRIO + relevance;
}
}
else if (guess && RElementCompletionComputer.this.inString && isPackageArg(argDef.name)) {
RElementCompletionComputer.this.pkgNamePrio= ARG_TYPE_PRIO + relevance;
}
}
if (RElementCompletionComputer.this.helpTopicPrio == NA_PRIO) {
if (typedDef) {
if ((argDef.type & ArgsDefinition.HELP_TOPIC_NAME) != 0
&& isValidNameContext(argDef) ) {
RElementCompletionComputer.this.helpTopicPrio= ARG_TYPE_PRIO + relevance;
}
}
}
return typedDef;
}
return false;
}
@Override
public void searchFDef(final RFrameSearchPath searchPath) {
super.searchFDef(searchPath);
if (this.matchCount == 0
&& this.coreFunction != null) {
final FCall.Args callArgs= fCallInfo.getNode().getArgsChild();
final ArgsDefinition coreDef= this.coreFunction.getArgs(
getElementName().getSegmentName() );
if (coreDef != null) {
checkArgsDef(RAst.matchArgs(callArgs, coreDef), false, 0);
}
}
}
@Override
protected void handleMatch(final IRMethod element, final IRFrame frame,
final RFrameIterator iterator) {
final ArgsDefinition argsDef= element.getArgsDefinition();
if (argsDef == null) {
return;
}
this.matchCount++;
final int relevance= iterator.getRelevance();
if (argName) {
for (int i= 0; i < argsDef.size(); i++) {
final ArgsDefinition.Arg arg= argsDef.get(i);
if (arg.name != null && arg.name.length() > 0 && !arg.name.equals("...")) {
if ((prefixSegmentName == null || arg.name.startsWith(prefixSegmentName))
&& this.argNames.add(arg.name)) {
final RElementName name= RElementName.create(RElementName.MAIN_DEFAULT, arg.name);
proposals.add(new RElementCompletionProposal.ArgumentProposal(
context, name, offset, element,
ARG_NAME_PRIO + relevance,
RElementCompletionComputer.this.labelProvider ));
}
}
}
}
if (argValue) {
final FCall.Args callArgs= fCallInfo.getNode().getArgsChild();
if (!checkArgsDef(RAst.matchArgs(callArgs, argsDef), true, relevance)
&& frame.getFrameType() == IRFrame.PACKAGE
&& this.coreFunction != null
&& this.coreFunction.getPackageNames().contains(frame.getElementName().getSegmentName()) ) {
final ArgsDefinition coreDef= this.coreFunction.getArgs(
getElementName().getSegmentName() );
if (coreDef != null) {
checkArgsDef(RAst.matchArgs(callArgs, coreDef), false, relevance);
}
}
}
}
}
final FCallHandler search= new FCallHandler();
search.searchFDef(fCallInfo.getSearchPath(getSearchMode(context)));
}
protected IRCoreAccess getRCoreAccess() {
return this.editor.getRCoreAccess();
}
protected final @Nullable RPkgManagerDataset getRPkgDataset() {
final IRPkgManager manager= RCore.getRPkgManager(getRCoreAccess().getREnv());
if (manager != null) {
return manager.getDataset();
}
return null;
}
protected final boolean isPackageName(final RElementName elementName) {
return ((elementName.getType() == RElementName.MAIN_DEFAULT
|| RElementName.isPackageFacetScopeType(elementName.getType()) )
&& elementName.getNextSegment() == null );
}
protected final void doComputePkgNameProposals(final RAssistInvocationContext context,
final int prio, final AssistProposalCollector proposals) {
final RElementName prefixName= context.getIdentifierElementName();
final String prefixSegmentName= nonNullAssert(prefixName.getSegmentName());
if (prefixName.getScope() != null
|| !isSymbolCandidate(prefixSegmentName)) {
return;
}
final RPkgManagerDataset rPkgDataset= getRPkgDataset();
final PrefixPattern pattern= new RSymbolComparator.PrefixPattern(prefixSegmentName);
final int offset= context.getInvocationOffset() - prefixSegmentName.length();
final Collection<String> envNames;
if (rPkgDataset != null) {
final RPkgCompilation<? extends RPkgBuilt> pkgs= rPkgDataset.getInstalled();
envNames= pkgs.getNames();
for (final String pkgName : envNames) {
if (pattern.matches(pkgName)) {
proposals.add(new RElementCompletionProposal.RPkgProposal(context,
RElementName.create(RElementName.SCOPE_PACKAGE, pkgName), offset,
pkgs.getFirst(pkgName), prio ));
}
}
}
else {
envNames= ImCollections.emptySet();
}
final Set<String> workspaceNames= RModel.getRModelManager().getPkgNames();
for (final String pkgName : workspaceNames) {
if (!envNames.contains(pkgName) && pattern.matches(pkgName)) {
proposals.add(new RElementCompletionProposal.RPkgProposal(context,
RElementName.create(RElementName.SCOPE_PACKAGE, pkgName), offset,
null, prio ));
}
}
}
protected final void doComputeHelpTopicProposals(final RAssistInvocationContext context,
final String topicType, final RAstNode node,
final int prio, final AssistProposalCollector proposals) {
// (topic != null) => ? / (topic == null) => help()
final RElementName prefixName= context.getIdentifierElementName();
final String prefixSegmentName= nonNullAssert(prefixName.getSegmentName());
if (topicType == null && (prefixName.getScope() != null || prefixSegmentName.isEmpty())) {
return;
}
final REnv rEnv= getRCoreAccess().getREnv();
if (rEnv == null) {
return;
}
final Map<String, Object> matches= new HashMap<>();
final RHelpManager rHelpManager= RCore.getRHelpManager();
final REnvHelp help= rHelpManager.getHelp(rEnv);
if (help != null) {
try {
final PrefixPattern pattern= new RSymbolComparator.PrefixPattern(prefixSegmentName);
final BiFunction<Object, Object, Object> mergeFun=
(final Object oldValue, final Object newValue) -> {
List<Object> list;
if (oldValue instanceof List) {
list= (List<Object>) oldValue;
}
else {
list= new ArrayList<>(4);
list.add(oldValue);
}
list.add(newValue);
return list;
};
this.searchPath.init(context, node, getSearchMode(context), prefixName.getScope());
final Set<String> pkgNames= new HashSet<>();
for (final IRFrame frame : this.searchPath) {
if (frame.getFrameType() == IRFrame.PACKAGE) {
final String pkgName= frame.getElementName().getSegmentName();
if (pkgName != null && pkgNames.add(pkgName)) {
final RPkgHelp pkgHelp= help.getPkgHelp(pkgName);
if (pkgHelp != null) {
for (final RHelpTopicEntry topicEntry : pkgHelp.getTopics()) {
final String topic= topicEntry.getTopic();
if (pattern.matches(topic)) {
matches.merge(topic, pkgName, mergeFun);
}
}
}
}
}
}
}
finally {
help.unlock();
}
}
final int offset= (context.getInvocationContentType() != IRDocumentConstants.R_DEFAULT_CONTENT_TYPE) ?
context.getIdentifierLastSegmentOffset() + 1 :
context.getIdentifierLastSegmentOffset();
for (final Map.Entry<String, Object> match : matches.entrySet()) {
proposals.add(new RSimpleCompletionProposal.RHelpTopicCompletionProposal(
context, match.getKey(), toString(match.getValue()),
offset, prio ));
}
}
}