[java-refactoring] IDEA-215147 Detect common formats of string values when suggesting variable names

GitOrigin-RevId: 68731a721306d012e37c01f71be4d66744b2ca9e
This commit is contained in:
Tagir Valeev
2023-02-03 11:41:44 +00:00
committed by intellij-monorepo-bot
parent ca947d5410
commit 7d8f8b64dc
8 changed files with 121 additions and 6 deletions
@@ -7,6 +7,7 @@ import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.util.text.LiteralNameSuggester;
import com.intellij.openapi.util.text.PastParticiple;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.pom.java.LanguageLevel;
@@ -24,6 +25,7 @@ import com.intellij.util.BitUtil;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.text.NameUtilCore;
import com.siyeh.ig.psiutils.ExpressionUtils;
import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
@@ -574,7 +576,13 @@ public class JavaCodeStyleManagerImpl extends JavaCodeStyleManager {
@NotNull
private NamesByExprInfo suggestVariableNameByExpression(@NotNull PsiExpression expr, @Nullable VariableKind variableKind) {
final LinkedHashSet<String> names = new LinkedHashSet<>();
List<String> fromLiteral = ExpressionUtils.nonStructuralChildren(expr)
.map(e -> e instanceof PsiLiteralExpression literal && literal.getValue() instanceof String str ? str : null)
.filter(Objects::nonNull)
.flatMap(literal -> LiteralNameSuggester.literalNames(literal).stream())
.distinct()
.toList();
final LinkedHashSet<String> names = new LinkedHashSet<>(fromLiteral);
ContainerUtil.addIfNotNull(names, suggestVariableNameFromConstant(expr, variableKind));
ContainerUtil.addIfNotNull(names, suggestVariableNameFromLiterals(expr));
@@ -1,6 +1,6 @@
// "<html> Change signature of A(<b>int</b>, <b>int</b>, <b>String</b>)</html>" "true-preview"
class A {
A(int i, int i1, String s) {
A(int i, int i1, String number) {
new A<caret>(1,1,"4");
}
}
@@ -1,3 +1,3 @@
A(int i, int i1, String s) {
A(int i, int i1, String number) {
new A(1,1,"4");
}
@@ -0,0 +1,61 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.openapi.util.text;
import org.intellij.lang.annotations.Language;
import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
/**
* A utility class to suggest variable names for literals based on their content
*/
public class LiteralNameSuggester {
private static final class PatternBasedSuggestions {
private final @NotNull Pattern pattern;
private final @NotNull List<@NotNull String> names;
PatternBasedSuggestions(@NotNull @Language("regexp") String pattern, @NotNull String @NotNull ... names) {
this.pattern = Pattern.compile(pattern);
this.names = Arrays.asList(names);
}
}
private static final PatternBasedSuggestions[] ourPatternBasedSuggestions = {
new PatternBasedSuggestions("(?i)https?://|file:///", "protocol"),
new PatternBasedSuggestions("(?i)(https?://|file:///)\\w.*", "url"),
new PatternBasedSuggestions("(/\\w+){2,}", "path", "filePath", "fileName"),
new PatternBasedSuggestions("(?i)(\\w+)\\.(jpg|jpeg|gif|png|apng)", "image", "img"),
new PatternBasedSuggestions("(?i)(\\w+)\\.(mp4|avi|mov)", "video"),
new PatternBasedSuggestions("(?i)(\\w+)\\.txt", "file", "fileName", "text"),
new PatternBasedSuggestions("text/plain|text/html|text/css|text/javascript|" +
"image/png|image/jpeg|image/gif|image/apng|image/webp|image/svg+xml|" +
"audio/mpeg|audio/webm|video/webm|multipart/form-data|application/json|" +
"application/zip|application/pdf|application/graphql", "contentType"),
new PatternBasedSuggestions(" ", "space", "indent"),
new PatternBasedSuggestions("#\\w+", "hashtag"),
new PatternBasedSuggestions(" {2,}", "spaces", "indent"),
new PatternBasedSuggestions("\t", "tab", "indent"),
new PatternBasedSuggestions("\t{2,}", "tabs", "indent"),
new PatternBasedSuggestions("\n", "lineBreak", "lineFeed", "lf"),
new PatternBasedSuggestions("\r", "lineBreak", "cr"),
new PatternBasedSuggestions("\r\n", "crlf"),
new PatternBasedSuggestions("(?i)[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,4}", "mail", "email"),
new PatternBasedSuggestions("\\d+", "number", "id"),
new PatternBasedSuggestions("(?i)[0-9a-f]{8}\\b-[0-9a-f]{4}\\b-[0-9a-f]{4}\\b-[0-9a-f]{4}\\b-[0-9a-f]{12}", "uuid", "guid"),
new PatternBasedSuggestions("SELECT\\s.*FROM\\s.*", "query", "sql"),
new PatternBasedSuggestions("(19|20)[0-9][0-9]([-/])(0?[1-9]|1[0-2])\\2(0?[1-9]|[12][0-9]|30|31)", "date"),
};
/**
* @param literalValue string literal value
* @return suggested names
*/
public static List<String> literalNames(String literalValue) {
return Arrays.stream(ourPatternBasedSuggestions)
.filter(suggestion -> suggestion.pattern.matcher(literalValue).matches())
.flatMap(suggestion -> suggestion.names.stream()).collect(Collectors.toList());
}
}
@@ -0,0 +1,46 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.util.text;
import com.intellij.openapi.util.text.LiteralNameSuggester;
import org.junit.Test;
import java.util.Arrays;
import java.util.List;
import static org.junit.Assert.assertEquals;
public class LiteralNameSuggesterTest {
@Test
public void testLiteralNameSuggester() {
String[][] data = {
{"http://xyz", "url"},
{"https://xyz", "url"},
{"http://", "protocol"},
{"file:///", "protocol"},
{"/etc/passwd", "path", "filePath", "fileName"},
{"image.jpg", "image", "img"},
{"PICTURE.PNG", "image", "img"},
{"movie.mp4", "video"},
{"test.txt", "file", "fileName", "text"},
{"application/json", "contentType"},
{" ", "space", "indent"},
{" ", "spaces", "indent"},
{" ", "spaces", "indent"},
{"\t", "tab", "indent"},
{"\t\t", "tabs", "indent"},
{"\n", "lineBreak", "lineFeed", "lf"},
{"\r", "lineBreak", "cr"},
{"\r\n", "crlf"},
{"support@example.com", "mail", "email"},
{"123", "number", "id"},
{"123e4567-e89b-12d3-a456-426614174000", "uuid", "guid"},
{"SELECT * FROM test", "query", "sql"},
{"2012-12-20", "date"},
{"2012/2/3", "date"}
};
for (String[] test : data) {
List<String> strings = LiteralNameSuggester.literalNames(test[0]);
assertEquals(test[0], strings, Arrays.asList(test).subList(1, test.length));
}
}
}
@@ -1,5 +1,5 @@
class K {
fun foo(i: Int, s: String): Boolean {
fun foo(i: Int, number: String): Boolean {
TODO("Not yet implemented")
}
@@ -1,7 +1,7 @@
class K {
companion object {
@JvmStatic
fun foo(i: Int, s: String): Boolean {
fun foo(i: Int, number: String): Boolean {
TODO("Not yet implemented")
}
}
@@ -2,6 +2,6 @@
fun test() {}
fun foo(i: Int, s: String): Boolean {
fun foo(i: Int, number: String): Boolean {
TODO("Not yet implemented")
}