Home » Modeling » TMF (Xtext) » Content assist question (an other one ...)
Content assist question [message #718940] |
Thu, 25 August 2011 12:12  |
Eclipse User |
|
|
|
Here is a sub-set of my grammar:
grammar org.xtext.example.mydsl.MyDsl with org.eclipse.xtext.common.Terminals
generate myDsl "http://www.xtext.org/example/mydsl/MyDsl"
import "http://www.eclipse.org/emf/2002/Ecore" as ecore
Model :
('Project Name' '=')? projectName = STRING ';'
(elements += (Entity))*
'end' 'model' ;
Entity:
Commodity | Plan ;
Commodity:
'commodity' name = ID '=' label = STRING ',' 'unit' '=' unit = STRING
',' 'price' '=' price = Value ';' ;
Plan:
'plan' name = ID '=' label = STRING ',' 'unit' '=' unit = STRING ';'
(planItems += PlanItem ';')*
'end' ';' ;
PlanReference : planRef = [Plan|ID] ;
PlanItem:
CommodityPlanItem | PlanPlanItem ;
CommodityPlanItem :
itemRef = [Commodity|ID] commCalcType = CommInPlanType
'[' (values+= Value )+ ']' ;
PlanPlanItem :
itemRef = PlanReference planCalcType = PlanInPlanType
'[' (values+= Value )+ ']' ;
Value returns ecore::EDouble :
SignedInt | FLOAT ;
SignedInt returns ecore::EInt : NegativeINT | INT ;
terminal NegativeINT returns ecore::EInt : '-' ('0'..'9')+ ;
enum CommInPlanType:
cons = 'cons'| prod = 'prod' | avail = 'avail' ;
enum PlanInPlanType:
annual = "annual" | phastot = "phastot"
| phasadd = "phasadd" | phasold = "phasold" ;
terminal FLOAT returns ecore::EDouble :
('-'|'+')? (INT '.' INT+ | '.' INT+ | INT '.') ('e' ('-'|'+') INT+)?;
terminal ID:
('A'..'Z'|'À'..'Ý') ('a'..'z'|'A'..'Z'|'0'..'9'|'_'|'À'..'ÿ')* ;
My problem is the following:
When I define a plan item and specify the ID of a Commodity, I would expect Ctrl-Space to show only the keywords corresponding to a commodity (prod, cons or avail), but I also get the keywords corresponding to a plan. My understanding is that the parser does not infer the type of PlanItem from the item ID, but from the following keyword.
I know I could solve the problem by changing the grammar by adding keywords to the two PlanItem types:
CommodityPlanItem :
'commItem' itemRef = [Commodity|ID] commCalcType = CommInPlanType
(('[' (values+= Value )+ ']')) ;
PlanPlanItem :
'planItem' itemRef = PlanReference planCalcType = PlanInPlanType
'[' (values+= Value )+ ']' ;
The drawback is that the model files that already exist would not be compatible with the revised version.
Is there a way to handle it through the scope provider ? I tried but could not find my way.
Any help is most welcome
MS
|
|
|
Re: Content assist question [message #719019 is a reply to message #718940] |
Thu, 25 August 2011 15:50   |
Eclipse User |
|
|
|
Hi,
what about simplyfing the grammar and solve the enum problem with a check + content assist?
the main promlem is that enums are pain in the ass in this relation
PlanItem:
itemRef = [Entity|ID] (commCalcType = CommInPlanType | planCalcType = PlanInPlanType)
'[' (values+= Value )+ ']' ;
enum CommInPlanType:
CommInPlanTypeNull | cons = 'cons'| prod = 'prod' | avail = 'avail' ;
enum PlanInPlanType:
PlanInPlanTypeNull | annual = "annual" | phastot = "phastot"
| phasadd = "phasadd" | phasold = "phasold" ;
public class MyDslProposalProvider extends AbstractMyDslProposalProvider {
@Override
public void completeKeyword(Keyword keyword,
ContentAssistContext context,
ICompletionProposalAcceptor acceptor) {
PlanItem item = (PlanItem) context.getCurrentModel();
EEnum eenum = getEEnum(keyword);
if (keyword.getValue().equals("CommInPlanTypeNull") || keyword.getValue().equals("PlanInPlanTypeNull")) {
return;
}
if (eenum == null) {
super.completeKeyword(keyword, context, acceptor);
} else {
if (item.getItemRef() instanceof Commodity) {
if (MyDslPackage.Literals.COMM_IN_PLAN_TYPE.equals(eenum)) {
super.completeKeyword(keyword, context, acceptor);
}
} else if (item.getItemRef() instanceof Commodity) {
if (MyDslPackage.Literals.PLAN_IN_PLAN_TYPE.equals(eenum)) {
super.completeKeyword(keyword, context, acceptor);
}
}
}
}
EEnum getEEnum(Keyword keyword) {
if (keyword.eContainer() instanceof EnumLiteralDeclaration) {
return ((EnumLiteralDeclaration)keyword.eContainer()).getEnumLiteral().getEEnum();
}
return null;
}
}
public class MyDslJavaValidator extends AbstractMyDslJavaValidator {
@Check
public void checkPlanItem(PlanItem item) {
if (CommInPlanType.COMM_IN_PLAN_TYPE_NULL.equals(item.getCommCalcType()) && PlanInPlanType.PLAN_IN_PLAN_TYPE_NULL.equals(item.getPlanCalcType())) {
error("please specify a void type", MyDslPackage.Literals.PLAN_ITEM__ITEM_REF);
}
if (item.getItemRef() instanceof Plan) {
if (!CommInPlanType.COMM_IN_PLAN_TYPE_NULL.equals(item.getCommCalcType())) {
error("keyword " + item.getCommCalcType().getLiteral() + " is not allowed here", MyDslPackage.Literals.PLAN_ITEM__COMM_CALC_TYPE);
}
}
if (item.getItemRef() instanceof Commodity) {
if (!PlanInPlanType.PLAN_IN_PLAN_TYPE_NULL.equals(item.getPlanCalcType())) {
error("keyword " + item.getPlanCalcType().getLiteral() + " is not allowed here", MyDslPackage.Literals.PLAN_ITEM__PLAN_CALC_TYPE);
}
}
}
}
~Christian
|
|
| |
Re: Content assist question [message #719479 is a reply to message #719024] |
Sat, 27 August 2011 08:30   |
Eclipse User |
|
|
|
Hi Christian.
I am making progress and give some details for the benefit of others that might be interested (the full software, called Mads, is at http://mads.sourceforge.net/). I still have one question.
The full grammar includes five types of Entity, each with a unique ID:
Entity:
Commodity | Plan | Script | Herd | Table;
A plan item can be any entity except Table:
PlanItem :
itemRef = [Entity|ID]
( (commCalcType = CommInPlanType '[' (values+= Value )+ ']' ';') // commodity in plan
| (planCalcType = PlanInPlanType '[' (values+= Value )+ ']' ';') // plan in plan
| ('[' (values+= Value )+ ']' ';') // herd in plan
| ';' // script in plan
);
Only Plan and Commodity have a result-type attribute and a series-of-values attribute, Herd has values, Script has no attribute.
The javaValidator and proposalProvider, derived from your message, are thus more complicated:
public class MyDslProposalProvider extends AbstractMyDslProposalProvider {
@Override
public void completeKeyword(Keyword keyword,
ContentAssistContext context,
ICompletionProposalAcceptor acceptor) {
EObject currItem = context.getCurrentModel();
if (currItem instanceof PlanItem) {
String proposal;
PlanItem item = (PlanItem) currItem;
// PlanItem item = (PlanItem) context.getCurrentModel();
EEnum eenum = getEEnum(keyword);
if (keyword.getValue().equals("CommInPlanTypeNull")
|| keyword.getValue().equals("PlanInPlanTypeNull")) {
return;
}
if (eenum == null) {
super.completeKeyword(keyword, context, acceptor);
} else {
if (item.getItemRef() instanceof Commodity) {
if (MyDslPackage.Literals.COMM_IN_PLAN_TYPE.equals(eenum)) {
super.completeKeyword(keyword, context, acceptor);
}
} else if (item.getItemRef() instanceof Plan) {
if (MyDslPackage.Literals.PLAN_IN_PLAN_TYPE.equals(eenum)) {
super.completeKeyword(keyword, context, acceptor);
}
}
}
if (item.getItemRef() instanceof Script) proposal = "script item = ID ; ";
else if (item.getItemRef() instanceof Herd) proposal = "herd item = ID [ series of values ] ; ";
else proposal = "item = ID resType [ series of values ] ; ";
proposal = getValueConverter().toString(proposal, "STRING");
ICompletionProposal completionProposal =
createCompletionProposal(proposal, context);
acceptor.accept(completionProposal);
} else if (currItem instanceof Model) {
super.completeKeyword(keyword, context, acceptor);
}
}
....
}
public class MyDslJavaValidator extends AbstractMyDslJavaValidator {
@Check
public void checkPlanItem(PlanItem item) {
if (item.getItemRef() instanceof Plan) {
if (!CommInPlanType.COMM_IN_PLAN_TYPE_NULL.equals(item.getCommCalcType())) {
error("keyword " + item.getCommCalcType().getLiteral() + " is not allowed here", MyDslPackage.Literals.PLAN_ITEM__COMM_CALC_TYPE);
}
if (PlanInPlanType.PLAN_IN_PLAN_TYPE_NULL.equals(item.getPlanCalcType())) {
error("Calculation type keyword is required for Plan Item", MyDslPackage.Literals.PLAN_ITEM__PLAN_CALC_TYPE);
}
// A plan cannot include itself through nested plans
Plan planRefPlan = (Plan) item.getItemRef();
Plan container = (Plan) item.eContainer(); // containing plan
String containerName = container.getName();
Boolean found = false;
found = scanPlan( planRefPlan, containerName, found);
if (found) {
error("A plan cannot include itself through nested plans",
MyDslPackage.Literals.PLAN_ITEM__ITEM_REF);
}
}
else if (item.getItemRef() instanceof Commodity) {
if (!PlanInPlanType.PLAN_IN_PLAN_TYPE_NULL.equals(item.getPlanCalcType())) {
error("keyword " + item.getPlanCalcType().getLiteral() + " is not allowed here", MyDslPackage.Literals.PLAN_ITEM__PLAN_CALC_TYPE);
}
if (CommInPlanType.COMM_IN_PLAN_TYPE_NULL.equals(item.getCommCalcType())) {
error("Calculation type keyword is required for Commodity Item", MyDslPackage.Literals.PLAN_ITEM__COMM_CALC_TYPE);
}
}
else if ((item.getItemRef() instanceof Herd) || (item.getItemRef() instanceof Script)) {
if (!CommInPlanType.COMM_IN_PLAN_TYPE_NULL.equals(item.getCommCalcType())) {
error("keyword " + item.getCommCalcType().getLiteral() + " is not allowed here", MyDslPackage.Literals.PLAN_ITEM__COMM_CALC_TYPE);
}
if (!PlanInPlanType.PLAN_IN_PLAN_TYPE_NULL.equals(item.getPlanCalcType())) {
error("keyword " + item.getPlanCalcType().getLiteral() + " is not allowed here", MyDslPackage.Literals.PLAN_ITEM__PLAN_CALC_TYPE);
}
if (item.getItemRef() instanceof Script){
if (item.getValues().size()!=0) {
error("Time series of Values is not allowed here", MyDslPackage.Literals.PLAN_ITEM__VALUES);
}
}
}
if (!(item.getItemRef() instanceof Script)){
if (item.getValues().size()==0) {
error("Time series of Values is required here", MyDslPackage.Literals.PLAN_ITEM__VALUES);
}
}
}
private Boolean scanPlan (Plan planRefPlan, String containerName, Boolean found){
// scans items of Plan planRef to see if it includes reference to container
...
return found;
}
}
===============
Now, here is my question:
Given the syntax of PlanItem, I always get ; and [ as part of the list of proposals. Could you help me find out how to control those in the proposals provider.
Thanks again for your assistance.
Michel
|
|
| |
Re: Content assist question [message #722702 is a reply to message #719480] |
Tue, 06 September 2011 10:44  |
Eclipse User |
|
|
|
Hi Christian,
I found it difficult to understand what goes on when keyword lists are set and gave up trying to do too much. Eventually I decided to change my syntax slightly - at the expense of not being compatible with the previous version.
Now my grammar is:
PlanItem:
(
('run' scriptRef = [Script|ID] SEMI)
| ( planItemRef = [Entity|ID]
(
commCalcType = CommInPlanType
| planCalcType = PlanInPlanType
| herdCalcType = HerdInPlanType // can only be phased
)
(('[' (values+= Value )+ ']')| (levelvalues=ReadFunc))
SEMI
)
);
On that basis I was able to get what I wanted on the basis of your model.
Now I am trying to deal in a similar way with other parts of my grammar, where again I have alternative proposals of items with different lists of features.
One thing I found odd was the fact that variables such as planCalcType above were always assigned a value by default, even if not set by the model, hence the need to add a dummy value like the PlanInPlanTypeNull you proposed. The main draw-back is that if somewhere else I use the default CA mechanism the this value will show up in the list.
Question to the developpers of XText: would it make sense to have null as default when the variable was not set ?
Cheers to all of you
MS
|
|
|
Goto Forum:
Current Time: Fri Jul 04 06:06:40 EDT 2025
Powered by FUDForum. Page generated in 0.13060 seconds
|