Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Formatter Maven plugin #864

Merged
merged 41 commits into from
Dec 11, 2024
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
cf5ef6e
Created formatter interface
maria77102 Oct 23, 2024
b124d3a
Implement interface + first Test
maria77102 Oct 23, 2024
777832c
Fix formatting test + implementation
maria77102 Oct 28, 2024
b7d3057
Enhanced test suite
maria77102 Oct 28, 2024
d2c7c2c
Implement suggested changes
maria77102 Oct 30, 2024
619d38c
Improved interface methods/exception handling and added logging
maria77102 Nov 6, 2024
a57b7ee
Added formatting tool method
maria77102 Nov 6, 2024
c8bc3b4
Add dependencies for logging in tools project
maria77102 Nov 6, 2024
6e09f6d
Improve implementation of command line tool
maria77102 Nov 6, 2024
abca32a
Formatting java code
maria77102 Nov 7, 2024
5591d3d
Made formatting happen in-memory
maria77102 Nov 7, 2024
b36f60c
Update logs when resource saved
maria77102 Nov 7, 2024
2f84974
Fixed logger class name
maria77102 Nov 7, 2024
7cf4bd2
Starting code for formatter plugin
maria77102 Nov 7, 2024
116995c
Merge branch 'main' into formatter-maven-plugin
maria77102 Nov 7, 2024
b65a756
Incorporate 'format' goal in existing plugin
maria77102 Nov 8, 2024
6ca9578
Cleanup files
maria77102 Nov 8, 2024
6e23194
Add test cases for edge cases and unusual behavior
maria77102 Nov 19, 2024
f8cd98e
Update code to deal with runtime exceptions
maria77102 Nov 19, 2024
956fb9c
Fix formatting errors with Choice and parenthesis
maria77102 Nov 19, 2024
e1165ac
Implement suggested changes
maria77102 Nov 19, 2024
ebcb6c2
Merge branch 'main' into formatter-maven-plugin
maria77102 Nov 20, 2024
a497664
Fix comment
maria77102 Nov 20, 2024
60038cd
Implement suggestion
maria77102 Nov 21, 2024
29d5c03
Fix only exists serialization issue
maria77102 Nov 21, 2024
a002f3e
Merge branch 'main' into formatter-maven-plugin
maria77102 Nov 21, 2024
0a6f1a9
Cleanup test
maria77102 Nov 21, 2024
bf9fd47
Update plugin to also use user-given formatting options
maria77102 Nov 26, 2024
da7f54b
Update plugin to use formatting options
maria77102 Nov 27, 2024
ed517eb
Update formatting strategy to avoid serialization
maria77102 Nov 27, 2024
2686331
Add handler support to service for processing formatted content
maria77102 Nov 28, 2024
2043ada
Implemented suggested changes
maria77102 Nov 28, 2024
7f60399
Update cmd tool + cleanup
maria77102 Dec 3, 2024
f977ebe
Format closing brackets to collapse in nested constructors
maria77102 Dec 4, 2024
7d81fc5
Introduce FormattingOptionsAdaptor for shared FormattingOptions logic
maria77102 Dec 5, 2024
f4b3fa4
Formatted collapsable closing brackets in nested constructors
maria77102 Dec 5, 2024
5126286
Fix collapsing closing brackets logic
maria77102 Dec 10, 2024
cc1a731
Add endpoint for default formatting options
maria77102 Dec 10, 2024
3786efc
Implement suggested changes
maria77102 Dec 10, 2024
b6333ee
Fix failing test
maria77102 Dec 10, 2024
fd93377
Cleanup code
maria77102 Dec 11, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -250,11 +250,13 @@ class RosettaExpressionFormatter extends AbstractRosettaFormatter2 {
}

