8151755: jshell tool: properly cover resolution issues in output configuration

8152246: jshell tool: history overflow

Reviewed-by: jlahoda
This commit is contained in:
Robert Field 2016-03-25 18:36:19 -07:00
parent af5594cf2c
commit e16d374ab5
7 changed files with 1113 additions and 1080 deletions

View File

@ -75,7 +75,6 @@ import jdk.jshell.MethodSnippet;
import jdk.jshell.PersistentSnippet;
import jdk.jshell.Snippet;
import jdk.jshell.Snippet.Status;
import jdk.jshell.Snippet.SubKind;
import jdk.jshell.SnippetEvent;
import jdk.jshell.SourceCodeAnalysis;
import jdk.jshell.SourceCodeAnalysis.CompletionInfo;
@ -95,10 +94,11 @@ import java.util.function.Function;
import java.util.function.Supplier;
import jdk.internal.jshell.tool.Feedback.FormatAction;
import jdk.internal.jshell.tool.Feedback.FormatCase;
import jdk.internal.jshell.tool.Feedback.FormatErrors;
import jdk.internal.jshell.tool.Feedback.FormatResolve;
import jdk.internal.jshell.tool.Feedback.FormatUnresolved;
import jdk.internal.jshell.tool.Feedback.FormatWhen;
import static java.util.stream.Collectors.toList;
import static jdk.jshell.Snippet.Kind.METHOD;
import static java.util.stream.Collectors.toMap;
import static jdk.jshell.Snippet.SubKind.VAR_VALUE_SUBKIND;
@ -113,6 +113,12 @@ public class JShellTool {
private static final Pattern HISTORY_ALL_START_FILENAME = Pattern.compile(
"((?<cmd>(all|history|start))(\\z|\\p{javaWhitespace}+))?(?<filename>.*)");
private static final String RECORD_SEPARATOR = "\u241E";
private static final String RB_NAME_PREFIX = "jdk.internal.jshell.tool.resources";
private static final String VERSION_RB_NAME = RB_NAME_PREFIX + ".version";
private static final String L10N_RB_NAME = RB_NAME_PREFIX + ".l10n";
private static ResourceBundle versionRB = null;
private static ResourceBundle outputRB = null;
final InputStream cmdin;
final PrintStream cmdout;
@ -148,82 +154,18 @@ public class JShellTool {
this.userout = userout;
this.usererr = usererr;
this.prefs = prefs;
initializeFeedbackModes();
}
/**
* Create the default set of feedback modes
*/
final void initializeFeedbackModes() {
// Initialize normal feedback mode
cmdSet("newmode normal command");
cmdSet("prompt normal '\n-> ' '>> '");
cmdSet("field normal pre '| '");
cmdSet("field normal post '%n'");
cmdSet("field normal errorpre '| '");
cmdSet("field normal errorpost '%n'");
cmdSet("field normal action 'Added' added-primary");
cmdSet("field normal action 'Modified' modified-primary");
cmdSet("field normal action 'Replaced' replaced-primary");
cmdSet("field normal action 'Overwrote' overwrote-primary");
cmdSet("field normal action 'Dropped' dropped-primary");
cmdSet("field normal action 'Rejected' rejected-primary");
cmdSet("field normal action ' Update added' added-update");
cmdSet("field normal action ' Update modified' modified-update");
cmdSet("field normal action ' Update replaced' replaced-update");
cmdSet("field normal action ' Update overwrote' overwrote-update");
cmdSet("field normal action ' Update dropped' dropped-update");
cmdSet("field normal action ' Update rejected' rejected-update");
cmdSet("field normal resolve '' ok-*");
cmdSet("field normal resolve ', however, it cannot be invoked until%s is declared' defined-primary");
cmdSet("field normal resolve ', however, it cannot be referenced until%s is declared' notdefined-primary");
cmdSet("field normal resolve ' which cannot be invoked until%s is declared' defined-update");
cmdSet("field normal resolve ' which cannot be referenced until%s is declared' notdefined-update");
cmdSet("field normal name '%s'");
cmdSet("field normal type '%s'");
cmdSet("field normal result '%s'");
cmdSet("format normal '' *-*-*");
cmdSet("format normal '{pre}{action} class {name}{resolve}{post}' class");
cmdSet("format normal '{pre}{action} interface {name}{resolve}{post}' interface");
cmdSet("format normal '{pre}{action} enum {name}{resolve}{post}' enum");
cmdSet("format normal '{pre}{action} annotation interface {name}{resolve}{post}' annotation");
cmdSet("format normal '{pre}{action} method {name}({type}){resolve}{post}' method");
cmdSet("format normal '{pre}{action} variable {name} of type {type}{resolve}{post}' vardecl");
cmdSet("format normal '{pre}{action} variable {name} of type {type} with initial value {result}{resolve}{post}' varinit");
cmdSet("format normal '{pre}{action} variable {name}{resolve}{post}' vardeclrecoverable");
cmdSet("format normal '{pre}{action} variable {name}, reset to null{post}' varreset-*-update");
cmdSet("format normal '{pre}Expression value is: {result}{post}" +
"{pre} assigned to temporary variable {name} of type {type}{post}' expression");
cmdSet("format normal '{pre}Variable {name} of type {type} has value {result}{post}' varvalue");
cmdSet("format normal '{pre}Variable {name} has been assigned the value {result}{post}' assignment");
cmdSet("feedback normal");
// Initialize off feedback mode
cmdSet("newmode off quiet");
cmdSet("prompt off '-> ' '>> '");
cmdSet("field off pre '| '");
cmdSet("field off post '%n'");
cmdSet("field off errorpre '| '");
cmdSet("field off errorpost '%n'");
cmdSet("format off '' *-*-*");
}
private IOContext input = null;
private boolean regenerateOnDeath = true;
private boolean live = false;
private boolean feedbackInitialized = false;
SourceCodeAnalysis analysis;
JShell state = null;
Subscription shutdownSubscription = null;
private boolean debug = false;
private boolean displayPrompt = true;
public boolean testPrompt = false;
private String cmdlineClasspath = null;
private String cmdlineStartup = null;
@ -326,6 +268,43 @@ public class JShellTool {
}
}
/**
* Print using resource bundle look-up and adding prefix and postfix
*
* @param key the resource key
*/
String getResourceString(String key) {
if (outputRB == null) {
try {
outputRB = ResourceBundle.getBundle(L10N_RB_NAME);
} catch (MissingResourceException mre) {
error("Cannot find ResourceBundle: %s", L10N_RB_NAME);
return "";
}
}
String s;
try {
s = outputRB.getString(key);
} catch (MissingResourceException mre) {
error("Missing resource: %s in %s", key, L10N_RB_NAME);
return "";
}
return s;
}
/**
* Print using resource bundle look-up and adding prefix and postfix
*
* @param key the resource key
*/
void hardrb(String key) {
String s = getResourceString(key);
String out = feedback.getPre()
+ s.substring(0, s.length() - 1).replaceAll("\\R", "\n" + feedback.getPre())
+ s.substring(s.length() - 1, s.length());
cmdout.print(out);
}
<T> void hardPairs(Stream<T> stream, Function<T, String> a, Function<T, String> b) {
Map<String, String> a2b = stream.collect(toMap(a, b,
(m1, m2) -> m1,
@ -342,26 +321,6 @@ public class JShellTool {
}
}
/**
* User custom feedback mode only
*
* @param fcase Event to report
* @param update Is this an update (rather than primary)
* @param fa Action
* @param fr Resolution status
* @param name Name string
* @param type Type string or null
* @param result Result value or null
* @param unresolved The unresolved symbols
*/
void custom(FormatCase fcase, boolean update, FormatAction fa, FormatResolve fr,
String name, String type, String unresolved, String result) {
String format = feedback.getFormat(fcase,
(update ? FormatWhen.UPDATE : FormatWhen.PRIMARY), fa, fr,
name != null, type != null, result != null);
fluffRaw(format, unresolved, name, type, result);
}
/**
* Trim whitespace off end of string
*
@ -544,7 +503,6 @@ public class JShellTool {
? currentNameSpace.tid(sn)
: errorNamespace.tid(sn))
.build();
analysis = state.sourceCodeAnalysis();
shutdownSubscription = state.onShutdown((JShell deadState) -> {
if (deadState == state) {
hard("State engine terminated.");
@ -552,7 +510,12 @@ public class JShellTool {
live = false;
}
});
analysis = state.sourceCodeAnalysis();
live = true;
if (!feedbackInitialized) {
startUpRun(getResourceString("startup.feedback"));
feedbackInitialized = true;
}
if (cmdlineClasspath != null) {
state.addToClasspath(cmdlineClasspath);
@ -568,12 +531,16 @@ public class JShellTool {
} else {
start = cmdlineStartup;
}
startUpRun(start);
currentNameSpace = mainNamespace;
}
//where
private void startUpRun(String start) {
try (IOContext suin = new FileScannerIOContext(new StringReader(start))) {
run(suin);
} catch (Exception ex) {
hard("Unexpected exception reading start-up: %s\n", ex);
}
currentNameSpace = mainNamespace;
}
private void closeState() {
@ -596,7 +563,7 @@ public class JShellTool {
String incomplete = "";
while (live) {
String prompt;
if (displayPrompt) {
if (currentNameSpace == mainNamespace) {
prompt = testPrompt
? incomplete.isEmpty()
? "\u0005" //ENQ
@ -679,21 +646,22 @@ public class JShellTool {
cmd = cmd.substring(0, idx);
}
Command[] candidates = findCommand(cmd, c -> c.kind.isRealCommand);
if (candidates.length == 0) {
if (!rerunHistoryEntryById(cmd.substring(1))) {
error("No such command or snippet id: %s", cmd);
switch (candidates.length) {
case 0:
if (!rerunHistoryEntryById(cmd.substring(1))) {
error("No such command or snippet id: %s", cmd);
fluff("Type /help for help.");
} break;
case 1:
Command command = candidates[0];
// If comand was successful and is of a replayable kind, add it the replayable history
if (command.run.apply(arg) && command.kind == CommandKind.REPLAY) {
addToReplayHistory((command.command + " " + arg).trim());
} break;
default:
error("Command: %s is ambiguous: %s", cmd, Arrays.stream(candidates).map(c -> c.command).collect(Collectors.joining(", ")));
fluff("Type /help for help.");
}
} else if (candidates.length == 1) {
Command command = candidates[0];
// If comand was successful and is of a replayable kind, add it the replayable history
if (command.run.apply(arg) && command.kind == CommandKind.REPLAY) {
addToReplayHistory((command.command + " " + arg).trim());
}
} else {
error("Command: %s is ambiguous: %s", cmd, Arrays.stream(candidates).map(c -> c.command).collect(Collectors.joining(", ")));
fluff("Type /help for help.");
break;
}
}
@ -1022,7 +990,7 @@ public class JShellTool {
" -- Display information about the specified help subject. Example: /help intro\n",
arg -> cmdHelp(arg),
EMPTY_COMPLETION_PROVIDER));
registerCommand(new Command("/set", "editor|start|feedback|newmode|prompt|format|field ...", "set jshell configuration information",
registerCommand(new Command("/set", "editor|start|feedback|newmode|prompt|format ...", "set jshell configuration information",
"Set jshell configuration information, including:\n" +
"the external editor to use, the start-up definitions to use, a new feedback mode,\n" +
"the command prompt, the feedback mode to use, or the format of output.\n" +
@ -1043,16 +1011,13 @@ public class JShellTool {
"/set prompt <mode> \"<prompt>\" \"<continuation-prompt>\"\n" +
" -- Set the displayed prompts for a given feedback mode.\n" +
"\n" +
"/set format <mode> \"<format>\" <selector>...\n" +
" -- Configure a feedback mode by setting the format to use in a specified set of cases.\n" +
"\n" +
"/set field name|type|result|when|action|resolve|pre|post|errorpre|errorpost \"<format>\" <format-case>...\n" +
" -- Set the format of a field within the <format-string> of a \"/set format\" command\n" +
"/set format <mode> <field> \"<format>\" <selector>...\n" +
" -- Configure a feedback mode by setting the format of a field when the selector matchs.\n" +
"\n" +
"To get more information about one of these forms, use /help with the form specified.\n" +
"For example: /help /set format\n",
arg -> cmdSet(arg),
new FixedCompletionProvider("format", "field", "feedback", "prompt", "newmode", "start", "editor")));
new FixedCompletionProvider("format", "feedback", "prompt", "newmode", "start", "editor")));
registerCommand(new Command("/?", "", "get information about jshell",
"Display information about jshell (abbreviation for /help).\n" +
"/?\n" +
@ -1153,11 +1118,9 @@ public class JShellTool {
// --- Command implementations ---
private static final String[] setSub = new String[]{
"format", "field", "feedback", "newmode", "prompt", "editor", "start"};
private static final String[] SET_SUBCOMMANDS = new String[]{
"format", "feedback", "newmode", "prompt", "editor", "start"};
// The /set command. Currently /set format, /set field and /set feedback.
// Other commands will fold here, see: 8148317
final boolean cmdSet(String arg) {
ArgTokenizer at = new ArgTokenizer(arg.trim());
String which = setSubCommand(at);
@ -1167,8 +1130,6 @@ public class JShellTool {
switch (which) {
case "format":
return feedback.setFormat(this, at);
case "field":
return feedback.setField(this, at);
case "feedback":
return feedback.setFeedback(this, at);
case "newmode":
@ -1229,9 +1190,6 @@ public class JShellTool {
case "format":
feedback.printFormatHelp(this);
return true;
case "field":
feedback.printFieldHelp(this);
return true;
case "feedback":
feedback.printFeedbackHelp(this);
return true;
@ -1265,13 +1223,13 @@ public class JShellTool {
}
String setSubCommand(ArgTokenizer at) {
String[] matches = at.next(setSub);
String[] matches = at.next(SET_SUBCOMMANDS);
if (matches == null) {
error("The /set command requires arguments. See: /help /set");
return null;
} else if (matches.length == 0) {
error("Not a valid argument to /set: %s", at.val());
fluff("/set is followed by one of: %s", Arrays.stream(setSub)
fluff("/set is followed by one of: %s", Arrays.stream(SET_SUBCOMMANDS)
.collect(Collectors.joining(", "))
);
return null;
@ -1349,8 +1307,20 @@ public class JShellTool {
regenerateOnDeath = false;
live = false;
if (!replayableHistory.isEmpty()) {
prefs.put(REPLAY_RESTORE_KEY, replayableHistory.stream().reduce(
(a, b) -> a + RECORD_SEPARATOR + b).get());
// Prevent history overflow by calculating what will fit, starting
// with must recent
int sepLen = RECORD_SEPARATOR.length();
int length = 0;
int first = replayableHistory.size();
while(length < Preferences.MAX_VALUE_LENGTH && --first >= 0) {
length += replayableHistory.get(first).length() + sepLen;
}
String hist = replayableHistory
.subList(first + 1, replayableHistory.size())
.stream()
.reduce( (a, b) -> a + RECORD_SEPARATOR + b)
.get();
prefs.put(REPLAY_RESTORE_KEY, hist);
}
fluff("Goodbye\n");
return true;
@ -1841,68 +1811,55 @@ public class JShellTool {
.collect(toList());
}
void printDiagnostics(String source, List<Diag> diagnostics, boolean embed) {
String padding = embed? " " : "";
for (Diag diag : diagnostics) {
//assert diag.getSource().equals(source);
if (!embed) {
if (diag.isError()) {
hard("Error:");
} else {
hard("Warning:");
}
void displayDiagnostics(String source, Diag diag, List<String> toDisplay) {
for (String line : diag.getMessage(null).split("\\r?\\n")) { // TODO: Internationalize
if (!line.trim().startsWith("location:")) {
toDisplay.add(line);
}
for (String line : diag.getMessage(null).split("\\r?\\n")) { // TODO: Internationalize
if (!line.trim().startsWith("location:")) {
hard("%s%s", padding, line);
}
}
int pstart = (int) diag.getStartPosition();
int pend = (int) diag.getEndPosition();
Matcher m = LINEBREAK.matcher(source);
int pstartl = 0;
int pendl = -2;
while (m.find(pstartl)) {
pendl = m.start();
if (pendl >= pstart) {
break;
} else {
pstartl = m.end();
}
}
if (pendl < pstart) {
pendl = source.length();
}
fluff("%s%s", padding, source.substring(pstartl, pendl));
StringBuilder sb = new StringBuilder();
int start = pstart - pstartl;
for (int i = 0; i < start; ++i) {
sb.append(' ');
}
sb.append('^');
boolean multiline = pend > pendl;
int end = (multiline ? pendl : pend) - pstartl - 1;
if (end > start) {
for (int i = start + 1; i < end; ++i) {
sb.append('-');
}
if (multiline) {
sb.append("-...");
} else {
sb.append('^');
}
}
fluff("%s%s", padding, sb.toString());
debug("printDiagnostics start-pos = %d ==> %d -- wrap = %s", diag.getStartPosition(), start, this);
debug("Code: %s", diag.getCode());
debug("Pos: %d (%d - %d)", diag.getPosition(),
diag.getStartPosition(), diag.getEndPosition());
}
int pstart = (int) diag.getStartPosition();
int pend = (int) diag.getEndPosition();
Matcher m = LINEBREAK.matcher(source);
int pstartl = 0;
int pendl = -2;
while (m.find(pstartl)) {
pendl = m.start();
if (pendl >= pstart) {
break;
} else {
pstartl = m.end();
}
}
if (pendl < pstart) {
pendl = source.length();
}
toDisplay.add(source.substring(pstartl, pendl));
StringBuilder sb = new StringBuilder();
int start = pstart - pstartl;
for (int i = 0; i < start; ++i) {
sb.append(' ');
}
sb.append('^');
boolean multiline = pend > pendl;
int end = (multiline ? pendl : pend) - pstartl - 1;
if (end > start) {
for (int i = start + 1; i < end; ++i) {
sb.append('-');
}
if (multiline) {
sb.append("-...");
} else {
sb.append('^');
}
}
toDisplay.add(sb.toString());
debug("printDiagnostics start-pos = %d ==> %d -- wrap = %s", diag.getStartPosition(), start, this);
debug("Code: %s", diag.getCode());
debug("Pos: %d (%d - %d)", diag.getPosition(),
diag.getStartPosition(), diag.getEndPosition());
}
private String processSource(String srcInput) throws IllegalStateException {
@ -1943,6 +1900,7 @@ public class JShellTool {
return failed;
}
// Handle incoming snippet events -- return true on failure
private boolean handleEvent(SnippetEvent ste) {
Snippet sn = ste.snippet();
if (sn == null) {
@ -1953,162 +1911,49 @@ public class JShellTool {
String source = sn.source();
if (ste.causeSnippet() == null) {
// main event
printDiagnostics(source, diagnostics, false);
if (ste.status().isActive) {
for (Diag d : diagnostics) {
if (d.isError()) {
hard("Error:");
} else {
hard("Warning:");
}
List<String> disp = new ArrayList<>();
displayDiagnostics(source, d, disp);
disp.stream()
.forEach(l -> hard(l));
}
if (ste.status() != Status.REJECTED) {
if (ste.exception() != null) {
if (ste.exception() instanceof EvalException) {
printEvalException((EvalException) ste.exception());
return true;
} else if (ste.exception() instanceof UnresolvedReferenceException) {
printUnresolved((UnresolvedReferenceException) ste.exception());
printUnresolvedException((UnresolvedReferenceException) ste.exception());
} else {
hard("Unexpected execution exception: %s", ste.exception());
return true;
}
} else {
displayDeclarationAndValue(ste, false, ste.value());
new DisplayEvent(ste, false, ste.value(), diagnostics).displayDeclarationAndValue();
}
} else if (ste.status() == Status.REJECTED) {
} else {
if (diagnostics.isEmpty()) {
hard("Failed.");
}
return true;
}
} else if (ste.status() == Status.REJECTED) {
//TODO -- I don't believe updates can cause failures any more
hard("Caused failure of dependent %s --", ((DeclarationSnippet) sn).name());
printDiagnostics(source, diagnostics, true);
} else {
// Update
if (sn instanceof DeclarationSnippet) {
// display update information
displayDeclarationAndValue(ste, true, ste.value());
List<Diag> other = errorsOnly(diagnostics);
if (other.size() > 0) {
printDiagnostics(source, other, true);
}
// display update information
new DisplayEvent(ste, true, ste.value(), other).displayDeclarationAndValue();
}
}
return false;
}
@SuppressWarnings("fallthrough")
private void displayDeclarationAndValue(SnippetEvent ste, boolean update, String value) {
Snippet key = ste.snippet();
FormatAction action;
Status status = ste.status();
switch (status) {
case VALID:
case RECOVERABLE_DEFINED:
case RECOVERABLE_NOT_DEFINED:
if (ste.previousStatus().isActive) {
action = ste.isSignatureChange()
? FormatAction.REPLACED
: FormatAction.MODIFIED;
} else {
action = FormatAction.ADDED;
}
break;
case OVERWRITTEN:
action = FormatAction.OVERWROTE;
break;
case DROPPED:
action = FormatAction.DROPPED;
break;
case REJECTED:
action = FormatAction.REJECTED;
break;
case NONEXISTENT:
default:
// Should not occur
error("Unexpected status: " + ste.previousStatus().toString() + "=>" + status.toString());
return;
}
FormatResolve resolution;
String unresolved;
if (key instanceof DeclarationSnippet && (status == Status.RECOVERABLE_DEFINED || status == Status.RECOVERABLE_NOT_DEFINED)) {
resolution = (status == Status.RECOVERABLE_NOT_DEFINED)
? FormatResolve.NOTDEFINED
: FormatResolve.DEFINED;
unresolved = unresolved((DeclarationSnippet) key);
} else {
resolution = FormatResolve.OK;
unresolved = "";
}
switch (key.subKind()) {
case CLASS_SUBKIND:
custom(FormatCase.CLASS, update, action, resolution,
((TypeDeclSnippet) key).name(), null, unresolved, null);
break;
case INTERFACE_SUBKIND:
custom(FormatCase.INTERFACE, update, action, resolution,
((TypeDeclSnippet) key).name(), null, unresolved, null);
break;
case ENUM_SUBKIND:
custom(FormatCase.ENUM, update, action, resolution,
((TypeDeclSnippet) key).name(), null, unresolved, null);
break;
case ANNOTATION_TYPE_SUBKIND:
custom(FormatCase.ANNOTATION, update, action, resolution,
((TypeDeclSnippet) key).name(), null, unresolved, null);
break;
case METHOD_SUBKIND:
custom(FormatCase.METHOD, update, action, resolution,
((MethodSnippet) key).name(), ((MethodSnippet) key).parameterTypes(), unresolved, null);
break;
case VAR_DECLARATION_SUBKIND:
case VAR_DECLARATION_WITH_INITIALIZER_SUBKIND: {
VarSnippet vk = (VarSnippet) key;
if (status == Status.RECOVERABLE_NOT_DEFINED) {
custom(FormatCase.VARDECLRECOVERABLE, update, action, resolution,
vk.name(), null, unresolved, null);
} else if (update && ste.isSignatureChange()) {
custom(FormatCase.VARRESET, update, action, resolution,
vk.name(), null, unresolved, value);
} else if (key.subKind() == SubKind.VAR_DECLARATION_WITH_INITIALIZER_SUBKIND) {
custom(FormatCase.VARINIT, update, action, resolution,
vk.name(), vk.typeName(), unresolved, value);
} else {
custom(FormatCase.VARDECL, update, action, resolution,
vk.name(), vk.typeName(), unresolved, value);
}
break;
}
case TEMP_VAR_EXPRESSION_SUBKIND: {
VarSnippet vk = (VarSnippet) key;
custom(FormatCase.EXPRESSION, update, action, resolution,
vk.name(), vk.typeName(), null, value);
break;
}
case OTHER_EXPRESSION_SUBKIND:
error("Unexpected expression form -- value is: %s", (value));
break;
case VAR_VALUE_SUBKIND: {
ExpressionSnippet ek = (ExpressionSnippet) key;
custom(FormatCase.VARVALUE, update, action, resolution,
ek.name(), ek.typeName(), null, value);
break;
}
case ASSIGNMENT_SUBKIND: {
ExpressionSnippet ek = (ExpressionSnippet) key;
custom(FormatCase.ASSIGNMENT, update, action, resolution,
ek.name(), ek.typeName(), null, value);
break;
}
case SINGLE_TYPE_IMPORT_SUBKIND:
case TYPE_IMPORT_ON_DEMAND_SUBKIND:
case SINGLE_STATIC_IMPORT_SUBKIND:
case STATIC_IMPORT_ON_DEMAND_SUBKIND:
custom(FormatCase.IMPORT, update, action, resolution,
((ImportSnippet) key).name(), null, null, null);
break;
case STATEMENT_SUBKIND:
custom(FormatCase.STATEMENT, update, action, resolution,
null, null, null, null);
break;
}
}
//where
void printStackTrace(StackTraceElement[] stes) {
for (StackTraceElement ste : stes) {
@ -2141,31 +1986,11 @@ public class JShellTool {
}
}
//where
void printUnresolved(UnresolvedReferenceException ex) {
void printUnresolvedException(UnresolvedReferenceException ex) {
DeclarationSnippet corralled = ex.getSnippet();
List<Diag> otherErrors = errorsOnly(state.diagnostics(corralled));
StringBuilder sb = new StringBuilder();
if (otherErrors.size() > 0) {
if (state.unresolvedDependencies(corralled).size() > 0) {
sb.append(" and");
}
if (otherErrors.size() == 1) {
sb.append(" this error is addressed --");
} else {
sb.append(" these errors are addressed --");
}
} else {
sb.append(".");
}
String format = corralled.kind() == METHOD
? "Attempted to call %s which cannot be invoked until%s"
: "Attempted to use %s which cannot be accessed until%s";
hard(format, corralled.name(),
unresolved(corralled), sb.toString());
if (otherErrors.size() > 0) {
printDiagnostics(corralled.source(), otherErrors, true);
}
new DisplayEvent(corralled, state.status(corralled), FormatAction.USED, true, null, otherErrors)
.displayDeclarationAndValue();
}
//where
void printEvalException(EvalException ex) {
@ -2176,26 +2001,180 @@ public class JShellTool {
}
printStackTrace(ex.getStackTrace());
}
//where
String unresolved(DeclarationSnippet key) {
List<String> unr = state.unresolvedDependencies(key);
StringBuilder sb = new StringBuilder();
int fromLast = unr.size();
if (fromLast > 0) {
sb.append(" ");
private FormatAction toAction(Status status, Status previousStatus, boolean isSignatureChange) {
FormatAction act;
switch (status) {
case VALID:
case RECOVERABLE_DEFINED:
case RECOVERABLE_NOT_DEFINED:
if (previousStatus.isActive) {
act = isSignatureChange
? FormatAction.REPLACED
: FormatAction.MODIFIED;
} else {
act = FormatAction.ADDED;
}
break;
case OVERWRITTEN:
act = FormatAction.OVERWROTE;
break;
case DROPPED:
act = FormatAction.DROPPED;
break;
case REJECTED:
case NONEXISTENT:
default:
// Should not occur
error("Unexpected status: " + previousStatus.toString() + "=>" + status.toString());
act = FormatAction.DROPPED;
}
for (String u : unr) {
--fromLast;
sb.append(u);
if (fromLast == 0) {
// No suffix
} else if (fromLast == 1) {
sb.append(", and ");
return act;
}
class DisplayEvent {
private final Snippet sn;
private final FormatAction action;
private final boolean update;
private final String value;
private final List<String> errorLines;
private final FormatResolve resolution;
private final String unresolved;
private final FormatUnresolved unrcnt;
private final FormatErrors errcnt;
DisplayEvent(SnippetEvent ste, boolean update, String value, List<Diag> errors) {
this(ste.snippet(), ste.status(), toAction(ste.status(), ste.previousStatus(), ste.isSignatureChange()), update, value, errors);
}
DisplayEvent(Snippet sn, Status status, FormatAction action, boolean update, String value, List<Diag> errors) {
this.sn = sn;
this.action = action;
this.update = update;
this.value = value;
this.errorLines = new ArrayList<>();
for (Diag d : errors) {
displayDiagnostics(sn.source(), d, errorLines);
}
int unresolvedCount;
if (sn instanceof DeclarationSnippet && (status == Status.RECOVERABLE_DEFINED || status == Status.RECOVERABLE_NOT_DEFINED)) {
resolution = (status == Status.RECOVERABLE_NOT_DEFINED)
? FormatResolve.NOTDEFINED
: FormatResolve.DEFINED;
unresolved = unresolved((DeclarationSnippet) sn);
unresolvedCount = state.unresolvedDependencies((DeclarationSnippet) sn).size();
} else {
sb.append(", ");
resolution = FormatResolve.OK;
unresolved = "";
unresolvedCount = 0;
}
unrcnt = unresolvedCount == 0
? FormatUnresolved.UNRESOLVED0
: unresolvedCount == 1
? FormatUnresolved.UNRESOLVED1
: FormatUnresolved.UNRESOLVED2;
errcnt = errors.isEmpty()
? FormatErrors.ERROR0
: errors.size() == 1
? FormatErrors.ERROR1
: FormatErrors.ERROR2;
}
private String unresolved(DeclarationSnippet key) {
List<String> unr = state.unresolvedDependencies(key);
StringBuilder sb = new StringBuilder();
int fromLast = unr.size();
if (fromLast > 0) {
sb.append(" ");
}
for (String u : unr) {
--fromLast;
sb.append(u);
switch (fromLast) {
// No suffix
case 0:
break;
case 1:
sb.append(", and ");
break;
default:
sb.append(", ");
break;
}
}
return sb.toString();
}
private void custom(FormatCase fcase, String name) {
custom(fcase, name, null);
}
private void custom(FormatCase fcase, String name, String type) {
String display = feedback.format(fcase, action, (update ? FormatWhen.UPDATE : FormatWhen.PRIMARY),
resolution, unrcnt, errcnt,
name, type, value, unresolved, errorLines);
if (interactive()) {
cmdout.print(display);
}
}
@SuppressWarnings("fallthrough")
private void displayDeclarationAndValue() {
switch (sn.subKind()) {
case CLASS_SUBKIND:
custom(FormatCase.CLASS, ((TypeDeclSnippet) sn).name());
break;
case INTERFACE_SUBKIND:
custom(FormatCase.INTERFACE, ((TypeDeclSnippet) sn).name());
break;
case ENUM_SUBKIND:
custom(FormatCase.ENUM, ((TypeDeclSnippet) sn).name());
break;
case ANNOTATION_TYPE_SUBKIND:
custom(FormatCase.ANNOTATION, ((TypeDeclSnippet) sn).name());
break;
case METHOD_SUBKIND:
custom(FormatCase.METHOD, ((MethodSnippet) sn).name(), ((MethodSnippet) sn).parameterTypes());
break;
case VAR_DECLARATION_SUBKIND: {
VarSnippet vk = (VarSnippet) sn;
custom(FormatCase.VARDECL, vk.name(), vk.typeName());
break;
}
case VAR_DECLARATION_WITH_INITIALIZER_SUBKIND: {
VarSnippet vk = (VarSnippet) sn;
custom(FormatCase.VARINIT, vk.name(), vk.typeName());
break;
}
case TEMP_VAR_EXPRESSION_SUBKIND: {
VarSnippet vk = (VarSnippet) sn;
custom(FormatCase.EXPRESSION, vk.name(), vk.typeName());
break;
}
case OTHER_EXPRESSION_SUBKIND:
error("Unexpected expression form -- value is: %s", (value));
break;
case VAR_VALUE_SUBKIND: {
ExpressionSnippet ek = (ExpressionSnippet) sn;
custom(FormatCase.VARVALUE, ek.name(), ek.typeName());
break;
}
case ASSIGNMENT_SUBKIND: {
ExpressionSnippet ek = (ExpressionSnippet) sn;
custom(FormatCase.ASSIGNMENT, ek.name(), ek.typeName());
break;
}
case SINGLE_TYPE_IMPORT_SUBKIND:
case TYPE_IMPORT_ON_DEMAND_SUBKIND:
case SINGLE_STATIC_IMPORT_SUBKIND:
case STATIC_IMPORT_ON_DEMAND_SUBKIND:
custom(FormatCase.IMPORT, ((ImportSnippet) sn).name());
break;
case STATEMENT_SUBKIND:
custom(FormatCase.STATEMENT, null);
break;
}
}
return sb.toString();
}
/** The current version number as a string.
@ -2210,13 +2189,10 @@ public class JShellTool {
return version("full"); // mm.mm.oo[-milestone]-build
}
private static final String versionRBName = "jdk.internal.jshell.tool.resources.version";
private static ResourceBundle versionRB;
private static String version(String key) {
if (versionRB == null) {
try {
versionRB = ResourceBundle.getBundle(versionRBName);
versionRB = ResourceBundle.getBundle(VERSION_RB_NAME);
} catch (MissingResourceException e) {
return "(version info not available)";
}

View File

@ -0,0 +1,179 @@
#
# Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 2 only, as
# published by the Free Software Foundation. Oracle designates this
# particular file as subject to the "Classpath" exception as provided
# by Oracle in the LICENSE file that accompanied this code.
#
# This code is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# version 2 for more details (a copy is included in the LICENSE file that
# accompanied this code).
#
# You should have received a copy of the GNU General Public License version
# 2 along with this work; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
#
# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
# or visit www.oracle.com if you need additional information or have any
# questions.
#
help.set.format = \
Set the format for reporting a snippet event.\n\
\n\
/set format <mode> <field> "<format>" <selector>...\n\
\n\
Where <mode> is the name of a previously defined feedback mode -- see '/help /set newmode'.\n\
Where <field> is the name of context-specific format to define.\n\
Where <format> is a quoted string which will be the value of the field if one of\n\
the selectors matches (or there are no selectors). When the format is used,\n\
field names enclosed in braces are replaced with the value of the field at that\n\
time. These fields may have been previously defined with this command or may be\n\
one of these predefined fields specific to the context:\n\t\
{name} == The name, e.g.: the variable name, ...\n\t\
{type} == The type name. The type of a variable or expression, the\n\t\t\t\
parameter types of a method\n\t\
{value} == The result value of an expression or variable initialization\n\t\
{unresolved} == The list of unresolved references\n\t\
{errors} == The list of recoverable errors (during the processing of the\n\t\t\t\
"display" field only)\n\t\
{err} == An unformatted error line (during the processing of the\n\t\t\t\
"errorline" field only)\n\
The following fields are accessed by the tool to determine the displayed feedback:\n\t\
{display} == The displayed message for a snippet event\n\t\
{errorline} == The format of one error line within the "errors" field\n\t\
{pre} == The feedback prefix (begins command feedback)\n\t\
{post} == The feedback postfix (ends command feedback)\n\t\
{errorpre} == The error prefix (begins error feedback)\n\t\
{errorpost} == The error postfix (ends error feedback)\n\
These fields have default settings (which may be overwritten).\n\
Where <selector> is the context in which the format is applied.\n\
The structure of selector is a hyphen separated list of selector kind lists.\n\
A selector kind list is a comma separated list of values of one selector kind.\n\
A selector matches if each selector kind list matches; A selector kind list\n\
matches if one of the values matches.\n
help.set.format.case = The case selector kind describes the kind of snippet. The values are:\n
help.set.format.action = The action selector kind describes what happened to the snippet. The values are:\n
help.set.format.when = The when-did-it-occur selector kind describes if this is a direct or indirect action. The values are:\n
help.set.format.resolve = The resolution-state selector kind describes the state of resolution/definition of the snippet. The values are:\n
help.set.format.unresolved = The unresolved-count selector kind describes the number of unresolved references. The values are:\n
help.set.format.errors = The errors-count selector kind describes the number of errors. The values are:\n
help.set.format.end = \n\
Examples:\n\t\
/set format myformat action 'Created' added-primary\n\t\
/set format myformat action 'Update replaced' replaced-update\n\t\
/set format myformat display '{pre}{action} class {name}{post}' class-ok\n\t\
/set format myformat display '{pre}{action} variable {name}, reset to null{post}' replaced-vardecl,varinit-ok-update\n\n\
Note that subsequent selectors for a field may overwrite some or all of previous used selectors -- last one wins\n
help.set.feedback = \
Set the feedback mode describing displayed feedback for entered snippets and commands.\n\
\n\
/set feedback <mode>\n\
\n\
Where <mode> is the name of a previously defined feedback mode.\n\
You may use just enough letters to make it unique.\n\
User-defined modes can be added, see '/help /set newmode'\n\
Currently defined feedback modes:\n
help.set.newmode = \
Create a user-defined feedback mode, optionally copying from an existing mode.\n\
\n\
/set newmode <new-mode> [command|quiet [<old-mode>]]\n\
\n\
Where <new-mode> is the name of a mode you wish to create.\n\
Where <old-mode> is the name of a previously defined feedback mode.\n\
If <old-mode> is present, its settings are copied to the new mode.\n\
'command' vs 'quiet' determines if informative/verifying command feedback is displayed.\n\
\n\
Once the new mode is created, use '/set format' and '/set prompt' to configure it.\n\
Use '/set feedback' to use the new mode.\n\
help.set.prompt = \
Set the prompts. Both the normal prompt and the continuation-prompt must be set.\n\
\n\
/set prompt <mode> \"<prompt>\" \"<continuation-propmt>\"\n\
\n\
Where <mode> is the name of a previously defined feedback mode.\n\
Where <prompt> and <continuation-propmt> are quoted strings printed as input prompts;\n\
Both may optionally contain '%s' which will be substituted with the next snippet id --\n\
note that what is entered may not be assigned that id, for example it may be an error or command.\n\
The continuation-prompt is used on the second and subsequent lines of a multi-line snippet.\n
startup.feedback = \
/set newmode normal command \n\
/set prompt normal '\\n-> ' '>> ' \n\
/set format normal pre '| ' \n\
/set format normal post '%n' \n\
/set format normal errorpre '| ' \n\
/set format normal errorpost '%n' \n\
\n\
/set format normal errorline '{post}{pre} {err}' \n\
\n\
/set format normal action 'Added' added-primary \n\
/set format normal action 'Modified' modified-primary \n\
/set format normal action 'Replaced' replaced-primary \n\
/set format normal action 'Overwrote' overwrote-primary \n\
/set format normal action 'Dropped' dropped-primary \n\
/set format normal action ' Update added' added-update \n\
/set format normal action ' Update modified' modified-update \n\
/set format normal action ' Update replaced' replaced-update \n\
/set format normal action ' Update overwrote' overwrote-update \n\
/set format normal action ' Update dropped' dropped-update \n\
\n\
/set format normal until ', however, it cannot be instanciated or its methods invoked until' defined-class-primary \n\
/set format normal until ', however, its methods cannot be invoked until' defined-interface-primary \n\
/set format normal until ', however, it cannot be used until' defined-enum,annotation-primary \n\
/set format normal until ', however, it cannot be invoked until' defined-method-primary \n\
/set format normal until ', however, it cannot be referenced until' notdefined-primary \n\
/set format normal until ' which cannot be instanciated or its methods invoked until' defined-class-update \n\
/set format normal until ' whose methods cannot be invoked until' defined-interface-update \n\
/set format normal until ' which cannot be invoked until' defined-method-update \n\
/set format normal until ' which cannot be referenced until' notdefined-update \n\
\n\
/set format normal unrerr '{unresolved} is declared' unresolved1-error0 \n\
/set format normal unrerr '{unresolved} are declared' unresolved2-error0 \n\
/set format normal unrerr ' this error is corrected: {errors}' unresolved0-error1 \n\
/set format normal unrerr '{unresolved} is declared and this error is corrected: {errors}' unresolved1-error1 \n\
/set format normal unrerr '{unresolved} are declared and this error is corrected: {errors}' unresolved2-error1 \n\
/set format normal unrerr ' these errors are corrected: {errors}' unresolved0-error2 \n\
/set format normal unrerr '{unresolved} is declared and these errors are corrected: {errors}' unresolved1-error2 \n\
/set format normal unrerr '{unresolved} are declared and these errors are corrected: {errors}' unresolved2-error2 \n\
\n\
/set format normal resolve '{until}{unrerr}' added,modified,replaced,used \n\
\n\
/set format normal typeKind 'class' class \n\
/set format normal typeKind 'interface' interface \n\
/set format normal typeKind 'enum' enum \n\
/set format normal typeKind 'annotation interface' annotation \n\
\n\
/set format normal display '{pre}{action} {typeKind} {name}{resolve}{post}' class,interface,enum,annotation \n\
/set format normal display '{pre}{action} method {name}({type}){resolve}{post}' method \n\
\n\
/set format normal display '{pre}{action} variable {name} of type {type}{resolve}{post}' vardecl \n\
/set format normal display '{pre}{action} variable {name} of type {type} with initial value {value}{resolve}{post}' varinit \n\
/set format normal display '{pre}{action} variable {name}, reset to null{post}' replaced-vardecl,varinit-ok-update \n\
/set format normal display '{pre}{action} variable {name}{resolve}{post}' vardecl,varinit-notdefined \n\
/set format normal display '{pre}{action} variable {name}{post}' overwrote,dropped-vardecl,varinit \n\
\n\
/set format normal display '{pre}Expression value is: {value}{post}{pre} assigned to temporary variable {name} of type {type}{post}' expression \n\
/set format normal display '{pre}Variable {name} of type {type} has value {value}{post}' varvalue \n\
/set format normal display '{pre}Variable {name} has been assigned the value {value}{post}' assignment \n\
\n\
/set format normal display '{pre}Attempted to use {typeKind} {name}{resolve}{post}' used-class,interface,enum,annotation \n\
/set format normal display '{pre}Attempted to call method {name}({type}){resolve}{post}' used-method \n\
\n\
/set feedback normal \n\
\n\
/set newmode off quiet \n\
/set prompt off '-> ' '>> ' \n\
/set format off pre '| ' \n\
/set format off post '%n' \n\
/set format off errorpre '| ' \n\
/set format off errorpost '%n' \n\
/set format off display '' \n

View File

@ -374,8 +374,8 @@ public class ReplToolTesting {
}
}
private void dropKey(boolean after, String cmd, String name, Map<String, ? extends MemberInfo> map) {
assertCommand(after, cmd, "");
private void dropKey(boolean after, String cmd, String name, Map<String, ? extends MemberInfo> map, String output) {
assertCommand(after, cmd, output);
if (after) {
map.remove(name);
for (int i = 0; i < keys.size(); ++i) {
@ -389,20 +389,20 @@ public class ReplToolTesting {
}
}
public void dropVariable(boolean after, String cmd, String name) {
dropKey(after, cmd, name, variables);
public void dropVariable(boolean after, String cmd, String name, String output) {
dropKey(after, cmd, name, variables, output);
}
public void dropMethod(boolean after, String cmd, String name) {
dropKey(after, cmd, name, methods);
public void dropMethod(boolean after, String cmd, String name, String output) {
dropKey(after, cmd, name, methods, output);
}
public void dropClass(boolean after, String cmd, String name) {
dropKey(after, cmd, name, classes);
public void dropClass(boolean after, String cmd, String name, String output) {
dropKey(after, cmd, name, classes, output);
}
public void dropImport(boolean after, String cmd, String name) {
dropKey(after, cmd, name, imports);
public void dropImport(boolean after, String cmd, String name, String output) {
dropKey(after, cmd, name, imports, output);
}
public void assertCommand(boolean after, String cmd, String out) {
@ -436,10 +436,10 @@ public class ReplToolTesting {
}
setCommandInput(cmd + "\n");
} else {
assertOutput(getCommandOutput(), out, "command");
assertOutput(getCommandErrorOutput(), err, "command error");
assertOutput(getUserOutput(), print, "user");
assertOutput(getUserErrorOutput(), usererr, "user error");
assertOutput(getCommandOutput(), out, "command output: " + cmd);
assertOutput(getCommandErrorOutput(), err, "command error: " + cmd);
assertOutput(getUserOutput(), print, "user output: " + cmd);
assertOutput(getUserErrorOutput(), usererr, "user error: " + cmd);
}
}
@ -476,9 +476,9 @@ public class ReplToolTesting {
return (output) -> assertTrue(output.startsWith(prefix), "Output: \'" + output + "' does not start with: " + prefix);
}
public void assertOutput(String got, String expected, String kind) {
public void assertOutput(String got, String expected, String display) {
if (expected != null) {
assertEquals(got, expected, "Kind: " + kind + ".\n");
assertEquals(got, expected, display + ".\n");
}
}

View File

@ -755,13 +755,13 @@ public class ToolBasicTest extends ReplToolTesting {
public void testDrop() {
test(false, new String[]{"-nostartup"},
a -> assertVariable(a, "int", "a"),
a -> dropVariable(a, "/drop 1", "int a = 0"),
a -> dropVariable(a, "/drop 1", "int a = 0", "| Dropped variable a\n"),
a -> assertMethod(a, "int b() { return 0; }", "()I", "b"),
a -> dropMethod(a, "/drop 2", "b ()I"),
a -> dropMethod(a, "/drop 2", "b ()I", "| Dropped method b()\n"),
a -> assertClass(a, "class A {}", "class", "A"),
a -> dropClass(a, "/drop 3", "class A"),
a -> dropClass(a, "/drop 3", "class A", "| Dropped class A\n"),
a -> assertImport(a, "import java.util.stream.*;", "", "java.util.stream.*"),
a -> dropImport(a, "/drop 4", "import java.util.stream.*"),
a -> dropImport(a, "/drop 4", "import java.util.stream.*", ""),
a -> assertCommandCheckOutput(a, "/vars", assertVariables()),
a -> assertCommandCheckOutput(a, "/methods", assertMethods()),
a -> assertCommandCheckOutput(a, "/classes", assertClasses()),
@ -769,11 +769,11 @@ public class ToolBasicTest extends ReplToolTesting {
);
test(false, new String[]{"-nostartup"},
a -> assertVariable(a, "int", "a"),
a -> dropVariable(a, "/drop a", "int a = 0"),
a -> dropVariable(a, "/drop a", "int a = 0", "| Dropped variable a\n"),
a -> assertMethod(a, "int b() { return 0; }", "()I", "b"),
a -> dropMethod(a, "/drop b", "b ()I"),
a -> dropMethod(a, "/drop b", "b ()I", "| Dropped method b()\n"),
a -> assertClass(a, "class A {}", "class", "A"),
a -> dropClass(a, "/drop A", "class A"),
a -> dropClass(a, "/drop A", "class A", "| Dropped class A\n"),
a -> assertCommandCheckOutput(a, "/vars", assertVariables()),
a -> assertCommandCheckOutput(a, "/methods", assertMethods()),
a -> assertCommandCheckOutput(a, "/classes", assertClasses()),

View File

@ -23,7 +23,7 @@
/*
* @test
* @bug 8148316 8148317
* @bug 8148316 8148317 8151755 8152246
* @summary Tests for output customization
* @library /tools/lib
* @modules jdk.compiler/com.sun.tools.javac.api
@ -33,6 +33,8 @@
* @build KullaTesting TestingInputStream ToolBox Compiler
* @run testng ToolFormatTest
*/
import java.util.ArrayList;
import java.util.List;
import org.testng.annotations.Test;
@Test
@ -42,28 +44,34 @@ public class ToolFormatTest extends ReplToolTesting {
try {
test(
(a) -> assertCommandOutputStartsWith(a, "/set newmode test command", "| Created new feedback mode: test"),
(a) -> assertCommand(a, "/set field test pre '$ '", ""),
(a) -> assertCommand(a, "/set field test post ''", ""),
(a) -> assertCommand(a, "/set field test action 'ADD ' added-primary", ""),
(a) -> assertCommand(a, "/set field test action 'MOD ' modified-primary", ""),
(a) -> assertCommand(a, "/set field test action 'REP ' replaced-primary", ""),
(a) -> assertCommand(a, "/set field test action 'UP-ADD ' added-update", ""),
(a) -> assertCommand(a, "/set field test action 'UP-MOD ' modified-update", ""),
(a) -> assertCommand(a, "/set field test action 'UP-REP ' replaced-update", ""),
(a) -> assertCommand(a, "/set field test resolve 'OK' ok-*", ""),
(a) -> assertCommand(a, "/set field test resolve 'DEF' defined-*", ""),
(a) -> assertCommand(a, "/set field test resolve 'NODEF' notdefined-*", ""),
(a) -> assertCommand(a, "/set field test name ':%s ' ", ""),
(a) -> assertCommand(a, "/set field test type '[%s]' ", ""),
(a) -> assertCommand(a, "/set field test result '=%s ' ", ""),
(a) -> assertCommand(a, "/set format test '{pre}{action}{type}{name}{result}{resolve}' *-*-*", ""),
(a) -> assertCommand(a, "/set format test '{pre}HI this is enum' enum", ""),
(a) -> assertCommand(a, "/set format test pre '$ '", ""),
(a) -> assertCommand(a, "/set format test post ''", ""),
(a) -> assertCommand(a, "/set format test act 'ADD' added", ""),
(a) -> assertCommand(a, "/set format test act 'MOD' modified", ""),
(a) -> assertCommand(a, "/set format test act 'REP' replaced", ""),
(a) -> assertCommand(a, "/set format test act 'OVR' overwrote", ""),
(a) -> assertCommand(a, "/set format test act 'USE' used", ""),
(a) -> assertCommand(a, "/set format test act 'DRP' dropped", ""),
(a) -> assertCommand(a, "/set format test up 'UP-' update", ""),
(a) -> assertCommand(a, "/set format test action '{up}{act} '", ""),
(a) -> assertCommand(a, "/set format test resolve 'OK' ok", ""),
(a) -> assertCommand(a, "/set format test resolve 'DEF' defined", ""),
(a) -> assertCommand(a, "/set format test resolve 'NODEF' notdefined", ""),
(a) -> assertCommand(a, "/set format test fname ':{name} ' ", ""),
(a) -> assertCommand(a, "/set format test ftype '[{type}]' method,expression", ""),
(a) -> assertCommand(a, "/set format test result '={value} ' expression", ""),
(a) -> assertCommand(a, "/set format test display '{pre}{action}{ftype}{fname}{result}{resolve}'", ""),
(a) -> assertCommand(a, "/set format test display '{pre}HI this is enum' enum", ""),
(a) -> assertCommand(a, "/set feedback test", "$ Feedback mode: test"),
(a) -> assertCommand(a, "class D {}", "$ ADD :D OK"),
(a) -> assertCommand(a, "void m() {}", "$ ADD []:m OK"),
(a) -> assertCommand(a, "interface EX extends EEX {}", "$ ADD :EX NODEF"),
(a) -> assertCommand(a, "56", "$ ADD [int]:$4 =56 OK"),
(a) -> assertCommand(a, "class D { int hh; }", "$ REP :D OK$ OVERWROTE-UPDATE:D OK"),
(a) -> assertCommand(a, "class D { int hh; }", "$ REP :D OK$ UP-OVR :D OK"),
(a) -> assertCommand(a, "enum E {A,B}", "$ HI this is enum"),
(a) -> assertCommand(a, "int z() { return f(); }", "$ ADD []:z DEF"),
(a) -> assertCommand(a, "z()", "$ UP-USE []:z DEF"),
(a) -> assertCommand(a, "/drop z", "$ DRP []:z OK"),
(a) -> assertCommandOutputStartsWith(a, "/set feedback normal", "| Feedback mode: normal")
);
} finally {
@ -72,7 +80,82 @@ public class ToolFormatTest extends ReplToolTesting {
}
}
public void testNewModeQuiet() {
public void testSetFormatSelector() {
List<ReplTest> tests = new ArrayList<>();
tests.add((a) -> assertCommandOutputStartsWith(a, "/set newmode ate quiet",
"| Created new feedback mode: ate"));
tests.add((a) -> assertCommand(a, "/set feedback ate", ""));
StringBuilder sb = new StringBuilder();
class KindList {
final String[] values;
final int matchIndex;
int current;
boolean match;
KindList(String[] values, int matchIndex) {
this.values = values;
this.matchIndex = matchIndex;
this.current = 1 << values.length;
}
boolean next() {
if (current <= 0) {
return false;
}
--current;
return true;
}
boolean append(boolean ahead) {
boolean any = false;
match = false;
for (int i = values.length - 1; i >= 0 ; --i) {
if ((current & (1 << i)) != 0) {
match |= i == matchIndex;
if (any) {
sb.append(",");
} else {
if (ahead) {
sb.append("-");
}
}
sb.append(values[i]);
any = true;
}
}
match |= !any;
return ahead || any;
}
}
KindList klcase = new KindList(new String[] {"class", "method", "expression", "vardecl"}, 2);
while (klcase.next()) {
KindList klact = new KindList(new String[] {"added", "modified", "replaced"}, 0);
while (klact.next()) {
KindList klwhen = new KindList(new String[] {"update", "primary"}, 1);
while (klwhen.next()) {
sb.setLength(0);
klwhen.append(
klact.append(
klcase.append(false)));
boolean match = klcase.match && klact.match && klwhen.match;
String select = sb.toString();
String yes = "+++" + select + "+++";
String no = "---" + select + "---";
String expect = match? yes : no;
tests.add((a) -> assertCommand(a, "/set format ate display '" + no + "'", ""));
tests.add((a) -> assertCommand(a, "/set format ate display '" + yes + "' " + select, ""));
tests.add((a) -> assertCommand(a, "\"" + select + "\"", expect));
}
}
}
tests.add((a) -> assertCommandOutputStartsWith(a, "/set feedback normal", "| Feedback mode: normal"));
try {
test(tests.toArray(new ReplTest[tests.size()]));
} finally {
assertCommandCheckOutput(false, "/set feedback normal", s -> {
});
}
}
public void testSetNewModeQuiet() {
try {
test(
(a) -> assertCommandOutputStartsWith(a, "/set newmode nmq quiet normal", "| Created new feedback mode: nmq"),
@ -82,7 +165,8 @@ public class ToolFormatTest extends ReplToolTesting {
(a) -> assertCommand(a, "/set newmode nmc command normal", ""),
(a) -> assertCommandOutputStartsWith(a, "/set feedback nmc", "| Feedback mode: nmc"),
(a) -> assertCommandOutputStartsWith(a, "/set newmode nm", "| Created new feedback mode: nm"),
(a) -> assertCommandOutputStartsWith(a, "/set feedback nm", "| Feedback mode: nm")
(a) -> assertCommandOutputStartsWith(a, "/set feedback nm", "| Feedback mode: nm"),
(a) -> assertCommandOutputStartsWith(a, "/set feedback normal", "| Feedback mode: normal")
);
} finally {
assertCommandCheckOutput(false, "/set feedback normal", s -> {
@ -93,38 +177,75 @@ public class ToolFormatTest extends ReplToolTesting {
public void testSetError() {
try {
test(
(a) -> assertCommandOutputStartsWith(a, "/set newmode te command normal", "| Created new feedback mode: te"),
(a) -> assertCommand(a, "/set field te errorpre 'ERROR: '", ""),
(a) -> assertCommandOutputStartsWith(a, "/set feedback te", ""),
(a) -> assertCommandCheckOutput(a, "/set ", assertStartsWith("ERROR: The /set command requires arguments")),
(a) -> assertCommandCheckOutput(a, "/set xyz", assertStartsWith("ERROR: Not a valid argument to /set")),
(a) -> assertCommandCheckOutput(a, "/set f", assertStartsWith("ERROR: Ambiguous argument to /set")),
(a) -> assertCommandCheckOutput(a, "/set feedback", assertStartsWith("ERROR: Expected a feedback mode")),
(a) -> assertCommandCheckOutput(a, "/set feedback xyz", assertStartsWith("ERROR: Does not match any current feedback mode")),
(a) -> assertCommandCheckOutput(a, "/set format", assertStartsWith("ERROR: Expected a feedback mode")),
(a) -> assertCommandCheckOutput(a, "/set format xyz", assertStartsWith("ERROR: Does not match any current feedback mode")),
(a) -> assertCommandCheckOutput(a, "/set format te", assertStartsWith("ERROR: Expected format missing")),
(a) -> assertCommandCheckOutput(a, "/set format te aaa", assertStartsWith("ERROR: Format 'aaa' must be quoted")),
(a) -> assertCommandCheckOutput(a, "/set format te 'aaa'", assertStartsWith("ERROR: At least one selector required")),
(a) -> assertCommandCheckOutput(a, "/set format te 'aaa' frog", assertStartsWith("ERROR: Not a valid case")),
(a) -> assertCommandCheckOutput(a, "/set format te 'aaa' import-frog", assertStartsWith("ERROR: Not a valid action")),
(a) -> assertCommandCheckOutput(a, "/set newmode", assertStartsWith("ERROR: Expected new feedback mode")),
(a) -> assertCommandCheckOutput(a, "/set newmode te", assertStartsWith("ERROR: Expected a new feedback mode name")),
(a) -> assertCommandCheckOutput(a, "/set newmode x xyz", assertStartsWith("ERROR: Specify either 'command' or 'quiet'")),
(a) -> assertCommandCheckOutput(a, "/set newmode x quiet y", assertStartsWith("ERROR: Does not match any current feedback mode")),
(a) -> assertCommandCheckOutput(a, "/set prompt", assertStartsWith("ERROR: Expected a feedback mode")),
(a) -> assertCommandCheckOutput(a, "/set prompt te", assertStartsWith("ERROR: Expected format missing")),
(a) -> assertCommandCheckOutput(a, "/set prompt te aaa xyz", assertStartsWith("ERROR: Format 'aaa' must be quoted")),
(a) -> assertCommandCheckOutput(a, "/set prompt te 'aaa' xyz", assertStartsWith("ERROR: Format 'xyz' must be quoted")),
(a) -> assertCommandCheckOutput(a, "/set prompt", assertStartsWith("ERROR: Expected a feedback mode")),
(a) -> assertCommandCheckOutput(a, "/set prompt te", assertStartsWith("ERROR: Expected format missing")),
(a) -> assertCommandCheckOutput(a, "/set prompt te aaa", assertStartsWith("ERROR: Format 'aaa' must be quoted")),
(a) -> assertCommandCheckOutput(a, "/set prompt te 'aaa'", assertStartsWith("ERROR: Expected format missing")),
(a) -> assertCommandCheckOutput(a, "/set field", assertStartsWith("ERROR: Expected a feedback mode")),
(a) -> assertCommandCheckOutput(a, "/set field xyz", assertStartsWith("ERROR: Does not match any current feedback mode: xyz")),
(a) -> assertCommandCheckOutput(a, "/set field te xyz", assertStartsWith("ERROR: Not a valid field: xyz, must be one of: when")),
(a) -> assertCommandCheckOutput(a, "/set field te action", assertStartsWith("ERROR: Expected format missing")),
(a) -> assertCommandCheckOutput(a, "/set field te action 'act'", assertStartsWith("ERROR: At least one selector required"))
(a) -> assertCommandOutputStartsWith(a, "/set newmode tee command foo",
"| Does not match any current feedback mode: foo"),
(a) -> assertCommandOutputStartsWith(a, "/set newmode tee flurb",
"| Specify either 'command' or 'quiet'"),
(a) -> assertCommandOutputStartsWith(a, "/set newmode te2",
"| Created new feedback mode: te2"),
(a) -> assertCommandOutputStartsWith(a, "/set newmode te2 command",
"| Expected a new feedback mode name. te2 is a known feedback mode"),
(a) -> assertCommandOutputStartsWith(a, "/set newmode te command normal",
"| Created new feedback mode: te"),
(a) -> assertCommand(a, "/set format te errorpre 'ERROR: '", ""),
(a) -> assertCommandOutputStartsWith(a, "/set feedback te",
""),
(a) -> assertCommandOutputStartsWith(a, "/set ",
"ERROR: The /set command requires arguments"),
(a) -> assertCommandOutputStartsWith(a, "/set xyz",
"ERROR: Not a valid argument to /set"),
(a) -> assertCommandOutputStartsWith(a, "/set f",
"ERROR: Ambiguous argument to /set"),
(a) -> assertCommandOutputStartsWith(a, "/set feedback",
"ERROR: Expected a feedback mode"),
(a) -> assertCommandOutputStartsWith(a, "/set feedback xyz",
"ERROR: Does not match any current feedback mode"),
(a) -> assertCommandOutputStartsWith(a, "/set format",
"ERROR: Expected a feedback mode"),
(a) -> assertCommandOutputStartsWith(a, "/set format xyz",
"ERROR: Does not match any current feedback mode"),
(a) -> assertCommandOutputStartsWith(a, "/set format t",
"ERROR: Matches more then one current feedback mode: t"),
(a) -> assertCommandOutputStartsWith(a, "/set format te",
"ERROR: Expected field name missing"),
(a) -> assertCommandOutputStartsWith(a, "/set format te fld",
"ERROR: Expected format missing"),
(a) -> assertCommandOutputStartsWith(a, "/set format te fld aaa",
"ERROR: Format 'aaa' must be quoted"),
(a) -> assertCommandOutputStartsWith(a, "/set format te fld 'aaa' frog",
"ERROR: Not a valid selector"),
(a) -> assertCommandOutputStartsWith(a, "/set format te fld 'aaa' import-frog",
"ERROR: Not a valid selector"),
(a) -> assertCommandOutputStartsWith(a, "/set format te fld 'aaa' import-import",
"ERROR: Selector kind in multiple sections of"),
(a) -> assertCommandOutputStartsWith(a, "/set format te fld 'aaa' import,added",
"ERROR: Different selector kinds in same sections of"),
(a) -> assertCommandOutputStartsWith(a, "/set newmode",
"ERROR: Expected new feedback mode"),
(a) -> assertCommandOutputStartsWith(a, "/set newmode te",
"ERROR: Expected a new feedback mode name"),
(a) -> assertCommandOutputStartsWith(a, "/set newmode x xyz",
"ERROR: Specify either 'command' or 'quiet'"),
(a) -> assertCommandOutputStartsWith(a, "/set newmode x quiet y",
"ERROR: Does not match any current feedback mode"),
(a) -> assertCommandOutputStartsWith(a, "/set prompt",
"ERROR: Expected a feedback mode"),
(a) -> assertCommandOutputStartsWith(a, "/set prompt te",
"ERROR: Expected format missing"),
(a) -> assertCommandOutputStartsWith(a, "/set prompt te aaa xyz",
"ERROR: Format 'aaa' must be quoted"),
(a) -> assertCommandOutputStartsWith(a, "/set prompt te 'aaa' xyz",
"ERROR: Format 'xyz' must be quoted"),
(a) -> assertCommandOutputStartsWith(a, "/set prompt",
"ERROR: Expected a feedback mode"),
(a) -> assertCommandOutputStartsWith(a, "/set prompt te",
"ERROR: Expected format missing"),
(a) -> assertCommandOutputStartsWith(a, "/set prompt te aaa",
"ERROR: Format 'aaa' must be quoted"),
(a) -> assertCommandOutputStartsWith(a, "/set prompt te 'aaa'",
"ERROR: Expected format missing"),
(a) -> assertCommandOutputStartsWith(a, "/set feedback normal",
"| Feedback mode: normal")
);
} finally {
assertCommandCheckOutput(false, "/set feedback normal", s -> {
@ -136,7 +257,7 @@ public class ToolFormatTest extends ReplToolTesting {
try {
test(
(a) -> assertCommandOutputContains(a, "/help /set", "command to launch"),
(a) -> assertCommandOutputContains(a, "/help /set format", "vardecl"),
(a) -> assertCommandOutputContains(a, "/help /set format", "display"),
(a) -> assertCommandOutputContains(a, "/hel /se for", "vardecl"),
(a) -> assertCommandOutputContains(a, "/help /set editor", "temporary file")
);
@ -150,7 +271,7 @@ public class ToolFormatTest extends ReplToolTesting {
try {
test(
(a) -> assertCommandOutputStartsWith(a, "/set newmode te command normal", "| Created new feedback mode: te"),
(a) -> assertCommand(a, "/set field te errorpre 'ERROR: '", ""),
(a) -> assertCommand(a, "/set format te errorpre 'ERROR: '", ""),
(a) -> assertCommandOutputStartsWith(a, "/set feedback te", "| Feedback mode: te"),
(a) -> assertCommandOutputContains(a, "/help /set xyz", "ERROR: Not a valid argument to /set: xyz"),
(a) -> assertCommandOutputContains(a, "/help /set f", "ERROR: Ambiguous argument to /set: f")

View File

@ -92,11 +92,11 @@ public class ToolReloadTest extends ReplToolTesting {
public void testReloadDrop() {
test(false, new String[]{"-nostartup"},
a -> assertVariable(a, "int", "a"),
a -> dropVariable(a, "/dr 1", "int a = 0"),
a -> dropVariable(a, "/dr 1", "int a = 0", "| Dropped variable a\n"),
a -> assertMethod(a, "int b() { return 0; }", "()I", "b"),
a -> dropMethod(a, "/drop b", "b ()I"),
a -> dropMethod(a, "/drop b", "b ()I", "| Dropped method b()\n"),
a -> assertClass(a, "class A {}", "class", "A"),
a -> dropClass(a, "/dr A", "class A"),
a -> dropClass(a, "/dr A", "class A", "| Dropped class A\n"),
a -> assertCommand(a, "/reload",
"| Restarting and restoring state.\n" +
"-: int a;\n" +
@ -115,11 +115,11 @@ public class ToolReloadTest extends ReplToolTesting {
public void testReloadQuiet() {
test(false, new String[]{"-nostartup"},
a -> assertVariable(a, "int", "a"),
a -> dropVariable(a, "/dr 1", "int a = 0"),
a -> dropVariable(a, "/dr 1", "int a = 0", "| Dropped variable a\n"),
a -> assertMethod(a, "int b() { return 0; }", "()I", "b"),
a -> dropMethod(a, "/drop b", "b ()I"),
a -> dropMethod(a, "/drop b", "b ()I", "| Dropped method b()\n"),
a -> assertClass(a, "class A {}", "class", "A"),
a -> dropClass(a, "/dr A", "class A"),
a -> dropClass(a, "/dr A", "class A", "| Dropped class A\n"),
a -> assertCommand(a, "/reload quiet",
"| Restarting and restoring state.\n"),
a -> assertCommandCheckOutput(a, "/vars", assertVariables()),