/** * Copyright (c) 2011 itemis AG 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: * itemis AG - initial API and implementation * */ package org.yakindu.sct.model.stext.ui.contentassist; import java.util.ArrayList; import java.util.List; import org.eclipse.emf.common.util.BasicEList; import org.eclipse.emf.common.util.EList; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.edit.provider.ComposedAdapterFactory; import org.eclipse.emf.edit.provider.IItemLabelProvider; import org.eclipse.jface.text.contentassist.ICompletionProposal; import org.eclipse.jface.viewers.ILabelProvider; import org.eclipse.xtext.Assignment; import org.eclipse.xtext.CrossReference; import org.eclipse.xtext.EnumLiteralDeclaration; import org.eclipse.xtext.Keyword; import org.eclipse.xtext.RuleCall; import org.eclipse.xtext.XtextFactory; import org.eclipse.xtext.resource.IEObjectDescription; import org.eclipse.xtext.ui.editor.contentassist.ConfigurableCompletionProposal; import org.eclipse.xtext.ui.editor.contentassist.ContentAssistContext; import org.eclipse.xtext.ui.editor.contentassist.ContentProposalLabelProvider; import org.eclipse.xtext.ui.editor.contentassist.ICompletionProposalAcceptor; import org.yakindu.base.expressions.expressions.ElementReferenceExpression; import org.yakindu.base.expressions.expressions.FeatureCall; import org.yakindu.base.types.Operation; import org.yakindu.base.types.Type; import org.yakindu.sct.model.stext.services.STextGrammarAccess; import org.yakindu.sct.model.stext.stext.InterfaceScope; import org.yakindu.sct.model.stext.stext.InternalScope; import org.yakindu.sct.model.stext.stext.SimpleScope; import org.yakindu.sct.model.stext.stext.StatechartSpecification; import org.yakindu.sct.model.stext.stext.TransitionSpecification; import org.yakindu.sct.model.stext.stext.VariableDefinition; import com.google.common.base.Function; import com.google.inject.Inject; /** * Several filters to make proposals more useful. * * @author muehlbrandt */ public class STextProposalProvider extends AbstractSTextProposalProvider { @Inject protected STextGrammarAccess grammarAccess; private ComposedAdapterFactory composedAdapterFactory = new ComposedAdapterFactory( ComposedAdapterFactory.Descriptor.Registry.INSTANCE); @Inject @ContentProposalLabelProvider private ILabelProvider labelProvider; /** * Validates if a keyword should be viewed by the proposal view. * * Builds dependent on the ContentAssistContext a list with keywords which * shouldn't be displayed by the proposal view. */ @Override public void completeKeyword(Keyword keyword, ContentAssistContext contentAssistContext, ICompletionProposalAcceptor acceptor) { List<Keyword> suppressKeywords = new ArrayList<Keyword>(); EObject rootModel = contentAssistContext.getRootModel(); if (rootModel instanceof TransitionSpecification) { suppressKeywords(suppressKeywords, (TransitionSpecification) rootModel); } else if (rootModel instanceof SimpleScope) { suppressKeywords(suppressKeywords, (SimpleScope) rootModel); } else if (rootModel instanceof StatechartSpecification) { suppressKeywords(suppressKeywords, (StatechartSpecification) rootModel); } EObject currentModel = contentAssistContext.getCurrentModel(); if (currentModel instanceof InterfaceScope) { suppressKeywords(suppressKeywords, (InterfaceScope) currentModel); } if (currentModel instanceof FeatureCall) { suppressKeywords(suppressKeywords, (FeatureCall) currentModel); } if (currentModel instanceof ElementReferenceExpression) { suppressKeywords(suppressKeywords, (ElementReferenceExpression) currentModel); } if (currentModel instanceof InternalScope) { suppressKeywords(suppressKeywords, (InternalScope) currentModel); } if (!suppressKeywords.contains(keyword)) { super.completeKeyword(keyword, contentAssistContext, new AcceptorDelegate(acceptor)); } } // context Transition protected void suppressKeywords(List<Keyword> suppressKeywords, TransitionSpecification model) { suppressKeywords.addAll(getKeywords(grammarAccess.getEntryEventAccess().getGroup().eContents())); suppressKeywords.addAll(getKeywords(grammarAccess.getExitEventAccess().getGroup().eContents())); } // context States protected void suppressKeywords(List<Keyword> suppressKeywords, SimpleScope model) { suppressKeywords.addAll(getKeywords(grammarAccess.getVariableDefinitionAccess().getGroup().eContents())); suppressKeywords.addAll(getKeywords(grammarAccess.getEventDefinitionAccess().getGroup().eContents())); suppressKeywords.addAll(getKeywords(grammarAccess.getDirectionAccess().getAlternatives().eContents())); suppressKeywords.addAll(getKeywords(grammarAccess.getOperationDefinitionAccess().getGroup().eContents())); } // context Statechart protected void suppressKeywords(List<Keyword> suppressKeywords, StatechartSpecification model) { suppressKeywords.addAll(getKeywords(grammarAccess.getExitEventAccess().getGroup().eContents())); suppressKeywords.addAll(getKeywords(grammarAccess.getEntryEventAccess().getGroup().eContents())); EList<EObject> importKeyWordList = new BasicEList<EObject>(); importKeyWordList.add(grammarAccess.getImportScopeAccess().getImportKeyword_1()); suppressKeywords.addAll(getKeywords(importKeyWordList)); } protected void suppressKeywords(List<Keyword> suppressKeywords, InterfaceScope model) { suppressKeywords.addAll(getKeywords(grammarAccess.getLocalReactionAccess().getGroup().eContents())); suppressKeywords.addAll(getKeywords(grammarAccess.getAlwaysEventAccess().getGroup().eContents())); suppressKeywords.addAll(getKeywords(grammarAccess.getTimeEventTypeAccess().getAlternatives().eContents())); suppressKeywords.add(grammarAccess.getDirectionAccess().getLOCALLocalKeyword_0_0()); } protected void suppressKeywords(List<Keyword> suppressKeywords, FeatureCall featureCall) { if (!(featureCall.getFeature() instanceof Operation)) { suppressKeywords .add(grammarAccess.getFeatureCallAccess().getOperationCallLeftParenthesisKeyword_1_3_0_0_0()); } } protected void suppressKeywords(List<Keyword> suppressKeywords, ElementReferenceExpression referenceExpression) { if (!(referenceExpression.getReference() instanceof Operation)) { suppressKeywords.add(grammarAccess.getElementReferenceExpressionAccess() .getOperationCallLeftParenthesisKeyword_2_0_0_0()); } } protected void suppressKeywords(List<Keyword> suppressKeywords, InternalScope model) { suppressKeywords.add(grammarAccess.getDirectionAccess().getINInKeyword_1_0()); suppressKeywords.add(grammarAccess.getDirectionAccess().getOUTOutKeyword_2_0()); } protected List<Keyword> getKeywords(EList<EObject> list) { final List<Keyword> keywords = new ArrayList<Keyword>(); for (EObject eObject : list) { if (eObject instanceof Keyword) { keywords.add((Keyword) eObject); } else if (eObject instanceof EnumLiteralDeclaration) { keywords.add(((EnumLiteralDeclaration) eObject).getLiteral()); } } return keywords; } protected Function<IEObjectDescription, ICompletionProposal> getProposalFactory(String ruleName, ContentAssistContext contentAssistContext) { return new DefaultProposalCreator(contentAssistContext, ruleName, getQualifiedNameConverter()) { @Override public ICompletionProposal apply(IEObjectDescription candidate) { ICompletionProposal proposal = super.apply(candidate); EObject eObjectOrProxy = candidate.getEObjectOrProxy(); if (eObjectOrProxy.eIsProxy()) { return proposal; } if (eObjectOrProxy instanceof Operation) { Operation operation = (Operation) eObjectOrProxy; if (operation.getParameters().size() > 0 && (proposal instanceof ConfigurableCompletionProposal)) { ConfigurableCompletionProposal configurableProposal = (ConfigurableCompletionProposal) proposal; configurableProposal.setReplacementString(configurableProposal.getReplacementString() + "()"); configurableProposal.setCursorPosition(configurableProposal.getCursorPosition() + 1); } } return proposal; } }; } @Override public void completeElementReferenceExpression_Reference(EObject model, Assignment assignment, ContentAssistContext context, ICompletionProposalAcceptor acceptor) { lookupCrossReference(((CrossReference) assignment.getTerminal()), context, acceptor); } @Override public void completeSimpleElementReferenceExpression_Reference(EObject model, Assignment assignment, ContentAssistContext context, ICompletionProposalAcceptor acceptor) { lookupCrossReference(((CrossReference) assignment.getTerminal()), context, acceptor); } @Override public void completeTypeSpecifier_Type(EObject model, Assignment assignment, ContentAssistContext context, ICompletionProposalAcceptor acceptor) { lookupCrossReference(((CrossReference) assignment.getTerminal()), context, acceptor); } @Override public void complete_BOOL(EObject model, RuleCall ruleCall, ContentAssistContext context, ICompletionProposalAcceptor acceptor) { ICompletionProposalAcceptor priorityOptimizer = getCustomAcceptor(model, "boolean", acceptor); for (String s : new String[] { "true", "false", "yes", "no" }) { ICompletionProposal proposal = createCompletionProposal(s, s + " - " + ruleCall.getRule().getName(), null, context); priorityOptimizer.accept(proposal); } } protected ICompletionProposalAcceptor getCustomAcceptor(EObject model, String typeName, ICompletionProposalAcceptor acceptor) { ICompletionProposalAcceptor priorityOptimizer = acceptor; if (model instanceof VariableDefinition) { VariableDefinition vd = (VariableDefinition) model; if (vd.getType() != null && typeName.equalsIgnoreCase(vd.getType().getName())) { priorityOptimizer = new ICompletionProposalAcceptor.Delegate(acceptor) { @Override public void accept(ICompletionProposal proposal) { alterPriority(proposal, 1); super.accept(proposal); } }; } } return priorityOptimizer; } @Override protected String getDisplayString(EObject element, String qualifiedNameAsString, String shortName) { if (element instanceof Type) { return super.getDisplayString(element, qualifiedNameAsString, shortName); } if (element == null || element.eIsProxy()) { return qualifiedNameAsString; } IItemLabelProvider adapter = (IItemLabelProvider) composedAdapterFactory.adapt(element, IItemLabelProvider.class); if (adapter != null) { return adapter.getText(element); } return super.getDisplayString(element, qualifiedNameAsString, shortName); } @Override public void complete_STRING(EObject model, RuleCall ruleCall, ContentAssistContext context, ICompletionProposalAcceptor acceptor) { super.complete_STRING(model, ruleCall, context, getCustomAcceptor(model, "string", acceptor)); } @Override public void complete_INT(EObject model, RuleCall ruleCall, ContentAssistContext context, ICompletionProposalAcceptor acceptor) { super.complete_INT(model, ruleCall, context, getCustomAcceptor(model, "integer", acceptor)); } public void complete_XID(EObject model, RuleCall ruleCall, ContentAssistContext context, ICompletionProposalAcceptor acceptor) { complete_ID(model, ruleCall, context, acceptor); } @Override public void complete_HEX(EObject model, RuleCall ruleCall, ContentAssistContext context, ICompletionProposalAcceptor acceptor) { ICompletionProposalAcceptor priorityOptimizer = getCustomAcceptor(model, "integer", acceptor); String proposalText = "0x1"; ICompletionProposal proposal = createCompletionProposal(proposalText, proposalText + " - " + ruleCall.getRule().getName(), null, context); if (proposal instanceof ConfigurableCompletionProposal) { ConfigurableCompletionProposal configurable = (ConfigurableCompletionProposal) proposal; configurable.setSelectionStart(configurable.getReplacementOffset() + 2); configurable.setSelectionLength(proposalText.length() - 2); configurable.setAutoInsertable(false); configurable.setSimpleLinkedMode(context.getViewer(), '\t', ' '); } priorityOptimizer.accept(proposal); } @Override public void complete_DOUBLE(EObject model, RuleCall ruleCall, ContentAssistContext context, ICompletionProposalAcceptor acceptor) { ICompletionProposalAcceptor priorityOptimizer = getCustomAcceptor(model, "real", acceptor); String proposalText = "0.1"; ICompletionProposal proposal = createCompletionProposal(proposalText, proposalText + " - " + ruleCall.getRule().getName(), null, context); priorityOptimizer.accept(proposal); } private void alterPriority(ICompletionProposal proposal, int delta) { if (proposal == null || !(proposal instanceof ConfigurableCompletionProposal)) return; ConfigurableCompletionProposal castedProposal = (ConfigurableCompletionProposal) proposal; castedProposal.setPriority(castedProposal.getPriority() + delta); } /** * The acceptor delegate creates a Dummy EObject of type Keyword for the * User Help Hover integration * */ public class AcceptorDelegate implements ICompletionProposalAcceptor { private final ICompletionProposalAcceptor delegate; public AcceptorDelegate(ICompletionProposalAcceptor delegate) { this.delegate = delegate; } public void accept(ICompletionProposal proposal) { if (proposal instanceof ConfigurableCompletionProposal) { Keyword keyword = XtextFactory.eINSTANCE.createKeyword(); keyword.setValue(proposal.getDisplayString()); ((ConfigurableCompletionProposal) proposal).setAdditionalProposalInfo(keyword); ((ConfigurableCompletionProposal) proposal).setHover(STextProposalProvider.this.getHover()); } delegate.accept(proposal); } public boolean canAcceptMoreProposals() { return delegate.canAcceptMoreProposals(); } } }