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
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,8 @@ void codeInsightUsesTheSelectedMavenAndGradleContexts() throws Exception {
for (var query : List.of(
List.of("describe", "module", "java.base"), List.of("describe", "type", "example.Example"),
List.of("describe", "member", "example.Example.run"), List.of("describe", "file", "Example.java"),
List.of("describe", "types", "example.Example"), List.of("find", "usages", "example.Example"),
List.of("describe", "types", "example.Example"), List.of("describe", "origin", "example.Example"),
List.of("find", "usages", "example.Example"),
List.of("find", "callers", "example.Example.run"), List.of("find", "callees", "example.Example.run"),
List.of("find", "impl", "example.Example"), List.of("find", "impl", "example.Example.run"),
List.of("find", "supertypes", "example.Example"))) {
Expand All @@ -248,15 +249,15 @@ void codeInsightUsesTheSelectedMavenAndGradleContexts() throws Exception {
var arguments = new ArrayList<>(List.of("-p", selector));
arguments.addAll(query);
assertEquals(0, fixture.run(arguments.toArray(String[]::new)), query.toString());
assertEquals(List.of("--class-path", "dependencies", "--source-path", "sources"), fixture.received.subList(0, 4));
assertEquals("--qualified-path", fixture.received.get(4));
assertEquals(query.get(1).equals("file") ? directory.resolve("Example.java").toString() : query.getLast(), fixture.received.getLast());
var received = query.get(1).equals("origin") ? fixture.toolQueries.getLast() : fixture.received;
assertEquals(List.of("--class-path", "dependencies", "--source-path", "sources"), received.subList(0, 4));
assertEquals(query.get(1).equals("file") ? directory.resolve("Example.java").toString() : query.getLast(), received.getLast());
assertEquals("test", value(fixture.resolution(), type.equals("maven") ? "--scope" : "--source-set"));
if (type.equals("gradle")) assertEquals("runtime", value(fixture.resolution(), "--classpath"));
if (query.get(1).equals("impl")) {
assertEquals(2, fixture.toolQueries.size());
assertEquals(fixture.received.subList(0, 4), fixture.toolQueries.getFirst().subList(0, 4));
assertEquals(query.getLast().equals("example.Example") ? "--subtypes" : "--overrides", fixture.received.get(5));
assertEquals(query.getLast().equals("example.Example") ? "--subtypes" : "--overrides", fixture.received.get(4));
assertEquals(1, fixture.queries.stream().filter(argumentsForJig -> argumentsForJig.contains("--resolve-options")).count());
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ class CodeInsightTest {
@Test
void describesExplicitSubjectKindsAndFindsExplicitRelationships() throws Exception {
for (var command : List.of("describe", "find")) {
var subjects = command.equals("describe") ? List.of("module", "type", "member", "file", "types")
var subjects = command.equals("describe") ? List.of("module", "type", "member", "file", "types", "origin")
: List.of("usages", "callers", "callees", "impl", "supertypes");
for (var subject : subjects) {
var invocation = JaInvocation.parse(Path.of(""), new String[] {"-p", ":client", command, subject, "example.Store"});
Expand All @@ -67,7 +67,7 @@ void requiresAnExplicitQuestionAndSingleSubject() {

@Test
void completesSubjectKindsAndQuestions() {
assertEquals(List.of("file", "member", "module", "type", "types"), complete(List.of("describe"), ""));
assertEquals(List.of("file", "member", "module", "origin", "type", "types"), complete(List.of("describe"), ""));
assertEquals(List.of("callees", "callers", "impl", "supertypes", "usages"), complete(List.of("find"), ""));
assertEquals(List.of("impl"), complete(List.of("find"), "im"));
}
Expand All @@ -92,7 +92,8 @@ void delegatesQuestionsWithTheSelectedCompilationContext(@TempDir Path directory
for (var query : List.of(
List.of("describe", "module", "java.base"), List.of("describe", "type", "java.lang.String"),
List.of("describe", "member", "java.lang.String.repeat"), List.of("describe", "file", "Example.java"),
List.of("describe", "types", "Example.java"), List.of("find", "usages", "java.lang.String"),
List.of("describe", "types", "Example.java"), List.of("describe", "origin", "java.lang.String"),
List.of("find", "usages", "java.lang.String"),
List.of("find", "callers", "java.lang.String.repeat"), List.of("find", "callees", "java.lang.String.repeat"),
List.of("find", "impl", "java.lang.Runnable"), List.of("find", "supertypes", "java.lang.String"))) {
var jist = new Probe("jist");
Expand All @@ -104,9 +105,9 @@ void delegatesQuestionsWithTheSelectedCompilationContext(@TempDir Path directory
JaInvocation.parse(directory, query.toArray(String[]::new)), InputStream.nullInputStream(), new PrintStream(output), new PrintStream(errors));
assertEquals(0, result, errors.toString());
var expected = new ArrayList<>(List.of("--module-source-path", "example.app=" + directory, "--module", "example.app"));
expected.add("--qualified-path");
if (query.getFirst().equals("describe")) {
if (query.get(1).equals("types")) expected.add("--referenced-types");
else if (query.get(1).equals("origin")) expected.addAll(List.of("-l", "--source", "none", "--qualified-path", "--visibility", "all"));
else {
if (query.get(1).equals("module")) expected.addAll(List.of("--kind", "module"));
else if (query.get(1).equals("member")) expected.addAll(List.of("--kind", "method", "--kind", "field"));
Expand All @@ -133,7 +134,8 @@ void semanticCompletionUsesTheSelectedProjectContext(@TempDir Path directory) th
for (var query : List.of(
List.of("describe", "module", "--kind", "module"), List.of("describe", "type", "--kind", "type"),
List.of("describe", "member", "--kind", "method", "--kind", "field"),
List.of("describe", "types", "--kind", "type"), List.of("find", "usages", "--usages"),
List.of("describe", "types", "--kind", "type"), List.of("describe", "origin"),
List.of("find", "usages", "--usages"),
List.of("find", "callers", "--callers"), List.of("find", "callees", "--callees"),
List.of("find", "impl", "--kind", "type", "--kind", "method"),
List.of("find", "supertypes", "--supertypes"))) {
Expand Down Expand Up @@ -165,13 +167,37 @@ void implementationSearchUsesReportedTypeKinds(@TempDir Path directory) throws E
var lookup = new ArrayList<>(context);
lookup.addAll(List.of("-l", "--kind", "type", "--source", "none", "--visibility", "all", "example.Root"));
var query = new ArrayList<>(context);
query.addAll(List.of("--qualified-path", "--subtypes", "example.Root"));
query.addAll(List.of("--subtypes", "example.Root"));
assertEquals(List.of(lookup, query), jist.invocations, kind);
assertEquals(1, jig.invocations.size());
assertEquals("", output.toString());
}
}

@Test
void originReportsOnlyTheExactSymbolAndItsOverloads(@TempDir Path directory) throws Exception {
Files.writeString(directory.resolve("module-info.java"), "module example.app {}");
var jist = new Probe("jist");
jist.symbols = """
class example.Root(/classes/example/Root.class)
method example.Root.run()(/classes/example/Root.class)
method example.Root.run(int)(/classes/example/Root.class)
class example.Root.run.Nested(/classes/example/Root$run$Nested.class)
""";
var output = new ByteArrayOutputStream();
var errors = new ByteArrayOutputStream();

int result = new CommandRunner(ModuleLayer.boot(), ToolRuntime.of(new Probe("jig"), jist), new ToolCatalog(List.of()), () -> null).run(
JaInvocation.parse(directory, new String[] {"describe", "origin", "example.Root.run"}), InputStream.nullInputStream(),
new PrintStream(output), new PrintStream(errors));

assertEquals(0, result, errors.toString());
assertEquals("""
method example.Root.run()(/classes/example/Root.class)
method example.Root.run(int)(/classes/example/Root.class)
""", output.toString());
}

@Test
void implementationSearchLeavesMethodValidationAndInheritanceToJist(@TempDir Path directory) throws Exception {
Files.writeString(directory.resolve("module-info.java"), "module example.app {}");
Expand Down
33 changes: 22 additions & 11 deletions src/com.netflix.tools.ja/com/netflix/tools/ja/CommandRunner.java
Original file line number Diff line number Diff line change
Expand Up @@ -370,9 +370,17 @@ case Browse(var type) -> {
resolved, in, out, err);
}
if (commandLine.command() instanceof Command.Describe(var request)) {
var arguments = insightArguments(request.arguments(commandLine.workingDirectory()));
return toolRunner.run(toolInvocation(commandLine, "jist", arguments), resolutionArguments,
resolved, in, out, err);
var invocation = toolInvocation(commandLine, "jist", request.arguments(commandLine.workingDirectory()));
if (request.subject() != DescribeRequest.Subject.ORIGIN) {
return toolRunner.run(invocation, resolutionArguments, resolved, in, out, err);
}
var origins = new ByteArrayOutputStream();
int result = toolRunner.run(invocation, resolutionArguments, resolved, in,
new PrintStream(origins, true, StandardCharsets.UTF_8), err);
if (result == 0) {
printExactOrigins(origins.toString(StandardCharsets.UTF_8), request.target(), out);
}
return result;
}
if (commandLine.command() instanceof Command.Find(var request)) {
var arguments = request.arguments();
Expand All @@ -385,7 +393,7 @@ case Browse(var type) -> {
}
arguments = request.implementationArguments(symbols.toString(StandardCharsets.UTF_8));
}
return toolRunner.run(toolInvocation(commandLine, "jist", insightArguments(arguments)), resolutionArguments,
return toolRunner.run(toolInvocation(commandLine, "jist", arguments), resolutionArguments,
resolved, in, out, err);
}
if (commandLine.command() instanceof Source(var symbol)) {
Expand All @@ -410,14 +418,17 @@ case Browse(var type) -> {
throw new IllegalArgumentException("Command " + commandLine.command() + " is not implemented");
}

private static List<String> insightArguments(List<String> arguments) {
if (System.console() != null) {
return arguments;
private static void printExactOrigins(String origins, String target, PrintStream out) {
origins.lines().filter(line -> originSymbol(line).equals(target)).forEach(out::println);
}

private static String originSymbol(String line) {
int separator = line.indexOf(' ');
if (separator < 0) {
return "";
}
var qualified = new ArrayList<String>();
qualified.add("--qualified-path");
qualified.addAll(arguments);
return List.copyOf(qualified);
int signature = line.indexOf('(', separator + 1);
return signature < 0 ? "" : line.substring(separator + 1, signature);
}

private static JaInvocation toolInvocation(JaInvocation invocation, String tool, List<String> arguments) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ public enum Subject {
TYPE("type", "Describe a Java type and its API", List.of("--kind", "type", "--kind", "method", "--kind", "field")),
MEMBER("member", "Describe a method or field", List.of("--kind", "method", "--kind", "field")),
FILE("file", "Describe declarations in a source or class file", List.of("--kind", "type", "--kind", "method", "--kind", "field")),
TYPES("types", "List resolved types for a Java type or source file", List.of("--referenced-types"));
TYPES("types", "List resolved types for a Java type or source file", List.of("--referenced-types")),
ORIGIN("origin", "Show where a Java symbol is resolved", List.of());

private final String command;
private final String description;
Expand Down Expand Up @@ -64,7 +65,7 @@ static Subject parse(String value) {

static DescribeRequest parse(List<String> arguments) {
if (arguments.size() != 2) {
throw new IllegalArgumentException("Usage: ja describe <module|type|member|file|types> <subject>");
throw new IllegalArgumentException("Usage: ja describe <module|type|member|file|types|origin> <subject>");
}
return new DescribeRequest(Subject.parse(arguments.getFirst()), arguments.getLast());
}
Expand All @@ -76,11 +77,15 @@ List<String> arguments(Path workingDirectory) {
if (subject == Subject.TYPES) {
return List.of("--referenced-types", resolvedTarget);
}
if (subject == Subject.ORIGIN) {
return List.of("-l", "--source", "none", "--qualified-path", "--visibility", "all", resolvedTarget);
}
var arguments = new ArrayList<>(subject.kinds);
arguments.addAll(List.of("--source", switch (subject) {
case MODULE -> "definition";
case MEMBER -> "doc";
case TYPE, FILE, TYPES -> "signature";
case ORIGIN -> throw new AssertionError();
}));
arguments.add(resolvedTarget);
return List.copyOf(arguments);
Expand Down
Loading