Commit bbbc2ada authored by Philip Makedonski's avatar Philip Makedonski
Browse files

+ improved proposals, #122

parent b92d2e05
Loading
Loading
Loading
Loading
Loading
+255 −17
Original line number Diff line number Diff line
@@ -3,25 +3,42 @@
 */
package org.etsi.mts.tdl.ui.contentassist;

import java.util.List;

import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.jface.viewers.StyledString;
import org.eclipse.xtext.AbstractElement;
import org.eclipse.xtext.AbstractRule;
import org.eclipse.xtext.Assignment;
import org.eclipse.xtext.CrossReference;
import org.eclipse.xtext.EcoreUtil2;
import org.eclipse.xtext.GrammarUtil;
import org.eclipse.xtext.ParserRule;
import org.eclipse.xtext.RuleCall;
import org.eclipse.xtext.nodemodel.util.NodeModelUtils;
import org.eclipse.xtext.ui.editor.contentassist.ContentAssistContext;
import org.eclipse.xtext.ui.editor.contentassist.ICompletionProposalAcceptor;
import org.eclipse.xtext.ui.editor.contentassist.IFollowElementAcceptor;
import org.eclipse.xtext.ui.editor.contentassist.AbstractContentProposalProvider.NullSafeCompletionProposalAcceptor;
import org.eclipse.xtext.util.Strings;
import org.etsi.mts.tdl.CastDataUse;
import org.etsi.mts.tdl.Connection;
import org.etsi.mts.tdl.DataElementUse;
import org.etsi.mts.tdl.DataInstance;
import org.etsi.mts.tdl.DataType;
import org.etsi.mts.tdl.DataUse;
import org.etsi.mts.tdl.FormalParameter;
import org.etsi.mts.tdl.Function;
import org.etsi.mts.tdl.Member;
import org.etsi.mts.tdl.MemberAssignment;
import org.etsi.mts.tdl.MemberReference;
import org.etsi.mts.tdl.Message;
import org.etsi.mts.tdl.NamedElement;
import org.etsi.mts.tdl.PackageableElement;
import org.etsi.mts.tdl.Parameter;
import org.etsi.mts.tdl.ParameterBinding;
import org.etsi.mts.tdl.SimpleDataType;
import org.etsi.mts.tdl.Target;

import org.etsi.mts.tdl.StructuredDataInstance;
@@ -32,13 +49,69 @@ import org.etsi.mts.tdl.StructuredDataType;
 * on how to customize the content assistant.
 */
public class TDLtxProposalProvider extends AbstractTDLtxProposalProvider {
	public static boolean traceCompletions = false;
	@Override
	public void createProposals(ContentAssistContext context, ICompletionProposalAcceptor acceptor) {
//		if (traceCompletions) {
//			for (AbstractElement element : context.getFirstSetGrammarElements()) {
//				System.out.println("--P: "+element.getClass().getSimpleName());
//			}
//		}
		super.createProposals(context, acceptor);
	}
	@Override
	public void completeRuleCall(RuleCall ruleCall, ContentAssistContext contentAssistContext,
			ICompletionProposalAcceptor acceptor) {
		if (traceCompletions) {
			AbstractRule calledRule = ruleCall.getRule();
			String methodName = "complete_" + calledRule.getName();
			System.out.println("--R: "+methodName);
		}
		super.completeRuleCall(ruleCall, contentAssistContext, acceptor);
	}

	@Override
	public void completeAssignment(Assignment assignment, ContentAssistContext contentAssistContext,
			ICompletionProposalAcceptor acceptor) {
		traceCompletions = false;
		if (traceCompletions) {
			ParserRule parserRule = GrammarUtil.containingParserRule(assignment);
			String methodName = "complete" + Strings.toFirstUpper(parserRule.getName()) + "_"
					+ Strings.toFirstUpper(assignment.getFeature());
			System.out.println("--A: "+methodName);
//		if (methodName.contains("Minus")) {
//			return;
//		}
		}
		super.completeAssignment(assignment, contentAssistContext, acceptor);
	}

