| /******************************************************************************* |
| * Copyright (c) 2010, 2015 Willink Transformations and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * E.D.Willink - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.ocl.xtext.essentialocl.ui.contentassist; |
| |
| import java.util.List; |
| |
| import org.eclipse.emf.ecore.EObject; |
| import org.eclipse.emf.ecore.EReference; |
| import org.eclipse.emf.ecore.resource.Resource; |
| import org.eclipse.emf.ecore.resource.Resource.Diagnostic; |
| import org.eclipse.jdt.annotation.NonNull; |
| import org.eclipse.jdt.annotation.Nullable; |
| import org.eclipse.jface.text.contentassist.ICompletionProposal; |
| import org.eclipse.ocl.pivot.Iteration; |
| import org.eclipse.ocl.pivot.Operation; |
| import org.eclipse.ocl.pivot.Parameter; |
| import org.eclipse.ocl.pivot.PivotFactory; |
| import org.eclipse.ocl.pivot.Property; |
| import org.eclipse.ocl.pivot.Type; |
| import org.eclipse.ocl.pivot.Variable; |
| import org.eclipse.ocl.pivot.utilities.Pivotable; |
| import org.eclipse.ocl.xtext.base.utilities.BaseCSResource; |
| import org.eclipse.ocl.xtext.base.utilities.ElementUtil; |
| import org.eclipse.ocl.xtext.basecs.PathElementCS; |
| import org.eclipse.ocl.xtext.basecs.PathNameCS; |
| import org.eclipse.ocl.xtext.essentialocl.attributes.NavigationUtil; |
| import org.eclipse.ocl.xtext.essentialoclcs.ExpCS; |
| import org.eclipse.ocl.xtext.essentialoclcs.InfixExpCS; |
| import org.eclipse.swt.graphics.Image; |
| import org.eclipse.xtext.AbstractElement; |
| import org.eclipse.xtext.Alternatives; |
| import org.eclipse.xtext.CrossReference; |
| import org.eclipse.xtext.Keyword; |
| import org.eclipse.xtext.RuleCall; |
| import org.eclipse.xtext.naming.IQualifiedNameConverter; |
| import org.eclipse.xtext.nodemodel.INode; |
| import org.eclipse.xtext.nodemodel.util.NodeModelUtils; |
| import org.eclipse.xtext.resource.IEObjectDescription; |
| import org.eclipse.xtext.resource.impl.ListBasedDiagnosticConsumer; |
| 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 com.google.common.base.Function; |
| import com.google.common.base.Predicate; |
| /** |
| * see http://www.eclipse.org/Xtext/documentation/latest/xtext.html#contentAssist on how to customize content assistant |
| */ |
| public class EssentialOCLProposalProvider extends AbstractEssentialOCLProposalProvider |
| { |
| private static final int BOOST_EXPLICIT_PROPERTY = 20; |
| private static final int BOOST_PARAMETER = 20; |
| private static final int BOOST_VARIABLE = 20; |
| private static final int BOOST_IMPLICIT_PROPERTY = 15; |
| private static final int BOOST_OPERATION = 10; |
| private static final int BOOST_ITERATION = 5; |
| private static final int BOOST_TYPE = 0; |
| private static final int BOOST_PACKAGE = -5; |
| |
| public class ClassSensitiveProposalCreator extends DefaultProposalCreator |
| { |
| public ClassSensitiveProposalCreator(ContentAssistContext contentAssistContext, String ruleName, IQualifiedNameConverter qualifiedNameConverter) { |
| super(contentAssistContext, ruleName, qualifiedNameConverter); |
| } |
| |
| @Override |
| public ICompletionProposal apply(IEObjectDescription candidate) { |
| ICompletionProposal proposal = super.apply(candidate); |
| EObject eObject = candidate.getEObjectOrProxy(); |
| if ((proposal instanceof ConfigurableCompletionProposal) && (eObject != null) && !eObject.eIsProxy()) { |
| ConfigurableCompletionProposal configurableCompletionProposal = (ConfigurableCompletionProposal)proposal; |
| int priority = configurableCompletionProposal.getPriority() + getPriorityBoost(eObject); |
| configurableCompletionProposal.setPriority(priority); |
| } |
| return proposal; |
| } |
| |
| } |
| |
| protected static Image collectionTypeImage = null; |
| private static Image primitiveTypeImage = null; |
| |
| @Override |
| public void complete_CollectionTypeIdentifier(EObject model, RuleCall ruleCall, ContentAssistContext context, ICompletionProposalAcceptor acceptor) { |
| if (collectionTypeImage == null) { |
| collectionTypeImage = getImage(PivotFactory.eINSTANCE.createCollectionType()); |
| } |
| proposeKeywordAlternatives(ruleCall, context, acceptor, collectionTypeImage); |
| super.complete_CollectionTypeIdentifier(model, ruleCall, context, acceptor); |
| } |
| |
| @Override |
| public void complete_UnaryOperatorName(EObject model, RuleCall ruleCall, ContentAssistContext context, ICompletionProposalAcceptor acceptor) { |
| proposeKeywordAlternatives(ruleCall, context, acceptor, null); |
| super.complete_UnaryOperatorName(model, ruleCall, context, acceptor); |
| } |
| |
| @Override |
| public void complete_BinaryOperatorName(EObject model, RuleCall ruleCall, ContentAssistContext context, ICompletionProposalAcceptor acceptor) { |
| proposeKeywordAlternatives(ruleCall, context, acceptor, null); |
| super.complete_BinaryOperatorName(model, ruleCall, context, acceptor); |
| } |
| |
| @Override |
| public void complete_NavigationOperatorName(EObject model, RuleCall ruleCall, ContentAssistContext context, ICompletionProposalAcceptor acceptor) { |
| proposeKeywordAlternatives(ruleCall, context, acceptor, null); |
| super.complete_NavigationOperatorName(model, ruleCall, context, acceptor); |
| } |
| |
| @Override |
| public void complete_PrimitiveTypeIdentifier(EObject model, RuleCall ruleCall, ContentAssistContext context, ICompletionProposalAcceptor acceptor) { |
| proposeKeywordAlternatives(ruleCall, context, acceptor, getPrimitiveTypeImage()); |
| } |
| |
| @Override |
| public void createProposals(ContentAssistContext context, ICompletionProposalAcceptor acceptor) { |
| EObject currentModel = context.getCurrentModel(); |
| if ((currentModel instanceof Pivotable) && ((Pivotable)currentModel).getPivot() == null) { |
| Resource eResource = currentModel.eResource(); |
| @NonNull List<Diagnostic> errors = eResource.getErrors(); |
| @SuppressWarnings("unused") int errorsSize = errors.size(); |
| if ((eResource instanceof BaseCSResource) && ElementUtil.hasSyntaxError(errors)) { |
| // |
| // If we skipped the semantic analysis because of syntax errors, take a shot at it now |
| // so that there is some semantic content from which to derive completion proposals. |
| // |
| BaseCSResource csResource = (BaseCSResource) eResource; |
| try { |
| ListBasedDiagnosticConsumer diagnosticsConsumer = new ListBasedDiagnosticConsumer(); |
| csResource.update(diagnosticsConsumer); |
| } |
| catch (Exception exception) { |
| /* Never let an Exception leak out to abort Xtext */ |
| exception.getClass(); // Just a debug breakpoint opportunity. |
| } |
| // assert errorsSize == errors.size(); |
| } |
| // System.out.println("createProposals: for " + context.getPreviousModel().eClass().getName() + " then " + currentModel.eClass().getName() + " with \"" + context.getPrefix() + "\""); |
| } |
| super.createProposals(context, acceptor); |
| } |
| |
| protected EObject getPathScope(EObject model, ContentAssistContext context) { |
| int offset = context.getOffset(); |
| INode currentNode = context.getCurrentNode(); |
| if (currentNode != null) { |
| INode offsetNode = NodeModelUtils.findLeafNodeAtOffset(currentNode, offset); |
| EObject eObject = NodeModelUtils.findActualSemanticObjectFor(offsetNode); |
| if (!(eObject instanceof PathElementCS)) { |
| offsetNode = NodeModelUtils.findLeafNodeAtOffset(currentNode, offset-1); |
| eObject = NodeModelUtils.findActualSemanticObjectFor(offsetNode); |
| } |
| if (eObject instanceof PathElementCS) { |
| model = eObject; |
| } |
| } |
| return model; |
| } |
| |
| protected Image getPrimitiveTypeImage() { |
| if (primitiveTypeImage == null) { |
| primitiveTypeImage = getImage(PivotFactory.eINSTANCE.createPrimitiveType()); |
| } |
| return primitiveTypeImage; |
| } |
| |
| /** |
| * Return a priority boost to prioritize eObject with respect to alternative proposals. |
| * <br> |
| * The return value should be small to avoid disrupting the default 100 spacing with double and three-quartering for prefix matches. |
| */ |
| protected int getPriorityBoost(@Nullable EObject eObject) { |
| if (eObject instanceof Property) { |
| return ((Property)eObject).isIsImplicit() ? BOOST_IMPLICIT_PROPERTY : BOOST_EXPLICIT_PROPERTY; |
| } |
| else if (eObject instanceof Iteration) { |
| return BOOST_ITERATION; |
| } |
| else if (eObject instanceof Operation) { |
| return BOOST_OPERATION; |
| } |
| else if (eObject instanceof Type) { |
| return BOOST_TYPE; |
| } |
| else if (eObject instanceof Parameter) { |
| return BOOST_PARAMETER; |
| } |
| else if (eObject instanceof Variable) { |
| return BOOST_VARIABLE; |
| } |
| else if (eObject instanceof org.eclipse.ocl.pivot.Package) { |
| return BOOST_PACKAGE; |
| } |
| else { |
| return 0; |
| } |
| } |
| |
| @Override |
| protected Function<IEObjectDescription, ICompletionProposal> getProposalFactory(String ruleName, ContentAssistContext contentAssistContext) { |
| return new ClassSensitiveProposalCreator(contentAssistContext, ruleName, getQualifiedNameConverter()); |
| } |
| |
| /* @Override |
| protected void invokeMethod(String methodName, ICompletionProposalAcceptor acceptor, Object... params) { |
| System.out.println(" invokeMethod: " + methodName); |
| super.invokeMethod(methodName, acceptor, params); |
| } */ |
| |
| @Override |
| protected void lookupCrossReference(CrossReference crossReference, |
| EReference reference, ContentAssistContext contentAssistContext, |
| ICompletionProposalAcceptor acceptor, |
| Predicate<IEObjectDescription> filter) { |
| EObject currentModel = contentAssistContext.getCurrentModel(); |
| if (currentModel instanceof InfixExpCS) { |
| EObject previousModel = contentAssistContext.getPreviousModel(); |
| if (NavigationUtil.isNavigationInfixExp(previousModel)) { |
| ExpCS argument = ((InfixExpCS)previousModel).getArgument(); |
| if (argument != null) { |
| currentModel = argument; |
| } |
| } |
| } |
| else if (currentModel instanceof PathNameCS) { |
| currentModel = getPathScope(currentModel, contentAssistContext); |
| } |
| String ruleName = null; |
| if (crossReference.getTerminal() instanceof RuleCall) { |
| ruleName = ((RuleCall) crossReference.getTerminal()).getRule().getName(); |
| } |
| lookupCrossReference(currentModel, reference, acceptor, filter, |
| getProposalFactory(ruleName, contentAssistContext)); |
| } |
| |
| @Override |
| protected void lookupCrossReference(EObject model, EReference reference, |
| ICompletionProposalAcceptor acceptor, |
| Predicate<IEObjectDescription> filter, |
| Function<IEObjectDescription, ICompletionProposal> proposalFactory) { |
| // System.out.println(" lookupCrossReference: " + reference.getEContainingClass().getName() + "::" + reference.getName()); |
| super.lookupCrossReference(model, reference, acceptor, filter, proposalFactory); |
| } |
| |
| protected void proposeKeywordAlternatives(RuleCall ruleCall, ContentAssistContext context, ICompletionProposalAcceptor acceptor, Image image) { |
| AbstractElement alternatives = ruleCall.getRule().getAlternatives(); |
| if (alternatives instanceof Alternatives) { |
| for (AbstractElement alternative : ((Alternatives)alternatives).getElements()) { |
| if (alternative instanceof Keyword) { |
| Keyword keyword = (Keyword)alternative; |
| String name = keyword.getValue(); |
| acceptor.accept(createCompletionProposal(name, name, image, context)); |
| } |
| } |
| } |
| else if (alternatives instanceof RuleCall) { |
| proposeKeywordAlternatives((RuleCall)alternatives, context, acceptor, image); |
| } |
| } |
| } |