[eclipse/xtext#1697] Abstract super class for java based formatters.

Remove API restrictions.
Testing java based formatter.

Signed-off-by: Arne Deutsch <Arne.Deutsch@itemis.de>
This commit is contained in:
Arne Deutsch 2020-09-11 10:49:36 +02:00
parent be53e1d841
commit a514456771
5 changed files with 257 additions and 25 deletions

View file

@ -0,0 +1,31 @@
/*******************************************************************************
* Copyright (c) 2020 itemis AG (http://www.itemis.eu) and others.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* SPDX-License-Identifier: EPL-2.0
*******************************************************************************/
package org.eclipse.xtext.formatting2.internal;
import org.eclipse.xtext.formatting2.AbstractJavaFormatter;
import org.eclipse.xtext.formatting2.IFormattableDocument;
import org.eclipse.xtext.formatting2.internal.formattertestlanguage.IDList;
import org.eclipse.xtext.formatting2.internal.services.FormatterTestLanguageGrammarAccess;
import com.google.inject.Inject;
/**
* @author Arne Deutsch - Initial contribution and API
*/
public class JavaFormatter extends AbstractJavaFormatter {
@Inject
FormatterTestLanguageGrammarAccess grammar;
protected void format(IDList list, IFormattableDocument doc) {
doc.prepend(regionFor(list).keyword("idlist"), it -> it.noSpace());
regionFor(list).ruleCallsTo(grammar.getIDRule()).forEach(region -> doc.prepend(region, it -> it.oneSpace()));
}
}

View file

@ -0,0 +1,34 @@
/*******************************************************************************
* Copyright (c) 2020 itemis AG (http://www.itemis.com) and others.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* SPDX-License-Identifier: EPL-2.0
*******************************************************************************/
package org.eclipse.xtext.formatting2.internal;
import org.eclipse.xtext.formatting2.internal.tests.FormatterTestLanguageInjectorProvider;
import org.eclipse.xtext.testing.InjectWith;
import org.eclipse.xtext.testing.XtextRunner;
import org.junit.Test;
import org.junit.runner.RunWith;
import com.google.inject.Inject;
/**
* @author Arne Deutsch - Initial contribution and API
*/
@RunWith(XtextRunner.class)
@InjectWith(FormatterTestLanguageInjectorProvider.class)
public class JavaFormatterTest {
@Inject
private JavaFormatterTestHelper helper;
@Test
public void javaFormatterWorks() {
helper.assertFormatted(" idlist a b c", "idlist a b c");
}
}

View file

@ -0,0 +1,34 @@
/*******************************************************************************
* Copyright (c) 2020 itemis AG (http://www.itemis.eu) and others.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* SPDX-License-Identifier: EPL-2.0
*******************************************************************************/
package org.eclipse.xtext.formatting2.internal;
import org.eclipse.xtext.formatting2.IFormatter2;
import org.eclipse.xtext.testing.formatter.FormatterTestHelper;
import org.eclipse.xtext.testing.formatter.FormatterTestRequest;
import com.google.inject.Inject;
/**
* @author Arne Deutsch - Initial contribution and API
*/
public class JavaFormatterTestHelper extends FormatterTestHelper {
@Inject
private JavaFormatter formatter;
public void assertFormatted(String original, String expectation) {
assertFormatted(r -> r.setToBeFormatted(original).setExpectation(expectation));
}
@Override
protected IFormatter2 createFormatter(FormatterTestRequest request) {
return formatter;
}
}

View file

@ -32,20 +32,7 @@ Export-Package: org.eclipse.xtext,
org.eclipse.xtend.core",
org.eclipse.xtext.formatting,
org.eclipse.xtext.formatting.impl,
org.eclipse.xtext.formatting2;
x-friends:="org.eclipse.xtext.generator,
org.eclipse.xtext.ide,
org.eclipse.xtext.ide.tests,
org.eclipse.xtext.purexbase,
org.eclipse.xtext.testing,
org.eclipse.xtext.testlanguages,
org.eclipse.xtext.tests,
org.eclipse.xtext.xbase,
org.eclipse.xtend.core,
org.eclipse.xtend.ide,
org.eclipse.xtext.xtext.generator,
org.eclipse.xtext.junit4,
org.eclipse.xtext.ui",
org.eclipse.xtext.formatting2,
org.eclipse.xtext.formatting2.debug;x-friends:="org.eclipse.xtext.ide,
org.eclipse.xtext.eclipse.tests,
org.eclipse.xtext.ide.tests,
@ -54,17 +41,7 @@ Export-Package: org.eclipse.xtext,
org.eclipse.xtext.tests",
org.eclipse.xtext.formatting2.internal;x-friends:="org.eclipse.xtext.tests,
org.eclipse.xtend.core",
org.eclipse.xtext.formatting2.regionaccess;x-friends:="org.eclipse.xtext.ide,
org.eclipse.xtext.ide.tests,
org.eclipse.xtext.purexbase,
org.eclipse.xtext.xbase,
org.eclipse.xtend.core,
org.eclipse.xtend.ide,
org.eclipse.xtend.core,
org.eclipse.xtext.junit4,
org.eclipse.xtext.testing,
org.eclipse.xtext.tests,
org.eclipse.xtext.ui",
org.eclipse.xtext.formatting2.regionaccess,
org.eclipse.xtext.formatting2.regionaccess.internal;x-friends:="org.eclipse.xtext.ide,
org.eclipse.xtend.core,
org.eclipse.xtend.ide,

View file

@ -0,0 +1,156 @@
/*******************************************************************************
* Copyright (c) 2020 itemis AG (http://www.itemis.eu) and others.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* SPDX-License-Identifier: EPL-2.0
*******************************************************************************/
package org.eclipse.xtext.formatting2;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.xtext.formatting2.regionaccess.IEObjectRegion;
import org.eclipse.xtext.formatting2.regionaccess.IHiddenRegion;
import org.eclipse.xtext.formatting2.regionaccess.ISemanticRegion;
import org.eclipse.xtext.formatting2.regionaccess.ISemanticRegionFinder;
import org.eclipse.xtext.formatting2.regionaccess.ISemanticRegionsFinder;
import org.eclipse.xtext.resource.XtextResource;
import org.eclipse.xtext.util.PolymorphicDispatcher;
import com.google.common.annotations.Beta;
/**
* <p>
* This is an abstract base class for language-specific formatters, intended to be extended with a java class.
* </p>
* <p>
* It is working very much like {@link AbstractFormatter2} but does support additional methods to simplify java
* formatter code.
* </p>
* <p>
* the "_format" methods are called by reflection. The name is important as well as there are exactly two arguments
* whereas the first one is of the element type you like to format and the second one of type
* {@link IFormattableDocument}. The methods should be protected.
* </p>
* <p>
* Example code for a java formatter:
*
* <pre>
* import static org.xtext.example.mydsl.myDsl.MyDslPackage.Literals.*;
*
* public class MyDslFormatter2 extends AbstractJavaFormatter {
* protected void _format(Model model, IFormattableDocument doc) {
* for (Parent parent : model.getParents())
* doc.format(parent);
* }
*
* protected void _format(Parent parent, IFormattableDocument doc) {
* doc.prepend(regionFor(parent).keyword("parent"), it -> it.noSpace());
* doc.append(regionFor(parent).keyword("parent"), it -> it.oneSpace());
* doc.append(regionFor(parent).feature(PARENT__NAME), it -> it.oneSpace());
* doc.prepend(regionFor(parent).keyword("{"), it -> it.oneSpace());
* doc.append(regionFor(parent).keyword("{"), it -> it.newLine());
* doc.interior(regionFor(parent).keyword("{"), regionFor(parent).keyword("}"), it -> it.indent());
* doc.append(regionFor(parent).keyword("}"), it -> it.setNewLines(1, 1, 2));
* for (Child child : parent.getChildren())
* doc.format(child);
* }
*
* protected void _format(Child child, IFormattableDocument doc) {
* doc.append(regionFor(child).keyword("child"), it -> it.oneSpace());
* doc.append(regionFor(child).feature(CHILD__NAME), it -> it.newLine());
* }
* }
* </pre>
* </p>
* <p>
* This is suitable for the following grammar:
*
* <pre>
* grammar org.xtext.example.mydsl.MyDsl with org.eclipse.xtext.common.Terminals
*
* generate myDsl "http://www.xtext.org/example/mydsl/MyDsl"
*
* Model: parents+=Parent*;
* Parent: 'parent' name=ID '!' ('{' children+=Child* '}')?;
* Child: 'child' name=ID;
* </pre>
* </p>
*/
@Beta
public abstract class AbstractJavaFormatter extends AbstractFormatter2 {
private PolymorphicDispatcher<Void> dispatcher = createPolymorhicDispatcher();
// reflective method that calls "_format" methods found in the implementing class.
@Override
public void format(Object child, IFormattableDocument document) {
if (child instanceof XtextResource) {
_format((XtextResource) child, document);
return;
} else if (child == null) {
_format((Void) null, document);
return;
}
try {
dispatcher.invoke(child, document);
} catch (SecurityException | IllegalArgumentException e) {
throw new RuntimeException(e);
}
}
/**
* Override if you like to specify formatting methods in different way then default (using annotations or similar).
*/
protected PolymorphicDispatcher<Void> createPolymorhicDispatcher() {
return PolymorphicDispatcher.createForSingleTarget(m -> "format".equals(m.getName())
&& m.getParameterCount() == 2 && m.getParameterTypes()[1] == IFormattableDocument.class, this);
}
// implementations that forward the methods of ITextRegionExtensions to simplify formatter code.
protected ISemanticRegionsFinder allRegionsFor(EObject semanticElement) {
return textRegionExtensions.allRegionsFor(semanticElement);
}
protected Iterable<ISemanticRegion> allSemanticRegions(EObject semanticElement) {
return textRegionExtensions.allSemanticRegions(semanticElement);
}
protected EObject grammarElement(EObject semanticElement) {
return textRegionExtensions.grammarElement(semanticElement);
}
protected ISemanticRegionFinder immediatelyFollowing(EObject semanticElement) {
return textRegionExtensions.immediatelyFollowing(semanticElement);
}
protected ISemanticRegionFinder immediatelyPreceding(EObject semanticElement) {
return textRegionExtensions.immediatelyPreceding(semanticElement);
}
protected boolean isMultiline(EObject semanticElement) {
return textRegionExtensions.isMultiline(semanticElement);
}
protected IHiddenRegion nextHiddenRegion(EObject semanticElement) {
return textRegionExtensions.nextHiddenRegion(semanticElement);
}
protected IHiddenRegion previousHiddenRegion(EObject semanticElement) {
return textRegionExtensions.previousHiddenRegion(semanticElement);
}
protected ISemanticRegionsFinder regionFor(EObject semanticElement) {
return textRegionExtensions.regionFor(semanticElement);
}
protected IEObjectRegion regionForEObject(EObject semanticElement) {
return textRegionExtensions.regionForEObject(semanticElement);
}
protected Iterable<ISemanticRegion> semanticRegions(EObject semanticElement) {
return textRegionExtensions.semanticRegions(semanticElement);
}
}