[generator] Added formatter fragment

Signed-off-by: Miro Spönemann <miro.spoenemann@itemis.de>
This commit is contained in:
Miro Spönemann 2015-07-13 09:54:03 +02:00
parent 23d213330e
commit 0670196ffb
10 changed files with 217 additions and 31 deletions

View file

@ -25,5 +25,8 @@ Import-Package: com.ibm.icu.text;version="4.0.0",
org.apache.log4j;version="1.2.15"
Export-Package: org.eclipse.xtext.xtext.generator,
org.eclipse.xtext.xtext.generator.builder,
org.eclipse.xtext.xtext.generator.exporting,
org.eclipse.xtext.xtext.generator.formatting,
org.eclipse.xtext.xtext.generator.model,
org.eclipse.xtext.xtext.generator.util,
org.eclipse.xtext.xtext.generator.xbase

View file

@ -77,6 +77,10 @@ class XtextGeneratorNaming {
new TypeReference(getRuntimeBasePackage, getName(grammar) + 'StandaloneSetupGenerated')
}
def getGrammarAccess() {
new TypeReference(getRuntimeBasePackage + '.services.' + getName(grammar) + 'GrammarAccess')
}
def getEclipsePluginBasePackage() {
if (eclipsePluginBasePackage === null)
eclipsePluginBasePackage = getNamespace(grammar) + '.ui'

View file

@ -0,0 +1,157 @@
/*******************************************************************************
* Copyright (c) 2015 itemis AG (http://www.itemis.eu) 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
*******************************************************************************/
package org.eclipse.xtext.xtext.generator.formatting
import com.google.common.collect.LinkedHashMultimap
import com.google.common.collect.Multimap
import com.google.inject.Inject
import java.util.Collection
import java.util.Set
import org.eclipse.emf.ecore.EClass
import org.eclipse.emf.ecore.EReference
import org.eclipse.emf.ecore.EStructuralFeature
import org.eclipse.xtend2.lib.StringConcatenationClient
import org.eclipse.xtext.Grammar
import org.eclipse.xtext.GrammarUtil
import org.eclipse.xtext.formatting2.AbstractFormatter2
import org.eclipse.xtext.formatting2.FormatterPreferenceValuesProvider
import org.eclipse.xtext.formatting2.FormatterPreferences
import org.eclipse.xtext.formatting2.IFormattableDocument
import org.eclipse.xtext.formatting2.IFormatter2
import org.eclipse.xtext.preferences.IPreferenceValuesProvider
import org.eclipse.xtext.xtext.generator.AbstractGeneratorFragment2
import org.eclipse.xtext.xtext.generator.IXtextProjectConfig
import org.eclipse.xtext.xtext.generator.LanguageConfig2
import org.eclipse.xtext.xtext.generator.model.FileAccessFactory
import org.eclipse.xtext.xtext.generator.model.FileSystemAccess
import org.eclipse.xtext.xtext.generator.model.GuiceModuleAccess
import org.eclipse.xtext.xtext.generator.model.TypeReference
import org.eclipse.xtext.xtext.generator.util.GenModelUtil2
import static extension org.eclipse.xtext.GrammarUtil.*
import static extension org.eclipse.xtext.xtext.generator.XtextGeneratorNaming.*
import static extension org.eclipse.xtext.xtext.generator.model.TypeReference.*
import static extension org.eclipse.xtext.xtext.generator.util.GrammarUtil2.*
class Formatter2Fragment2 extends AbstractGeneratorFragment2 {
@Inject IXtextProjectConfig projectConfig
@Inject FileAccessFactory fileAccessFactory
@Inject extension FileSystemAccess.Extensions
def TypeReference getFormatter2Stub(Grammar grammar) {
new TypeReference(grammar.naming.runtimeBasePackage + '.formatting2.' + GrammarUtil.getName(grammar) + 'Formatter')
}
override generate(LanguageConfig2 language) {
val StringConcatenationClient statement =
'''binder.bind(«IPreferenceValuesProvider».class).annotatedWith(«FormatterPreferences».class).to(«FormatterPreferenceValuesProvider».class);'''
new GuiceModuleAccess.BindingFactory()
.addTypeToType(IFormatter2.typeRef, language.grammar.formatter2Stub)
.addConfiguredBinding(FormatterPreferences.simpleName, statement)
.contributeTo(language.runtimeGenModule)
new GuiceModuleAccess.BindingFactory()
.addTypeToType('org.eclipse.xtext.ui.editor.formatting.IContentFormatterFactory'.typeRef,
'org.eclipse.xtext.ui.editor.formatting2.ContentFormatterFactory'.typeRef)
.contributeTo(language.eclipsePluginGenModule)
if (!projectConfig.runtimeSrc.containsXtendFile(language.grammar.formatter2Stub))
doGenerateStubFile(language)
}
protected def doGenerateStubFile(LanguageConfig2 language) {
val xtendFile = fileAccessFactory.with(language).createXtendFile(language.grammar.formatter2Stub)
val type2ref = LinkedHashMultimap.<EClass, EReference>create
getLocallyAssignedContainmentReferences(language.grammar, type2ref)
val inheritedTypes = LinkedHashMultimap.<EClass, EReference>create
getInheritedContainmentReferences(language.grammar, inheritedTypes, newHashSet)
xtendFile.javaContent = '''
class «language.grammar.formatter2Stub.simpleName» extends «language.stubSuperClass» {
@«Inject» extension «language.naming.grammarAccess»
«FOR type : type2ref.keySet»
«type.generateFormatMethod(language, type2ref.get(type), inheritedTypes.containsKey(type))»
«ENDFOR»
}
'''
xtendFile.writeTo(projectConfig.runtimeSrc)
}
protected def StringConcatenationClient generateFormatMethod(EClass clazz, LanguageConfig2 language,
Collection<EReference> containmentRefs, boolean isOverriding) '''
«IF isOverriding»override«ELSE»def«ENDIF» dispatch void format(«clazz» «clazz.toName», extension «IFormattableDocument» document) {
// TODO: format HiddenRegions around keywords, attributes, cross references, etc.
«FOR ref:containmentRefs»
«IF ref.isMany»
for («ref.EReferenceType» «ref.name» : «clazz.toName».«ref.getGetAccessor(language)»()) {
format(«ref.name», document);
}
«ELSE»
format(«clazz.toName».«ref.getGetAccessor(language)»(), document);
«ENDIF»
«ENDFOR»
}
'''
protected def void getLocallyAssignedContainmentReferences(Grammar grammar, Multimap<EClass, EReference> type2ref) {
for (assignment : grammar.containedAssignments) {
val type = assignment.findCurrentType
if (type instanceof EClass) {
val feature = type.getEStructuralFeature(assignment.feature)
if (feature instanceof EReference && (feature as EReference).isContainment) {
type2ref.put(type, feature as EReference)
}
}
}
for (action : grammar.containedActions) {
val featureName = action.feature
if (featureName != null) {
val type = action.type.classifier
if (type instanceof EClass) {
val feature = type.getEStructuralFeature(featureName)
if (feature instanceof EReference && (feature as EReference).isContainment) {
type2ref.put(type, feature as EReference)
}
}
}
}
}
protected def void getInheritedContainmentReferences(Grammar grammar, Multimap<EClass, EReference> type2ref,
Set<Grammar> visitedGrammars) {
visitedGrammars.add(grammar)
for (Grammar usedGrammar : grammar.usedGrammars) {
if (!visitedGrammars.contains(usedGrammar)) {
getLocallyAssignedContainmentReferences(usedGrammar, type2ref)
getInheritedContainmentReferences(usedGrammar, type2ref, visitedGrammars)
}
}
}
protected def TypeReference getStubSuperClass(LanguageConfig2 language) {
val superGrammar = language.grammar.nonTerminalsSuperGrammar
if (superGrammar != null)
return superGrammar.formatter2Stub
else
return AbstractFormatter2.typeRef
}
protected def String toName(EClass clazz) {
clazz.name.toLowerCase
}
protected def String getGetAccessor(EStructuralFeature feature, LanguageConfig2 language) {
GenModelUtil2.getGenFeature(feature, language.resourceSet).getAccessor
}
}

View file

@ -10,9 +10,9 @@ package org.eclipse.xtext.xtext.generator.model
import com.google.inject.Inject
import com.google.inject.Provider
import org.eclipse.emf.ecore.resource.ResourceSet
import org.eclipse.xtend.lib.annotations.Accessors
import org.eclipse.xtext.parser.IEncodingProvider
import org.eclipse.xtext.xtext.generator.CodeConfig
import org.eclipse.xtext.xtext.generator.LanguageConfig2
class FileAccessFactory {
@ -22,23 +22,28 @@ class FileAccessFactory {
@Inject Provider<ResourceSet> resourceSetProvider
@Accessors(PUBLIC_SETTER)
ResourceSet resourceSet
def TextFileAccess createTextFile() {
new TextFileAccess(encodingProvider)
}
def JavaFileAccess createJavaFile(TypeReference typeRef) {
val file = new JavaFileAccess(typeRef, codeConfig, encodingProvider)
file.resourceSet = resourceSet ?: resourceSetProvider.get
file.resourceSet = resourceSetProvider.get
return file
}
def XtendFileAccess createXtendFile(TypeReference typeRef) {
val file = new XtendFileAccess(typeRef, codeConfig, encodingProvider)
file.resourceSet = resourceSet ?: resourceSetProvider.get
file.resourceSet = resourceSetProvider.get
return file
}
def FileAccessFactory with(LanguageConfig2 language) {
val result = new FileAccessFactory
result.codeConfig = this.codeConfig
result.encodingProvider = this.encodingProvider
result.resourceSetProvider = [language.resourceSet]
return result
}
}

View file

@ -14,7 +14,7 @@ import org.eclipse.emf.ecore.EClass
import org.eclipse.emf.ecore.resource.ResourceSet
import org.eclipse.xtend.lib.annotations.Accessors
import org.eclipse.xtend.lib.annotations.EqualsHashCode
import org.eclipse.xtext.xtext.generator.GenModelUtil
import org.eclipse.xtext.xtext.generator.util.GenModelUtil2
@Accessors
@EqualsHashCode
@ -89,7 +89,7 @@ class TypeReference {
}
new(EClass clazz, ResourceSet resourceSet, List<TypeReference> arguments) {
this(GenModelUtil.getGenClass(clazz, resourceSet).qualifiedInterfaceName, arguments)
this(GenModelUtil2.getGenClass(clazz, resourceSet).qualifiedInterfaceName, arguments)
}
private static def getPackageName(String qualifiedName) {

View file

@ -1,11 +1,11 @@
/**
/*******************************************************************************
* Copyright (c) 2011 itemis AG (http://www.itemis.eu) 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
*/
package org.eclipse.xtext.xtext.generator
*******************************************************************************/
package org.eclipse.xtext.xtext.generator.util
import org.eclipse.emf.codegen.ecore.genmodel.GenClass
import org.eclipse.emf.codegen.ecore.genmodel.GenClassifier
@ -26,7 +26,7 @@ import org.eclipse.emf.ecore.plugin.EcorePlugin
import org.eclipse.emf.ecore.resource.Resource
import org.eclipse.emf.ecore.resource.ResourceSet
class GenModelUtil {
class GenModelUtil2 {
def static GenClass getGenClass(EClass cls, ResourceSet resourceSet) {
return getGenClassifier(cls, resourceSet) as GenClass

View file

@ -0,0 +1,29 @@
/*******************************************************************************
* Copyright (c) 2015 itemis AG (http://www.itemis.eu) 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
*******************************************************************************/
package org.eclipse.xtext.xtext.generator.util
import org.eclipse.xtext.Grammar
class GrammarUtil2 {
static def boolean inherits(Grammar grammar, String languageID) {
if (grammar.name == languageID)
return true
for (grammar2 : grammar.usedGrammars) {
if (grammar2.inherits(languageID)) {
return true
}
}
return false
}
static def Grammar getNonTerminalsSuperGrammar(Grammar grammar) {
grammar.usedGrammars.findFirst[name != 'org.eclipse.xtext.common.Terminals']
}
}

View file

@ -30,18 +30,19 @@ import org.eclipse.xtext.xtext.generator.model.FileSystemAccess
import org.eclipse.xtext.xtext.generator.model.GuiceModuleAccess
import org.eclipse.xtext.xtext.generator.model.TypeReference
import static extension org.eclipse.xtext.xtext.generator.GenModelUtil.*
import static extension org.eclipse.xtext.xtext.generator.model.TypeReference.*
import static extension org.eclipse.xtext.xtext.generator.util.GenModelUtil2.*
import static extension org.eclipse.xtext.xtext.generator.util.GrammarUtil2.*
@Accessors(PUBLIC_SETTER)
class XbaseGeneratorFragment2 extends AbstractGeneratorFragment2 {
static def boolean inheritsXbase(Grammar grammar) {
GrammarUtil.inherits(grammar, 'org.eclipse.xtext.xbase.Xbase')
grammar.inherits('org.eclipse.xtext.xbase.Xbase')
}
static def boolean inheritsXbaseWithAnnotations(Grammar grammar) {
GrammarUtil.inherits(grammar, 'org.eclipse.xtext.xbase.annotations.XbaseWithAnnotations')
grammar.inherits('org.eclipse.xtext.xbase.annotations.XbaseWithAnnotations')
}
static def boolean usesXImportSection(Grammar grammar) {

View file

@ -9,15 +9,16 @@ package org.eclipse.xtext.xtext.generator.xbase
import com.google.inject.Inject
import org.eclipse.xtext.Grammar
import org.eclipse.xtext.GrammarUtil
import org.eclipse.xtext.xtext.generator.AbstractGeneratorFragment2
import org.eclipse.xtext.xtext.generator.IXtextProjectConfig
import org.eclipse.xtext.xtext.generator.LanguageConfig2
import static extension org.eclipse.xtext.xtext.generator.util.GrammarUtil2.*
class XtypeGeneratorFragment2 extends AbstractGeneratorFragment2 {
static def boolean inheritsXtype(Grammar grammar) {
GrammarUtil.inherits(grammar, 'org.eclipse.xtext.xbase.Xtype')
grammar.inherits('org.eclipse.xtext.xbase.Xtype')
}
@Inject IXtextProjectConfig projectConfig

View file

@ -270,20 +270,6 @@ public class GrammarUtil {
collectAllUsedGrammars(grammars, grammar);
return grammars;
}
/**
* @since 2.9
*/
public static boolean inherits(Grammar grammar, String languageID) {
if (grammar.getName().equals(languageID))
return true;
for (Grammar grammar2 : grammar.getUsedGrammars()) {
if (inherits(grammar2, languageID)) {
return true;
}
}
return false;
}
private static void collectAllUsedGrammars(List<Grammar> grammars, Grammar grammar) {
grammars.addAll(grammar.getUsedGrammars());