8159027: JShell API: SourceCodeAnalysis.Suggestion has constructor, ..

Reviewed-by: jlahoda
This commit is contained in:
Robert Field 2016-08-15 11:39:53 -07:00
parent 1c1ec9a26e
commit 67028ff853
4 changed files with 154 additions and 71 deletions

View File

@ -923,7 +923,7 @@ public class JShellTool implements MessageHandler {
for (String alternative : alternatives) { for (String alternative : alternatives) {
if (alternative.startsWith(input)) { if (alternative.startsWith(input)) {
result.add(new Suggestion(alternative, false)); result.add(new ArgSuggestion(alternative));
} }
} }
@ -951,7 +951,7 @@ public class JShellTool implements MessageHandler {
List<Suggestion> result = new ArrayList<>(); List<Suggestion> result = new ArrayList<>();
try (Stream<Path> dir = Files.list(current)) { try (Stream<Path> dir = Files.list(current)) {
dir.filter(f -> accept.test(f) && f.getFileName().toString().startsWith(prefix)) dir.filter(f -> accept.test(f) && f.getFileName().toString().startsWith(prefix))
.map(f -> new Suggestion(f.getFileName() + (Files.isDirectory(f) ? "/" : ""), false)) .map(f -> new ArgSuggestion(f.getFileName() + (Files.isDirectory(f) ? "/" : "")))
.forEach(result::add); .forEach(result::add);
} catch (IOException ex) { } catch (IOException ex) {
//ignore... //ignore...
@ -959,7 +959,7 @@ public class JShellTool implements MessageHandler {
if (path.isEmpty()) { if (path.isEmpty()) {
StreamSupport.stream(FileSystems.getDefault().getRootDirectories().spliterator(), false) StreamSupport.stream(FileSystems.getDefault().getRootDirectories().spliterator(), false)
.filter(root -> accept.test(root) && root.toString().startsWith(prefix)) .filter(root -> accept.test(root) && root.toString().startsWith(prefix))
.map(root -> new Suggestion(root.toString(), false)) .map(root -> new ArgSuggestion(root.toString()))
.forEach(result::add); .forEach(result::add);
} }
anchor[0] = path.length(); anchor[0] = path.length();
@ -981,7 +981,7 @@ public class JShellTool implements MessageHandler {
? Stream.of(String.valueOf(k.id()), ((DeclarationSnippet) k).name()) ? Stream.of(String.valueOf(k.id()), ((DeclarationSnippet) k).name())
: Stream.of(String.valueOf(k.id()))) : Stream.of(String.valueOf(k.id())))
.filter(k -> k.startsWith(prefix)) .filter(k -> k.startsWith(prefix))
.map(k -> new Suggestion(k, false)) .map(k -> new ArgSuggestion(k))
.collect(Collectors.toList()); .collect(Collectors.toList());
}; };
} }
@ -1146,7 +1146,7 @@ public class JShellTool implements MessageHandler {
.filter(cmd -> cmd.kind.shouldSuggestCompletions) .filter(cmd -> cmd.kind.shouldSuggestCompletions)
.map(cmd -> cmd.command) .map(cmd -> cmd.command)
.filter(key -> key.startsWith(prefix)) .filter(key -> key.startsWith(prefix))
.map(key -> new Suggestion(key + " ", false)); .map(key -> new ArgSuggestion(key + " "));
anchor[0] = 0; anchor[0] = 0;
} else { } else {
String arg = prefix.substring(space + 1); String arg = prefix.substring(space + 1);
@ -2457,6 +2457,42 @@ public class JShellTool implements MessageHandler {
this.tid = tid; this.tid = tid;
} }
} }
private static class ArgSuggestion implements Suggestion {
private final String continuation;
/**
* Create a {@code Suggestion} instance.
*
* @param continuation a candidate continuation of the user's input
*/
public ArgSuggestion(String continuation) {
this.continuation = continuation;
}
/**
* The candidate continuation of the given user's input.
*
* @return the continuation string
*/
@Override
public String continuation() {
return continuation;
}
/**
* Indicates whether input continuation matches the target type and is thus
* more likely to be the desired continuation. A matching continuation is
* preferred.
*
* @return {@code false}, non-types analysis
*/
@Override
public boolean matchesType() {
return false;
}
}
} }
abstract class NonInteractiveIOContext extends IOContext { abstract class NonInteractiveIOContext extends IOContext {

View File

@ -144,30 +144,16 @@ public abstract class SourceCodeAnalysis {
/** /**
* The result of {@code analyzeCompletion(String input)}. * The result of {@code analyzeCompletion(String input)}.
* Describes the completeness and position of the first snippet in the given input. * Describes the completeness of the first snippet in the given input.
*/ */
public static class CompletionInfo { public interface CompletionInfo {
private final Completeness completeness;
private final int unitEndPos;
private final String source;
private final String remaining;
CompletionInfo(Completeness completeness, int unitEndPos, String source, String remaining) {
this.completeness = completeness;
this.unitEndPos = unitEndPos;
this.source = source;
this.remaining = remaining;
}
/** /**
* The analyzed completeness of the input. * The analyzed completeness of the input.
* *
* @return an enum describing the completeness of the input string. * @return an enum describing the completeness of the input string.
*/ */
public Completeness completeness() { Completeness completeness();
return completeness;
}
/** /**
* Input remaining after the complete part of the source. * Input remaining after the complete part of the source.
@ -175,9 +161,7 @@ public abstract class SourceCodeAnalysis {
* @return the portion of the input string that remains after the * @return the portion of the input string that remains after the
* complete Snippet * complete Snippet
*/ */
public String remaining() { String remaining();
return remaining;
}
/** /**
* Source code for the first Snippet of code input. For example, first * Source code for the first Snippet of code input. For example, first
@ -186,18 +170,7 @@ public abstract class SourceCodeAnalysis {
* *
* @return the source of the first encountered Snippet * @return the source of the first encountered Snippet
*/ */
public String source() { String source();
return source;
}
/**
* The end of the first Snippet of source.
*
* @return the position of the end of the first Snippet in the input.
*/
public int unitEndPos() {
return unitEndPos;
}
} }
/** /**
@ -272,30 +245,14 @@ public abstract class SourceCodeAnalysis {
/** /**
* A candidate for continuation of the given user's input. * A candidate for continuation of the given user's input.
*/ */
public static class Suggestion { public interface Suggestion {
private final String continuation;
private final boolean matchesType;
/**
* Create a {@code Suggestion} instance.
*
* @param continuation a candidate continuation of the user's input
* @param matchesType does the candidate match the target type
*/
public Suggestion(String continuation, boolean matchesType) {
this.continuation = continuation;
this.matchesType = matchesType;
}
/** /**
* The candidate continuation of the given user's input. * The candidate continuation of the given user's input.
* *
* @return the continuation string * @return the continuation string
*/ */
public String continuation() { String continuation();
return continuation;
}
/** /**
* Indicates whether input continuation matches the target type and is thus * Indicates whether input continuation matches the target type and is thus
@ -305,9 +262,7 @@ public abstract class SourceCodeAnalysis {
* @return {@code true} if this suggested continuation matches the * @return {@code true} if this suggested continuation matches the
* target type; otherwise {@code false} * target type; otherwise {@code false}
*/ */
public boolean matchesType() { boolean matchesType();
return matchesType;
}
} }
/** /**

View File

@ -171,13 +171,13 @@ class SourceCodeAnalysisImpl extends SourceCodeAnalysis {
MaskCommentsAndModifiers mcm = new MaskCommentsAndModifiers(srcInput, false); MaskCommentsAndModifiers mcm = new MaskCommentsAndModifiers(srcInput, false);
if (mcm.endsWithOpenComment()) { if (mcm.endsWithOpenComment()) {
proc.debug(DBG_COMPA, "Incomplete (open comment): %s\n", srcInput); proc.debug(DBG_COMPA, "Incomplete (open comment): %s\n", srcInput);
return new CompletionInfo(DEFINITELY_INCOMPLETE, srcInput.length(), null, srcInput + '\n'); return new CompletionInfoImpl(DEFINITELY_INCOMPLETE, null, srcInput + '\n');
} }
String cleared = mcm.cleared(); String cleared = mcm.cleared();
String trimmedInput = Util.trimEnd(cleared); String trimmedInput = Util.trimEnd(cleared);
if (trimmedInput.isEmpty()) { if (trimmedInput.isEmpty()) {
// Just comment or empty // Just comment or empty
return new CompletionInfo(Completeness.EMPTY, srcInput.length(), srcInput, ""); return new CompletionInfoImpl(Completeness.EMPTY, srcInput, "");
} }
CaInfo info = ca.scan(trimmedInput); CaInfo info = ca.scan(trimmedInput);
Completeness status = info.status; Completeness status = info.status;
@ -195,12 +195,12 @@ class SourceCodeAnalysisImpl extends SourceCodeAnalysis {
+ mcm.mask().substring(nonCommentNonWhiteLength); + mcm.mask().substring(nonCommentNonWhiteLength);
proc.debug(DBG_COMPA, "Complete: %s\n", compileSource); proc.debug(DBG_COMPA, "Complete: %s\n", compileSource);
proc.debug(DBG_COMPA, " nothing remains.\n"); proc.debug(DBG_COMPA, " nothing remains.\n");
return new CompletionInfo(status, unitEndPos, compileSource, ""); return new CompletionInfoImpl(status, compileSource, "");
} else { } else {
String remain = srcInput.substring(unitEndPos); String remain = srcInput.substring(unitEndPos);
proc.debug(DBG_COMPA, "Complete: %s\n", src); proc.debug(DBG_COMPA, "Complete: %s\n", src);
proc.debug(DBG_COMPA, " remaining: %s\n", remain); proc.debug(DBG_COMPA, " remaining: %s\n", remain);
return new CompletionInfo(status, unitEndPos, src, remain); return new CompletionInfoImpl(status, src, remain);
} }
case COMPLETE_WITH_SEMI: case COMPLETE_WITH_SEMI:
// The unit is the whole non-coment/white input plus semicolon // The unit is the whole non-coment/white input plus semicolon
@ -209,19 +209,19 @@ class SourceCodeAnalysisImpl extends SourceCodeAnalysis {
+ mcm.mask().substring(nonCommentNonWhiteLength); + mcm.mask().substring(nonCommentNonWhiteLength);
proc.debug(DBG_COMPA, "Complete with semi: %s\n", compileSource); proc.debug(DBG_COMPA, "Complete with semi: %s\n", compileSource);
proc.debug(DBG_COMPA, " nothing remains.\n"); proc.debug(DBG_COMPA, " nothing remains.\n");
return new CompletionInfo(status, unitEndPos, compileSource, ""); return new CompletionInfoImpl(status, compileSource, "");
case DEFINITELY_INCOMPLETE: case DEFINITELY_INCOMPLETE:
proc.debug(DBG_COMPA, "Incomplete: %s\n", srcInput); proc.debug(DBG_COMPA, "Incomplete: %s\n", srcInput);
return new CompletionInfo(status, unitEndPos, null, srcInput + '\n'); return new CompletionInfoImpl(status, null, srcInput + '\n');
case CONSIDERED_INCOMPLETE: case CONSIDERED_INCOMPLETE:
proc.debug(DBG_COMPA, "Considered incomplete: %s\n", srcInput); proc.debug(DBG_COMPA, "Considered incomplete: %s\n", srcInput);
return new CompletionInfo(status, unitEndPos, null, srcInput + '\n'); return new CompletionInfoImpl(status, null, srcInput + '\n');
case EMPTY: case EMPTY:
proc.debug(DBG_COMPA, "Detected empty: %s\n", srcInput); proc.debug(DBG_COMPA, "Detected empty: %s\n", srcInput);
return new CompletionInfo(status, unitEndPos, srcInput, ""); return new CompletionInfoImpl(status, srcInput, "");
case UNKNOWN: case UNKNOWN:
proc.debug(DBG_COMPA, "Detected error: %s\n", srcInput); proc.debug(DBG_COMPA, "Detected error: %s\n", srcInput);
return new CompletionInfo(status, unitEndPos, srcInput, ""); return new CompletionInfoImpl(status, srcInput, "");
} }
throw new InternalError(); throw new InternalError();
} }
@ -665,7 +665,7 @@ class SourceCodeAnalysisImpl extends SourceCodeAnalysis {
if (c.getKind() == ElementKind.CONSTRUCTOR || c.getKind() == ElementKind.METHOD) { if (c.getKind() == ElementKind.CONSTRUCTOR || c.getKind() == ElementKind.METHOD) {
simpleName += paren.apply(hasParams.contains(simpleName)); simpleName += paren.apply(hasParams.contains(simpleName));
} }
result.add(new Suggestion(simpleName, smart.test(c))); result.add(new SuggestionImpl(simpleName, smart.test(c)));
} }
} }
@ -1700,4 +1700,98 @@ class SourceCodeAnalysisImpl extends SourceCodeAnalysis {
} }
} }
} }
/**
* A candidate for continuation of the given user's input.
*/
private static class SuggestionImpl implements Suggestion {
private final String continuation;
private final boolean matchesType;
/**
* Create a {@code Suggestion} instance.
*
* @param continuation a candidate continuation of the user's input
* @param matchesType does the candidate match the target type
*/
public SuggestionImpl(String continuation, boolean matchesType) {
this.continuation = continuation;
this.matchesType = matchesType;
}
/**
* The candidate continuation of the given user's input.
*
* @return the continuation string
*/
@Override
public String continuation() {
return continuation;
}
/**
* Indicates whether input continuation matches the target type and is thus
* more likely to be the desired continuation. A matching continuation is
* preferred.
*
* @return {@code true} if this suggested continuation matches the
* target type; otherwise {@code false}
*/
@Override
public boolean matchesType() {
return matchesType;
}
}
/**
* The result of {@code analyzeCompletion(String input)}.
* Describes the completeness and position of the first snippet in the given input.
*/
private static class CompletionInfoImpl implements CompletionInfo {
private final Completeness completeness;
private final String source;
private final String remaining;
CompletionInfoImpl(Completeness completeness, String source, String remaining) {
this.completeness = completeness;
this.source = source;
this.remaining = remaining;
}
/**
* The analyzed completeness of the input.
*
* @return an enum describing the completeness of the input string.
*/
@Override
public Completeness completeness() {
return completeness;
}
/**
* Input remaining after the complete part of the source.
*
* @return the portion of the input string that remains after the
* complete Snippet
*/
@Override
public String remaining() {
return remaining;
}
/**
* Source code for the first Snippet of code input. For example, first
* statement, or first method declaration. Trailing semicolons will be
* added, as needed.
*
* @return the source of the first encountered Snippet
*/
@Override
public String source() {
return source;
}
}
} }

View File

@ -253,10 +253,8 @@ public class CompletenessStressTest extends KullaTesting {
writer.write(String.format("Empty statement: row %d, column %d: -- %s\n", writer.write(String.format("Empty statement: row %d, column %d: -- %s\n",
start, end, unit)); start, end, unit));
} else { } else {
String oops = unit.substring(max(0, ci.unitEndPos() - 10), ci.unitEndPos()) + "|||" +
unit.substring(ci.unitEndPos(), min(unit.length(), ci.unitEndPos() + 10));
writer.write(String.format("Expected %s got %s: '%s' row %d, column %d: -- %s\n", writer.write(String.format("Expected %s got %s: '%s' row %d, column %d: -- %s\n",
expected, ci.completeness(), oops, row, column, unit)); expected, ci.completeness(), unit, row, column, unit));
return false; return false;
} }
} }