private def dispatch void unsafeFormatExpression(RosettaSymbolReference expr, extension IFormattableDocument document, FormattingMode mode) {
val extension referenceCallGrammarAccess = rosettaReferenceOrFunctionCallAccess
SimonCockx marked this conversation as resolved.
Show resolved Hide resolved

if (expr.explicitArguments) {
expr.regionFor.keywords(',').forEach[
prepend[noSpace]
]
expr.regionFor.keyword('(')
expr.regionFor.keyword(explicitArgumentsLeftParenthesisKeyword_0_2_0_0)
.prepend[noSpace]

formatInlineOrMultiline(document, expr, mode.singleLineIf(expr.shouldBeOnSingleLine),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ import com.regnosys.rosetta.rosetta.TypeParameter
import com.regnosys.rosetta.rosetta.TypeCallArgument
import javax.inject.Inject
import com.regnosys.rosetta.rosetta.RosettaRule
import com.regnosys.rosetta.rosetta.simple.Choice

class RosettaFormatter extends AbstractRosettaFormatter2 {

Expand Down Expand Up @@ -236,6 +237,18 @@ class RosettaFormatter extends AbstractRosettaFormatter2 {
ele.regionFor.assignment(nameAssignment_1)
.prepend[oneSpace]
}

def dispatch void format(Choice ele, extension IFormattableDocument document) {
ele.regionFor.keyword('choice')
maria77102 marked this conversation as resolved.
Show resolved Hide resolved
.append[oneSpace]
ele.regionFor.keyword(':')
.prepend[noSpace]
maria77102 marked this conversation as resolved.
Show resolved Hide resolved
ele.indentInner(document)
ele.attributes.forEach[
prepend[newLine]
format
]
maria77102 marked this conversation as resolved.
Show resolved Hide resolved
}


def dispatch void format(Data ele, extension IFormattableDocument document) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.regnosys.rosetta.formatting2;

import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.io.ByteArrayInputStream;
Expand All @@ -9,6 +10,7 @@
import java.io.UncheckedIOException;

import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.xtext.formatting2.ConflictingFormattingException;
import org.eclipse.xtext.formatting2.FormatterRequest;
import org.eclipse.xtext.formatting2.IFormatter2;
import org.eclipse.xtext.formatting2.regionaccess.ITextRegionAccess;
Expand Down Expand Up @@ -48,16 +50,37 @@ public void formatCollection(Collection<Resource> resources, ITypedPreferenceVal

@Override
public void formatXtextResource(XtextResource resource, ITypedPreferenceValues preferenceValues) {
LOGGER.info("Formatting file at location " + resource.getURI());

// setup request and formatter
FormatterRequest req = formatterRequestProvider.get();
req.setPreferences(preferenceValues);
IFormatter2 formatter = iFormatter2Provider.get();

ITextRegionAccess regionAccess = regionBuilder.forNodeModel(resource).create();
ITextRegionAccess regionAccess = null;
try {
regionAccess = regionBuilder.forNodeModel(resource).create();
} catch (Exception e) {
LOGGER.info("Resource " + resource.getURI() + " is empty.");
return;
}

req.setTextRegionAccess(regionAccess);

// list contains all the replacements which should be applied to resource
List<ITextReplacement> replacements = formatter.format(req);
List<ITextReplacement> replacements = new ArrayList<>();
maria77102 marked this conversation as resolved.
Show resolved Hide resolved
try {
replacements = formatter.format(req); // throws exception
} catch (RuntimeException e) {
LOGGER.error("RuntimeException in " + resource.getURI() + ": " + e.getMessage());
maria77102 marked this conversation as resolved.
Show resolved Hide resolved
}

// Abort if replacements is empty (either because nothing to format or method
// throws exception
maria77102 marked this conversation as resolved.
Show resolved Hide resolved
if (replacements.isEmpty()) {
LOGGER.info("No replacements to apply, skipping file");
return;
}

// formatting using TextRegionRewriter
ITextRegionRewriter regionRewriter = regionAccess.getRewriter();
Expand All @@ -72,5 +95,7 @@ public void formatXtextResource(XtextResource resource, ITypedPreferenceValues p
throw new UncheckedIOException(
"Since the resource is an in-memory string, this exception is not expected to be ever thrown.", e);
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -17,36 +17,51 @@
package com.regnosys.rosetta.serialization;

import java.util.List;
import java.util.Set;

import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.xtext.parsetree.reconstr.impl.DefaultTransientValueService;

import com.regnosys.rosetta.rosetta.expression.ExpressionPackage;
import com.regnosys.rosetta.rosetta.expression.RosettaExpression;
import com.regnosys.rosetta.rosetta.simple.SimplePackage;

public class RosettaTransientValueService extends DefaultTransientValueService {
private EStructuralFeature generatedInputWasSetFeature = ExpressionPackage.eINSTANCE.getHasGeneratedInput_GeneratedInputWasSet();
private EStructuralFeature implicitVariableIsInContextFeature = ExpressionPackage.eINSTANCE.getRosettaSymbolReference_ImplicitVariableIsInContext();

private final Set<EStructuralFeature> ignoredFeatures;

public RosettaTransientValueService() {
EStructuralFeature generatedInputWasSetFeature = ExpressionPackage.eINSTANCE
.getHasGeneratedInput_GeneratedInputWasSet();
EStructuralFeature implicitVariableIsInContextFeature = ExpressionPackage.eINSTANCE
.getRosettaSymbolReference_ImplicitVariableIsInContext();
EStructuralFeature hardcodedConditionFeature = SimplePackage.eINSTANCE.getChoice__hardcodedConditions();
EStructuralFeature hardcodedNameFeature = SimplePackage.eINSTANCE.getChoiceOption__hardcodedName();
EStructuralFeature hardcodedCardinalityFeature = SimplePackage.eINSTANCE
.getChoiceOption__hardcodedCardinality();
ignoredFeatures = Set.of(generatedInputWasSetFeature, implicitVariableIsInContextFeature,
hardcodedConditionFeature, hardcodedNameFeature, hardcodedCardinalityFeature);

}

@Override
public boolean isCheckElementsIndividually(EObject owner, EStructuralFeature feature) {
return true;
}

@Override
public boolean isTransient(EObject owner, EStructuralFeature feature, int index) {
if (super.isTransient(owner, feature, index)) {
return true;
}
if (feature.equals(generatedInputWasSetFeature) || feature.equals(implicitVariableIsInContextFeature)) {
if (ignoredFeatures.contains(feature)) {
return true;
}
Object value = owner.eGet(feature);
if (index >= 0 && value instanceof List<?>) {
value = ((List<?>)value).get(index);
value = ((List<?>) value).get(index);
}
if (value instanceof RosettaExpression && ((RosettaExpression)value).isGenerated()) {
if (value instanceof RosettaExpression && ((RosettaExpression) value).isGenerated()) {
return true;
}
return false;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package com.regnosys.rosetta.maven;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.stream.Collectors;

import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.inject.Injector;
import com.regnosys.rosetta.RosettaStandaloneSetup;
import com.regnosys.rosetta.builtin.RosettaBuiltinsService;
import com.regnosys.rosetta.formatting2.ResourceFormatterService;

@Mojo(name = "format")
public class ResourceFormatterMojo extends AbstractMojo {
maria77102 marked this conversation as resolved.
Show resolved Hide resolved
private static Logger LOGGER = LoggerFactory.getLogger(ResourceFormatterMojo.class);

@Parameter(property = "path", required = true)
private String path;

@Override
public void execute() throws MojoExecutionException, MojoFailureException {
Path directory = Paths.get(path);
LOGGER.info("Mojo running on path:" + directory.toString());

Injector inj = new RosettaStandaloneSetup().createInjectorAndDoEMFRegistration();
ResourceSet resourceSet = inj.getInstance(ResourceSet.class);
ResourceFormatterService formatterService = inj.getInstance(ResourceFormatterService.class);

RosettaBuiltinsService builtins = inj.getInstance(RosettaBuiltinsService.class);
resourceSet.getResource(builtins.basicTypesURI, true);
resourceSet.getResource(builtins.annotationsURI, true);

try {
// Find all .rosetta files in the directory and load them from disk
List<Resource> resources = Files.walk(directory).filter(path -> path.toString().endsWith(".rosetta"))
.map(file -> resourceSet.getResource(URI.createFileURI(file.toString()), true))
.collect(Collectors.toList());

// format resources
formatterService.formatCollection(resources, null);

// save each resource
resources.forEach(resource -> {
try {
resource.save(null);
LOGGER.info("Successfully formatted and saved file at location " + resource.getURI());
} catch (IOException e) {
LOGGER.error("Error saving file at location " + resource.getURI() + ": " + e.getMessage());
} catch (RuntimeException e) {
LOGGER.error("RuntimeException while saving in following file: " + resource.getURI() + ": "
+ e.getMessage());
}
});
} catch (IOException e) {
maria77102 marked this conversation as resolved.
Show resolved Hide resolved
LOGGER.error("Error processing files: " + e.getMessage());
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -761,4 +761,26 @@ class RosettaExpressionFormattingTest {
(["This", "is", "a", "loooooooooooooooooooooooong", "list"] count > 10)
'''
}

@Test
def void testFunctionCallInParenthesis() {
'''
(SomeFunc
(
))
''' -> '''
(SomeFunc())
'''
}

@Test
def void testConditionalInParenthesis() {
'''
(if True
then 10)
''' -> '''
(if True then 10)
'''
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -588,4 +588,25 @@ class RosettaFormattingTest {
add out -> other: [in1, in1, in1, in1]
'''
}

@Test
def void testChoice() {
'''
namespace "test"
version "test"
choice Test:
A

B

''' -> '''
namespace "test"
version "test"

choice Test:
A
B
'''
}

}