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 34 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
1 change: 1 addition & 0 deletions rosetta-lang/model/RosettaExpression.xcore
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,7 @@ class JoinOperation extends RosettaBinaryOperation {

class RosettaOnlyExistsExpression extends RosettaExpression {
contains RosettaExpression[] args
boolean hasParentheses
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -789,7 +789,7 @@ RosettaCalcConditionalExpression returns RosettaExpression:
;

RosettaCalcOnlyExists returns RosettaExpression:
{RosettaOnlyExistsExpression} (args+=RosettaOnlyExistsElement | ('(' args+=RosettaOnlyExistsElement (',' args+=RosettaOnlyExistsElement)* ')')) 'only' 'exists'
{RosettaOnlyExistsExpression} (args+=RosettaOnlyExistsElement | (hasParentheses?='(' args+=RosettaOnlyExistsElement (',' args+=RosettaOnlyExistsElement)* ')')) 'only' 'exists'
;

RosettaOnlyExistsElement returns RosettaExpression:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.regnosys.rosetta.formatting2;

import org.eclipse.emf.ecore.resource.Resource;

/**
* Functional interface for handling a formatted resource and its corresponding
* formatted text.
* <p>
* This interface allows customization of how formatted resources are processed,
* such as saving the content, logging it, or using it for validations or
* assertions in tests.
* </p>
*/
@FunctionalInterface
public interface IFormattedResourceAcceptor {

/**
* Accepts a formatted resource and its formatted content for further
* processing.
*
* @param resource the {@link Resource} that was formatted
* @param formattedText the formatted content as a {@link String}
*/
void accept(Resource resource, String formattedText);
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,64 +7,124 @@
import org.eclipse.xtext.resource.XtextResource;

public interface ResourceFormatterService {

/**
* Formats each {@link XtextResource} in the provided collection in-memory.
* Formats each {@link XtextResource} in the provided collection in-memory and
* passes the formatted content to a handler for further processing.
* <p>
* This method iterates over the given collection of resources and applies formatting
* directly to each resource. Formatting may include indentation, spacing adjustments,
* and other stylistic improvements to ensure consistency and readability of the resources.
* This method iterates over the given collection of resources and applies
* formatting directly to each resource. Formatting may include indentation,
* spacing adjustments, and other stylistic improvements to ensure consistency
* and readability of the resources.
* </p>
*
* @param resources a collection of {@link XtextResource} objects to be formatted
* <p>
* The handler, represented as a {@link java.util.function.BiConsumer}, is
* called with two arguments: the {@link Resource} being formatted, and the
* resulting formatted text as a {@link String}. This allows the caller to
* specify actions such as saving the formatted content, logging it, or
* collecting it for assertions in a test.
* </p>
*
* @param resources a collection of {@link XtextResource} objects to be
* formatted
* @param acceptor an {@link IFormattedResourceAcceptor} to process the
* formatted resource and its content
*/
default void formatCollection(Collection<Resource> resources) {
formatCollection(resources, null);
default void formatCollection(Collection<Resource> resources, IFormattedResourceAcceptor acceptor) {
formatCollection(resources, null, acceptor);
}

/**
* Formats the given {@link XtextResource} in-memory.
* Formats the given {@link XtextResource} in-memory and passes the formatted
* content to a handler for further processing.
* <p>
* This method applies formatting directly to the specified resource. Formatting can include
* adjustments to indentation, spacing, and other stylistic elements to ensure consistency
* and readability of the resource content.
* This method applies formatting directly to the specified resource. Formatting
* can include adjustments to indentation, spacing, and other stylistic elements
* to ensure consistency and readability of the resource content.
* </p>
*
* <p>
* The handler, represented as a {@link java.util.function.BiConsumer}, is
* called with two arguments: the {@link Resource} being formatted, and the
* resulting formatted text as a {@link String}. This allows the caller to
* specify actions such as saving the formatted content, logging it, or
* collecting it for assertions in a test.
* </p>
*
* @param resources the {@link XtextResource} to format
* @param preferenceValues an {@link ITypedPreferenceValues} object containing formatting preferences,
* or {@code null} if no preferences are specified
* @param acceptor an {@link IFormattedResourceAcceptor} to process the
* formatted resource and its content
*/
default void formatXtextResource(XtextResource resource) {
formatXtextResource(resource, null);
default void formatXtextResource(XtextResource resource, IFormattedResourceAcceptor acceptor) {
formatXtextResource(resource, null, acceptor);
}

/**
* Formats each {@link XtextResource} in the provided collection in-memory, with specified formatting preferences.
* Formats each {@link XtextResource} in the provided collection in-memory,
* applying specified formatting preferences, and passes the formatted content
* to a handler for further processing.
* <p>
* This method iterates over the given collection of resources and applies formatting
* directly to each resource. Formatting may include indentation, spacing adjustments,
* and other stylistic improvements to ensure consistency and readability of the resources.
* The formatting can be customized based on the specified {@link ITypedPreferenceValues}.
* If no preferences are required, {@code preferenceValues} can be set to {@code null}.
* This method iterates over the given collection of resources, formats each
* resource according to the provided preferences, and invokes a handler to
* process the formatted content. Formatting includes indentation, spacing
* adjustments, and other stylistic refinements to ensure consistency and
* readability of the resources. The formatting behavior can be customized based
* on the provided {@link ITypedPreferenceValues}.
* </p>
* <p>
* The handler, represented as a {@link java.util.function.BiConsumer}, is
* called for each resource with two arguments: the {@link Resource} being
* formatted, and the resulting formatted text as a {@link String}. This allows
* the caller to specify actions such as saving the formatted content, logging
* it, or collecting it for assertions in a test.
* </p>
* <p>
* If no formatting preferences are required, the {@code preferenceValues}
* parameter can be set to {@code null}.
* </p>
*
* @param resources a collection of {@link XtextResource} objects to be formatted
* @param preferenceValues an {@link ITypedPreferenceValues} object containing formatting preferences,
* or {@code null} if no preferences are specified
* @param resources a collection of {@link XtextResource} objects to be
* formatted
* @param preferenceValues an {@link ITypedPreferenceValues} object containing
* formatting preferences, or {@code null} if no
* preferences are specified
* @param acceptor an {@link IFormattedResourceAcceptor} to process the
* formatted resource and its content
*/
void formatCollection(Collection<Resource> resources, ITypedPreferenceValues preferenceValues);

void formatCollection(Collection<Resource> resources, ITypedPreferenceValues preferenceValues,
IFormattedResourceAcceptor acceptor);

/**
* Formats the given {@link XtextResource} in-memory.
* Formats the given {@link XtextResource} in-memory, applying specified
* formatting preferences, and passes the formatted content to a handler for
* further processing.
* <p>
* This method applies formatting directly to the specified resource. Formatting can include
* adjustments to indentation, spacing, and other stylistic elements to ensure consistency
* and readability of the resource content.
* The formatting can be customized based on the specified {@link ITypedPreferenceValues}.
* If no preferences are required, {@code preferenceValues} can be set to {@code null}.
* This method formats each resource according to the provided preferences, and
* invokes a handler to process the formatted content. Formatting includes
* indentation, spacing adjustments, and other stylistic refinements to ensure
* consistency and readability of the resources. The formatting behavior can be
* customized based on the provided {@link ITypedPreferenceValues}.
* </p>
*
* @param resource the {@link XtextResource} to format
* <p>
* The handler, represented as a {@link java.util.function.BiConsumer}, is
* called with two arguments: the {@link Resource} being formatted, and the
* resulting formatted text as a {@link String}. This allows the caller to
* specify actions such as saving the formatted content, logging it, or
* collecting it for assertions in a test.
* </p>
* <p>
* If no formatting preferences are required, the {@code preferenceValues}
* parameter can be set to {@code null}.
* </p>
*
* @param resource the {@link XtextResource} to be formatted
* @param preferenceValues an {@link ITypedPreferenceValues} object containing
* formatting preferences, or {@code null} if no
* preferences are specified
* @param acceptor an {@link IFormattedResourceAcceptor} to process the
* formatted resource and its content
*/
void formatXtextResource(XtextResource resource, ITypedPreferenceValues preferenceValues);
void formatXtextResource(XtextResource resource, ITypedPreferenceValues preferenceValues,
IFormattedResourceAcceptor acceptor);
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import com.regnosys.rosetta.rosetta.expression.RosettaOperation
import com.regnosys.rosetta.rosetta.expression.ThenOperation
import com.regnosys.rosetta.rosetta.expression.RosettaConstructorExpression
import com.regnosys.rosetta.rosetta.expression.RosettaDeepFeatureCall
import com.regnosys.rosetta.rosetta.expression.ConstructorKeyValuePair

class RosettaExpressionFormatter extends AbstractRosettaFormatter2 {

Expand Down Expand Up @@ -114,30 +115,47 @@ class RosettaExpressionFormatter extends AbstractRosettaFormatter2 {

private def dispatch void unsafeFormatExpression(RosettaConstructorExpression expr, extension IFormattableDocument document, FormattingMode mode) {
val extension constructorGrammarAccess = rosettaCalcConstructorExpressionAccess


val innermostConstructorChild = findInnermostConstructorChild(expr) as RosettaConstructorExpression
interior(
expr.regionFor.keyword(leftCurlyBracketKeyword_2)
.prepend[oneSpace]
.append[newLine],
expr.regionFor.keyword(rightCurlyBracketKeyword_4)
.prepend[newLine],
innermostConstructorChild.regionFor.keyword(rightCurlyBracketKeyword_4)
.prepend[if (hasLastChildConstructor(expr)) noSpace else newLine],
[indent]
)

expr.regionFor.keyword(rightCurlyBracketKeyword_4)
.prepend[if (hasLastChildConstructor(expr)) noSpace else newLine]

expr.regionFor.keywords(',').forEach[
prepend[noSpace]
append[newLine]
]

expr.values.forEach[
indentInner(document)
regionFor.keyword(':').
prepend[noSpace]
regionFor.keyword(':')
.prepend[noSpace]
.append[oneSpace]
value.formatExpression(document, mode)
]
}

private def findInnermostConstructorChild(RosettaConstructorExpression expr) {
if (hasLastChildConstructor(expr)) {
val lastChild = expr.values.last
findInnermostConstructorChild(lastChild.value as RosettaConstructorExpression)
} else {
expr
}
}

private def hasLastChildConstructor(RosettaConstructorExpression expr) {
val lastChild = expr.values.last
!expr.implicitEmpty && lastChild instanceof ConstructorKeyValuePair && lastChild.value instanceof RosettaConstructorExpression
}

private def dispatch void unsafeFormatExpression(ListLiteral expr, extension IFormattableDocument document, FormattingMode mode) {
expr.regionFor.keywords(',').forEach[
prepend[noSpace]
Expand Down Expand Up @@ -250,11 +268,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,22 @@ class RosettaFormatter extends AbstractRosettaFormatter2 {
ele.regionFor.assignment(nameAssignment_1)
.prepend[oneSpace]
}

def dispatch void format(Choice ele, extension IFormattableDocument document) {
ele.regionFor.keyword(choiceAccess.choiceKeyword_0)
.append[oneSpace]
ele.regionFor.keyword(':')
.prepend[noSpace]
maria77102 marked this conversation as resolved.
Show resolved Hide resolved
ele.indentInner(document)
ele.formatDefinition(document)
ele.attributes.head
.prepend[setNewLines(1, 2, 2)]
.format
ele.attributes.tail.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,12 +1,8 @@
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;
import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;

import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.xtext.formatting2.FormatterRequest;
Expand Down Expand Up @@ -35,10 +31,11 @@ public class XtextResourceFormatter implements ResourceFormatterService {
private TextRegionAccessBuilder regionBuilder;

@Override
public void formatCollection(Collection<Resource> resources, ITypedPreferenceValues preferenceValues) {
public void formatCollection(Collection<Resource> resources, ITypedPreferenceValues preferenceValues,
IFormattedResourceAcceptor acceptor) {
resources.stream().forEach(resource -> {
if (resource instanceof XtextResource) {
formatXtextResource((XtextResource) resource, preferenceValues);
formatXtextResource((XtextResource) resource, preferenceValues, acceptor);

} else {
LOGGER.debug("Resource is not of type XtextResource and will be skipped: " + resource.getURI());
Expand All @@ -47,7 +44,15 @@ public void formatCollection(Collection<Resource> resources, ITypedPreferenceVal
}

@Override
public void formatXtextResource(XtextResource resource, ITypedPreferenceValues preferenceValues) {
public void formatXtextResource(XtextResource resource, ITypedPreferenceValues preferenceValues,
IFormattedResourceAcceptor acceptor) {
if (!resource.getAllContents().hasNext()) {
LOGGER.info("Resource " + resource.getURI() + " is empty.");
return;
}

LOGGER.info("Formatting file at location " + resource.getURI());

// setup request and formatter
FormatterRequest req = formatterRequestProvider.get();
req.setPreferences(preferenceValues);
Expand All @@ -57,20 +62,20 @@ public void formatXtextResource(XtextResource resource, ITypedPreferenceValues p
req.setTextRegionAccess(regionAccess);

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

// formatting using TextRegionRewriter
ITextRegionRewriter regionRewriter = regionAccess.getRewriter();
String formattedString = regionRewriter.renderToString(regionAccess.regionForDocument(), replacements);

// With the formatted text, update the resource
InputStream resultStream = new ByteArrayInputStream(formattedString.getBytes(StandardCharsets.UTF_8));
resource.unload();
try {
resource.load(resultStream, null);
} catch (IOException e) {
throw new UncheckedIOException(
"Since the resource is an in-memory string, this exception is not expected to be ever thrown.", e);
}
// Perform handler operation
acceptor.accept(resource, formattedString);
}

}
Loading
Loading