All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.xtext.xbase.ui.contentassist.XbaseProposalProvider Maven / Gradle / Ivy

/*
* generated by Xtext
*/
package org.eclipse.xtext.xbase.ui.contentassist;

import static org.eclipse.xtext.util.Strings.*;

import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.jdt.core.search.IJavaSearchConstants;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.contentassist.ICompletionProposal;
import org.eclipse.jface.text.contentassist.IContextInformation;
import org.eclipse.jface.viewers.StyledString;
import org.eclipse.xtext.AbstractElement;
import org.eclipse.xtext.Assignment;
import org.eclipse.xtext.CrossReference;
import org.eclipse.xtext.GrammarUtil;
import org.eclipse.xtext.Group;
import org.eclipse.xtext.Keyword;
import org.eclipse.xtext.RuleCall;
import org.eclipse.xtext.common.types.JvmExecutable;
import org.eclipse.xtext.common.types.JvmFeature;
import org.eclipse.xtext.common.types.JvmFormalParameter;
import org.eclipse.xtext.common.types.JvmIdentifiableElement;
import org.eclipse.xtext.common.types.JvmTypeReference;
import org.eclipse.xtext.common.types.TypesPackage;
import org.eclipse.xtext.common.types.xtext.ui.ITypesProposalProvider;
import org.eclipse.xtext.common.types.xtext.ui.TypeMatchFilters;
import org.eclipse.xtext.conversion.IValueConverter;
import org.eclipse.xtext.conversion.ValueConverterException;
import org.eclipse.xtext.conversion.impl.QualifiedNameValueConverter;
import org.eclipse.xtext.naming.IQualifiedNameConverter;
import org.eclipse.xtext.naming.QualifiedName;
import org.eclipse.xtext.nodemodel.ICompositeNode;
import org.eclipse.xtext.nodemodel.ILeafNode;
import org.eclipse.xtext.nodemodel.INode;
import org.eclipse.xtext.nodemodel.util.NodeModelUtils;
import org.eclipse.xtext.resource.IEObjectDescription;
import org.eclipse.xtext.scoping.IScope;
import org.eclipse.xtext.ui.editor.contentassist.ConfigurableCompletionProposal;
import org.eclipse.xtext.ui.editor.contentassist.ContentAssistContext;
import org.eclipse.xtext.ui.editor.contentassist.ICompletionProposalAcceptor;
import org.eclipse.xtext.ui.editor.contentassist.PrefixMatcher;
import org.eclipse.xtext.ui.editor.contentassist.RepeatedContentAssistProcessor;
import org.eclipse.xtext.xbase.XAbstractFeatureCall;
import org.eclipse.xtext.xbase.XAssignment;
import org.eclipse.xtext.xbase.XBasicForLoopExpression;
import org.eclipse.xtext.xbase.XBinaryOperation;
import org.eclipse.xtext.xbase.XBlockExpression;
import org.eclipse.xtext.xbase.XClosure;
import org.eclipse.xtext.xbase.XExpression;
import org.eclipse.xtext.xbase.XFeatureCall;
import org.eclipse.xtext.xbase.XMemberFeatureCall;
import org.eclipse.xtext.xbase.XbasePackage;
import org.eclipse.xtext.xbase.conversion.XbaseQualifiedNameValueConverter;
import org.eclipse.xtext.xbase.scoping.SyntaxFilteredScopes;
import org.eclipse.xtext.xbase.scoping.batch.IIdentifiableElementDescription;
import org.eclipse.xtext.xbase.scoping.featurecalls.OperatorMapping;
import org.eclipse.xtext.xbase.typesystem.IBatchTypeResolver;
import org.eclipse.xtext.xbase.typesystem.IExpressionScope;
import org.eclipse.xtext.xbase.typesystem.IResolvedTypes;
import org.eclipse.xtext.xbase.typesystem.references.LightweightTypeReference;
import org.eclipse.xtext.xbase.typesystem.references.LightweightTypeReferenceFactory;
import org.eclipse.xtext.xtype.XtypePackage;

import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.Maps;
import com.google.inject.Inject;

/**
 * See https://www.eclipse.org/Xtext/documentation/304_ide_concepts.html#content-assist
 * on how to customize the content assistant.
 */
public class XbaseProposalProvider extends AbstractXbaseProposalProvider implements RepeatedContentAssistProcessor.ModeAware {
	
	public static class ValidFeatureDescription implements Predicate {

		@Inject
		private OperatorMapping operatorMapping;
		
		@Override
		public boolean apply(IEObjectDescription input) {
			if (input instanceof IIdentifiableElementDescription) {
				final IIdentifiableElementDescription desc = (IIdentifiableElementDescription) input;
				if (!desc.isVisible() || !desc.isValidStaticState()) // || !desc.isValid())
					return false;
				
				// filter operator method names from CA
				if (input.getName().getFirstSegment().startsWith("operator_")) {
					return operatorMapping.getOperator(input.getName()) == null;
				}
				return true;
			}
			return true;
		}
		
	}
	
	private final static Logger log = Logger.getLogger(XbaseProposalProvider.class);
	
	/**
	 * the user data key used to store the IEObjectDescription in the {@link ConfigurableCompletionProposal#setAdditionalData(String, Object)}
	 */
	public final static String DESCRIPTION_KEY = "xbase.description";
	
	@Inject
	private ITypesProposalProvider typeProposalProvider;
	
	@Inject
	private ValidFeatureDescription featureDescriptionPredicate;
	
