Skip to content
51 changes: 41 additions & 10 deletions rewrite-core/src/main/java/org/openrewrite/text/Find.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import lombok.EqualsAndHashCode;
import lombok.Value;
import org.apache.commons.lang3.BooleanUtils;
import org.jetbrains.annotations.NotNull;
Comment thread
timtebeek marked this conversation as resolved.
Outdated
import org.jspecify.annotations.Nullable;
import org.openrewrite.*;
import org.openrewrite.binary.Binary;
Expand Down Expand Up @@ -97,6 +98,14 @@ public String getDescription() {
@Nullable
Boolean description;

@Option(displayName = "Context size for Datatable",
description = "The number of characters to include in the datatable before and after the match. Default `0`, " +
"`-1` indicates that the whole text should be used.",
required = false,
example = "50")
@Nullable
Integer contextSize;

@Override
public String getInstanceName() {
return String.format("Find text `%s`", find);
Expand All @@ -106,6 +115,7 @@ public String getInstanceName() {
public TreeVisitor<?, ExecutionContext> getVisitor() {

TreeVisitor<?, ExecutionContext> visitor = new TreeVisitor<Tree, ExecutionContext>() {

@Override
public Tree visit(@Nullable Tree tree, ExecutionContext ctx) {
SourceFile sourceFile = (SourceFile) requireNonNull(tree);
Expand Down Expand Up @@ -164,24 +174,45 @@ public Tree visit(@Nullable Tree tree, ExecutionContext ctx) {
}

int startLine = lastNewLineIndex + 1;
int endLine = rawText.indexOf('\n', matcher.end());
int endLine = nextNewLineIndex > matcher.end() ? nextNewLineIndex : rawText.indexOf('\n', matcher.end());
if (endLine == -1) {
endLine = rawText.length();
}

//noinspection StringBufferReplaceableByString
textMatches.insertRow(ctx, new TextMatches.Row(
sourceFilePath,
new StringBuilder(endLine - startLine + 3)
.append(rawText, startLine, matchStart)
.append("~~>")
.append(rawText, matchStart, endLine)
.toString()
));
String context = truncateContext(endLine, startLine, matcher, matchStart, rawText);

textMatches.insertRow(ctx, new TextMatches.Row(sourceFilePath, context));
} while (matcher.find());
snippets.add(snippet(rawText.substring(previousEnd)));
return plainText.withText("").withSnippets(snippets);
}

private @NotNull String truncateContext(int endLine, int startLine, Matcher matcher, int matchStart, String rawText) {
Comment thread
timtebeek marked this conversation as resolved.
Outdated
if (contextSize == null || contextSize == 0) {
return "...~~>" + matcher.group() + "...";
}

if (contextSize == -1) {
return rawText.substring(0, matchStart) + "~~>" + rawText.substring(matchStart);
}

StringBuilder context = new StringBuilder(Math.min(endLine - startLine + 3, contextSize * 2 + matcher.group().length() + 3));
if (matchStart - startLine > contextSize) {
context.append("...");
context.append(rawText, matchStart - (contextSize - 3), matchStart);
} else {
context.append(rawText, startLine, matchStart);
}
context.append("~~>");
int matchLength = matcher.group().length();
if (endLine - (matchStart + 3 + matchLength) > contextSize) {
context.append(rawText, matchStart, matchStart + 3 + matchLength + (contextSize - 3));
context.append("...");
} else {
context.append(rawText, matchStart, endLine);
}
return context.toString();
}
};
if (filePattern != null) {
visitor = Preconditions.check(new FindSourceFiles(filePattern), visitor);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -336,7 +336,7 @@ void preconditionOnNestedDeclarative() {
void exposesUnderlyingDataTables() {
DeclarativeRecipe dr = new DeclarativeRecipe("org.openrewrite.DeclarativeDataTable", "declarative with data table",
"test", emptySet(), null, URI.create("dummy"), true, Collections.emptyList());
dr.addUninitialized(new Find("sam", null, null, null, null, null, null));
dr.addUninitialized(new Find("sam", null, null, null, null, null, null, null));
dr.initialize(List.of(), Map.of());
assertThat(dr.getDataTableDescriptors()).anyMatch(it -> "org.openrewrite.table.TextMatches".equals(it.getName()));
}
Expand Down
119 changes: 108 additions & 11 deletions rewrite-core/src/test/java/org/openrewrite/text/FindTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class FindTest implements RewriteTest {
@Test
void regex() {
rewriteRun(
spec -> spec.recipe(new Find("[T\\s]", true, true, null, null, null, null)),
spec -> spec.recipe(new Find("[T\\s]", true, true, null, null, null, null, null)),
text(
"""
This is\ttext.
Expand All @@ -45,7 +45,7 @@ void regex() {
@Test
void dataTable() {
rewriteRun(
spec -> spec.recipe(new Find("text", null, null, null, null, null, null))
spec -> spec.recipe(new Find("text", null, null, null, null, null, null, 50))
.dataTable(TextMatches.Row.class, rows -> {
assertThat(rows).hasSize(1);
assertThat(rows.get(0).getMatch()).isEqualTo("This is ~~>text.");
Expand All @@ -68,7 +68,7 @@ void dataTable() {
@Test
void plainText() {
rewriteRun(
spec -> spec.recipe(new Find("\\s", null, null, null, null, null, null)),
spec -> spec.recipe(new Find("\\s", null, null, null, null, null, null, 50)),
text(
"""
This i\\s text.
Expand All @@ -83,7 +83,7 @@ void plainText() {
@Test
void caseInsensitive() {
rewriteRun(
spec -> spec.recipe(new Find("text", null, null, null, null, "**/foo/**;**/baz/**", null)),
spec -> spec.recipe(new Find("text", null, null, null, null, "**/foo/**;**/baz/**", null, 50)),
dir("foo",
text(
"""
Expand Down Expand Up @@ -115,7 +115,7 @@ void caseInsensitive() {
@Test
void regexBasicMultiLine() {
rewriteRun(
spec -> spec.recipe(new Find("[T\\s]", true, true, true, null, null, null)),
spec -> spec.recipe(new Find("[T\\s]", true, true, true, null, null, null, 50)),
text(
"""
This is\ttext.
Expand All @@ -132,7 +132,7 @@ void regexBasicMultiLine() {
@Test
void regexWithoutMultilineAndDotall() {
rewriteRun(
spec -> spec.recipe(new Find("^This.*below\\.$", true, true, false, false, null, null)),
spec -> spec.recipe(new Find("^This.*below\\.$", true, true, false, false, null, null, 50)),
text(
"""
This is text.
Expand All @@ -148,7 +148,7 @@ void regexWithoutMultilineAndDotall() {
@Test
void regexMatchingWhitespaceWithoutMultilineWithDotall() {
rewriteRun(
spec -> spec.recipe(new Find("One.Two$", true, true, false, true, null, null)),
spec -> spec.recipe(new Find("One.Two$", true, true, false, true, null, null, 50)),
//language=csv
text( // the `.` above matches the space character on the same line
"""
Expand All @@ -163,7 +163,7 @@ void regexMatchingWhitespaceWithoutMultilineWithDotall() {
@Test
void regexWithoutMultilineAndWithDotAll() {
rewriteRun(
spec -> spec.recipe(new Find("^This.*below\\.$", true, true, false, true, null, null)),
spec -> spec.recipe(new Find("^This.*below\\.$", true, true, false, true, null, null, 50)),
text(
"""
This is text.
Expand All @@ -186,7 +186,7 @@ void regexWithoutMultilineAndWithDotAll() {
@Test
void regexWithMultilineAndWithoutDotall() {
rewriteRun(
spec -> spec.recipe(new Find("^This.*below\\.$", true, true, true, false, null, null)),
spec -> spec.recipe(new Find("^This.*below\\.$", true, true, true, false, null, null, 50)),
text(
"""
This is text.
Expand All @@ -209,7 +209,7 @@ void regexWithMultilineAndWithoutDotall() {
@Test
void regexWithBothMultilineAndDotAll() {
rewriteRun(
spec -> spec.recipe(new Find("^This.*below\\.$", true, true, true, true, null, null)),
spec -> spec.recipe(new Find("^This.*below\\.$", true, true, true, true, null, null, 50)),
text(
"""
The first line.
Expand All @@ -232,7 +232,7 @@ void regexWithBothMultilineAndDotAll() {
@Test
void description() {
rewriteRun(
spec -> spec.recipe(new Find("text", null, null, null, null, null, true)),
spec -> spec.recipe(new Find("text", null, null, null, null, null, true, 50)),
text(
"""
This is text.
Expand All @@ -243,4 +243,101 @@ void description() {
)
);
}

@Test
void longLine() {
rewriteRun(
spec -> spec.recipe(new Find("very", null, null, null, null, null, null, 50))
.dataTable(TextMatches.Row.class, rows -> {
assertThat(rows).hasSize(18);
assertThat(rows).satisfiesExactly(
r -> assertThat(r.getMatch()).isEqualTo("This is a ~~>very, very, very, very, very, very, very, very, very, ..."),
r -> assertThat(r.getMatch()).isEqualTo("This is a very, ~~>very, very, very, very, very, very, very, very, very, ..."),
r -> assertThat(r.getMatch()).isEqualTo("This is a very, very, ~~>very, very, very, very, very, very, very, very, very, ..."),
r -> assertThat(r.getMatch()).isEqualTo("This is a very, very, very, ~~>very, very, very, very, very, very, very, very, very, ..."),
r -> assertThat(r.getMatch()).isEqualTo("This is a very, very, very, very, ~~>very, very, very, very, very, very, very, very, very, ..."),
r -> assertThat(r.getMatch()).isEqualTo("This is a very, very, very, very, very, ~~>very, very, very, very, very, very, very, very, very, ..."),
r -> assertThat(r.getMatch()).isEqualTo("This is a very, very, very, very, very, very, ~~>very, very, very, very, very, very, very, very, very, ..."),
r -> assertThat(r.getMatch()).isEqualTo("...is a very, very, very, very, very, very, very, ~~>very, very, very, very, very, very, very, very, very, ..."),
r -> assertThat(r.getMatch()).isEqualTo("...ery, very, very, very, very, very, very, very, ~~>very, very, very, very, very, very, very, very, very, ..."),
r -> assertThat(r.getMatch()).isEqualTo("...ery, very, very, very, very, very, very, very, ~~>very, very, very, very, very, very, very, very, very l..."),
r -> assertThat(r.getMatch()).isEqualTo("...ery, very, very, very, very, very, very, very, ~~>very, very, very, very, very, very, very, very long line."),
r -> assertThat(r.getMatch()).isEqualTo("...ery, very, very, very, very, very, very, very, ~~>very, very, very, very, very, very, very long line."),
r -> assertThat(r.getMatch()).isEqualTo("...ery, very, very, very, very, very, very, very, ~~>very, very, very, very, very, very long line."),
r -> assertThat(r.getMatch()).isEqualTo("...ery, very, very, very, very, very, very, very, ~~>very, very, very, very, very long line."),
r -> assertThat(r.getMatch()).isEqualTo("...ery, very, very, very, very, very, very, very, ~~>very, very, very, very long line."),
r -> assertThat(r.getMatch()).isEqualTo("...ery, very, very, very, very, very, very, very, ~~>very, very, very long line."),
r -> assertThat(r.getMatch()).isEqualTo("...ery, very, very, very, very, very, very, very, ~~>very, very long line."),
r -> assertThat(r.getMatch()).isEqualTo("...ery, very, very, very, very, very, very, very, ~~>very long line.")
);
}),
text(
"""
This is a very, very, very, very, very, very, very, very, very, very, very, very, very, very, very, very, very, very long line.
""",
"""
This is a ~~>very, ~~>very, ~~>very, ~~>very, ~~>very, ~~>very, ~~>very, ~~>very, ~~>very, ~~>very, ~~>very, ~~>very, ~~>very, ~~>very, ~~>very, ~~>very, ~~>very, ~~>very long line.
"""
)
);
}

@Test
void justMatch() {
rewriteRun(
spec -> spec.recipe(new Find("very", null, null, null, null, null, null, null))
.dataTable(TextMatches.Row.class, rows -> {
assertThat(rows).hasSize(18);
assertThat(rows).allSatisfy(
r -> assertThat(r.getMatch()).isEqualTo("...~~>very...")
);
}),
text(
"""
This is a very, very, very, very, very, very, very, very, very, very, very, very, very, very, very, very, very, very long line.
""",
"""
This is a ~~>very, ~~>very, ~~>very, ~~>very, ~~>very, ~~>very, ~~>very, ~~>very, ~~>very, ~~>very, ~~>very, ~~>very, ~~>very, ~~>very, ~~>very, ~~>very, ~~>very, ~~>very long line.
"""
)
);
}

@Test
void noTruncate() {
rewriteRun(
spec -> spec.recipe(new Find("very", null, null, null, null, null, null, -1))
.dataTable(TextMatches.Row.class, rows -> {
assertThat(rows).hasSize(18);
assertThat(rows).satisfiesExactly(
r -> assertThat(r.getMatch()).isEqualTo("This is a ~~>very, very, very, very, very, very, very, very, very, very, very, very, very, very, very, very, very, very long line."),
r -> assertThat(r.getMatch()).isEqualTo("This is a very, ~~>very, very, very, very, very, very, very, very, very, very, very, very, very, very, very, very, very long line."),
r -> assertThat(r.getMatch()).isEqualTo("This is a very, very, ~~>very, very, very, very, very, very, very, very, very, very, very, very, very, very, very, very long line."),
r -> assertThat(r.getMatch()).isEqualTo("This is a very, very, very, ~~>very, very, very, very, very, very, very, very, very, very, very, very, very, very, very long line."),
r -> assertThat(r.getMatch()).isEqualTo("This is a very, very, very, very, ~~>very, very, very, very, very, very, very, very, very, very, very, very, very, very long line."),
r -> assertThat(r.getMatch()).isEqualTo("This is a very, very, very, very, very, ~~>very, very, very, very, very, very, very, very, very, very, very, very, very long line."),
r -> assertThat(r.getMatch()).isEqualTo("This is a very, very, very, very, very, very, ~~>very, very, very, very, very, very, very, very, very, very, very, very long line."),
r -> assertThat(r.getMatch()).isEqualTo("This is a very, very, very, very, very, very, very, ~~>very, very, very, very, very, very, very, very, very, very, very long line."),
r -> assertThat(r.getMatch()).isEqualTo("This is a very, very, very, very, very, very, very, very, ~~>very, very, very, very, very, very, very, very, very, very long line."),
r -> assertThat(r.getMatch()).isEqualTo("This is a very, very, very, very, very, very, very, very, very, ~~>very, very, very, very, very, very, very, very, very long line."),
r -> assertThat(r.getMatch()).isEqualTo("This is a very, very, very, very, very, very, very, very, very, very, ~~>very, very, very, very, very, very, very, very long line."),
r -> assertThat(r.getMatch()).isEqualTo("This is a very, very, very, very, very, very, very, very, very, very, very, ~~>very, very, very, very, very, very, very long line."),
r -> assertThat(r.getMatch()).isEqualTo("This is a very, very, very, very, very, very, very, very, very, very, very, very, ~~>very, very, very, very, very, very long line."),
r -> assertThat(r.getMatch()).isEqualTo("This is a very, very, very, very, very, very, very, very, very, very, very, very, very, ~~>very, very, very, very, very long line."),
r -> assertThat(r.getMatch()).isEqualTo("This is a very, very, very, very, very, very, very, very, very, very, very, very, very, very, ~~>very, very, very, very long line."),
r -> assertThat(r.getMatch()).isEqualTo("This is a very, very, very, very, very, very, very, very, very, very, very, very, very, very, very, ~~>very, very, very long line."),
r -> assertThat(r.getMatch()).isEqualTo("This is a very, very, very, very, very, very, very, very, very, very, very, very, very, very, very, very, ~~>very, very long line."),
r -> assertThat(r.getMatch()).isEqualTo("This is a very, very, very, very, very, very, very, very, very, very, very, very, very, very, very, very, very, ~~>very long line.")
);
}),
text(
"""
This is a very, very, very, very, very, very, very, very, very, very, very, very, very, very, very, very, very, very long line.
""",
"""
This is a ~~>very, ~~>very, ~~>very, ~~>very, ~~>very, ~~>very, ~~>very, ~~>very, ~~>very, ~~>very, ~~>very, ~~>very, ~~>very, ~~>very, ~~>very, ~~>very, ~~>very, ~~>very long line.
"""
)
);
}
}
Loading