	@Override
	public void complete_DataUse(EObject model, RuleCall ruleCall, ContentAssistContext context,
			ICompletionProposalAcceptor acceptor) {
		// TODO Auto-generated method stub
		if (model instanceof ParameterBinding) {
//			complete_ParameterBinding(model, ruleCall, context, acceptor);
			return;
		}
		super.complete_DataUse(model, ruleCall, context, acceptor);
	}

	@Override
	public void complete_DataUseWrapped(EObject model, RuleCall ruleCall, ContentAssistContext context,
			ICompletionProposalAcceptor acceptor) {
		// TODO Auto-generated method stub
		if (model instanceof ParameterBinding) {
			return;
		}
		super.complete_DataUse(model, ruleCall, context, acceptor);
	}

	@Override
	public void complete_ParameterBinding(EObject model, RuleCall ruleCall, ContentAssistContext context,
			ICompletionProposalAcceptor acceptor) {
		// TODO Auto-generated method stub
		super.complete_ParameterBinding(model, ruleCall, context, acceptor);
//		System.out.println("PB:");
	}
	
	@Override
@@ -49,15 +122,47 @@ public class TDLtxProposalProvider extends AbstractTDLtxProposalProvider {
//		completeRuleCall(((RuleCall)assignment.getTerminal()), context, acceptor);

		if (model instanceof DataElementUse) {
			for (Member m : ((StructuredDataType)((DataElementUse) model).resolveDataType()).allMembers()) {
				acceptor.accept(createCompletionProposal(m.getName(), new StyledString(m.getName()), getImage(m), 1000, "", context));
			}
			completeMemberReferenceForDataUse(model, context, acceptor, "", " = ?", " : assign to member");
		}
		//after comma
//		System.out.println("s1");
//		System.out.println(assignment);
	}

	@Override
	public void completeParameterBindingFragment_Argument(EObject model, Assignment assignment,
			ContentAssistContext context, ICompletionProposalAcceptor acceptor) {
		// TODO Auto-generated method stub
		super.completeParameterBindingFragment_Argument(model, assignment, context, acceptor);
	}
	@Override
	public void completeParameterBinding_DataUse(EObject model, Assignment assignment, ContentAssistContext context,
			ICompletionProposalAcceptor acceptor) {
		// TODO Auto-generated method stub
		
		if (model instanceof ParameterBinding) {
			return;
		}

		super.completeParameterBinding_DataUse(model, assignment, context, acceptor);
	}

	@Override
	public void completeMemberAssignment_MemberSpec(EObject model, Assignment assignment, ContentAssistContext context,
			ICompletionProposalAcceptor acceptor) {
		// TODO Auto-generated method stub
		var ma = ((MemberAssignment) model);
		var type = ma.getMember().getDataType();
		completeAssignment(model, context, acceptor, type);
	}

	@Override
	public void complete_MemberAssignment(EObject model, RuleCall ruleCall, ContentAssistContext context,
			ICompletionProposalAcceptor acceptor) {
		// TODO Auto-generated method stub
		System.out.println("MemberAssignment: "+model);
		completeMemberReferenceForDataUse(model, context, acceptor, "", " = ?", " : assign to member");
	}
	
