Content Assist problem [message #1778788] |
Thu, 21 December 2017 13:05 |
Romain Bernard Messages: 27 Registered: July 2009 |
Junior Member |
|
|
Hi Xtext team,
I have a strange content assist problem for a language that extends xbase. In some very specific and strange circumstances (see tests below), the content assist does not work anymore, and seems to depend on other elements defined before the place where the problem occurs.
As far as I understand, the computation of followElements by the parser does not produce the right elements in the test with errors...
I also attach a complete example...
In advance, thank you for your help !
Kind regards,
Romain
grammar :
grammar org.xtext.example.buggy.MyDsl with org.eclipse.xtext.xbase.annotations.XbaseWithAnnotations
generate myDsl "http://www.xtext.org/example/buggy/MyDsl"
import 'http://www.eclipse.org/xtext/xbase/Xbase' as xbase
Model:
package=PackageDeclaration;
PackageDeclaration:
'package' name=QualifiedName ';'?
importSection=XImportSection?
rootElement=RootElement;
RootElement:
'class' name=ID body=XBlockExpression;
AbstractElement returns xbase::XExpression:
InstructionA | InstructionB
;
InstructionA returns xbase::XExpression:
{InstructionA} 'A' "intProperty:" intProperty=XExpression body=XBlockExpression
;
InstructionB returns xbase::XExpression:
{InstructionB}
'B' "enumProperty:" enumProperty=EnumType body=XBlockExpression;
enum EnumType:
literal1 = "literal1" | literal2 = "literal2"
;
@ Override XExpression returns xbase::XExpression:
XAssignment | AbstractElement;
JvmModelInferrer:
package org.xtext.example.buggy.jvmmodel
import com.google.inject.Inject
import org.eclipse.xtext.xbase.jvmmodel.AbstractModelInferrer
import org.eclipse.xtext.xbase.jvmmodel.IJvmDeclaredTypeAcceptor
import org.eclipse.xtext.xbase.jvmmodel.JvmTypesBuilder
import org.xtext.example.buggy.myDsl.Model
class MyDslJvmModelInferrer extends AbstractModelInferrer {
@Inject extension JvmTypesBuilder
def dispatch void infer(Model element, IJvmDeclaredTypeAcceptor acceptor, boolean isPreIndexingPhase) {
val pkg = element.package
val rootElement = pkg?.rootElement
acceptor.accept(element.toClass(pkg.name + "." + rootElement.name)) [
members += element.toMethod("main", typeRef("void")) [
body = rootElement.body
]
]
}
}
Compiler:
package org.xtext.example.buggy.jvmmodel
import org.eclipse.xtext.xbase.XExpression
import org.eclipse.xtext.xbase.compiler.XbaseCompiler
import org.eclipse.xtext.xbase.compiler.output.ITreeAppendable
import org.xtext.example.buggy.myDsl.InstructionA
import org.xtext.example.buggy.myDsl.InstructionB
class MyDslCompiler extends XbaseCompiler {
override protected doInternalToJavaStatement(XExpression expr, ITreeAppendable it, boolean isReferenced) {
switch expr {
InstructionA: {
newLine
append('''// code comes here for InstructionA''')
expr.body.internalToJavaStatement(it, false)
}
InstructionB: {
newLine
append('''// code comes here for InstructionA''')
expr.body.internalToJavaStatement(it, false)
}
default: {
super.doInternalToJavaStatement(expr, it, isReferenced)
}
}
}
override protected internalToConvertedExpression(XExpression obj, ITreeAppendable it) {
if (hasName(obj)) {
append(getName(obj))
} else
super.internalToConvertedExpression(obj, it)
}
}
TypesComputer:
package org.xtext.example.buggy.jvmmodel
import org.eclipse.xtext.xbase.XExpression
import org.eclipse.xtext.xbase.annotations.typesystem.XbaseWithAnnotationsTypeComputer
import org.eclipse.xtext.xbase.typesystem.computation.ITypeComputationState
import org.xtext.example.buggy.myDsl.InstructionA
import org.xtext.example.buggy.myDsl.InstructionB
import org.xtext.example.buggy.myDsl.RootElement
class MyDslTypeComputer extends XbaseWithAnnotationsTypeComputer {
override computeTypes(XExpression expression, ITypeComputationState state) {
if (expression instanceof InstructionA) {
_computeTypes(expression, state);
} else if (expression instanceof InstructionB) {
_computeTypes(expression, state);
} else if (expression instanceof RootElement) {
_computeTypes(expression, state);
} else {
super.computeTypes(expression, state)
}
}
protected def _computeTypes(RootElement expression, ITypeComputationState state) {
state.acceptActualType(getTypeForName(Void.TYPE, state));
if (expression.body !== null) {
computeTypes(expression.body, state)
}
}
protected def _computeTypes(InstructionA expression, ITypeComputationState state) {
state.acceptActualType(getTypeForName(Void.TYPE, state));
if (expression.intProperty !== null) {
computeTypes(expression.intProperty, state)
}
if (expression.body !== null) {
computeTypes(expression.body, state)
}
}
protected def _computeTypes(InstructionB expression, ITypeComputationState state) {
state.acceptActualType(getTypeForName(Void.TYPE, state));
if (expression.body !== null) {
computeTypes(expression.body, state)
}
}
}
Validator:
package org.xtext.example.buggy.validation
import org.eclipse.xtext.xbase.XExpression
class MyDslValidator extends AbstractMyDslValidator {
def override checkInnerExpressions(XExpression expr) {
}
}
I wrote tests to reproduce the problem :
package org.xtext.example.buggy.ui.tests
import org.eclipse.xtext.testing.InjectWith
import org.eclipse.xtext.testing.XtextRunner
import org.eclipse.xtext.xbase.junit.ui.AbstractContentAssistTest
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(XtextRunner)
@InjectWith(MyDslUiInjectorProvider)
class ContentAssistTest extends AbstractContentAssistTest {
/**
* Always work outside of a for() {} instruction
*/
@Test
def void testWithSuccess1() {
newBuilder.append('''package aaa
class Test {
A intProperty:10 { }
B enumProperty: ''').assertText("literal1", "literal2")
}
/**
* Works inside a for instruction, if not preceded by A
*/
@Test
def void testWithSuccess2() {
newBuilder.append('''
package aaa
class Test {
for (var i = 1; i < 10; i++) {
B enumProperty: ''').assertText("literal1", "literal2")
}
/**
* FIXME
* Does not work if preceded by A, and if A's intProperty uses a XNumberLiteral
*/
@Test
def void testWithError1() {
newBuilder.append('''
package aaa
class Test {
for (var i = 1; i < 10; i++) {
A intProperty:10 { }
B enumProperty: ''').assertText("literal1", "literal2")
}
/**
* But works if preceded by a A instruction with a XFeatureCall integer expression (here a simple reference to a variable) !
*/
@Test
def void testWithSuccess3() {
newBuilder.append('''
package aaa
class Test {
var intValue = 10
for (var i = 1; i < 10; i++) {
A intProperty:intValue { }
B enumProperty: ''').assertText("literal1", "literal2")
}
/**
* Works inside while
*/
@Test
def void testWithSuccess4() {
newBuilder.append('''
package aaa
class Test {
var intValue = 10
while (intValue < 70) {
A intProperty:10 { }
B enumProperty: ''').assertText("literal1", "literal2")
}
/**
* Works inside if
*/
@Test
def void testWithSuccess5() {
newBuilder.append('''
package aaa
class Test {
var intValue = 10
if (intValue>80) {
A intProperty:10 { }
B enumProperty: ''').assertText("literal1", "literal2")
}
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Powered by
FUDForum. Page generated in 0.03649 seconds