-
Notifications
You must be signed in to change notification settings - Fork 16
Development Template Proposals
Templates proposals are code assist elements like the following screenshots
XText has a way to add new proposals for your language described in this link (also check xtext doc in depth)
But we have built our own small internal DSL to make it easier to add new templates for a given language element (semantic model).
You need to modify the class WollokTemplateProposalProvider
Add a new entry like the following
<< WNamedObject >>
'''object ${objectName} {
}
'''
Internally they are all messages and it is a tricky way to model a DSL in xtend with operator overloading. But that's internal details. Overall you need to understand that it has 2 parts:
- The class of the language element (semantic model). Which is usually the same name as the grammar rule
- A template with the form of a string
For example if you want to add a new template for an if expression
<< WIfExpression >>
'''if() {
}
else {
}
'''
In the template you can include variables with the form
${name}
For example
'''if(${condicion}) {
${then}
}
else {
}
'''
Names are arbitrary. They won't do much, just that it tells de IDE that the user will need to fill something there. So the cursor will be positioned automatically in the first variable and he will be able to navigate to the next one with TAB.
Example screenshot
There's an extra element you need to define, which is the name and the description of the template proposal. Otherwise it will look like the last item in the above image.
As this depends on the language, we have it internationalized (i18n). And our DSL provides a convention over configuration way to define this.
You just need to include two new entries in the messages.properties for:
- name
- description
The file is located in
org.uqbar.project.wollok.ui/src/org/uqbar/project/wollok/ui/messages.properties
org.uqbar.project.wollok.ui/src/org/uqbar/project/wollok/ui/messages_es.properties
The entry convention there is:
WollokTemplateProposalProvider_SEMANTICMODELCLASSNAME_name = THE NAME HERE
WollokTemplateProposalProvider_SEMANTICMODELCLASSNAME_description = THE DESCRIPTION HERE
Where you need to change SEMANTICMODELCLASSNAME with your language element class and the value, of course The other parts are "fixed".
Sample for the if example:
WollokTemplateProposalProvider_WIfExpression_name = If expression
WollokTemplateProposalProvider_WIfExpression_description = Add if expression
Sí señor y no señor.
Hay que entender que eclipse tiene 2 tipos de elementos que se muestran en el autocomplete: Proposals: xtext ya provee en base a la gramática (parser). Pero bueno, podrían ser cualquier tipo de proposals. Abajo es la API genérica de eclipse Template proposals: estos son los templates más elaborados como el que te arma ya un if else, o un try catch y te mete variables con el cursor ya puesto para que escribas el contenido Template Proposals
Para los templates proposals tenemos documentación
https://github.com/uqbar-project/wollok/wiki/Development%20-%20Template%20Proposals
Igual lo que más explico ahí es un pequeño DSL interno que hice arriba de la API para generar estos templates más "directos" o simples. Por ejemplo si agregás una nueva regla
WIfComprehension: 'for '(' e=Exp 'in' generator=Gen conditions+=cond ')' '{' body=Body '}'
Es muy probable que quieras un template (porque sino te va proponiendo keyword por keyword. Querés algo así:
for ( e in list if blah) { body }
Entonces eso lo podemos hacer fácil con nuestro DSL
<< WIfComprehension >> ''' for ( ${e} in ${list} if ${cond}) { ${body} } '''
Recién agregué una sección acá de cómo testear los proposals usando XPect y una extensión que le hicimos
Este tipo de tests sirven para las dos cosas, porque testean todas las opciones que aparecen: proposals y template proposals.
(Regular) Proposals
Acá no tenemos nada nuestro hecho arriba de xtext. Y tampoco tenemos documentación (este mail podría ser la gestación). La doc de xtext justo acá http://www.eclipse.org/Xtext/documentation/310_eclipse_support.html#content-assist
Pero trato de aplicarlo a wollok.
Hay que tocar en la clase WollokDslProposalProvider Como varias otras cosas de XText se hace agregando métodos que sigan una convención. Ejemplo, si querés proponer imports, primero tenés que entender cómo es la regla de la gramática
Import: 'import' importedNamespace=QualifiedNameWithWildcard;
Ahí vemos que un Import tiene como una propiedad llamada "importedNamespace". A esa parte es a la que vamos a proponer cosas.
Entonces sobrescribimos el método:
override completeImport_ImportedNamespace(EObject model, Assignment assignment, ContentAssistContext context, ICompletionProposalAcceptor acceptor) {
}
Los nombres fijate que tienen la convención
complete${regla}_${atributo}(ObjetoRegla, ...blah, aceptor)
Después adentro es pelearse con la API para crear los proposals y agregarlos al aceptor
Nosotros ya tenemos un par de métodos útiles para crear los objetos proposals (porque tiene una clase específica, no es sólo texto, con un ícono, descripción, etc).
Por ejemplo para imports estamos haciendo esto
val content = model.file.resourceSet.allContents.filter(WLibraryElement)
// add other files content here
content.forEach[
if (it instanceof WMethodContainer)
acceptor.accept(createCompletionProposal(fqn, fqn, image, context))
]
La primera linea busca todos los elementos (clase, mixin, objeto, package) en todos los archivos del "classpath" digamos. y después (de un pequeño filtro extra -me pregunto si no habría que refactorizar esto y filtrar directo una sóla vez por WMethodContainer) ... creamos un proposal para cada uno (con createCompletionProposal(), método útil nuestro) y se lo pasamos al acceptor.
O sea.. fijate que básicamente es de bastante alto nivel. Buscás los objetos que querés proponer (métodos, variables, lo que sea) Creas un proposal con cada uno y lo agregás al acceptor. Esos "objetos" están directamente relacionados con la gramática Entonces es crucial entender la gramática de Wollok (ej WMethodContainer, WLibraryElement). Esa es "la dificultad". Y obvio la dificultad propia de lo que quieras hacer en el automcomplete. Por ejemplo si querés proponer sólo mensajes válidos, capaz necesitás un sistema de tipos atrás :P