	@Inject
	private XbaseQualifiedNameValueConverter qualifiedNameValueConverter;
	
	@Inject
	private StaticQualifierPrefixMatcher staticQualifierPrefixMatcher;
	
	@Inject
	private IBatchTypeResolver typeResolver;
	
	@Inject
	private SyntaxFilteredScopes syntaxFilteredScopes;
	
	@Override
	public String getNextCategory() {
		return getXbaseCrossReferenceProposalCreator().getNextCategory();
	}
	
	@Override
	public void nextMode() {
		getXbaseCrossReferenceProposalCreator().nextMode();
	}
	
	@Override
	public void reset() {
		getXbaseCrossReferenceProposalCreator().reset();
	}
	
	@Override
	public boolean isLastMode() {
		return getXbaseCrossReferenceProposalCreator().isLastMode();
	}
	
	public XbaseReferenceProposalCreator getXbaseCrossReferenceProposalCreator() {
		return (XbaseReferenceProposalCreator) super.getCrossReferenceProposalCreator();
	}
	
	@Override
	public void completeXImportDeclaration_ImportedType(EObject model, Assignment assignment, ContentAssistContext context,
			ICompletionProposalAcceptor acceptor) {
		completeJavaTypes(context, XtypePackage.Literals.XIMPORT_DECLARATION__IMPORTED_TYPE, true,
				getQualifiedNameValueConverter(), createVisibilityFilter(context, IJavaSearchConstants.TYPE), acceptor);
	}

	@Override
	public void completeJvmParameterizedTypeReference_Type(EObject model, Assignment assignment,
			ContentAssistContext context, ICompletionProposalAcceptor acceptor) {
		if (getXbaseCrossReferenceProposalCreator().isShowTypeProposals() || getXbaseCrossReferenceProposalCreator().isShowSmartProposals()) {
			completeJavaTypes(context, TypesPackage.Literals.JVM_PARAMETERIZED_TYPE_REFERENCE__TYPE, acceptor);
		}
	}
	
	@Override
	public void completeXConstructorCall_Constructor(EObject model, Assignment assignment,
			ContentAssistContext context, ICompletionProposalAcceptor acceptor) {
		completeJavaTypes(context, TypesPackage.Literals.JVM_PARAMETERIZED_TYPE_REFERENCE__TYPE, true,
				qualifiedNameValueConverter, TypeMatchFilters.and(TypeMatchFilters.canInstantiate(), createVisibilityFilter(context)), acceptor);
	}
	
	@Override
	public void completeXRelationalExpression_Type(EObject model, Assignment assignment, ContentAssistContext context,
			ICompletionProposalAcceptor acceptor) {
		completeJavaTypes(context, TypesPackage.Literals.JVM_PARAMETERIZED_TYPE_REFERENCE__TYPE, acceptor);
	}

	protected void completeJavaTypes(ContentAssistContext context, EReference reference, ICompletionProposalAcceptor acceptor) {
		completeJavaTypes(context, reference, qualifiedNameValueConverter, createVisibilityFilter(context), acceptor);
	}
	
	protected void completeJavaTypes(ContentAssistContext context, EReference reference, ITypesProposalProvider.Filter filter, ICompletionProposalAcceptor acceptor) {
		completeJavaTypes(context, reference, qualifiedNameValueConverter, filter, acceptor);
	}
	
	protected void completeJavaTypes(ContentAssistContext context, EReference reference, IValueConverter valueConverter, ITypesProposalProvider.Filter filter, ICompletionProposalAcceptor acceptor) {
		completeJavaTypes(context, reference, false, valueConverter, filter, acceptor);
	}

	protected void completeJavaTypes(ContentAssistContext context, EReference reference, boolean forced,
			IValueConverter valueConverter, ITypesProposalProvider.Filter filter,
			ICompletionProposalAcceptor acceptor) {
		String prefix = context.getPrefix();
		if (prefix.length() > 0) {
			if (Character.isJavaIdentifierStart(context.getPrefix().charAt(0))) {
				if (!forced && getXbaseCrossReferenceProposalCreator().isShowSmartProposals()) {
					if (!prefix.contains(".") && !prefix.contains("::") && !Character.isUpperCase(prefix.charAt(0)))
						return;
				}
				typeProposalProvider.createTypeProposals(this, context, reference, filter, valueConverter, acceptor);
			}
		} else {
			if (forced || !getXbaseCrossReferenceProposalCreator().isShowSmartProposals()) {
				INode lastCompleteNode = context.getLastCompleteNode();
				if (lastCompleteNode instanceof ILeafNode && !((ILeafNode) lastCompleteNode).isHidden()) {
					if (lastCompleteNode.getLength() > 0 && lastCompleteNode.getTotalEndOffset() == context.getOffset()) {
						String text = lastCompleteNode.getText();
						char lastChar = text.charAt(text.length() - 1);
						if (Character.isJavaIdentifierPart(lastChar)) {
							return;
						}
					}
				}
				typeProposalProvider.createTypeProposals(this, context, reference, filter, valueConverter, acceptor);
			}
		}
	}
	
	@Override
	public void completeXTypeLiteral_Type(EObject model, Assignment assignment, ContentAssistContext context,
			ICompletionProposalAcceptor acceptor) {
		completeJavaTypes(context, XbasePackage.Literals.XTYPE_LITERAL__TYPE, true, qualifiedNameValueConverter, createVisibilityFilter(context), acceptor);
	}
	