	@Override
	public void complete_ParameterBindingFragment(EObject model, RuleCall ruleCall, ContentAssistContext context,
			ICompletionProposalAcceptor acceptor) {
@@ -70,8 +175,48 @@ public class TDLtxProposalProvider extends AbstractTDLtxProposalProvider {
//				acceptor.accept(createCompletionProposal(m.getName(), new StyledString(m.getName()), getImage(m), 1000, "", context));
//			}
//		}
//		System.out.println("s2");
//		System.out.println(NodeModelUtils.findActualNodeFor(model).getText());
		if (model instanceof ParameterBinding) {
			var pb = ((ParameterBinding) model);
			var type = pb.getParameter().getDataType();
			//TODO: add anything that contains string fields?
			//start with 
			completeAssignment(model, context, acceptor, type);
		}	
		if ((model instanceof DataElementUse)) {
			if (!context.getPrefix().equals("(")) {
				completeMemberReferenceForDataUse(model, context, acceptor, "(", " = ?)", " : assign to member");
			} else {
				completeMemberReferenceForDataUse(model, context, acceptor, "", "", "");
			}
		}
	}
	private void completeAssignment(EObject model, ContentAssistContext context, ICompletionProposalAcceptor acceptor, DataType type) {
		List<NamedElement> namedElements = EcoreUtil2.getAllContentsOfType(model.eResource(), NamedElement.class);
		for (NamedElement ne : namedElements) {
			boolean accept = false;
			//TODO: priorities
			if (ne instanceof DataInstance && ((DataInstance)ne).getDataType().conformsTo(type)) {
				accept = true;
			} else if (ne instanceof DataType && ((DataType) ne).conformsTo(type)) {
				accept = true;
			} else if (ne instanceof Function && ((Function) ne).getReturnType().conformsTo(type)) {
				accept = true;
			} else if (ne instanceof FormalParameter && EcoreUtil2.isAncestor(EcoreUtil2.getContainerOfType(model, PackageableElement.class), ne)) {
				accept = true;
			} else if (ne instanceof Parameter && ((Parameter) ne).getDataType().conformsTo(type) && ne.container() instanceof StructuredDataType) {
				StructuredDataType dt = ((StructuredDataType) ne.container());
				List<StructuredDataInstance> compatibleDataInstances = EcoreUtil2.getAllContentsOfType(model.eResource(), StructuredDataInstance.class).stream()
					.filter(sdi -> sdi.getDataType().conformsTo(dt))
					.toList();
				//TODO: also consider where dt is used recursively via members / extensions?
				for (StructuredDataInstance sdi : compatibleDataInstances) {
					acceptor.accept(createCompletionProposal(sdi.getName()+"."+ne.getName(), new StyledString(sdi.getName()+"."+ne.getName()).append(" : in "+sdi.getQualifiedName(), StyledString.DECORATIONS_STYLER), getImage(ne), 900, "", context));
				}
			}
			if (accept) {
				acceptor.accept(createCompletionProposal(ne.getName(), new StyledString(ne.getName()), getImage(ne), 1000, "", context));
			}
		}
	}

	@Override
@@ -81,10 +226,10 @@ public class TDLtxProposalProvider extends AbstractTDLtxProposalProvider {
		//super.completeMemberAssignment_Member(model, assignment, context, acceptor);
		
		// compute the plain proposal
		for (Member m : ((StructuredDataType)
				((StructuredDataInstance)model).getDataType()).allMembers()) {
			acceptor.accept(createCompletionProposal(m.getName(), context));
		}
//		for (Member m : ((StructuredDataType)
//				((StructuredDataInstance)model).getDataType()).allMembers()) {
//			acceptor.accept(createCompletionProposal(m.getName(), context));
//		}
	    // Create and register the completion proposal:
	    // The proposal may be null as the createCompletionProposal(..) 
	    // methods check for valid prefixes and terminal token conflicts.
@@ -108,7 +253,9 @@ public class TDLtxProposalProvider extends AbstractTDLtxProposalProvider {
	@Override
	public void complete_ReductionFragment(EObject model, RuleCall ruleCall, ContentAssistContext context,
			ICompletionProposalAcceptor acceptor) {
		completeMemberReferenceForDataUse(model, context, acceptor, ".");
		if (!context.getPrefix().equals("(")) {
			completeMemberReferenceForDataUse(model, context, acceptor, ".", "", " : access member");
		}
	}
	
	@Override
@@ -123,7 +270,7 @@ public class TDLtxProposalProvider extends AbstractTDLtxProposalProvider {
			ICompletionProposalAcceptor acceptor) {
		// TODO Auto-generated method stub
//		super.complete_MemberReference(model, ruleCall, context, acceptor);
		completeMemberReferenceForDataUse(model, context, acceptor, "");
		completeMemberReferenceForDataUse(model, context, acceptor, "", "", "");
//		if (model instanceof MemberReference) {
//			DataType dataType = ((MemberReference)model).getMember().getDataType();
//			if (dataType instanceof StructuredDataType) {
@@ -135,27 +282,44 @@ public class TDLtxProposalProvider extends AbstractTDLtxProposalProvider {
		
	}

	private void completeMemberReferenceForDataUse(EObject model, ContentAssistContext context, ICompletionProposalAcceptor acceptor, String prefix) {
	private void completeMemberReferenceForDataUse(EObject model, ContentAssistContext context, ICompletionProposalAcceptor acceptor, String prefix, String suffix, String description) {
		if (model instanceof CastDataUse) {
			DataType dataType = ((CastDataUse) model).getDataType();
			if (dataType instanceof StructuredDataType) {
				for (Member m : ((StructuredDataType) dataType).allMembers()) {
					acceptor.accept(createCompletionProposal(prefix+m.getName(), new StyledString(m.getName()), getImage(m), 1000, "", context));
					acceptor.accept(createCompletionProposal(m.getName()+suffix, new StyledString(prefix+m.getName()+suffix).append(description, StyledString.DECORATIONS_STYLER), getImage(m), 1000, "", context));
				}
			}
		} else if (model instanceof DataElementUse) {
			//TODO: add suffix? e.g. =
//			System.out.println(model);
			NamedElement dataElement = ((DataElementUse) model).getDataElement();
			if (dataElement instanceof StructuredDataType) { 
				for (Member m : ((StructuredDataType) dataElement).allMembers()) {
					acceptor.accept(createCompletionProposal(prefix+m.getName(), new StyledString(m.getName()), getImage(m), 100000, "", context));
					acceptor.accept(createCompletionProposal(prefix+m.getName()+suffix, new StyledString(prefix+m.getName()+suffix).append(description, StyledString.DECORATIONS_STYLER), getImage(m), 100000, "", context));
				}
			} else if (dataElement instanceof StructuredDataInstance) {
				for (Member m : ((StructuredDataType) ((StructuredDataInstance)dataElement).getDataType()).allMembers()) {
					acceptor.accept(createCompletionProposal(prefix+m.getName(), new StyledString(m.getName()), getImage(m), 100000, "", context));
					acceptor.accept(createCompletionProposal(prefix+m.getName()+suffix, new StyledString(prefix+m.getName()+suffix).append(description, StyledString.DECORATIONS_STYLER), getImage(m), 100000, "", context));
				}
			}
			//TODO: function / parameter
		} else if (model instanceof StructuredDataInstance) {
			StructuredDataInstance instance = (StructuredDataInstance) model;
			DataType dataType = instance.getDataType();
			//get assigned members of instance
			List<Member> assigned = instance.getMemberAssignment().stream()
				.map(MemberAssignment::getMember)
				.toList();

			//TODO: reuse
			if (dataType instanceof StructuredDataType) {
				for (Member m : ((StructuredDataType) dataType).allMembers()) {
					if (!assigned.contains(m)) {
						acceptor.accept(createCompletionProposal(prefix+m.getName()+suffix, new StyledString(prefix+m.getName()+suffix).append(description, StyledString.DECORATIONS_STYLER), getImage(m), 1000, "", context));
					}
				}
			}
		}
	}
	
@@ -170,6 +334,9 @@ public class TDLtxProposalProvider extends AbstractTDLtxProposalProvider {
	public void completePredefinedFunctionCallBinary_Function(EObject model, Assignment assignment,
			ContentAssistContext context, ICompletionProposalAcceptor acceptor) {
		// TODO: handle here or in scope provider - exclude incorrect proposals 
		if (model instanceof ParameterBinding) {
			return;
		}
		super.completePredefinedFunctionCallBinary_Function(model, assignment, context, acceptor);
	}

@@ -184,9 +351,34 @@ public class TDLtxProposalProvider extends AbstractTDLtxProposalProvider {
	public void complete_PredefinedFunctionCallBinary(EObject model, RuleCall ruleCall, ContentAssistContext context,
			ICompletionProposalAcceptor acceptor) {
		// TODO: refine?
		if (model instanceof ParameterBinding) {
			return;
		}
		super.complete_PredefinedIdentifierBinary(model, ruleCall, context, acceptor);
		super.complete_PredefinedFunctionCallBinary(model, ruleCall, context, acceptor);
	}

	@Override
	public void complete_PredefinedFunctionCall(EObject model, RuleCall ruleCall, ContentAssistContext context, ICompletionProposalAcceptor acceptor) {
		if (model instanceof ParameterBinding) {
			return;
		}
		super.complete_PredefinedFunctionCall(model, ruleCall, context, acceptor);}

	@Override
	public void complete_PredefinedIdentifierBinary(EObject model, RuleCall ruleCall, ContentAssistContext context,
			ICompletionProposalAcceptor acceptor) {
		// TODO Auto-generated method stub
		if (model instanceof ParameterBinding) {
			return;
		}
		if (model instanceof SimpleDataType) {
			return;
		}
		super.complete_PredefinedIdentifierBinary(model, ruleCall, context, acceptor);
	}

	
	@Override
	public void complete_TargetMessage(EObject model, RuleCall ruleCall, ContentAssistContext context,
			ICompletionProposalAcceptor acceptor) {
@@ -218,4 +410,50 @@ public class TDLtxProposalProvider extends AbstractTDLtxProposalProvider {
		super.complete_TimeConstraintFragment(model, ruleCall, context, acceptor);
	}
	
	public void completeVariableUse_ComponentInstance(EObject model, Assignment assignment, ContentAssistContext context, ICompletionProposalAcceptor acceptor) {
		if (model instanceof ParameterBinding) {
			return;
		}
		lookupCrossReference(((CrossReference)assignment.getTerminal()), context, acceptor);
	}

	public void completePredefinedFunctionCallSize_Function(EObject model, Assignment assignment, ContentAssistContext context, ICompletionProposalAcceptor acceptor) {
		if (model instanceof ParameterBinding || model instanceof MemberAssignment) {
//			complete_ParameterBinding(model, ruleCall, context, acceptor);
			return;
		}
		lookupCrossReference(((CrossReference)assignment.getTerminal()), context, acceptor);
	}

	public void completePredefinedFunctionCallNot_Function(EObject model, Assignment assignment, ContentAssistContext context, ICompletionProposalAcceptor acceptor) {
		if (model instanceof ParameterBinding || model instanceof MemberAssignment) {
//			complete_ParameterBinding(model, ruleCall, context, acceptor);
			return;
		}
		lookupCrossReference(((CrossReference)assignment.getTerminal()), context, acceptor);
	}

	public void completePredefinedFunctionCallMinus_Function(EObject model, Assignment assignment, ContentAssistContext context, ICompletionProposalAcceptor acceptor) {
		if (model instanceof ParameterBinding || model instanceof MemberAssignment) {
//			complete_ParameterBinding(model, ruleCall, context, acceptor);
			return;
		}
		lookupCrossReference(((CrossReference)assignment.getTerminal()), context, acceptor);
	}


	public void completeDataElementUse_DataElement(EObject model, Assignment assignment, ContentAssistContext context, ICompletionProposalAcceptor acceptor) {
		if (model instanceof ParameterBinding || model instanceof MemberAssignment) {
//			complete_ParameterBinding(model, ruleCall, context, acceptor);
			return;
		}

		lookupCrossReference(((CrossReference)assignment.getTerminal()), context, acceptor);
	}
	public void complete_DataElementUse(EObject model, RuleCall ruleCall, ContentAssistContext context, ICompletionProposalAcceptor acceptor) {
		if (model instanceof ParameterBinding || model instanceof MemberAssignment) {
//			complete_ParameterBinding(model, ruleCall, context, acceptor);
			return;
		}
	}
}