Skip to content

Commit

Permalink
Add insertBefore option to MergeYaml (#4992)
Browse files Browse the repository at this point in the history
  • Loading branch information
jevanlingen authored Feb 6, 2025
1 parent c2531c7 commit ec68384
Show file tree
Hide file tree
Showing 4 changed files with 240 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ public TreeVisitor<?, ExecutionContext> getVisitor(Accumulator acc) {
if(acc.snippet == null) {
return TreeVisitor.noop();
}
TreeVisitor<?, ExecutionContext> visitor = new MergeYaml(newKey, acc.snippet, false, null, null).getVisitor();
TreeVisitor<?, ExecutionContext> visitor = new MergeYaml(newKey, acc.snippet, false, null, null, null).getVisitor();
if(newFilePath == null) {
visitor = Preconditions.check(new FindSourceFiles(acc.path.toString()).getVisitor(), visitor);
} else {
Expand Down
19 changes: 13 additions & 6 deletions rewrite-yaml/src/main/java/org/openrewrite/yaml/MergeYaml.java
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,13 @@ public class MergeYaml extends Recipe {
@Nullable
String filePattern;

@Option(displayName = "Insert before property",
description = "Choose an insertion point when multiple mappings exist. Takes the `key` JsonPath into account.",
required = false,
example = "some-key")
@Nullable
String insertBefore;

@Override
public Validated<Object> validate() {
return super.validate()
Expand Down Expand Up @@ -112,7 +119,7 @@ public TreeVisitor<?, ExecutionContext> getVisitor() {
public Yaml.Document visitDocument(Yaml.Document document, ExecutionContext ctx) {
if ("$".equals(key)) {
Yaml.Document d = document.withBlock((Yaml.Block)
new MergeYamlVisitor<>(document.getBlock(), yaml, Boolean.TRUE.equals(acceptTheirs), objectIdentifyingProperty)
new MergeYamlVisitor<>(document.getBlock(), yaml, Boolean.TRUE.equals(acceptTheirs), objectIdentifyingProperty, insertBefore)
.visitNonNull(document.getBlock(), ctx, getCursor())
);
return getCursor().getMessage(REMOVE_PREFIX, false) ? d.withEnd(d.getEnd().withPrefix("")) : d;
Expand All @@ -134,13 +141,13 @@ public Yaml.Document visitDocument(Yaml.Document document, ExecutionContext ctx)
// No matching element already exists, so it must be constructed
//noinspection LanguageMismatch
return d.withBlock((Yaml.Block) new MergeYamlVisitor<>(d.getBlock(), snippet,
Boolean.TRUE.equals(acceptTheirs), objectIdentifyingProperty).visitNonNull(d.getBlock(),
Boolean.TRUE.equals(acceptTheirs), objectIdentifyingProperty, insertBefore).visitNonNull(d.getBlock(),
ctx, getCursor()));
}
return d;
}

public String indent(String text) {
private String indent(String text) {
int index = text.indexOf('\n');
if (index == -1 || index == text.length() - 1) {
return text;
Expand Down Expand Up @@ -179,7 +186,7 @@ public Yaml.Mapping visitMapping(Yaml.Mapping mapping, ExecutionContext ctx) {
if (matcher.matches(getCursor())) {
getCursor().putMessageOnFirstEnclosing(Yaml.Document.class, FOUND_MATCHING_ELEMENT, true);
m = (Yaml.Mapping) new MergeYamlVisitor<>(mapping, incoming, Boolean.TRUE.equals(acceptTheirs),
objectIdentifyingProperty).visitNonNull(mapping, ctx, getCursor().getParentOrThrow());
objectIdentifyingProperty, insertBefore).visitNonNull(mapping, ctx, getCursor().getParentOrThrow());
}
return m;
}
Expand All @@ -189,7 +196,7 @@ public Yaml.Mapping.Entry visitMappingEntry(Yaml.Mapping.Entry entry, ExecutionC
if (matcher.matches(getCursor())) {
getCursor().putMessageOnFirstEnclosing(Yaml.Document.class, FOUND_MATCHING_ELEMENT, true);
Yaml.Block value = (Yaml.Block) new MergeYamlVisitor<>(entry.getValue(), incoming,
Boolean.TRUE.equals(acceptTheirs), objectIdentifyingProperty).visitNonNull(entry.getValue(),
Boolean.TRUE.equals(acceptTheirs), objectIdentifyingProperty, insertBefore).visitNonNull(entry.getValue(),
ctx, getCursor());
if (value instanceof Yaml.Scalar && value.getPrefix().isEmpty()) {
value = value.withPrefix(" ");
Expand All @@ -205,7 +212,7 @@ public Yaml.Sequence visitSequence(Yaml.Sequence sequence, ExecutionContext ctx)
getCursor().putMessageOnFirstEnclosing(Yaml.Document.class, FOUND_MATCHING_ELEMENT, true);
return sequence.withEntries(ListUtils.map(sequence.getEntries(),
entry -> entry.withBlock((Yaml.Block) new MergeYamlVisitor<>(entry.getBlock(), incoming,
Boolean.TRUE.equals(acceptTheirs), objectIdentifyingProperty)
Boolean.TRUE.equals(acceptTheirs), objectIdentifyingProperty, insertBefore)
.visitNonNull(entry.getBlock(), ctx, new Cursor(getCursor(), entry)))));
}
return super.visitSequence(sequence, ctx);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,13 @@ public class MergeYamlVisitor<P> extends YamlVisitor<P> {
@Nullable
private final String objectIdentifyingProperty;

@Nullable
private final String insertBefore;

private boolean shouldAutoFormat = true;

public MergeYamlVisitor(Yaml.Block block, Yaml incoming, boolean acceptTheirs, @Nullable String objectIdentifyingProperty, boolean shouldAutoFormat) {
this(block, incoming, acceptTheirs, objectIdentifyingProperty);
this(block, incoming, acceptTheirs, objectIdentifyingProperty, null);
this.shouldAutoFormat = shouldAutoFormat;
}

Expand All @@ -81,7 +84,7 @@ private String linebreak() {
return linebreak;
}

public MergeYamlVisitor(Yaml scope, @Language("yml") String yamlString, boolean acceptTheirs, @Nullable String objectIdentifyingProperty) {
public MergeYamlVisitor(Yaml scope, @Language("yml") String yamlString, boolean acceptTheirs, @Nullable String objectIdentifyingProperty, @Nullable String insertBefore) {
this(scope,
new YamlParser().parse(yamlString)
.findFirst()
Expand All @@ -100,7 +103,8 @@ public MergeYamlVisitor(Yaml scope, @Language("yml") String yamlString, boolean
})
.orElseThrow(() -> new IllegalArgumentException("Could not parse as YAML")),
acceptTheirs,
objectIdentifyingProperty);
objectIdentifyingProperty,
insertBefore);
}

@Override
Expand Down Expand Up @@ -180,8 +184,8 @@ private Yaml.Mapping mergeMapping(Yaml.Mapping m1, Yaml.Mapping m2, P p, Cursor
return existingEntry;
});

// Merge existing and new entries together
List<Yaml.Mapping.Entry> mutatedEntries = concatAll(mergedEntries, map(m2.getEntries(), it -> {
// Transform new entries with spacing, remove entries already existing in original mapping
List<Yaml.Mapping.Entry> newEntries = map(m2.getEntries(), it -> {
for (Yaml.Mapping.Entry existingEntry : m1.getEntries()) {
if (keyMatches(existingEntry, it)) {
return null;
Expand All @@ -192,7 +196,21 @@ private Yaml.Mapping mergeMapping(Yaml.Mapping m1, Yaml.Mapping m2, P p, Cursor
it = it.withValue(it.getValue().withMarkers(it.getValue().getMarkers().add(marker)));
}
return shouldAutoFormat ? autoFormat(it, p, cursor) : it;
}));
});

// Merge existing and new entries together
List<Yaml.Mapping.Entry> mutatedEntries;
if (StringUtils.isBlank(insertBefore) || newEntries.isEmpty()) {
mutatedEntries = concatAll(mergedEntries, newEntries);
} else {
mutatedEntries = new ArrayList<>();
for (Yaml.Mapping.Entry existingEntry : mergedEntries) {
if (insertBefore.equals(existingEntry.getKey().getValue())) {
mutatedEntries.addAll(newEntries);
}
mutatedEntries.add(existingEntry);
}
}

// copy comment to previous element if needed
if (m1.getEntries().size() < mutatedEntries.size() && !getCursor().isRoot()) {
Expand Down
Loading

0 comments on commit ec68384

Please sign in to comment.