	public void proposeDeclaringTypeForStaticInvocation(EObject model, Assignment assignment, ContentAssistContext context,
			ICompletionProposalAcceptor acceptor){
		if (getXbaseCrossReferenceProposalCreator().isShowTypeProposals() || getXbaseCrossReferenceProposalCreator().isShowSmartProposals()) {
			ContentAssistContext modifiedContext = context.copy().setMatcher(staticQualifierPrefixMatcher).toContext();
			completeJavaTypes(modifiedContext, TypesPackage.Literals.JVM_PARAMETERIZED_TYPE_REFERENCE__TYPE, qualifiedNameValueConverter, createVisibilityFilter(context), acceptor);
		}
	}
	
	protected ITypesProposalProvider.Filter createVisibilityFilter(ContentAssistContext context) {
		return createVisibilityFilter(context, IJavaSearchConstants.TYPE);
	}
	
	protected ITypesProposalProvider.Filter createVisibilityFilter(ContentAssistContext context, int searchFor) {
		return TypeMatchFilters.and(TypeMatchFilters.isNotInternal(searchFor), TypeMatchFilters.isAcceptableByPreference());
	}
	
	@Override
	public void completeKeyword(Keyword keyword, ContentAssistContext contentAssistContext,
			ICompletionProposalAcceptor acceptor) {
		if (isKeywordWorthyToPropose(keyword, contentAssistContext)) {
			super.completeKeyword(keyword, contentAssistContext, acceptor);
		}
	}
	
	protected boolean isKeywordWorthyToPropose(Keyword keyword, ContentAssistContext context) {
		if (isKeywordWorthyToPropose(keyword)) {
			if ("as".equals(keyword.getValue()) || "instanceof".equals(keyword.getValue())) {
				EObject previousModel = context.getPreviousModel();
				if (previousModel instanceof XExpression) {
					if (context.getPrefix().length() == 0) {
						if (NodeModelUtils.getNode(previousModel).getEndOffset() > context.getOffset()) {
							return false;
						}
					}
					LightweightTypeReference type = typeResolver.resolveTypes(previousModel).getActualType((XExpression) previousModel);
					if (type == null || type.isPrimitiveVoid()) {
						return false;
					}
				}
			}
			return true;
		}
		return false;
	}

	protected boolean isKeywordWorthyToPropose(Keyword keyword) {
		return keyword.getValue().length() > 1 && Character.isLetter(keyword.getValue().charAt(0));
	}
	
	@Override
	protected void lookupCrossReference(CrossReference crossReference, ContentAssistContext contentAssistContext,
			ICompletionProposalAcceptor acceptor) {
		lookupCrossReference(crossReference, contentAssistContext, acceptor, getFeatureDescriptionPredicate(contentAssistContext));
	}
	
	@Override
	public void completeXFeatureCall_Feature(EObject model, Assignment assignment, ContentAssistContext context,
			ICompletionProposalAcceptor acceptor) {
		if (model != null) {
			if (typeResolver.resolveTypes(model).hasExpressionScope(model, IExpressionScope.Anchor.WITHIN)) {
				return;
			}
		}
		if (model instanceof XMemberFeatureCall) {
			ICompositeNode node = NodeModelUtils.getNode(model);
			int endOffset = node.getEndOffset();
			if (isInMemberFeatureCall(model, endOffset, context)) {
				return;
			}
		}
		createLocalVariableAndImplicitProposals(model, IExpressionScope.Anchor.AFTER, context, acceptor);
	}

	@Override
	public void completeXForLoopExpression_EachExpression(EObject model, Assignment assignment,
			ContentAssistContext context, ICompletionProposalAcceptor acceptor) {
		createLocalVariableAndImplicitProposals(model, IExpressionScope.Anchor.WITHIN, context, acceptor);
	}
	
	@Override
	public void completeXForLoopExpression_ForExpression(EObject model, Assignment assignment,
			ContentAssistContext context, ICompletionProposalAcceptor acceptor) {
		createLocalVariableAndImplicitProposals(model, IExpressionScope.Anchor.BEFORE, context, acceptor);
	}
	
	@Override
	public void completeXSwitchExpression_Default(EObject model, Assignment assignment, ContentAssistContext context,
			ICompletionProposalAcceptor acceptor) {
		createLocalVariableAndImplicitProposals(model, IExpressionScope.Anchor.WITHIN, context, acceptor);
	}
	
	@Override
	public void completeXCasePart_Then(EObject model, Assignment assignment, ContentAssistContext context,
			ICompletionProposalAcceptor acceptor) {
		createLocalVariableAndImplicitProposals(model, IExpressionScope.Anchor.AFTER, context, acceptor);
	}
	
	@Override
	public void completeXCasePart_Case(EObject model, Assignment assignment, ContentAssistContext context,
			ICompletionProposalAcceptor acceptor) {
		createLocalVariableAndImplicitProposals(model, IExpressionScope.Anchor.WITHIN, context, acceptor);
	}
	
	@Override
	public XbaseReferenceProposalCreator getCrossReferenceProposalCreator() {
		return (XbaseReferenceProposalCreator) super.getCrossReferenceProposalCreator();
	}
	
	@Override
	public void completeXBlockExpression_Expressions(EObject model, Assignment assignment,
			ContentAssistContext context, ICompletionProposalAcceptor acceptor) {
		completeWithinBlock(model, context, acceptor);
	}
	
