Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
74 changes: 46 additions & 28 deletions eo-maven-plugin/src/main/java/org/eolang/maven/MjFormat.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
import com.jcabi.xml.XML;
import java.io.IOException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
import java.util.EnumMap;
import java.util.Map;
Expand Down Expand Up @@ -38,7 +37,21 @@
* files with their canonical form instead of failing, much like
* {@code gofmt -w} or {@code spotless:apply}.</p>
*
* <p>Parsing a source is by far the costliest thing this goal does, so it
* does it as few times as it can: the sources are checked concurrently,
* through {@link Threaded}, the way every other goal walks them, and each
* settling pass keeps the tree it parsed instead of parsing the same text
* again to lay it out.</p>
*
* @since 0.57.0
* @todo #6263:30min Parse every {@code .eo} source once per build.

Check warning on line 47 in eo-maven-plugin/src/main/java/org/eolang/maven/MjFormat.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Complete the task associated to this TODO comment.

See more on https://sonarcloud.io/project/issues?id=objectionary_eo&issues=AZ_yiM50Sv3EudqIw_Rr&open=AZ_yiM50Sv3EudqIw_Rr&pullRequest=6626
* This goal parses each source and throws the tree away, and then the
* {@code compile} goal parses the very same text again seconds later,
* so a clean build of {@code eo-runtime} parses its 170 sources twice.
* Hand the settled tree of {@link #canonical(Path, String)} over to
* {@link Parsing} instead, keyed by the source hash the way
* {@link GlobalCache} already keys its footprints, so that the second
* parse is skipped when the format goal has just produced the same tree.
*/
@Mojo(
name = "format",
Expand Down Expand Up @@ -106,36 +119,39 @@
void exec() throws IOException {
final long start = System.currentTimeMillis();
final Collection<TjForeign> sources = this.scopedTojos().withSources();
final Collection<Path> divergent = new ArrayList<>(0);
for (final TjForeign tojo : sources) {
if (this.reformat(tojo.source())) {
divergent.add(tojo.source());
}
}
this.report(sources.size(), divergent, System.currentTimeMillis() - start);
this.report(
sources.size(),
new Threaded<>(sources, tojo -> this.reformat(tojo.source())).total(),
System.currentTimeMillis() - start
);
}

/**
* Reformat a single source, either fixing it or reporting the diff.
* @param source The path of the {@code .eo} file
* @return TRUE if the file diverged from the canonical form
* @return One if the file diverged from the canonical form, zero otherwise
* @throws IOException If fails to read or write the file
*/
private boolean reformat(final Path source) throws IOException {
private int reformat(final Path source) throws IOException {
final String actual = new UncheckedText(new TextOf(source)).asString();
final String canonical = this.canonical(source, actual);
final Diff diff = new Diff(actual, canonical);
final boolean diverged = !diff.same();
if (diverged && this.autoFix) {
new Saved(canonical, source).value();
Logger.info(this, "Reformatted %[file]s", source);
} else if (diverged) {
Logger.warn(
this,
"%[file]s is not formatted canonically:%n%s",
source,
diff.colored()
);
final int diverged;
if (diff.same()) {
diverged = 0;
} else {
diverged = 1;
if (this.autoFix) {
new Saved(canonical, source).value();
Logger.info(this, "Reformatted %[file]s", source);
} else {
Logger.warn(
this,
"%[file]s is not formatted canonically:%n%s",
source,
diff.colored()
);
}
}
return diverged;
}
Expand All @@ -161,14 +177,16 @@
*/
private String canonical(final Path path, final String source) throws IOException {
String structure = source;
XML tree = MjFormat.parsed(path, structure);
for (int pass = 0; pass < MjFormat.SETTLE; ++pass) {
final String next = new Xmir(MjFormat.parsed(path, structure)).toEO();
final String next = new Xmir(tree).toEO();
if (next.equals(structure)) {
break;
}
structure = next;
tree = MjFormat.parsed(path, structure);
}
return new Xmir(MjFormat.parsed(path, structure), this.weights()).toEO();
return new Xmir(tree, this.weights()).toEO();
}

/**
Expand Down Expand Up @@ -318,11 +336,11 @@
/**
* Report the outcome, failing the build if needed.
* @param total The number of registered sources
* @param divergent The sources that diverged from the canonical form
* @param divergent How many sources diverged from the canonical form
* @param millis The elapsed time, in milliseconds
*/
private void report(final int total, final Collection<Path> divergent, final long millis) {
if (divergent.isEmpty()) {
private void report(final int total, final int divergent, final long millis) {
if (divergent == 0) {
Logger.info(
this,
"All %d EO source(s) are formatted canonically, took %[ms]s to check",
Expand All @@ -332,13 +350,13 @@
Logger.info(
this,
"Reformatted %d of %d EO source(s), took %[ms]s",
divergent.size(), total, millis
divergent, total, millis
);
} else {
throw new IllegalStateException(
String.format(
"%d of %d EO source(s) are not formatted canonically; %s",
divergent.size(),
divergent,
total,
"run with -Deo.autoFix to reformat them automatically"
)
Expand Down
28 changes: 28 additions & 0 deletions eo-maven-plugin/src/test/java/org/eolang/maven/MjFormatTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
import java.io.PrintWriter;
import java.io.StringWriter;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;
import org.cactoos.text.TextOf;
import org.eolang.parser.EoSyntax;
import org.eolang.printer.Xmir;
Expand Down Expand Up @@ -82,6 +85,31 @@ void reformatsDivergentSourceWhenAutoFixIsOn(@Mktmp final Path temp) throws Exce
);
}

@Test
void reformatsEverySourceOfABatch(@Mktmp final Path temp) throws Exception {
final int total = 24;
final String canonical = MjFormatTest.canonical(new HelloWorld().asString());
final String divergent = MjFormatTest.divergent(new HelloWorld().asString());
final FakeMaven maven = new FakeMaven(temp).with("autoFix", true);
for (int idx = 0; idx < total; ++idx) {
maven.withProgram(divergent);
}
final Map<String, Path> result = maven.execute(MjFormat.class).result();
final Collection<String> formatted = new ArrayList<>(total);
for (int idx = 0; idx < total; ++idx) {
formatted.add(
new TextOf(
result.get(String.format("foo/x/main%s.eo", FakeMaven.suffix(idx)))
).asString()
);
}
MatcherAssert.assertThat(
"every source of the batch must be rewritten into its canonical form",
formatted,
Matchers.everyItem(Matchers.equalTo(canonical))
);
}

@Test
void failsWhenSourceDoesNotParse(@Mktmp final Path temp) {
final IllegalStateException exception = Assertions.assertThrows(
Expand Down
Loading