blob: 515940b568c75800c5ebb5aa6d2304affc4c1a1c [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 org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.BadPartitioningException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.statet.jcommons.collections.ImCollections;
import org.eclipse.statet.jcommons.collections.ImList;
import org.eclipse.statet.jcommons.lang.NonNullByDefault;
import org.eclipse.statet.jcommons.lang.Nullable;
import org.eclipse.statet.jcommons.text.core.SearchPattern;
import org.eclipse.statet.ecommons.text.core.FragmentDocument;
import org.eclipse.statet.ecommons.text.core.PartitionConstraint;
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.RElementCompletionProposal.RElementProposalParameters;
import org.eclipse.statet.internal.r.ui.editors.RHelpTopicCompletionProposal;
import org.eclipse.statet.internal.r.ui.editors.RPkgCompletionProposal;
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.ElementLabelProvider;
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.AssistProposal;
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.ContentAssistComputer;
import org.eclipse.statet.ltk.ui.sourceediting.assist.SourceProposal.ProposalParameters;
import org.eclipse.statet.r.core.RCore;
import org.eclipse.statet.r.core.RSearchPattern;
import org.eclipse.statet.r.core.RSymbolComparator;
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.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.runtime.RPkgManagerDataset;
@NonNullByDefault
public class RElementCompletionComputer implements ContentAssistComputer {
private static final PartitionConstraint NO_R_COMMENT_CONSTRAINT= new PartitionConstraint() {
@Override
public boolean matches(final String partitionType) {
return (partitionType != IRDocumentConstants.R_COMMENT_CONTENT_TYPE);
};
};
private static final ImList<String> fgKeywords;
static {
final ArrayList<String> list= new ArrayList<>();
Collections.addAll(list, RTokens.CONSTANT_WORDS);
Collections.addAll(list, RTokens.FLOWCONTROL_WORDS);
fgKeywords= ImCollections.toList(list, RSymbolComparator.R_NAMES_COLLATOR);
}
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 ElementLabelProvider labelProvider= new RLabelProvider(RLabelProvider.NAMESPACE);
private final int mode;
private int searchMatchRules;
private final RFrameSearchPath searchPath= new RFrameSearchPath();
private boolean inDefault;
private boolean inString;
private int pkgNamePrio;
private int helpTopicPrio;
protected RElementCompletionComputer(final int mode) {
this.mode= mode;
}
public RElementCompletionComputer() {
this(0);
}
@Override
public void onSessionStarted(final ISourceEditor editor, final ContentAssist assist) {
int matchRules= SearchPattern.PREFIX_MATCH;
if (assist.getShowSubstringMatches()) {
matchRules |= SearchPattern.SUBSTRING_MATCH;
}
this.searchMatchRules= matchRules;
}
@Override
public void onSessionEnded() {
this.searchPath.clear();
}
protected final int getSearchMode(final RAssistInvocationContext context) {
if (this.mode != 0) {
return this.mode;
}
return context.getDefaultRFrameSearchMode();
}
protected int getSearchMatchRules() {
return this.searchMatchRules;
}
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(@Nullable 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 @Nullable 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 void computeCompletionProposals(final AssistInvocationContext context,
final int mode, final AssistProposalCollector proposals,
final IProgressMonitor monitor) {
if (context instanceof RAssistInvocationContext) {
computeCompletionProposals((RAssistInvocationContext) context, mode, proposals,
monitor );
}
}
@Override
public void computeInformationProposals(final AssistInvocationContext context,
final AssistProposalCollector proposals, final IProgressMonitor monitor) {
if (context instanceof RAssistInvocationContext) {
doComputeContextProposals((RAssistInvocationContext) context, proposals, monitor);
}
}
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 @Nullable 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 FragmentDocument) {
final FragmentDocument inputDoc= (FragmentDocument) 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 RElementProposalParameters parameters= new RElementProposalParameters(
context, context.getIdentifierLastSegmentOffset(),
new RSearchPattern(getSearchMatchRules(), prefixSegmentName),
this.labelProvider );
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();
parameters.baseRelevance= iter.getRelevance();
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)
&& parameters.matchesNamePattern(elementName.getSegmentName()) ) {
if ((parameters.baseRelevance < 0) && !isRich
&& mainNames.contains(elementName.getSegmentName()) ) {
continue;
}
parameters.replacementName= elementName;
parameters.element= element;
final AssistProposal proposal= new RElementCompletionProposal(parameters);
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();
parameters.baseRelevance= 0;
if (envir instanceof IRFrameInSource) {
final IRFrameInSource sframe= (IRFrameInSource) envir;
final Set<String> elementNames= sframe.getAllAccessNames();
for (final String candidate : elementNames) {
if (candidate != null
&& !(candidate.equals(prefixSegmentName)
&& (sframe.getAllAccessOf(candidate, false).size() <= 1) )
&& !mainNames.contains(candidate)
&& parameters.matchesNamePattern(candidate) ) {
final AssistProposal proposal= new RSimpleCompletionProposal(parameters,
candidate );
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 ProposalParameters<RAssistInvocationContext> parameters= new ProposalParameters<>(
context, context.getIdentifierOffset(),
new RSearchPattern(SearchPattern.PREFIX_MATCH, prefixSegmentName) );
final List<String> keywords= fgKeywords;
for (final String keyword : keywords) {
if (parameters.matchesNamePattern(keyword)) {
proposals.add(new RSimpleCompletionProposal(parameters, keyword));
}
}
}
}
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 RElementProposalParameters parameters= new RElementProposalParameters(
context, context.getIdentifierLastSegmentOffset(),
new RSearchPattern(getSearchMatchRules(), namePrefix),
this.labelProvider );
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();
parameters.baseRelevance= iter.getRelevance();
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();
if (isCompletable(elementSegment)
&& parameters.matchesNamePattern(candidate) ) {
if ((parameters.baseRelevance > 0) && !isRich
&& mainNames.contains(candidate) ) {
continue ITER_ELEMENTS;
}
parameters.replacementName= elementSegment;
parameters.element= child;
final AssistProposal proposal= new RElementCompletionProposal(parameters);
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();
parameters.baseRelevance= 0;
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)
&& !candidate.equals(namePrefix)
&& !mainNames.contains(candidate)
&& parameters.matchesNamePattern(candidate) ) {
final AssistProposal proposal= new RSimpleCompletionProposal(parameters,
candidate );
mainNames.add(candidate);
proposals.add(proposal);
}
}
}
}
}
}
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 RElementProposalParameters parameters= new RElementProposalParameters(
context, Math.max(fCallInfo.getNode().getArgsOpenOffset() + 1, 0),
this.labelProvider );
final FCallNamePattern pattern= new FCallNamePattern(fCallInfo.getAccess()) {
@Override
protected void handleMatch(final IRMethod element, final IRFrame frame,
final RFrameIterator iterator) {
parameters.baseRelevance= iterator.getRelevance();
parameters.replacementName= element.getElementName();
parameters.element= element;
proposals.add(new RElementCompletionProposal.ContextInformationProposal(parameters));
}
};
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());
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;
}
}
}
final RElementProposalParameters parameters= new RElementProposalParameters(
context, context.getIdentifierOffset(),
new RSearchPattern(getSearchMatchRules(), prefixSegmentName),
this.labelProvider );
class FCallHandler extends FCallNamePattern {
private final @Nullable 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 iter) {
final ArgsDefinition argsDef= element.getArgsDefinition();
if (argsDef == null) {
return;
}
final int contextRelevance= iter.getRelevance();
this.matchCount++;
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 (parameters.matchesNamePattern(arg.name)
&& this.argNames.add(arg.name)) {
final RElementName name= RElementName.create(RElementName.MAIN_DEFAULT, arg.name);
parameters.baseRelevance= contextRelevance + ARG_NAME_PRIO;
parameters.replacementName= name;
parameters.element= element;
proposals.add(new RElementCompletionProposal.ArgumentProposal(parameters));
}
}
}
}
if (argValue) {
final FCall.Args callArgs= fCallInfo.getNode().getArgsChild();
if (!checkArgsDef(RAst.matchArgs(callArgs, argsDef), true, contextRelevance)
&& 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, contextRelevance);
}
}
}
}
}
final FCallHandler search= new FCallHandler();
search.searchFDef(fCallInfo.getSearchPath(getSearchMode(context)));
}
protected final @Nullable RPkgManagerDataset getRPkgDataset(final RAssistInvocationContext context) {
final IRPkgManager manager= RCore.getRPkgManager(context.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 RElementProposalParameters parameters= new RElementProposalParameters(
context, context.getInvocationOffset() - prefixSegmentName.length(),
new RSearchPattern(getSearchMatchRules(), prefixSegmentName), prio,
this.labelProvider );
final RPkgManagerDataset rPkgDataset= getRPkgDataset(context);
final Collection<String> envNames;
if (rPkgDataset != null) {
final RPkgCompilation<? extends RPkgBuilt> pkgs= rPkgDataset.getInstalled();
envNames= pkgs.getNames();
for (final String pkgName : envNames) {
if (parameters.matchesNamePattern(pkgName)) {
parameters.replacementName= RElementName.create(RElementName.SCOPE_PACKAGE, pkgName);
final AssistProposal proposal= new RPkgCompletionProposal(parameters,
pkgs.getFirst(pkgName) );
proposals.add(proposal);
}
}
}
else {
envNames= ImCollections.emptySet();
}
final Set<String> workspaceNames= RModel.getRModelManager().getPkgNames();
for (final String pkgName : workspaceNames) {
if (!envNames.contains(pkgName)
&& parameters.matchesNamePattern(pkgName) ) {
parameters.replacementName= RElementName.create(RElementName.SCOPE_PACKAGE, pkgName);
proposals.add(new RPkgCompletionProposal(parameters, null));
}
}
}
protected final void doComputeHelpTopicProposals(final RAssistInvocationContext context,
final @Nullable 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= context.getRCoreAccess().getREnv();
if (rEnv == null) {
return;
}
final RHelpManager rHelpManager= RCore.getRHelpManager();
final REnvHelp help= rHelpManager.getHelp(rEnv);
if (help != null) {
try {
final ProposalParameters<RAssistInvocationContext> parameters= new ProposalParameters<>(
context, (context.getInvocationContentType() != IRDocumentConstants.R_DEFAULT_CONTENT_TYPE) ?
context.getIdentifierLastSegmentOffset() + 1 :
context.getIdentifierLastSegmentOffset(),
new RSearchPattern(getSearchMatchRules(), prefixSegmentName),
prio );
final Map<String, RHelpTopicCompletionProposal> map= new HashMap<>();
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 (parameters.matchesNamePattern(topic)) {
RHelpTopicCompletionProposal proposal= map.get(topic);
if (proposal == null) {
proposal= new RHelpTopicCompletionProposal(
parameters, topic, pkgName );
map.put(topic, proposal);
proposals.add(proposal);
}
else {
proposal.addPackage(pkgName);
}
}
}
}
}
}
}
}
finally {
help.unlock();
}
}
}
}