	protected void completeWithinBlock(EObject model, ContentAssistContext context, ICompletionProposalAcceptor acceptor) {
		ICompositeNode node = NodeModelUtils.getNode(model);
		if (node.getOffset() >= context.getOffset()) {
			createLocalVariableAndImplicitProposals(model, IExpressionScope.Anchor.BEFORE, context, acceptor);
			return;
		}
		if (model instanceof XBlockExpression) {
			List children = ((XBlockExpression) model).getExpressions();
			if (!children.isEmpty()) {
				for(int i = children.size() - 1; i >= 0; i--) {
					XExpression child = children.get(i);
					ICompositeNode childNode = NodeModelUtils.getNode(child);
					if (childNode.getEndOffset() <= context.getOffset()) {
						createLocalVariableAndImplicitProposals(child, IExpressionScope.Anchor.AFTER, context, acceptor);
						return;
					}
				}
			}
		}
		int endOffset = node.getEndOffset();
		if (endOffset <= context.getOffset()) {
			if (model instanceof XFeatureCall && model.eContainer() instanceof XClosure || endOffset == context.getOffset() && context.getPrefix().length() == 0)
				return;
			if (isInMemberFeatureCall(model, endOffset, context))
				return;
			createLocalVariableAndImplicitProposals(model, IExpressionScope.Anchor.AFTER, context, acceptor);
			return;
		} else if (isInMemberFeatureCall(model, endOffset, context)) {
			return;
		}
		if (model instanceof XClosure)
			return;
		createLocalVariableAndImplicitProposals(model, IExpressionScope.Anchor.BEFORE, context, acceptor);
	}
	
	protected boolean isInMemberFeatureCall(EObject model, int endOffset, ContentAssistContext context) {
		if (model instanceof XMemberFeatureCall && endOffset >= context.getOffset()) {
			List featureNodes = NodeModelUtils.findNodesForFeature(model, XbasePackage.Literals.XABSTRACT_FEATURE_CALL__FEATURE);
			if (!featureNodes.isEmpty()) {
				INode featureNode = featureNodes.get(0);
				if (featureNode.getTotalOffset() < context.getOffset() && featureNode.getTotalEndOffset() >= context.getOffset()) {
					return true;
				}
			}
		}
		return false;
	}
	
	@Override
	public void completeXExpressionInClosure_Expressions(EObject model, Assignment assignment,
			ContentAssistContext context, ICompletionProposalAcceptor acceptor) {
		completeWithinBlock(model, context, acceptor);
	}
	
	@Override
	public void completeXAssignment_Feature(EObject model, Assignment assignment, ContentAssistContext context,
			ICompletionProposalAcceptor acceptor) {
		String ruleName = getConcreteSyntaxRuleName(assignment);
		// unfortunately we have two different kinds of 'feature' assignemnts in the rule XAssignment
		// thus we have to find the operator rule here
		if (isOperatorRule(ruleName)) {
			completeBinaryOperationFeature(model, assignment, context, acceptor);
		}
	}
	
	protected boolean isOperatorRule(String ruleName) {
		return ruleName != null && ruleName.startsWith("Op");
	}

	@Override
	public void completeXOrExpression_Feature(EObject model, Assignment assignment, ContentAssistContext context,
			ICompletionProposalAcceptor acceptor) {
		completeBinaryOperationFeature(model, assignment, context, acceptor);
	}
	
	@Override
	public void completeXAndExpression_Feature(EObject model, Assignment assignment, ContentAssistContext context,
			ICompletionProposalAcceptor acceptor) {
		completeBinaryOperationFeature(model, assignment, context, acceptor);
	}
	
	@Override
	public void completeXEqualityExpression_Feature(EObject model, Assignment assignment, ContentAssistContext context,
			ICompletionProposalAcceptor acceptor) {
		completeBinaryOperationFeature(model, assignment, context, acceptor);
	}
	
	@Override
	public void completeXRelationalExpression_Feature(EObject model, Assignment assignment, ContentAssistContext context,
			ICompletionProposalAcceptor acceptor) {
		completeBinaryOperationFeature(model, assignment, context, acceptor);
	}
	
	@Override
	public void completeXOtherOperatorExpression_Feature(EObject model, Assignment assignment, ContentAssistContext context,
			ICompletionProposalAcceptor acceptor) {
		completeBinaryOperationFeature(model, assignment, context, acceptor);
	}
	
	@Override
	public void completeXAdditiveExpression_Feature(EObject model, Assignment assignment, ContentAssistContext context,
			ICompletionProposalAcceptor acceptor) {
		completeBinaryOperationFeature(model, assignment, context, acceptor);
	}
	
	@Override
	public void completeXMultiplicativeExpression_Feature(EObject model, Assignment assignment, ContentAssistContext context,
			ICompletionProposalAcceptor acceptor) {
		completeBinaryOperationFeature(model, assignment, context, acceptor);
	}

	@Override
	public void completeXUnaryOperation_Feature(EObject model, Assignment assignment, ContentAssistContext context,
			ICompletionProposalAcceptor acceptor) {
		// don't propose unary operations like !, +, -
	}
	
	@Override
	public void completeXPostfixOperation_Feature(EObject model, Assignment assignment, ContentAssistContext context,
			ICompletionProposalAcceptor acceptor) {
		completeBinaryOperationFeature(model, assignment, context, acceptor);
	}
	
	protected void completeBinaryOperationFeature(EObject model, Assignment assignment, ContentAssistContext context,
			ICompletionProposalAcceptor acceptor) {
		if (model instanceof XBinaryOperation) {
			if (context.getPrefix().length() == 0) { // check for a cursor inbetween an operator
				INode currentNode = context.getCurrentNode();
				int offset = currentNode.getOffset();
				int endOffset = currentNode.getEndOffset();
				if (offset < context.getOffset() && endOffset >= context.getOffset()) {
					if (currentNode.getGrammarElement() instanceof CrossReference) {
						// don't propose another binary operator
						return;
					}
				}
			}
			if (NodeModelUtils.findActualNodeFor(model).getEndOffset() <= context.getOffset()) {
				createReceiverProposals((XExpression) model,
						(CrossReference) assignment.getTerminal(), context, acceptor);
			} else {
				createReceiverProposals(((XBinaryOperation) model).getLeftOperand(),
						(CrossReference) assignment.getTerminal(), context, acceptor);
			}
		} else {
			EObject previousModel = context.getPreviousModel();
			if (previousModel instanceof XExpression) {
				if (context.getPrefix().length() == 0) {
					if (NodeModelUtils.getNode(previousModel).getEndOffset() > context.getOffset()) {
						return;
					}
				}
				createReceiverProposals((XExpression) previousModel,
						(CrossReference) assignment.getTerminal(), context, acceptor);
			}
		}
	}
	
	@Override
	public void completeXCatchClause_Expression(EObject model, Assignment assignment, ContentAssistContext context,
			ICompletionProposalAcceptor acceptor) {
		createLocalVariableAndImplicitProposals(model, IExpressionScope.Anchor.WITHIN, context, acceptor);
	}
	
	@Override
	public void complete_XExpression(EObject model, RuleCall ruleCall, ContentAssistContext context,
			ICompletionProposalAcceptor acceptor) {
		// likely XParenthesizedExpression
		EObject container = ruleCall.eContainer();
		// avoid dependency on XbaseGrammarAccess
		if (container instanceof Group && "XParenthesizedExpression".equals(GrammarUtil.containingRule(ruleCall).getName())) {
			createLocalVariableAndImplicitProposals(model, IExpressionScope.Anchor.WITHIN, context, acceptor);
		}
	}
	
	@Override
	public void completeXBasicForLoopExpression_InitExpressions(EObject model, Assignment assignment,
			ContentAssistContext context, ICompletionProposalAcceptor acceptor) {
		ICompositeNode node = NodeModelUtils.getNode(model);
		if (node.getOffset() >= context.getOffset()) {
			createLocalVariableAndImplicitProposals(model, IExpressionScope.Anchor.BEFORE, context, acceptor);
			return;
		}
		if (model instanceof XBasicForLoopExpression) {
			List children = ((XBasicForLoopExpression) model).getInitExpressions();
			if (!children.isEmpty()) {
				for(int i = children.size() - 1; i >= 0; i--) {
					XExpression child = children.get(i);
					ICompositeNode childNode = NodeModelUtils.getNode(child);
					if (childNode.getEndOffset() <= context.getOffset()) {
						createLocalVariableAndImplicitProposals(child, IExpressionScope.Anchor.AFTER, context, acceptor);
						return;
					}
				}
			}
		}
		createLocalVariableAndImplicitProposals(model, IExpressionScope.Anchor.BEFORE, context, acceptor);
	}
	
	@Override
	public void completeXBasicForLoopExpression_UpdateExpressions(EObject model, Assignment assignment,
			ContentAssistContext context, ICompletionProposalAcceptor acceptor) {
		createLocalVariableAndImplicitProposals(model, IExpressionScope.Anchor.WITHIN, context, acceptor);
	}
	
	@Override
	public void completeXBasicForLoopExpression_Expression(EObject model, Assignment assignment,
			ContentAssistContext context, ICompletionProposalAcceptor acceptor) {
		createLocalVariableAndImplicitProposals(model, IExpressionScope.Anchor.WITHIN, context, acceptor);
	}
	
	@Override
	public void completeXBasicForLoopExpression_EachExpression(EObject model, Assignment assignment,
			ContentAssistContext context, ICompletionProposalAcceptor acceptor) {
		createLocalVariableAndImplicitProposals(model, IExpressionScope.Anchor.WITHIN, context, acceptor);
	}
	
	@Override
	public void completeXClosure_Expression(EObject model, Assignment assignment, ContentAssistContext context,
			ICompletionProposalAcceptor acceptor) {
		createLocalVariableAndImplicitProposals(model, IExpressionScope.Anchor.WITHIN, context, acceptor);
	}
	
	@Override
	public void completeXShortClosure_Expression(EObject model, Assignment assignment, ContentAssistContext context,
			ICompletionProposalAcceptor acceptor) {
		createLocalVariableAndImplicitProposals(model, IExpressionScope.Anchor.WITHIN, context, acceptor);
	}
	
	@Override
	public void completeXMemberFeatureCall_Feature(EObject model, Assignment assignment, ContentAssistContext context,
			ICompletionProposalAcceptor acceptor) {
		if (model instanceof XMemberFeatureCall) {
			createReceiverProposals(((XMemberFeatureCall) model).getMemberCallTarget(), (CrossReference) assignment.getTerminal(),
					context, acceptor);
		} else if (model instanceof XAssignment) {
			createReceiverProposals(((XAssignment) model).getAssignable(), (CrossReference) assignment.getTerminal(),
					context, acceptor);
		}
	}
	
	@Override
	public void completeXVariableDeclaration_Right(EObject model, Assignment assignment, ContentAssistContext context,
			ICompletionProposalAcceptor acceptor) {
		createLocalVariableAndImplicitProposals(model, IExpressionScope.Anchor.BEFORE, context, acceptor);
	}
	
	protected void createLocalVariableAndImplicitProposals(
			EObject context,
			IExpressionScope.Anchor anchor,
			ContentAssistContext contentAssistContext,
			ICompletionProposalAcceptor acceptor) {
		String prefix = contentAssistContext.getPrefix();
		if (prefix.length() > 0) {
			if (!Character.isJavaIdentifierStart(prefix.charAt(0))) {
				return;
			}
		}
		
//		long time = System.currentTimeMillis();
		Function proposalFactory = getProposalFactory(getFeatureCallRuleName(), contentAssistContext);
		IResolvedTypes resolvedTypes = context != null ? typeResolver.resolveTypes(context) : typeResolver.resolveTypes(contentAssistContext.getResource());
		IExpressionScope expressionScope = resolvedTypes.getExpressionScope(context, anchor);
		// TODO use the type name information
		IScope scope = expressionScope.getFeatureScope();
		getCrossReferenceProposalCreator().lookupCrossReference(scope, context, XbasePackage.Literals.XABSTRACT_FEATURE_CALL__FEATURE, acceptor, getFeatureDescriptionPredicate(contentAssistContext), proposalFactory);
//		System.out.printf("XbaseProposalProvider.createLocalVariableAndImplicitProposals = %d\n", System.currentTimeMillis() - time);
//		time = System.currentTimeMillis();
		
		// TODO use the type name information
		proposeDeclaringTypeForStaticInvocation(context, null /* ignore */, contentAssistContext, acceptor);
//		System.out.printf("XbaseProposalProvider.proposeDeclaringTypeForStaticInvocation = %d\n", System.currentTimeMillis() - time);
	}

	protected String getFeatureCallRuleName() {
		return "IdOrSuper";
	}
	
	/**
	 * Create proposal for {@link XAbstractFeatureCall#getFeature() simple feature calls} that use an IdOrSuper
	 * as concrete syntax.
	 */
	protected void createLocalVariableAndImplicitProposals(EObject context, ContentAssistContext contentAssistContext, ICompletionProposalAcceptor acceptor) {
		createLocalVariableAndImplicitProposals(context, IExpressionScope.Anchor.BEFORE, contentAssistContext, acceptor);
	}

	protected void createReceiverProposals(XExpression receiver, CrossReference crossReference, ContentAssistContext contentAssistContext, ICompletionProposalAcceptor acceptor) {
//		long time = System.currentTimeMillis();
		String ruleName = getConcreteSyntaxRuleName(crossReference);
		Function proposalFactory = getProposalFactory(ruleName, contentAssistContext);
		IResolvedTypes resolvedTypes = typeResolver.resolveTypes(receiver);
		LightweightTypeReference receiverType = resolvedTypes.getActualType(receiver);
		if (receiverType == null || receiverType.isPrimitiveVoid()) {
			return;
		}
		IExpressionScope expressionScope = resolvedTypes.getExpressionScope(receiver, IExpressionScope.Anchor.RECEIVER);
		// TODO exploit the type name information
		IScope scope;
		if (contentAssistContext.getCurrentModel() != receiver) {
			EObject currentModel = contentAssistContext.getCurrentModel();
			if (currentModel instanceof XMemberFeatureCall && ((XMemberFeatureCall) currentModel).getMemberCallTarget() == receiver) {
				scope = filterByConcreteSyntax(expressionScope.getFeatureScope((XAbstractFeatureCall) currentModel), crossReference);
			} else {
				scope = filterByConcreteSyntax(expressionScope.getFeatureScope(), crossReference);
			}
		} else {
			scope = filterByConcreteSyntax(expressionScope.getFeatureScope(), crossReference);
		}
		getCrossReferenceProposalCreator().lookupCrossReference(scope, receiver, XbasePackage.Literals.XABSTRACT_FEATURE_CALL__FEATURE, acceptor, getFeatureDescriptionPredicate(contentAssistContext), proposalFactory);
//		System.out.printf("XbaseProposalProvider.createReceiverProposals = %d\n", System.currentTimeMillis() - time);
	}
	
	protected IScope filterByConcreteSyntax(IScope parent, AbstractElement syntax) {
		// filter down the x-refs to operators
		// rather than enumerating all proposals, just consider the syntactically valid entries
		return syntaxFilteredScopes.create(parent, syntax);
	}
	
	protected String getConcreteSyntaxRuleName(Assignment assignment) {
		AbstractElement terminal = assignment.getTerminal();
		if (terminal instanceof CrossReference) {
			return getConcreteSyntaxRuleName((CrossReference) terminal);
		}
		String ruleName = null;
		if (terminal instanceof RuleCall) {
			ruleName = getConcreteSyntaxRuleName((RuleCall) terminal);
		}
		return ruleName;
	}
	
	protected String getConcreteSyntaxRuleName(RuleCall ruleCall) {
		String ruleName = ruleCall.getRule().getName();
		return ruleName;
	}

	protected String getConcreteSyntaxRuleName(CrossReference crossReference) {
		String ruleName = null;
		if (crossReference.getTerminal() instanceof RuleCall) {
			ruleName = getConcreteSyntaxRuleName((RuleCall) crossReference.getTerminal());
		}
		return ruleName;
	}

	protected boolean doNotProposeFeatureOfBinaryOperation(ContentAssistContext contentAssistContext,
			XBinaryOperation binaryOperation) {
		List nodesForFeature = NodeModelUtils.findNodesForFeature(binaryOperation, XbasePackage.Literals.XABSTRACT_FEATURE_CALL__FEATURE);
		if (!nodesForFeature.isEmpty()) {
			INode node = nodesForFeature.get(0);
			if (node.getOffset() < contentAssistContext.getOffset() - contentAssistContext.getPrefix().length()) {
				XExpression rightOperand = binaryOperation.getRightOperand();
				if (rightOperand == null)
					return true;
				ICompositeNode rightOperandNode = NodeModelUtils.findActualNodeFor(rightOperand);
				if (rightOperandNode != null) {
					if (rightOperandNode.getOffset() >= contentAssistContext.getOffset())
						return true;
					if (isParentOf(rightOperandNode, contentAssistContext.getLastCompleteNode()))
						return true;
				}
			}
		}
		return false;
	}
	
	protected boolean isParentOf(INode node, INode child) {
		if (node == null)
			return false;
		while(child != null && node.equals(child)) {
			child = child.getParent();
		}
		return node.equals(child);
	}

	@Override
	protected Function getProposalFactory(final String ruleName,
			final ContentAssistContext contentAssistContext) {
		return new DefaultProposalCreator(contentAssistContext, ruleName, getQualifiedNameConverter()) {
			
			private Map simpleNameToParameterList = Maps.newHashMap();
			
			@Override
			public ICompletionProposal apply(final IEObjectDescription candidate) {
				IEObjectDescription myCandidate = candidate;
				ContentAssistContext myContentAssistContext = contentAssistContext;
				if (myCandidate instanceof MultiNameDescription) {
					final MultiNameDescription multiNamed = (MultiNameDescription) candidate;
					myCandidate = multiNamed.getDelegate();
					myContentAssistContext = myContentAssistContext.copy().setMatcher(new PrefixMatcher() {
						@Override
						public boolean isCandidateMatchingPrefix(String name, String prefix) {
							PrefixMatcher delegateMatcher = contentAssistContext.getMatcher();
							if (delegateMatcher.isCandidateMatchingPrefix(name, prefix))
								return true;
							IQualifiedNameConverter converter = getQualifiedNameConverter();
							String unconvertedName = converter.toString(candidate.getName());
							if (!unconvertedName.equals(name) && delegateMatcher.isCandidateMatchingPrefix(unconvertedName, prefix))
								return true;
							for(QualifiedName otherName: multiNamed.getOtherNames()) {
								String alternative = converter.toString(otherName);
								if (delegateMatcher.isCandidateMatchingPrefix(alternative, prefix))
									return true;
								String convertedAlternative = valueConverter != null ? valueConverter.toString(alternative) : getValueConverter().toString(alternative, ruleName);
								if (!convertedAlternative.equals(alternative) &&
										delegateMatcher.isCandidateMatchingPrefix(convertedAlternative, prefix)) {
									return true;
								}
							}
							return false;
						}
					}).toContext();
				}
				if (myCandidate instanceof IIdentifiableElementDescription && (isIdRule(ruleName))) {
					ICompletionProposal result = null;
					String proposal = getQualifiedNameConverter().toString(myCandidate.getName());
					if (valueConverter != null) {
						try {
							proposal = getValueConverter().toString(proposal, ruleName);
						} catch (ValueConverterException e) {
							log.debug(e.getMessage(), e);
							return null;
						}
					} else if (ruleName != null) {
						try {
							proposal = getValueConverter().toString(proposal, ruleName);
						} catch (ValueConverterException e) {
							log.debug(e.getMessage(), e);
							return null;
						}
					}
					ProposalBracketInfo bracketInfo = getProposalBracketInfo(myCandidate, contentAssistContext);
					proposal += bracketInfo.brackets;
					int insignificantParameters = 0;
					if(myCandidate instanceof IIdentifiableElementDescription) {
						IIdentifiableElementDescription casted = (IIdentifiableElementDescription) myCandidate;
						insignificantParameters = casted.getNumberOfIrrelevantParameters();
					}
					LightweightTypeReferenceFactory converter = getTypeConverter(contentAssistContext.getResource());
					EObject objectOrProxy = myCandidate.getEObjectOrProxy();
					StyledString displayString;
					if (objectOrProxy instanceof JvmFeature) {
						if (bracketInfo.brackets.startsWith(" =")) {
							displayString = getStyledDisplayString((JvmFeature)objectOrProxy,
									false, insignificantParameters,
									getQualifiedNameConverter().toString(myCandidate.getQualifiedName()),
									getQualifiedNameConverter().toString(myCandidate.getName()) + bracketInfo.brackets,
									converter);
						} else {
							displayString = getStyledDisplayString((JvmFeature)objectOrProxy,
									!isEmpty(bracketInfo.brackets), insignificantParameters,
									getQualifiedNameConverter().toString(myCandidate.getQualifiedName()),
									getQualifiedNameConverter().toString(myCandidate.getName()),
									converter);
						}
					} else {
						displayString = getStyledDisplayString(objectOrProxy,
								getQualifiedNameConverter().toString(myCandidate.getQualifiedName()),
								getQualifiedNameConverter().toString(myCandidate.getName()));
					}
					result = createCompletionProposal(proposal, displayString, null, myContentAssistContext);
					if (result instanceof ConfigurableCompletionProposal) {
						ConfigurableCompletionProposal casted = (ConfigurableCompletionProposal) result;
						casted.setAdditionalData(DESCRIPTION_KEY, myCandidate);
						casted.setAdditionalProposalInfo(objectOrProxy);
						casted.setHover(getHover());
						int offset = casted.getReplacementOffset() + proposal.length();
						casted.setCursorPosition(casted.getCursorPosition() + bracketInfo.caretOffset);
						if (bracketInfo.selectionOffset != 0) {
							offset += bracketInfo.selectionOffset;
							casted.setSelectionStart(offset);
							casted.setSelectionLength(bracketInfo.selectionLength);
							casted.setAutoInsertable(false);
							casted.setSimpleLinkedMode(myContentAssistContext.getViewer(), '\t', '\n', '\r');
						}
						if (objectOrProxy instanceof JvmExecutable) {
							final JvmExecutable executable = (JvmExecutable) objectOrProxy;
							StyledString parameterList = new StyledString();
							appendParameters(parameterList, executable, insignificantParameters, converter);
							// TODO how should we display overloaded methods were one variant does not take arguments? -> empty parentheses? '' ?
							if (parameterList.length() > 0) {
								ParameterData parameterData = simpleNameToParameterList.get(myCandidate.getName());
								if (parameterData == null) {
									parameterData = new ParameterData();
									simpleNameToParameterList.put(myCandidate.getName(), parameterData);
								}
								parameterData.addOverloaded(parameterList.toString(), executable.isVarArgs());
								IContextInformation contextInformation = new ParameterContextInformation(parameterData, displayString.toString(), offset, offset);
								casted.setContextInformation(contextInformation);
							}
							// If the user types 'is' as a prefix for boolean properties, we want to keep that after applying the proposal
							if (executable.getSimpleName().startsWith("is") && executable.getSimpleName().length() > 2 && executable.getParameters().size() - insignificantParameters == 0) {
								((ConfigurableCompletionProposal) result).setTextApplier(new ConfigurableCompletionProposal.IReplacementTextApplier() {
									@Override
									public void apply(IDocument document, ConfigurableCompletionProposal proposal) throws BadLocationException {
										String replacementString = proposal.getReplacementString();
										if (proposal.getReplacementLength() >= 2) {
											String is = document.get(proposal.getReplacementOffset(), 2);
											if ("is".equals(is)) {
												replacementString = getValueConverter().toString(executable.getSimpleName(), ruleName);
											}
										}
										proposal.setCursorPosition(replacementString.length());
										document.replace(proposal.getReplacementOffset(), proposal.getReplacementLength(), replacementString);
									}
								});
							}
						}
					}
					getPriorityHelper().adjustCrossReferencePriority(result, myContentAssistContext.getPrefix());
					return result;
				}
				return super.apply(candidate);
			}
		};
	}
	
	protected static class ProposalBracketInfo {
		String brackets = "";
		int selectionOffset = 0;
		int selectionLength = 0;
		int caretOffset = 0;
	}
	
	protected ProposalBracketInfo getProposalBracketInfo(IEObjectDescription proposedDescription, ContentAssistContext contentAssistContext) {
		ProposalBracketInfo info = new ProposalBracketInfo();
		if (proposedDescription instanceof IIdentifiableElementDescription) {
			IIdentifiableElementDescription jvmFeatureDescription = (IIdentifiableElementDescription)proposedDescription;
			JvmIdentifiableElement jvmFeature = jvmFeatureDescription.getElementOrProxy();
			if(jvmFeature instanceof JvmExecutable) {
				List parameters = ((JvmExecutable) jvmFeature).getParameters();
				if (jvmFeatureDescription.getNumberOfParameters() == 1) {
					if (jvmFeature.getSimpleName().startsWith("set") && !proposedDescription.getName().getFirstSegment().startsWith("set")) {
						info.brackets = " = value";
						info.selectionOffset = -"value".length();
						info.selectionLength = "value".length();
						return info;
					}
					JvmTypeReference parameterType = parameters.get(parameters.size()-1).getParameterType();
					LightweightTypeReference light = getTypeConverter(contentAssistContext.getResource()).toLightweightReference(parameterType);
					if(light.isFunctionType()) {
						int numParameters = light.getAsFunctionTypeReference().getParameterTypes().size();
						if(numParameters == 1) {
							info.brackets = "[]";
							info.caretOffset = -1;
							return info;
						} else if(numParameters == 0) {
					 		info.brackets = "[|]";
							info.caretOffset = -1;
							return info;
						} else {
					 		final StringBuilder b = new StringBuilder();
					 		for(int i=0; i getFeatureDescriptionPredicate(ContentAssistContext contentAssistContext) {
		return featureDescriptionPredicate;
	}
	
	protected QualifiedNameValueConverter getQualifiedNameValueConverter() {
		return qualifiedNameValueConverter;
	}
	
	protected ITypesProposalProvider getTypesProposalProvider() {
		return typeProposalProvider;
	}
	
	protected boolean isIdRule(final String ruleName) {
		return "IdOrSuper".equals(ruleName) || "ValidID".equals(ruleName) || "FeatureCallID".equals(ruleName);
	}
	
	public boolean isExplicitOperationCall(IIdentifiableElementDescription desc) {
		return desc.getNumberOfParameters() > 0;
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy