8148316: jshell tool: Configurable output format
8148317: jshell tool: unify commands into /set
8149524: JShell: CompletenessAnalysis fails on class Case<E1 extends Enum<E1>, E2 extends Enum<E2>, E3 extends Enum<E3>> {}
Reviewed-by: jlahoda
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/ArgTokenizer.java Tue Mar 08 11:53:35 2016 -0800
@@ -0,0 +1,271 @@
+/*
+ * Copyright (c) 1995, 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.
+ */
+
+package jdk.internal.jshell.tool;
+
+import java.util.Arrays;
+import java.util.stream.Stream;
+
+/**
+ * Parse command arguments, derived from StreamTokenizer by
+ * @author James Gosling
+ */
+class ArgTokenizer {
+
+ private final String str;
+ private final int length;
+ private int next = 0;
+ private char buf[] = new char[20];
+ private int mark;
+
+ private final byte ctype[] = new byte[256];
+ private static final byte CT_ALPHA = 0;
+ private static final byte CT_WHITESPACE = 1;
+ private static final byte CT_QUOTE = 8;
+
+ private String sval;
+ private boolean isQuoted = false;
+
+ ArgTokenizer(String arg) {
+ this.str = arg;
+ this.length = arg.length();
+ quoteChar('"');
+ quoteChar('\'');
+ whitespaceChars(0x09, 0x0D);
+ whitespaceChars(0x1C, 0x20);
+ whitespaceChars(0x85, 0x85);
+ whitespaceChars(0xA0, 0xA0);
+ }
+
+ String next() {
+ nextToken();
+ return sval;
+ }
+
+ String[] next(String... strings) {
+ return next(Arrays.stream(strings));
+ }
+
+ String[] next(Stream<String> stream) {
+ nextToken();
+ if (sval == null) {
+ return null;
+ }
+ String[] matches = stream
+ .filter(s -> s.startsWith(sval))
+ .toArray(size -> new String[size]);
+ return matches;
+ }
+
+ String val() {
+ return sval;
+ }
+
+ boolean isQuoted() {
+ return isQuoted;
+ }
+
+ String whole() {
+ return str;
+ }
+
+ void mark() {
+ mark = next;
+ }
+
+ void rewind() {
+ next = mark;
+ }
+
+ /**
+ * Reads a single character.
+ *
+ * @return The character read, or -1 if the end of the stream has been
+ * reached
+ */
+ private int read() {
+ if (next >= length) {
+ return -1;
+ }
+ return str.charAt(next++);
+ }
+
+ /**
+ * Specifies that all characters <i>c</i> in the range
+ * <code>low <= <i>c</i> <= high</code>
+ * are white space characters. White space characters serve only to
+ * separate tokens in the input stream.
+ *
+ * <p>Any other attribute settings for the characters in the specified
+ * range are cleared.
+ *
+ * @param low the low end of the range.
+ * @param hi the high end of the range.
+ */
+ private void whitespaceChars(int low, int hi) {
+ if (low < 0)
+ low = 0;
+ if (hi >= ctype.length)
+ hi = ctype.length - 1;
+ while (low <= hi)
+ ctype[low++] = CT_WHITESPACE;
+ }
+
+ /**
+ * Specifies that matching pairs of this character delimit string
+ * constants in this tokenizer.
+ * <p>
+ * If a string quote character is encountered, then a string is
+ * recognized, consisting of all characters after (but not including)
+ * the string quote character, up to (but not including) the next
+ * occurrence of that same string quote character, or a line
+ * terminator, or end of file. The usual escape sequences such as
+ * {@code "\u005Cn"} and {@code "\u005Ct"} are recognized and
+ * converted to single characters as the string is parsed.
+ *
+ * <p>Any other attribute settings for the specified character are cleared.
+ *
+ * @param ch the character.
+ */
+ private void quoteChar(int ch) {
+ if (ch >= 0 && ch < ctype.length)
+ ctype[ch] = CT_QUOTE;
+ }
+
+ private int unicode2ctype(int c) {
+ switch (c) {
+ case 0x1680:
+ case 0x180E:
+ case 0x200A:
+ case 0x202F:
+ case 0x205F:
+ case 0x3000:
+ return CT_WHITESPACE;
+ default:
+ return CT_ALPHA;
+ }
+ }
+
+ /**
+ * Parses the next token of this tokenizer.
+ */
+ public void nextToken() {
+ byte ct[] = ctype;
+ int c;
+ int lctype;
+ sval = null;
+ isQuoted = false;
+
+ do {
+ c = read();
+ if (c < 0) {
+ return;
+ }
+ lctype = (c < 256) ? ct[c] : unicode2ctype(c);
+ } while (lctype == CT_WHITESPACE);
+
+ if (lctype == CT_ALPHA) {
+ int i = 0;
+ do {
+ if (i >= buf.length) {
+ buf = Arrays.copyOf(buf, buf.length * 2);
+ }
+ buf[i++] = (char) c;
+ c = read();
+ lctype = c < 0 ? CT_WHITESPACE : (c < 256)? ct[c] : unicode2ctype(c);
+ } while (lctype == CT_ALPHA);
+ if (c >= 0) --next; // push last back
+ sval = String.copyValueOf(buf, 0, i);
+ return;
+ }
+
+ if (lctype == CT_QUOTE) {
+ int quote = c;
+ int i = 0;
+ /* Invariants (because \Octal needs a lookahead):
+ * (i) c contains char value
+ * (ii) d contains the lookahead
+ */
+ int d = read();
+ while (d >= 0 && d != quote) {
+ if (d == '\\') {
+ c = read();
+ int first = c; /* To allow \377, but not \477 */
+ if (c >= '0' && c <= '7') {
+ c = c - '0';
+ int c2 = read();
+ if ('0' <= c2 && c2 <= '7') {
+ c = (c << 3) + (c2 - '0');
+ c2 = read();
+ if ('0' <= c2 && c2 <= '7' && first <= '3') {
+ c = (c << 3) + (c2 - '0');
+ d = read();
+ } else
+ d = c2;
+ } else
+ d = c2;
+ } else {
+ switch (c) {
+ case 'a':
+ c = 0x7;
+ break;
+ case 'b':
+ c = '\b';
+ break;
+ case 'f':
+ c = 0xC;
+ break;
+ case 'n':
+ c = '\n';
+ break;
+ case 'r':
+ c = '\r';
+ break;
+ case 't':
+ c = '\t';
+ break;
+ case 'v':
+ c = 0xB;
+ break;
+ }
+ d = read();
+ }
+ } else {
+ c = d;
+ d = read();
+ }
+ if (i >= buf.length) {
+ buf = Arrays.copyOf(buf, buf.length * 2);
+ }
+ buf[i++] = (char)c;
+ }
+
+ if (d == quote) {
+ isQuoted = true;
+ }
+ sval = String.copyValueOf(buf, 0, i);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/Feedback.java Tue Mar 08 11:53:35 2016 -0800
@@ -0,0 +1,1049 @@
+/*
+ * 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.
+ */
+
+package jdk.internal.jshell.tool;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.EnumMap;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+/**
+ * Feedback customization support
+ *
+ * @author Robert Field
+ */
+class Feedback {
+
+ // Patern for substituted fields within a customized format string
+ private static final Pattern FIELD_PATTERN = Pattern.compile("\\{(.*?)\\}");
+
+ // Current mode
+ private Mode mode = new Mode("", false); // initial value placeholder during start-up
+
+ // Mapping of mode names to mode modes
+ private final Map<String, Mode> modeMap = new HashMap<>();
+
+ public boolean shouldDisplayCommandFluff() {
+ return mode.commandFluff;
+ }
+
+ public String getPre() {
+ return mode.pre;
+ }
+
+ public String getPost() {
+ return mode.post;
+ }
+
+ public String getErrorPre() {
+ return mode.errorPre;
+ }
+
+ public String getErrorPost() {
+ return mode.errorPost;
+ }
+
+ public String getFormat(FormatCase fc, FormatWhen fw, FormatAction fa, FormatResolve fr,
+ boolean hasName, boolean hasType, boolean hasResult) {
+ return mode.getFormat(fc, fw, fa, fr, hasName, hasType, hasResult);
+ }
+
+ public String getPrompt(String nextId) {
+ return mode.getPrompt(nextId);
+ }
+
+ public String getContinuationPrompt(String nextId) {
+ return mode.getContinuationPrompt(nextId);
+ }
+
+ public boolean setFeedback(JShellTool tool, ArgTokenizer at) {
+ return new FormatSetter(tool, at).setFeedback();
+ }
+
+ public boolean setField(JShellTool tool, ArgTokenizer at) {
+ return new FormatSetter(tool, at).setField();
+ }
+
+ public boolean setFormat(JShellTool tool, ArgTokenizer at) {
+ return new FormatSetter(tool, at).setFormat();
+ }
+
+ public boolean setNewMode(JShellTool tool, ArgTokenizer at) {
+ return new FormatSetter(tool, at).setNewMode();
+ }
+
+ public boolean setPrompt(JShellTool tool, ArgTokenizer at) {
+ return new FormatSetter(tool, at).setPrompt();
+ }
+
+ public void printFeedbackHelp(JShellTool tool) {
+ new FormatSetter(tool, null).printFeedbackHelp();
+ }
+
+ public void printFieldHelp(JShellTool tool) {
+ new FormatSetter(tool, null).printFieldHelp();
+ }
+
+ public void printFormatHelp(JShellTool tool) {
+ new FormatSetter(tool, null).printFormatHelp();
+ }
+
+ public void printNewModeHelp(JShellTool tool) {
+ new FormatSetter(tool, null).printNewModeHelp();
+ }
+
+ public void printPromptHelp(JShellTool tool) {
+ new FormatSetter(tool, null).printPromptHelp();
+ }
+
+ /**
+ * Holds all the context of a mode mode
+ */
+ private class Mode {
+
+ // Use name of mode mode
+
+ final String name;
+
+ // Display command verification/information
+ final boolean commandFluff;
+
+ // event cases: class, method
+ final EnumMap<FormatCase, EnumMap<FormatAction, EnumMap<FormatWhen, String>>> cases;
+
+ // action names: add. modified, replaced, ...
+ final EnumMap<FormatAction, EnumMap<FormatWhen, String>> actions;
+
+ // resolution status description format with %s for unresolved
+ final EnumMap<FormatResolve, EnumMap<FormatWhen, String>> resolves;
+
+ // primary snippet vs update
+ final EnumMap<FormatWhen, String> whens;
+
+ // fixed map of how to get format string for a field, given a specific formatting contet
+ final EnumMap<FormatField, Function<Context, String>> fields;
+
+ // format wrappers for name, type, and result
+ String fname = "%s";
+ String ftype = "%s";
+ String fresult = "%s";
+
+ // start and end, also used by hard-coded output
+ String pre = "| ";
+ String post = "\n";
+ String errorPre = "| Error: ";
+ String errorPost = "\n";
+
+ String prompt = "\n-> ";
+ String continuationPrompt = ">> ";
+
+ /**
+ * The context of a specific mode to potentially display.
+ */
+ class Context {
+
+ final FormatCase fc;
+ final FormatAction fa;
+ final FormatResolve fr;
+ final FormatWhen fw;
+ final boolean hasName;
+ final boolean hasType;
+ final boolean hasResult;
+
+ Context(FormatCase fc, FormatWhen fw, FormatAction fa, FormatResolve fr,
+ boolean hasName, boolean hasType, boolean hasResult) {
+ this.fc = fc;
+ this.fa = fa;
+ this.fr = fr;
+ this.fw = fw;
+ this.hasName = hasName;
+ this.hasType = hasType;
+ this.hasResult = hasResult;
+ }
+
+ String when() {
+ return whens.get(fw);
+ }
+
+ String action() {
+ return actions.get(fa).get(fw);
+ }
+
+ String resolve() {
+ return String.format(resolves.get(fr).get(fw), FormatField.RESOLVE.form);
+ }
+
+ String name() {
+ return hasName
+ ? String.format(fname, FormatField.NAME.form)
+ : "";
+ }
+
+ String type() {
+ return hasType
+ ? String.format(ftype, FormatField.TYPE.form)
+ : "";
+ }
+
+ String result() {
+ return hasResult
+ ? String.format(fresult, FormatField.RESULT.form)
+ : "";
+ }
+
+ /**
+ * Lookup format based on case, action, and whether it update.
+ * Replace fields with context specific formats.
+ *
+ * @return format string
+ */
+ String format() {
+ String format = cases.get(fc).get(fa).get(fw);
+ if (format == null) {
+ return "";
+ }
+ Matcher m = FIELD_PATTERN.matcher(format);
+ StringBuffer sb = new StringBuffer(format.length());
+ while (m.find()) {
+ String fieldName = m.group(1).toUpperCase(Locale.US);
+ String sub = null;
+ for (FormatField f : FormatField.values()) {
+ if (f.name().startsWith(fieldName)) {
+ sub = fields.get(f).apply(this);
+ break;
+ }
+ }
+ if (sub != null) {
+ m.appendReplacement(sb, Matcher.quoteReplacement(sub));
+ }
+ }
+ m.appendTail(sb);
+ return sb.toString();
+ }
+ }
+
+ {
+ // set fixed mappings of fields
+ fields = new EnumMap<>(FormatField.class);
+ fields.put(FormatField.WHEN, c -> c.when());
+ fields.put(FormatField.ACTION, c -> c.action());
+ fields.put(FormatField.RESOLVE, c -> c.resolve());
+ fields.put(FormatField.NAME, c -> c.name());
+ fields.put(FormatField.TYPE, c -> c.type());
+ fields.put(FormatField.RESULT, c -> c.result());
+ fields.put(FormatField.PRE, c -> pre);
+ fields.put(FormatField.POST, c -> post);
+ fields.put(FormatField.ERRORPRE, c -> errorPre);
+ fields.put(FormatField.ERRORPOST, c -> errorPost);
+ }
+
+ /**
+ * Set up an empty mode.
+ *
+ * @param name
+ * @param commandFluff True if should display command fluff messages
+ */
+ Mode(String name, boolean commandFluff) {
+ this.name = name;
+ this.commandFluff = commandFluff;
+ cases = new EnumMap<>(FormatCase.class);
+ for (FormatCase fc : FormatCase.values()) {
+ EnumMap<FormatAction, EnumMap<FormatWhen, String>> ac = new EnumMap<>(FormatAction.class);
+ cases.put(fc, ac);
+ for (FormatAction fa : FormatAction.values()) {
+ EnumMap<FormatWhen, String> aw = new EnumMap<>(FormatWhen.class);
+ ac.put(fa, aw);
+ for (FormatWhen fw : FormatWhen.values()) {
+ aw.put(fw, "");
+ }
+ }
+ }
+
+ actions = new EnumMap<>(FormatAction.class);
+ for (FormatAction fa : FormatAction.values()) {
+ EnumMap<FormatWhen, String> afw = new EnumMap<>(FormatWhen.class);
+ actions.put(fa, afw);
+ for (FormatWhen fw : FormatWhen.values()) {
+ afw.put(fw, fa.name() + "-" + fw.name());
+ }
+ }
+
+ resolves = new EnumMap<>(FormatResolve.class);
+ for (FormatResolve fr : FormatResolve.values()) {
+ EnumMap<FormatWhen, String> arw = new EnumMap<>(FormatWhen.class);
+ resolves.put(fr, arw);
+ for (FormatWhen fw : FormatWhen.values()) {
+ arw.put(fw, fr.name() + "-" + fw.name() + ": %s");
+ }
+ }
+
+ whens = new EnumMap<>(FormatWhen.class);
+ for (FormatWhen fw : FormatWhen.values()) {
+ whens.put(fw, fw.name());
+ }
+ }
+
+ /**
+ * Set up a copied mode.
+ *
+ * @param name
+ * @param commandFluff True if should display command fluff messages
+ * @param m Mode to copy
+ */
+ Mode(String name, boolean commandFluff, Mode m) {
+ this.name = name;
+ this.commandFluff = commandFluff;
+ cases = new EnumMap<>(FormatCase.class);
+ for (FormatCase fc : FormatCase.values()) {
+ EnumMap<FormatAction, EnumMap<FormatWhen, String>> ac = new EnumMap<>(FormatAction.class);
+ EnumMap<FormatAction, EnumMap<FormatWhen, String>> mc = m.cases.get(fc);
+ cases.put(fc, ac);
+ for (FormatAction fa : FormatAction.values()) {
+ EnumMap<FormatWhen, String> aw = new EnumMap<>(mc.get(fa));
+ ac.put(fa, aw);
+ }
+ }
+
+ actions = new EnumMap<>(FormatAction.class);
+ for (FormatAction fa : FormatAction.values()) {
+ EnumMap<FormatWhen, String> afw = new EnumMap<>(m.actions.get(fa));
+ actions.put(fa, afw);
+ }
+
+ resolves = new EnumMap<>(FormatResolve.class);
+ for (FormatResolve fr : FormatResolve.values()) {
+ EnumMap<FormatWhen, String> arw = new EnumMap<>(m.resolves.get(fr));
+ resolves.put(fr, arw);
+ }
+
+ whens = new EnumMap<>(m.whens);
+
+ this.fname = m.fname;
+ this.ftype = m.ftype;
+ this.fresult = m.fresult;
+ this.pre = m.pre;
+ this.post = m.post;
+ this.errorPre = m.errorPre;
+ this.errorPost = m.errorPost;
+ this.prompt = m.prompt;
+ this.continuationPrompt = m.continuationPrompt;
+ }
+
+ String getFormat(FormatCase fc, FormatWhen fw, FormatAction fa, FormatResolve fr,
+ boolean hasName, boolean hasType, boolean hasResult) {
+ Context context = new Context(fc, fw, fa, fr,
+ hasName, hasType, hasResult);
+ return context.format();
+ }
+
+ void setCases(String format, Collection<FormatCase> cc, Collection<FormatAction> ca, Collection<FormatWhen> cw) {
+ for (FormatCase fc : cc) {
+ EnumMap<FormatAction, EnumMap<FormatWhen, String>> ma = cases.get(fc);
+ for (FormatAction fa : ca) {
+ EnumMap<FormatWhen, String> mw = ma.get(fa);
+ for (FormatWhen fw : cw) {
+ mw.put(fw, format);
+ }
+ }
+ }
+ }
+
+ void setActions(String format, Collection<FormatAction> ca, Collection<FormatWhen> cw) {
+ for (FormatAction fa : ca) {
+ EnumMap<FormatWhen, String> mw = actions.get(fa);
+ for (FormatWhen fw : cw) {
+ mw.put(fw, format);
+ }
+ }
+ }
+
+ void setResolves(String format, Collection<FormatResolve> cr, Collection<FormatWhen> cw) {
+ for (FormatResolve fr : cr) {
+ EnumMap<FormatWhen, String> mw = resolves.get(fr);
+ for (FormatWhen fw : cw) {
+ mw.put(fw, format);
+ }
+ }
+ }
+
+ void setWhens(String format, Collection<FormatWhen> cw) {
+ for (FormatWhen fw : cw) {
+ whens.put(fw, format);
+ }
+ }
+
+ void setName(String s) {
+ fname = s;
+ }
+
+ void setType(String s) {
+ ftype = s;
+ }
+
+ void setResult(String s) {
+ fresult = s;
+ }
+
+ void setPre(String s) {
+ pre = s;
+ }
+
+ void setPost(String s) {
+ post = s;
+ }
+
+ void setErrorPre(String s) {
+ errorPre = s;
+ }
+
+ void setErrorPost(String s) {
+ errorPost = s;
+ }
+
+ String getPre() {
+ return pre;
+ }
+
+ String getPost() {
+ return post;
+ }
+
+ String getErrorPre() {
+ return errorPre;
+ }
+
+ String getErrorPost() {
+ return errorPost;
+ }
+
+ void setPrompts(String prompt, String continuationPrompt) {
+ this.prompt = prompt;
+ this.continuationPrompt = continuationPrompt;
+ }
+
+ String getPrompt(String nextId) {
+ return String.format(prompt, nextId);
+ }
+
+ String getContinuationPrompt(String nextId) {
+ return String.format(continuationPrompt, nextId);
+ }
+ }
+
+ /**
+ * The brace delimited substitutions
+ */
+ public enum FormatField {
+ WHEN,
+ ACTION,
+ RESOLVE("%1$s"),
+ NAME("%2$s"),
+ TYPE("%3$s"),
+ RESULT("%4$s"),
+ PRE,
+ POST,
+ ERRORPRE,
+ ERRORPOST;
+ String form;
+
+ FormatField(String s) {
+ this.form = s;
+ }
+
+ FormatField() {
+ this.form = null;
+ }
+ }
+
+ /**
+ * The event cases
+ */
+ public enum FormatCase {
+ IMPORT("import declaration: {action} {name}"),
+ CLASS("class, interface, enum, or annotation declaration: {action} {name} {resolve}"),
+ INTERFACE("class, interface, enum, or annotation declaration: {action} {name} {resolve}"),
+ ENUM("class, interface, enum, or annotation declaration: {action} {name} {resolve}"),
+ ANNOTATION("annotation interface declaration: {action} {name} {resolve}"),
+ METHOD("method declaration: {action} {name} {type}==parameter-types {resolve}"),
+ VARDECL("variable declaration: {action} {name} {type} {resolve}"),
+ VARDECLRECOVERABLE("recoverably failed variable declaration: {action} {name} {resolve}"),
+ VARINIT("variable declaration with init: {action} {name} {type} {resolve} {result}"),
+ VARRESET("variable reset on update: {action} {name}"),
+ EXPRESSION("expression: {action}=='Saved to scratch variable' {name} {type} {result}"),
+ VARVALUE("variable value expression: {action} {name} {type} {result}"),
+ ASSIGNMENT("assign variable: {action} {name} {type} {result}"),
+ STATEMENT("statement: {action}");
+ String doc;
+
+ private FormatCase(String doc) {
+ this.doc = doc;
+ }
+ }
+
+ /**
+ * The event actions
+ */
+ public enum FormatAction {
+ ADDED("snippet has been added"),
+ MODIFIED("an existing snippet has been modified"),
+ REPLACED("an existing snippet has been replaced with a new snippet"),
+ OVERWROTE("an existing snippet has been overwritten"),
+ DROPPED("snippet has been dropped"),
+ REJECTED("snippet has failed and been rejected");
+ String doc;
+
+ private FormatAction(String doc) {
+ this.doc = doc;
+ }
+ }
+
+ /**
+ * When the event occurs: primary or update
+ */
+ public enum FormatWhen {
+ PRIMARY("the entered snippet"),
+ UPDATE("an update to a dependent snippet");
+ String doc;
+
+ private FormatWhen(String doc) {
+ this.doc = doc;
+ }
+ }
+
+ /**
+ * Resolution problems with event
+ */
+ public enum FormatResolve {
+ OK("resolved correctly"),
+ DEFINED("defined despite recoverably unresolved references"),
+ NOTDEFINED("not defined because of recoverably unresolved references");
+ String doc;
+
+ private FormatResolve(String doc) {
+ this.doc = doc;
+ }
+ }
+
+ // Class used to set custom eval output formats
+ // For both /set format and /set field -- Parse arguments, setting custom format, or printing error
+ private class FormatSetter {
+
+ private final ArgTokenizer at;
+ private final JShellTool tool;
+ boolean valid = true;
+
+ class Case<E1 extends Enum<E1>, E2 extends Enum<E2>, E3 extends Enum<E3>> {
+
+ Set<E1> e1;
+ Set<E2> e2;
+ Set<E3> e3;
+
+ Case(Set<E1> e1, Set<E2> e2, Set<E3> e3) {
+ this.e1 = e1;
+ this.e2 = e2;
+ this.e3 = e3;
+ }
+
+ Case(Set<E1> e1, Set<E2> e2) {
+ this.e1 = e1;
+ this.e2 = e2;
+ }
+ }
+
+ FormatSetter(JShellTool tool, ArgTokenizer at) {
+ this.tool = tool;
+ this.at = at;
+ }
+
+ void hard(String format, Object... args) {
+ tool.hard(format, args);
+ }
+
+ <E extends Enum<E>> void hardEnums(EnumSet<E> es, Function<E, String> e2s) {
+ hardPairs(es.stream(), ev -> ev.name().toLowerCase(Locale.US), e2s);
+ }
+
+ <T> void hardPairs(Stream<T> stream, Function<T, String> a, Function<T, String> b) {
+ tool.hardPairs(stream, a, b);
+ }
+
+ void fluff(String format, Object... args) {
+ tool.fluff(format, args);
+ }
+
+ void error(String format, Object... args) {
+ tool.error(format, args);
+ }
+
+ void errorat(String format, Object... args) {
+ Object[] a2 = Arrays.copyOf(args, args.length + 1);
+ a2[args.length] = at.whole();
+ tool.error(format + " -- /set %s", a2);
+ }
+
+ void fluffRaw(String format, Object... args) {
+ tool.fluffRaw(format, args);
+ }
+
+ // For /set prompt <mode> "<prompt>" "<continuation-prompt>"
+ boolean setPrompt() {
+ Mode m = nextMode();
+ String prompt = nextFormat();
+ String continuationPrompt = nextFormat();
+ if (valid) {
+ m.setPrompts(prompt, continuationPrompt);
+ } else {
+ fluff("See '/help /set prompt' for help");
+ }
+ return valid;
+ }
+
+ // For /set newmode <new-mode> [command|quiet [<old-mode>]]
+ boolean setNewMode() {
+ String umode = at.next();
+ if (umode == null) {
+ errorat("Expected new feedback mode");
+ valid = false;
+ }
+ if (modeMap.containsKey(umode)) {
+ errorat("Expected a new feedback mode name. %s is a known feedback mode", umode);
+ valid = false;
+ }
+ String[] fluffOpt = at.next("command", "quiet");
+ boolean fluff = fluffOpt == null || fluffOpt.length != 1 || "command".equals(fluffOpt[0]);
+ if (fluffOpt != null && fluffOpt.length != 1) {
+ errorat("Specify either 'command' or 'quiet'");
+ valid = false;
+ }
+ Mode om = null;
+ String omode = at.next();
+ if (omode != null) {
+ om = toMode(omode);
+ }
+ if (valid) {
+ Mode nm = (om != null)
+ ? new Mode(umode, fluff, om)
+ : new Mode(umode, fluff);
+ modeMap.put(umode, nm);
+ fluff("Created new feedback mode: %s", nm.name);
+ } else {
+ fluff("See '/help /set newmode' for help");
+ }
+ return valid;
+ }
+
+ // For /set feedback <mode>
+ boolean setFeedback() {
+ Mode m = nextMode();
+ if (valid && m != null) {
+ mode = m;
+ fluff("Feedback mode: %s", mode.name);
+ } else {
+ fluff("See '/help /set feedback' for help");
+ }
+ return valid;
+ }
+
+ // For /set format <mode> "<format>" <selector>...
+ boolean setFormat() {
+ Mode m = nextMode();
+ String format = nextFormat();
+ if (valid) {
+ List<Case<FormatCase, FormatAction, FormatWhen>> specs = new ArrayList<>();
+ String s;
+ while ((s = at.next()) != null) {
+ String[] d = s.split("-");
+ specs.add(new Case<>(
+ parseFormatCase(d, 0),
+ parseFormatAction(d, 1),
+ parseFormatWhen(d, 2)
+ ));
+ }
+ if (valid && specs.isEmpty()) {
+ errorat("At least one selector required");
+ valid = false;
+ }
+ if (valid) {
+ // set the format in the specified cases
+ specs.stream()
+ .forEach(c -> m.setCases(format, c.e1, c.e2, c.e3));
+ }
+ }
+ if (!valid) {
+ fluff("See '/help /set format' for help");
+ }
+ return valid;
+ }
+
+ // For /set field mode <field> "<format>" <selector>...
+ boolean setField() {
+ Mode m = nextMode();
+ String fieldName = at.next();
+ FormatField field = parseFormatSelector(fieldName, EnumSet.allOf(FormatField.class), "field");
+ String format = nextFormat();
+ if (valid) {
+ switch (field) {
+ case ACTION: {
+ List<Case<FormatAction, FormatWhen, FormatWhen>> specs = new ArrayList<>();
+ String s;
+ while ((s = at.next()) != null) {
+ String[] d = s.split("-");
+ specs.add(new Case<>(
+ parseFormatAction(d, 0),
+ parseFormatWhen(d, 1)
+ ));
+ }
+ if (valid && specs.isEmpty()) {
+ errorat("At least one selector required");
+ valid = false;
+ }
+ if (valid) {
+ // set the format of the specified actions
+ specs.stream()
+ .forEach(c -> m.setActions(format, c.e1, c.e2));
+ }
+ break;
+ }
+ case RESOLVE: {
+ List<Case<FormatResolve, FormatWhen, FormatWhen>> specs = new ArrayList<>();
+ String s;
+ while ((s = at.next()) != null) {
+ String[] d = s.split("-");
+ specs.add(new Case<>(
+ parseFormatResolve(d, 0),
+ parseFormatWhen(d, 1)
+ ));
+ }
+ if (valid && specs.isEmpty()) {
+ errorat("At least one selector required");
+ valid = false;
+ }
+ if (valid) {
+ // set the format of the specified resolves
+ specs.stream()
+ .forEach(c -> m.setResolves(format, c.e1, c.e2));
+ }
+ break;
+ }
+ case WHEN: {
+ List<Case<FormatWhen, FormatWhen, FormatWhen>> specs = new ArrayList<>();
+ String s;
+ while ((s = at.next()) != null) {
+ String[] d = s.split("-");
+ specs.add(new Case<>(
+ parseFormatWhen(d, 1),
+ null
+ ));
+ }
+ if (valid && specs.isEmpty()) {
+ errorat("At least one selector required");
+ valid = false;
+ }
+ if (valid) {
+ // set the format of the specified whens
+ specs.stream()
+ .forEach(c -> m.setWhens(format, c.e1));
+ }
+ break;
+ }
+ case NAME: {
+ m.setName(format);
+ break;
+ }
+ case TYPE: {
+ m.setType(format);
+ break;
+ }
+ case RESULT: {
+ m.setResult(format);
+ break;
+ }
+ case PRE: {
+ m.setPre(format);
+ break;
+ }
+ case POST: {
+ m.setPost(format);
+ break;
+ }
+ case ERRORPRE: {
+ m.setErrorPre(format);
+ break;
+ }
+ case ERRORPOST: {
+ m.setErrorPost(format);
+ break;
+ }
+ }
+ }
+ if (!valid) {
+ fluff("See '/help /set field' for help");
+ }
+ return valid;
+ }
+
+ Mode nextMode() {
+ String umode = at.next();
+ return toMode(umode);
+ }
+
+ Mode toMode(String umode) {
+ if (umode == null) {
+ errorat("Expected a feedback mode");
+ valid = false;
+ return null;
+ }
+ Mode m = modeMap.get(umode);
+ if (m != null) {
+ return m;
+ }
+ // Failing an exact match, go searching
+ Mode[] matches = modeMap.entrySet().stream()
+ .filter(e -> e.getKey().startsWith(umode))
+ .map(e -> e.getValue())
+ .toArray(size -> new Mode[size]);
+ if (matches.length == 1) {
+ return matches[0];
+ } else {
+ valid = false;
+ if (matches.length == 0) {
+ errorat("Does not match any current feedback mode: %s", umode);
+ } else {
+ errorat("Matchs more then one current feedback mode: %s", umode);
+ }
+ fluff("The feedback mode should be one of the following:");
+ modeMap.keySet().stream()
+ .forEach(mk -> fluff(" %s", mk));
+ fluff("You may also use just enough letters to make it unique.");
+ return null;
+ }
+ }
+
+ // Test if the format string is correctly
+ final String nextFormat() {
+ String format = at.next();
+ if (format == null) {
+ errorat("Expected format missing");
+ valid = false;
+ return null;
+ }
+ if (!at.isQuoted()) {
+ errorat("Format '%s' must be quoted", format);
+ valid = false;
+ return null;
+ }
+ return format;
+ }
+
+ final Set<FormatCase> parseFormatCase(String[] s, int i) {
+ return parseFormatSelectorStar(s, i, FormatCase.class, EnumSet.allOf(FormatCase.class), "case");
+ }
+
+ final Set<FormatAction> parseFormatAction(String[] s, int i) {
+ return parseFormatSelectorStar(s, i, FormatAction.class,
+ EnumSet.of(FormatAction.ADDED, FormatAction.MODIFIED, FormatAction.REPLACED), "action");
+ }
+
+ final Set<FormatResolve> parseFormatResolve(String[] s, int i) {
+ return parseFormatSelectorStar(s, i, FormatResolve.class,
+ EnumSet.of(FormatResolve.DEFINED, FormatResolve.NOTDEFINED), "resolve");
+ }
+
+ final Set<FormatWhen> parseFormatWhen(String[] s, int i) {
+ return parseFormatSelectorStar(s, i, FormatWhen.class, EnumSet.of(FormatWhen.PRIMARY), "when");
+ }
+
+ /**
+ * In a selector x-y-z , parse x, y, or z -- whether they are missing,
+ * or a comma separated list of identifiers and stars.
+ *
+ * @param <E> The enum this selector should belong to
+ * @param sa The array of selector strings
+ * @param i The index of which selector string to use
+ * @param klass The class of the enum that should be used
+ * @param defaults The set of enum values to use if the selector is
+ * missing
+ * @return The set of enum values specified by this selector
+ */
+ final <E extends Enum<E>> Set<E> parseFormatSelectorStar(String[] sa, int i, Class<E> klass, EnumSet<E> defaults, String label) {
+ String s = sa.length > i
+ ? sa[i]
+ : null;
+ if (s == null || s.isEmpty()) {
+ return defaults;
+ }
+ Set<E> set = EnumSet.noneOf(klass);
+ EnumSet<E> values = EnumSet.allOf(klass);
+ for (String as : s.split(",")) {
+ if (as.equals("*")) {
+ set.addAll(values);
+ } else if (!as.isEmpty()) {
+ set.add(parseFormatSelector(as, values, label));
+ }
+ }
+ return set;
+ }
+
+ /**
+ * In a x-y-a,b selector, parse an x, y, a, or b -- that is an
+ * identifier
+ *
+ * @param <E> The enum this selector should belong to
+ * @param s The string to parse: x, y, or z
+ * @param values The allowed of this enum
+ * @return The enum value
+ */
+ final <E extends Enum<E>> E parseFormatSelector(String s, EnumSet<E> values, String label) {
+ if (s == null) {
+ valid = false;
+ return null;
+ }
+ String u = s.toUpperCase(Locale.US);
+ for (E c : values) {
+ if (c.name().startsWith(u)) {
+ return c;
+ }
+ }
+
+ errorat("Not a valid %s: %s, must be one of: %s", label, s,
+ values.stream().map(v -> v.name().toLowerCase(Locale.US)).collect(Collectors.joining(" ")));
+ valid = false;
+ return values.iterator().next();
+ }
+
+ final void printFormatHelp() {
+ hard("Set the format for reporting a snippet event.");
+ hard("");
+ hard("/set format <mode> \"<format>\" <selector>...");
+ hard("");
+ hard("Where <mode> is the name of a previously defined feedback mode -- see '/help /set newmode'.");
+ hard("Where <format> is a quoted string which will have these field substitutions:");
+ hard(" {action} == The action, e.g.: Added, Modified, Assigned, ...");
+ hard(" {name} == The name, e.g.: the variable name, ...");
+ hard(" {type} == The type name");
+ hard(" {resolve} == Unresolved info, e.g.: ', however, it cannot be invoked until'");
+ hard(" {result} == The result value");
+ hard(" {when} == The entered snippet or a resultant update");
+ hard(" {pre} == The feedback prefix");
+ hard(" {post} == The feedback postfix");
+ hard(" {errorpre} == The error prefix");
+ hard(" {errorpost} == The error postfix");
+ hard("Use '/set field' to set the format of these substitutions.");
+ hard("Where <selector> is the context in which the format is applied.");
+ hard("The structure of selector is: <case>[-<action>[-<when>]]");
+ hard("Where each field component may be missing (indicating defaults),");
+ hard("star (indicating all), or a comma separated list of field values.");
+ hard("For case, the field values are:");
+ hardEnums(EnumSet.allOf(FormatCase.class), ev -> ev.doc);
+ hard("For action, the field values are:");
+ hardEnums(EnumSet.allOf(FormatAction.class), ev -> ev.doc);
+ hard("For when, the field values are:");
+ hardEnums(EnumSet.allOf(FormatWhen.class), ev -> ev.doc);
+ hard("");
+ hard("Example:");
+ hard(" /set format example '{pre}{action} variable {name}, reset to null{post}' varreset-*-update");
+ }
+
+ final void printFieldHelp() {
+ hard("Set the format of a field substitution as used in '/set format'.");
+ hard("");
+ hard("/set field <mode> <field> \"<format>\" <selector>...");
+ hard("");
+ hard("Where <mode> is the name of a previously defined feedback mode -- see '/set newmode'.");
+ hard("Where <field> is context-specific format to set, each with its own selector structure:");
+ hard(" action == The action. The selector: <action>-<when>.");
+ hard(" name == The name. '%%s' is the name. No selectors.");
+ hard(" type == The type name. '%%s' is the type. No selectors.");
+ hard(" resolve == Unresolved info. '%%s' is the unresolved list. The selector: <resolve>-<when>.");
+ hard(" result == The result value. '%%s' is the result value. No selectors.");
+ hard(" when == The entered snippet or a resultant update. The selector: <when>");
+ hard(" pre == The feedback prefix. No selectors.");
+ hard(" post == The feedback postfix. No selectors.");
+ hard(" errorpre == The error prefix. No selectors.");
+ hard(" errorpost == The error postfix. No selectors.");
+ hard("Where <format> is a quoted string -- see the description specific to the field (above).");
+ hard("Where <selector> is the context in which the format is applied (see above).");
+ hard("For action, the field values are:");
+ hardEnums(EnumSet.allOf(FormatAction.class), ev -> ev.doc);
+ hard("For when, the field values are:");
+ hardEnums(EnumSet.allOf(FormatWhen.class), ev -> ev.doc);
+ hard("For resolve, the field values are:");
+ hardEnums(EnumSet.allOf(FormatResolve.class), ev -> ev.doc);
+ hard("");
+ hard("Example:");
+ hard(" /set field example resolve ' which cannot be invoked until%%s is declared' defined-update");
+ }
+
+ final void printFeedbackHelp() {
+ hard("Set the feedback mode describing displayed feedback for entered snippets and commands.");
+ hard("");
+ hard("/set feedback <mode>");
+ hard("");
+ hard("Where <mode> is the name of a previously defined feedback mode.");
+ hard("Currently defined feedback modes:");
+ modeMap.keySet().stream()
+ .forEach(m -> hard(" %s", m));
+ hard("User-defined modes can be added, see '/help /set newmode'");
+ }
+
+ final void printNewModeHelp() {
+ hard("Create a user-defined feedback mode, optionally copying from an existing mode.");
+ hard("");
+ hard("/set newmode <new-mode> [command|quiet [<old-mode>]]");
+ hard("");
+ hard("Where <new-mode> is the name of a mode you wish to create.");
+ hard("Where <old-mode> is the name of a previously defined feedback mode.");
+ hard("If <old-mode> is present, its settings are copied to the new mode.");
+ hard("'command' vs 'quiet' determines if informative/verifying command feedback is displayed.");
+ hard("");
+ hard("Once the new mode is created, use '/set format', '/set field', and '/set prompt' to configure it.");
+ hard("Use '/set feedback' to use the new mode.");
+ }
+
+ final void printPromptHelp() {
+ hard("Set the prompts. Both the normal prompt and the continuation-prompt must be set.");
+ hard("");
+ hard("/set prompt <mode> \"<prompt>\" \"<continuation-propmt>\"");
+ hard("");
+ hard("Where <mode> is the name of a previously defined feedback mode.");
+ hard("Where <prompt> and <continuation-propmt> are quoted strings printed as input promptds;");
+ hard("Both may optionally contain '%%s' which will be substituted with the next snippet id --");
+ hard("note that what is entered may not be assigned that id, for example it may be an error or command.");
+ hard("The continuation-prompt is used on the second and subsequent lines of a multi-line snippet.");
+ }
+ }
+}
--- a/langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/JShellTool.java Tue Mar 08 11:37:00 2016 -0800
+++ b/langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/JShellTool.java Tue Mar 08 11:53:35 2016 -0800
@@ -1,6 +1,5 @@
-
/*
- * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 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
@@ -35,7 +34,6 @@
import java.io.InputStream;
import java.io.PrintStream;
import java.io.Reader;
-import java.io.StreamTokenizer;
import java.io.StringReader;
import java.nio.charset.Charset;
import java.nio.file.AccessDeniedException;
@@ -66,24 +64,25 @@
import jdk.internal.jshell.debug.InternalDebugControl;
import jdk.internal.jshell.tool.IOContext.InputInterruptedException;
+import jdk.jshell.DeclarationSnippet;
import jdk.jshell.Diag;
import jdk.jshell.EvalException;
+import jdk.jshell.ExpressionSnippet;
+import jdk.jshell.ImportSnippet;
import jdk.jshell.JShell;
-import jdk.jshell.Snippet;
-import jdk.jshell.DeclarationSnippet;
-import jdk.jshell.TypeDeclSnippet;
+import jdk.jshell.JShell.Subscription;
import jdk.jshell.MethodSnippet;
import jdk.jshell.PersistentSnippet;
-import jdk.jshell.VarSnippet;
-import jdk.jshell.ExpressionSnippet;
+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;
import jdk.jshell.SourceCodeAnalysis.Suggestion;
-import jdk.jshell.SnippetEvent;
+import jdk.jshell.TypeDeclSnippet;
import jdk.jshell.UnresolvedReferenceException;
-import jdk.jshell.Snippet.SubKind;
-import jdk.jshell.JShell.Subscription;
+import jdk.jshell.VarSnippet;
import static java.nio.file.StandardOpenOption.CREATE;
import static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING;
@@ -94,7 +93,12 @@
import java.util.Spliterators;
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.FormatResolve;
+import jdk.internal.jshell.tool.Feedback.FormatWhen;
import static java.util.stream.Collectors.toList;
+import static java.util.stream.Collectors.toMap;
import static jdk.jshell.Snippet.SubKind.VAR_VALUE_SUBKIND;
/**
@@ -103,6 +107,7 @@
*/
public class JShellTool {
+ private static final String LINE_SEP = System.getProperty("line.separator");
private static final Pattern LINEBREAK = Pattern.compile("\\R");
private static final Pattern HISTORY_ALL_START_FILENAME = Pattern.compile(
"((?<cmd>(all|history|start))(\\z|\\p{javaWhitespace}+))?(?<filename>.*)");
@@ -116,6 +121,8 @@
final PrintStream userout;
final PrintStream usererr;
+ final Feedback feedback = new Feedback();
+
/**
* The constructor for the tool (used by tool launch via main and by test
* harnesses to capture ins and outs.
@@ -137,6 +144,70 @@
this.userin = userin;
this.userout = userout;
this.usererr = usererr;
+ 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;
@@ -150,7 +221,6 @@
private boolean debug = false;
private boolean displayPrompt = true;
public boolean testPrompt = false;
- private Feedback feedback = Feedback.Default;
private String cmdlineClasspath = null;
private String cmdlineStartup = null;
private String[] editor = null;
@@ -185,6 +255,15 @@
Map<Snippet,SnippetInfo> mapSnippet;
+ /**
+ * Is the input/output currently interactive
+ *
+ * @return true if console
+ */
+ boolean interactive() {
+ return input != null && input.interactiveOutput();
+ }
+
void debug(String format, Object... args) {
if (debug) {
cmderr.printf(format + "\n", args);
@@ -192,38 +271,98 @@
}
/**
- * For more verbose feedback modes
+ * Base output for command output -- no pre- or post-fix
+ *
+ * @param printf format
+ * @param printf args
+ */
+ void rawout(String format, Object... args) {
+ cmdout.printf(format, args);
+ }
+
+ /**
+ * Must show command output
+ *
+ * @param format printf format
+ * @param args printf args
+ */
+ void hard(String format, Object... args) {
+ rawout(feedback.getPre() + format + feedback.getPost(), args);
+ }
+
+ /**
+ * Error command output
+ *
+ * @param format printf format
+ * @param args printf args
+ */
+ void error(String format, Object... args) {
+ rawout(feedback.getErrorPre() + format + feedback.getErrorPost(), args);
+ }
+
+ /**
+ * Optional output
+ *
* @param format printf format
* @param args printf args
*/
void fluff(String format, Object... args) {
- if (feedback() != Feedback.Off && feedback() != Feedback.Concise) {
+ if (feedback.shouldDisplayCommandFluff() && interactive()) {
hard(format, args);
}
}
/**
- * For concise feedback mode only
+ * Optional output -- with embedded per- and post-fix
+ *
* @param format printf format
* @param args printf args
*/
- void concise(String format, Object... args) {
- if (feedback() == Feedback.Concise) {
- hard(format, args);
+ void fluffRaw(String format, Object... args) {
+ if (feedback.shouldDisplayCommandFluff() && interactive()) {
+ rawout(format, args);
+ }
+ }
+
+ <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,
+ () -> new LinkedHashMap<>()));
+ int aLen = 0;
+ for (String av : a2b.keySet()) {
+ aLen = Math.max(aLen, av.length());
+ }
+ String format = " %-" + aLen + "s -- %s";
+ String indentedNewLine = LINE_SEP + feedback.getPre()
+ + String.format(" %-" + (aLen + 4) + "s", "");
+ for (Entry<String, String> e : a2b.entrySet()) {
+ hard(format, e.getKey(), e.getValue().replaceAll("\n", indentedNewLine));
}
}
/**
- * For all feedback modes -- must show
- * @param format printf format
- * @param args printf args
+ * 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 hard(String format, Object... args) {
- cmdout.printf("| " + format + "\n", args);
+ 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
+ *
* @param s
* @return
*/
@@ -276,8 +415,8 @@
}
if (regenerateOnDeath) {
- fluff("Welcome to JShell -- Version %s", version());
- fluff("Type /help for help");
+ hard("Welcome to JShell -- Version %s", version());
+ hard("Type /help for help");
}
try {
@@ -369,14 +508,14 @@
}
private void printUsage() {
- cmdout.printf("Usage: jshell <options> <load files>\n");
- cmdout.printf("where possible options include:\n");
- cmdout.printf(" -classpath <path> Specify where to find user class files\n");
- cmdout.printf(" -cp <path> Specify where to find user class files\n");
- cmdout.printf(" -startup <file> One run replacement for the start-up definitions\n");
- cmdout.printf(" -nostartup Do not run the start-up definitions\n");
- cmdout.printf(" -help Print a synopsis of standard options\n");
- cmdout.printf(" -version Version information\n");
+ rawout("Usage: jshell <options> <load files>\n");
+ rawout("where possible options include:\n");
+ rawout(" -classpath <path> Specify where to find user class files\n");
+ rawout(" -cp <path> Specify where to find user class files\n");
+ rawout(" -startup <file> One run replacement for the start-up definitions\n");
+ rawout(" -nostartup Do not run the start-up definitions\n");
+ rawout(" -help Print a synopsis of standard options\n");
+ rawout(" -version Version information\n");
}
private void resetState() {
@@ -460,10 +599,8 @@
? "\u0005" //ENQ
: "\u0006" //ACK
: incomplete.isEmpty()
- ? feedback() == Feedback.Concise
- ? "-> "
- : "\n-> "
- : ">> "
+ ? feedback.getPrompt(currentNameSpace.tidNext())
+ : feedback.getContinuationPrompt(currentNameSpace.tidNext())
;
} else {
prompt = "";
@@ -541,7 +678,7 @@
Command[] candidates = findCommand(cmd, c -> c.kind.isRealCommand);
if (candidates.length == 0) {
if (!rerunHistoryEntryById(cmd.substring(1))) {
- hard("No such command or snippet id: %s", cmd);
+ error("No such command or snippet id: %s", cmd);
fluff("Type /help for help.");
}
} else if (candidates.length == 1) {
@@ -552,7 +689,7 @@
addToReplayHistory((command.command + " " + arg).trim());
}
} else {
- hard("Command: %s is ambiguous: %s", cmd, Arrays.stream(candidates).map(c -> c.command).collect(Collectors.joining(", ")));
+ error("Command: %s is ambiguous: %s", cmd, Arrays.stream(candidates).map(c -> c.command).collect(Collectors.joining(", ")));
fluff("Type /help for help.");
}
}
@@ -635,45 +772,6 @@
}
}
- class ArgTokenizer extends StreamTokenizer {
-
- ArgTokenizer(String arg) {
- super(new StringReader(arg));
- resetSyntax();
- wordChars(0x00, 0xFF);
- quoteChar('"');
- quoteChar('\'');
-
- whitespaceChars(0x09, 0x0D);
- whitespaceChars(0x1C, 0x20);
- whitespaceChars(0x85, 0x85);
- whitespaceChars(0xA0, 0xA0);
- whitespaceChars(0x1680, 0x1680);
- whitespaceChars(0x180E, 0x180E);
- whitespaceChars(0x2000, 0x200A);
- whitespaceChars(0x202F, 0x202F);
- whitespaceChars(0x205F, 0x205F);
- whitespaceChars(0x3000, 0x3000);
- }
-
- String next() {
- try {
- nextToken();
- } catch (Throwable t) {
- return null;
- }
- return sval;
- }
-
- String val() {
- return sval;
- }
-
- boolean isQuoted() {
- return ttype == '\'' || ttype == '"';
- }
- }
-
static final class FixedCompletionProvider implements CompletionProvider {
private final String[] alternatives;
@@ -801,16 +899,9 @@
" -- List the snippet with the specified snippet id\n",
arg -> cmdList(arg),
editKeywordCompletion()));
- registerCommand(new Command("/seteditor", "<command>", "set the external editor command to use",
- "Specify the command to launch for the /edit command.\n" +
- "The command is an operating system dependent string.\n" +
- "The command may include space-separated arguments (such as flags).\n" +
- "When /edit is used, temporary file to edit will be appended as the last argument.\n",
- arg -> cmdSetEditor(arg),
- EMPTY_COMPLETION_PROVIDER));
registerCommand(new Command("/edit", "<name or id>", "edit a source entry referenced by name or id",
"Edit a snippet or snippets of source in an external editor.\n" +
- "The editor to use is set with /seteditor.\n" +
+ "The editor to use is set with /set editor.\n" +
"If no editor has been set, a simple editor will be launched.\n\n" +
"/edit <name>\n" +
" -- Edit the snippet or snippets with the specified name (preference for active snippets)\n" +
@@ -875,7 +966,7 @@
" * Start-up code is re-executed.\n" +
" * The execution state is restarted.\n" +
" * The classpath is cleared.\n" +
- "Tool settings are maintained: /feedback, /prompt, and /seteditor\n" +
+ "Tool settings are maintained, as set with: /set ...\n" +
"Save any work before using this command\n",
arg -> cmdReset(),
EMPTY_COMPLETION_PROVIDER));
@@ -895,25 +986,6 @@
" -- With the 'quiet' argument the replay is not shown. Errors will display.\n",
arg -> cmdReload(arg),
reloadCompletion()));
- registerCommand(new Command("/feedback", "<level>", "feedback information: off, concise, normal, verbose, default, or ?",
- "Set the level of feedback describing the effect of commands and snippets.\n\n" +
- "/feedback off\n" +
- " -- Give no feedback\n" +
- "/feedback concise\n" +
- " -- Brief and generally symbolic feedback\n" +
- "/feedback normal\n" +
- " -- Give a natural language description of the actions\n" +
- "/feedback verbose\n" +
- " -- Like normal but with side-effects described\n" +
- "/feedback default\n" +
- " -- Same as normal for user input, off for input from a file\n",
- arg -> cmdFeedback(arg),
- new FixedCompletionProvider("off", "concise", "normal", "verbose", "default", "?")));
- registerCommand(new Command("/prompt", null, "toggle display of a prompt",
- "Toggle between displaying an input prompt and not displaying a prompt.\n" +
- "Particularly useful when pasting large amounts of text.\n",
- arg -> cmdPrompt(),
- EMPTY_COMPLETION_PROVIDER));
registerCommand(new Command("/classpath", "<path>", "add a path to the classpath",
"Append a additional path to the classpath.\n",
arg -> cmdClasspath(arg),
@@ -923,10 +995,6 @@
"Display the history of snippet and command input since this jshell was launched.\n",
arg -> cmdHistory(),
EMPTY_COMPLETION_PROVIDER));
- registerCommand(new Command("/setstart", "<file>", "read file and set as the new start-up definitions",
- "The contents of the specified file become the default start-up snippets and commands.\n",
- arg -> cmdSetStart(arg),
- FILE_COMPLETION_PROVIDER));
registerCommand(new Command("/debug", null, "toggle debugging of the jshell",
"Display debugging information for the jshelll implementation.\n" +
"0: Debugging off\n" +
@@ -951,6 +1019,37 @@
" -- 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",
+ "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" +
+ "\n" +
+ "/set editor <command> <optional-arg>...\n" +
+ " -- Specify the command to launch for the /edit command.\n" +
+ " The <command> is an operating system dependent string.\n" +
+ "\n" +
+ "/set start <file>\n" +
+ " -- The contents of the specified <file> become the default start-up snippets and commands.\n" +
+ "\n" +
+ "/set feedback <mode>\n" +
+ " -- Set the feedback mode describing displayed feedback for entered snippets and commands.\n" +
+ "\n" +
+ "/set newmode <new-mode> [command|quiet [<old-mode>]]\n" +
+ " -- Create a user-defined feedback mode, optionally copying from an existing mode.\n" +
+ "\n" +
+ "/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" +
+ "\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")));
registerCommand(new Command("/?", "", "get information about jshell",
"Display information about jshell (abbreviation for /help).\n" +
"/?\n" +
@@ -1051,21 +1150,138 @@
// --- Command implementations ---
- boolean cmdSetEditor(String arg) {
- if (arg.isEmpty()) {
- hard("/seteditor requires a path argument");
+ private static final String[] setSub = new String[]{
+ "format", "field", "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);
+ if (which == null) {
return false;
- } else {
- List<String> ed = new ArrayList<>();
- ArgTokenizer at = new ArgTokenizer(arg);
- String n;
- while ((n = at.next()) != null) ed.add(n);
- editor = ed.toArray(new String[ed.size()]);
- fluff("Editor set to: %s", arg);
- return true;
+ }
+ 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":
+ return feedback.setNewMode(this, at);
+ case "prompt":
+ return feedback.setPrompt(this, at);
+ case "editor": {
+ String prog = at.next();
+ if (prog == null) {
+ hard("The '/set editor' command requires a path argument");
+ return false;
+ } else {
+ List<String> ed = new ArrayList<>();
+ ed.add(prog);
+ String n;
+ while ((n = at.next()) != null) {
+ ed.add(n);
+ }
+ editor = ed.toArray(new String[ed.size()]);
+ fluff("Editor set to: %s", arg);
+ return true;
+ }
+ }
+ case "start": {
+ String filename = at.next();
+ if (filename == null) {
+ hard("The '/set start' command requires a filename argument.");
+ } else {
+ try {
+ byte[] encoded = Files.readAllBytes(toPathResolvingUserHome(filename));
+ String init = new String(encoded);
+ PREFS.put(STARTUP_KEY, init);
+ } catch (AccessDeniedException e) {
+ hard("File '%s' for /set start is not accessible.", filename);
+ return false;
+ } catch (NoSuchFileException e) {
+ hard("File '%s' for /set start is not found.", filename);
+ return false;
+ } catch (Exception e) {
+ hard("Exception while reading start set file: %s", e);
+ return false;
+ }
+ }
+ return true;
+ }
+ default:
+ hard("Error: Invalid /set argument: %s", which);
+ return false;
}
}
+ boolean printSetHelp(ArgTokenizer at) {
+ String which = setSubCommand(at);
+ if (which == null) {
+ return false;
+ }
+ switch (which) {
+ case "format":
+ feedback.printFormatHelp(this);
+ return true;
+ case "field":
+ feedback.printFieldHelp(this);
+ return true;
+ case "feedback":
+ feedback.printFeedbackHelp(this);
+ return true;
+ case "newmode":
+ feedback.printNewModeHelp(this);
+ return true;
+ case "prompt":
+ feedback.printPromptHelp(this);
+ return true;
+ case "editor":
+ hard("Specify the command to launch for the /edit command.");
+ hard("");
+ hard("/set editor <command> <optional-arg>...");
+ hard("");
+ hard("The <command> is an operating system dependent string.");
+ hard("The <command> may include space-separated arguments (such as flags) -- <optional-arg>....");
+ hard("When /edit is used, the temporary file to edit will be appended as the last argument.");
+ return true;
+ case "start":
+ hard("Set the start-up configuration -- a sequence of snippets and commands read at start-up.");
+ hard("");
+ hard("/set start <file>");
+ hard("");
+ hard("The contents of the specified <file> become the default start-up snippets and commands --");
+ hard("which are run when the jshell tool is started or reset.");
+ return true;
+ default:
+ hard("Error: Invalid /set argument: %s", which);
+ return false;
+ }
+ }
+
+ String setSubCommand(ArgTokenizer at) {
+ String[] matches = at.next(setSub);
+ 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)
+ .collect(Collectors.joining(", "))
+ );
+ return null;
+ } else if (matches.length > 1) {
+ error("Ambiguous argument to /set: %s", at.val());
+ fluff("Use one of: %s", Arrays.stream(matches)
+ .collect(Collectors.joining(", "))
+ );
+ return null;
+ }
+ return matches[0];
+ }
+
boolean cmdClasspath(String arg) {
if (arg.isEmpty()) {
hard("/classpath requires a path argument");
@@ -1137,91 +1353,50 @@
return true;
}
- private boolean cmdFeedback(String arg) {
- switch (arg) {
- case "":
- case "d":
- case "default":
- feedback = Feedback.Default;
- break;
- case "o":
- case "off":
- feedback = Feedback.Off;
- break;
- case "c":
- case "concise":
- feedback = Feedback.Concise;
- break;
- case "n":
- case "normal":
- feedback = Feedback.Normal;
- break;
- case "v":
- case "verbose":
- feedback = Feedback.Verbose;
- break;
- default:
- hard("Follow /feedback with of the following:");
- hard(" off (errors and critical output only)");
- hard(" concise");
- hard(" normal");
- hard(" verbose");
- hard(" default");
- hard("You may also use just the first letter, for example: /f c");
- hard("In interactive mode 'default' is the same as 'normal', from a file it is the same as 'off'");
- return false;
+ boolean cmdHelp(String arg) {
+ ArgTokenizer at = new ArgTokenizer(arg);
+ String subject = at.next();
+ if (subject != null) {
+ Command[] matches = commands.values().stream()
+ .filter(c -> c.command.startsWith(subject))
+ .toArray(size -> new Command[size]);
+ at.mark();
+ String sub = at.next();
+ if (sub != null && matches.length == 1 && matches[0].command.equals("/set")) {
+ at.rewind();
+ return printSetHelp(at);
+ }
+ if (matches.length > 0) {
+ for (Command c : matches) {
+ hard("");
+ hard("%s", c.command);
+ hard("");
+ hard("%s", c.help.replaceAll("\n", LINE_SEP + feedback.getPre()));
+ }
+ return true;
+ } else {
+ error("No commands or subjects start with the provided argument: %s\n\n", arg);
+ }
}
- fluff("Feedback mode: %s", feedback.name().toLowerCase());
- return true;
- }
-
- boolean cmdHelp(String arg) {
- if (!arg.isEmpty()) {
- StringBuilder sb = new StringBuilder();
- commands.values().stream()
- .filter(c -> c.command.startsWith(arg))
- .forEach(c -> {
- sb.append("\n");
- sb.append(c.command);
- sb.append("\n\n");
- sb.append(c.help);
- sb.append("\n");
- });
- if (sb.length() > 0) {
- cmdout.print(sb);
- return true;
- }
- cmdout.printf("No commands or subjects start with the provided argument: %s\n\n", arg);
- }
- int synopsisLen = 0;
- Map<String, String> synopsis2Description = new LinkedHashMap<>();
- for (Command cmd : new LinkedHashSet<>(commands.values())) {
- if (!cmd.kind.showInHelp)
- continue;
- StringBuilder synopsis = new StringBuilder();
- synopsis.append(cmd.command);
- if (cmd.params != null)
- synopsis.append(" ").append(cmd.params);
- synopsis2Description.put(synopsis.toString(), cmd.description);
- synopsisLen = Math.max(synopsisLen, synopsis.length());
- }
- cmdout.println("Type a Java language expression, statement, or declaration.");
- cmdout.println("Or type one of the following commands:\n");
- for (Entry<String, String> e : synopsis2Description.entrySet()) {
- cmdout.print(String.format("%-" + synopsisLen + "s", e.getKey()));
- cmdout.print(" -- ");
- String indentedNewLine = System.getProperty("line.separator") +
- String.format("%-" + (synopsisLen + 4) + "s", "");
- cmdout.println(e.getValue().replace("\n", indentedNewLine));
- }
- cmdout.println();
- cmdout.println("For more information type '/help' followed by the name of command or a subject.");
- cmdout.println("For example '/help /list' or '/help intro'. Subjects:\n");
- commands.values().stream()
- .filter(c -> c.kind == CommandKind.HELP_SUBJECT)
- .forEach(c -> {
- cmdout.printf("%-12s -- %s\n", c.command, c.description);
- });
+ hard("Type a Java language expression, statement, or declaration.");
+ hard("Or type one of the following commands:");
+ hard("");
+ hardPairs(commands.values().stream()
+ .filter(cmd -> cmd.kind.showInHelp),
+ cmd -> (cmd.params != null)
+ ? cmd.command + " " + cmd.params
+ : cmd.command,
+ cmd -> cmd.description
+ );
+ hard("");
+ hard("For more information type '/help' followed by the name of command or a subject.");
+ hard("For example '/help /list' or '/help intro'. Subjects:");
+ hard("");
+ hardPairs(commands.values().stream()
+ .filter(cmd -> cmd.kind == CommandKind.HELP_SUBJECT),
+ cmd -> cmd.command,
+ cmd -> cmd.description
+ );
return true;
}
@@ -1482,13 +1657,6 @@
return true;
}
- private boolean cmdPrompt() {
- displayPrompt = !displayPrompt;
- fluff("Prompt will %sdisplay. Use /prompt to toggle.", displayPrompt ? "" : "NOT ");
- concise("Prompt: %s", displayPrompt ? "on" : "off");
- return true;
- }
-
private boolean cmdReset() {
live = false;
fluff("Resetting state.");
@@ -1577,28 +1745,6 @@
return true;
}
- private boolean cmdSetStart(String filename) {
- if (filename.isEmpty()) {
- hard("The /setstart command requires a filename argument.");
- } else {
- try {
- byte[] encoded = Files.readAllBytes(toPathResolvingUserHome(filename));
- String init = new String(encoded);
- PREFS.put(STARTUP_KEY, init);
- } catch (AccessDeniedException e) {
- hard("File '%s' for /setstart is not accessible.", filename);
- return false;
- } catch (NoSuchFileException e) {
- hard("File '%s' for /setstart is not found.", filename);
- return false;
- } catch (Exception e) {
- hard("Exception while reading start set file: %s", e);
- return false;
- }
- }
- return true;
- }
-
private boolean cmdVars() {
for (VarSnippet vk : state.variables()) {
String val = state.status(vk) == Status.VALID
@@ -1831,14 +1977,10 @@
printDiagnostics(source, diagnostics, true);
} else {
// Update
- SubKind subkind = sn.subKind();
- if (sn instanceof DeclarationSnippet
- && (feedback() == Feedback.Verbose
- || ste.status() == Status.OVERWRITTEN
- || subkind == SubKind.VAR_DECLARATION_SUBKIND
- || subkind == SubKind.VAR_DECLARATION_WITH_INITIALIZER_SUBKIND)) {
- // Under the conditions, display update information
- displayDeclarationAndValue(ste, true, null);
+ 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);
@@ -1851,118 +1993,117 @@
@SuppressWarnings("fallthrough")
private void displayDeclarationAndValue(SnippetEvent ste, boolean update, String value) {
Snippet key = ste.snippet();
- String declared;
+ FormatAction action;
Status status = ste.status();
switch (status) {
case VALID:
case RECOVERABLE_DEFINED:
case RECOVERABLE_NOT_DEFINED:
if (ste.previousStatus().isActive) {
- declared = ste.isSignatureChange()
- ? "Replaced"
- : "Modified";
+ action = ste.isSignatureChange()
+ ? FormatAction.REPLACED
+ : FormatAction.MODIFIED;
} else {
- declared = "Added";
+ action = FormatAction.ADDED;
}
break;
case OVERWRITTEN:
- declared = "Overwrote";
+ action = FormatAction.OVERWROTE;
break;
case DROPPED:
- declared = "Dropped";
+ action = FormatAction.DROPPED;
break;
case REJECTED:
- declared = "Rejected";
+ action = FormatAction.REJECTED;
break;
case NONEXISTENT:
default:
// Should not occur
- declared = ste.previousStatus().toString() + "=>" + status.toString();
+ error("Unexpected status: " + ste.previousStatus().toString() + "=>" + status.toString());
+ return;
}
- if (update) {
- declared = " Update " + declared.toLowerCase();
- }
- String however;
+ FormatResolve resolution;
+ String unresolved;
if (key instanceof DeclarationSnippet && (status == Status.RECOVERABLE_DEFINED || status == Status.RECOVERABLE_NOT_DEFINED)) {
- String cannotUntil = (status == Status.RECOVERABLE_NOT_DEFINED)
- ? " cannot be referenced until"
- : " cannot be invoked until";
- however = (update? " which" : ", however, it") + cannotUntil + unresolved((DeclarationSnippet) key);
+ resolution = (status == Status.RECOVERABLE_NOT_DEFINED)
+ ? FormatResolve.NOTDEFINED
+ : FormatResolve.DEFINED;
+ unresolved = unresolved((DeclarationSnippet) key);
} else {
- however = "";
+ resolution = FormatResolve.OK;
+ unresolved = "";
}
switch (key.subKind()) {
case CLASS_SUBKIND:
- fluff("%s class %s%s", declared, ((TypeDeclSnippet) key).name(), however);
+ custom(FormatCase.CLASS, update, action, resolution,
+ ((TypeDeclSnippet) key).name(), null, unresolved, null);
break;
case INTERFACE_SUBKIND:
- fluff("%s interface %s%s", declared, ((TypeDeclSnippet) key).name(), however);
+ custom(FormatCase.INTERFACE, update, action, resolution,
+ ((TypeDeclSnippet) key).name(), null, unresolved, null);
break;
case ENUM_SUBKIND:
- fluff("%s enum %s%s", declared, ((TypeDeclSnippet) key).name(), however);
+ custom(FormatCase.ENUM, update, action, resolution,
+ ((TypeDeclSnippet) key).name(), null, unresolved, null);
break;
case ANNOTATION_TYPE_SUBKIND:
- fluff("%s annotation interface %s%s", declared, ((TypeDeclSnippet) key).name(), however);
+ custom(FormatCase.ANNOTATION, update, action, resolution,
+ ((TypeDeclSnippet) key).name(), null, unresolved, null);
break;
case METHOD_SUBKIND:
- fluff("%s method %s(%s)%s", declared, ((MethodSnippet) key).name(),
- ((MethodSnippet) key).parameterTypes(), however);
+ custom(FormatCase.METHOD, update, action, resolution,
+ ((MethodSnippet) key).name(), ((MethodSnippet) key).parameterTypes(), unresolved, null);
break;
case VAR_DECLARATION_SUBKIND:
- if (!update) {
- VarSnippet vk = (VarSnippet) key;
- if (status == Status.RECOVERABLE_NOT_DEFINED) {
- fluff("%s variable %s%s", declared, vk.name(), however);
- } else {
- fluff("%s variable %s of type %s%s", declared, vk.name(), vk.typeName(), however);
- }
- break;
- }
- // Fall through
case VAR_DECLARATION_WITH_INITIALIZER_SUBKIND: {
VarSnippet vk = (VarSnippet) key;
if (status == Status.RECOVERABLE_NOT_DEFINED) {
- if (!update) {
- fluff("%s variable %s%s", declared, vk.name(), however);
- break;
- }
- } else if (update) {
- if (ste.isSignatureChange()) {
- hard("%s variable %s, reset to null", declared, vk.name());
- }
+ 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 {
- fluff("%s variable %s of type %s with initial value %s",
- declared, vk.name(), vk.typeName(), value);
- concise("%s : %s", vk.name(), value);
+ custom(FormatCase.VARDECL, update, action, resolution,
+ vk.name(), vk.typeName(), unresolved, value);
}
break;
}
case TEMP_VAR_EXPRESSION_SUBKIND: {
VarSnippet vk = (VarSnippet) key;
- if (update) {
- hard("%s temporary variable %s, reset to null", declared, vk.name());
- } else {
- fluff("Expression value is: %s", (value));
- fluff(" assigned to temporary variable %s of type %s", vk.name(), vk.typeName());
- concise("%s : %s", vk.name(), value);
- }
+ custom(FormatCase.EXPRESSION, update, action, resolution,
+ vk.name(), vk.typeName(), null, value);
break;
}
case OTHER_EXPRESSION_SUBKIND:
- fluff("Expression value is: %s", (value));
+ error("Unexpected expression form -- value is: %s", (value));
break;
case VAR_VALUE_SUBKIND: {
ExpressionSnippet ek = (ExpressionSnippet) key;
- fluff("Variable %s of type %s has value %s", ek.name(), ek.typeName(), (value));
- concise("%s : %s", ek.name(), value);
+ custom(FormatCase.VARVALUE, update, action, resolution,
+ ek.name(), ek.typeName(), null, value);
break;
}
case ASSIGNMENT_SUBKIND: {
ExpressionSnippet ek = (ExpressionSnippet) key;
- fluff("Variable %s has been assigned the value %s", ek.name(), (value));
- concise("%s : %s", ek.name(), value);
+ 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
@@ -2048,34 +2189,9 @@
sb.append(", ");
}
}
- switch (unr.size()) {
- case 0:
- break;
- case 1:
- sb.append(" is declared");
- break;
- default:
- sb.append(" are declared");
- break;
- }
return sb.toString();
}
- enum Feedback {
- Default,
- Off,
- Concise,
- Normal,
- Verbose
- }
-
- Feedback feedback() {
- if (feedback == Feedback.Default) {
- return input == null || input.interactiveOutput() ? Feedback.Normal : Feedback.Off;
- }
- return feedback;
- }
-
/** The current version number as a string.
*/
static String version() {
--- a/langtools/src/jdk.jshell/share/classes/jdk/jshell/CompletenessAnalyzer.java Tue Mar 08 11:37:00 2016 -0800
+++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/CompletenessAnalyzer.java Tue Mar 08 11:53:35 2016 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 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
@@ -231,7 +231,7 @@
// Declarations and type parameters (thus expressions)
EXTENDS(TokenKind.EXTENDS, XEXPR|XDECL), // extends
- COMMA(TokenKind.COMMA, XEXPR|XDECL|XSTART), // ,
+ COMMA(TokenKind.COMMA, XEXPR|XDECL), // ,
AMP(TokenKind.AMP, XEXPR|XDECL), // &
GT(TokenKind.GT, XEXPR|XDECL), // >
LT(TokenKind.LT, XEXPR|XDECL1), // <
--- a/langtools/test/jdk/jshell/CommandCompletionTest.java Tue Mar 08 11:37:00 2016 -0800
+++ b/langtools/test/jdk/jshell/CommandCompletionTest.java Tue Mar 08 11:53:35 2016 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 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
@@ -50,13 +50,9 @@
public class CommandCompletionTest extends ReplToolTesting {
public void testCommand() {
- assertCompletion("/f|", false, "/feedback ");
assertCompletion("/deb|", false);
- assertCompletion("/feedback v|", false, "verbose");
assertCompletion("/c|", false, "/classes ", "/classpath ");
assertCompletion("/h|", false, "/help ", "/history ");
- assertCompletion("/feedback |", false,
- "?", "concise", "default", "normal", "off", "verbose");
}
public void testList() {
@@ -108,7 +104,7 @@
public void testSave() throws IOException {
Compiler compiler = new Compiler();
- assertCompletion("/s|", false, "/save ", "/seteditor ", "/setstart ");
+ assertCompletion("/s|", false, "/save ", "/set ");
List<String> p1 = listFiles(Paths.get(""));
Collections.addAll(p1, "all ", "history ", "start ");
FileSystems.getDefault().getRootDirectories().forEach(s -> p1.add(s.toString()));
--- a/langtools/test/jdk/jshell/CompletenessTest.java Tue Mar 08 11:37:00 2016 -0800
+++ b/langtools/test/jdk/jshell/CompletenessTest.java Tue Mar 08 11:53:35 2016 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 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
@@ -23,6 +23,7 @@
/*
* @test
+ * @bug 8149524
* @summary Test SourceCodeAnalysis
* @build KullaTesting TestingInputStream
* @run testng CompletenessTest
@@ -60,6 +61,7 @@
"try { } finally { }",
"try (java.util.zip.ZipFile zf = new java.util.zip.ZipFile(zipFileName)) { }",
"foo: while (true) { printf(\"Innn\"); break foo; }",
+ "class Case<E1 extends Enum<E1>, E2 extends Enum<E2>, E3 extends Enum<E3>> {}",
";",
};
--- a/langtools/test/jdk/jshell/ExternalEditorTest.java Tue Mar 08 11:37:00 2016 -0800
+++ b/langtools/test/jdk/jshell/ExternalEditorTest.java Tue Mar 08 11:53:35 2016 -0800
@@ -113,7 +113,7 @@
@Override
public void testEditor(boolean defaultStartup, String[] args, ReplTest... tests) {
ReplTest[] t = new ReplTest[tests.length + 1];
- t[0] = a -> assertCommandCheckOutput(a, "/seteditor " + executionScript,
+ t[0] = a -> assertCommandCheckOutput(a, "/set editor " + executionScript,
assertStartsWith("| Editor set to: " + executionScript));
System.arraycopy(tests, 0, t, 1, tests.length);
super.testEditor(defaultStartup, args, t);
@@ -193,8 +193,8 @@
@Test
public void setUnknownEditor() {
test(
- a -> assertCommand(a, "/seteditor", "| /seteditor requires a path argument\n"),
- a -> assertCommand(a, "/seteditor UNKNOWN", "| Editor set to: UNKNOWN\n"),
+ a -> assertCommand(a, "/set editor", "| /set editor requires a path argument\n"),
+ a -> assertCommand(a, "/set editor UNKNOWN", "| Editor set to: UNKNOWN\n"),
a -> assertCommand(a, "int a;", null),
a -> assertCommand(a, "/e 1",
"| Edit Error: process IO failure: Cannot run program \"UNKNOWN\": error=2, No such file or directory\n")
@@ -204,7 +204,7 @@
@Test(enabled = false)
public void testRemoveTempFile() {
test(new String[]{"-nostartup"},
- a -> assertCommandCheckOutput(a, "/seteditor " + executionScript,
+ a -> assertCommandCheckOutput(a, "/set editor " + executionScript,
assertStartsWith("| Editor set to: " + executionScript)),
a -> assertVariable(a, "int", "a", "0", "0"),
a -> assertEditOutput(a, "/e 1", assertStartsWith("| Edit Error: Failure read edit file:"), () -> {
--- a/langtools/test/jdk/jshell/ReplToolTesting.java Tue Mar 08 11:37:00 2016 -0800
+++ b/langtools/test/jdk/jshell/ReplToolTesting.java Tue Mar 08 11:53:35 2016 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 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
@@ -397,6 +397,15 @@
assertCommand(after, cmd, out, "", null, "", "");
}
+ public void assertCommandOutputContains(boolean after, String cmd, String has) {
+ assertCommandCheckOutput(after, cmd, (s) ->
+ assertTrue(s.contains(has), "Output: \'" + s + "' does not contain: " + has));
+ }
+
+ public void assertCommandOutputStartsWith(boolean after, String cmd, String starts) {
+ assertCommandCheckOutput(after, cmd, assertStartsWith(starts));
+ }
+
public void assertCommandCheckOutput(boolean after, String cmd, Consumer<String> check) {
if (!after) {
assertCommand(false, cmd, null);
@@ -437,13 +446,13 @@
}
private List<String> computeCompletions(String code, boolean isSmart) {
- JShellTool repl = this.repl != null ? this.repl
+ JShellTool js = this.repl != null ? this.repl
: new JShellTool(null, null, null, null, null, null, null);
int cursor = code.indexOf('|');
code = code.replace("|", "");
assertTrue(cursor > -1, "'|' not found: " + code);
List<Suggestion> completions =
- repl.commandCompletionSuggestions(code, cursor, new int[1]); //XXX: ignoring anchor for now
+ js.commandCompletionSuggestions(code, cursor, new int[1]); //XXX: ignoring anchor for now
return completions.stream()
.filter(s -> isSmart == s.isSmart)
.map(s -> s.continuation)
@@ -481,6 +490,15 @@
return name.hashCode();
}
+ @Override
+ public boolean equals(Object o) {
+ if (o instanceof MemberInfo) {
+ MemberInfo mi = (MemberInfo) o;
+ return name.equals(mi.name);
+ }
+ return false;
+ }
+
public abstract Consumer<String> checkOutput();
public String getSource() {
@@ -537,6 +555,11 @@
}
@Override
+ public int hashCode() {
+ return name.hashCode();
+ }
+
+ @Override
public boolean equals(Object o) {
if (o instanceof VariableInfo) {
VariableInfo v = (VariableInfo) o;
@@ -585,6 +608,10 @@
return s -> assertTrue(checkOutput.test(s), "Expected: '" + expectedOutput + "', actual: " + s);
}
+ @Override
+ public int hashCode() {
+ return (name.hashCode() << 2) ^ type.hashCode() ;
+ }
@Override
public boolean equals(Object o) {
@@ -616,6 +643,11 @@
}
@Override
+ public int hashCode() {
+ return name.hashCode() ;
+ }
+
+ @Override
public boolean equals(Object o) {
if (o instanceof ClassInfo) {
ClassInfo c = (ClassInfo) o;
@@ -641,6 +673,11 @@
}
@Override
+ public int hashCode() {
+ return (name.hashCode() << 2) ^ type.hashCode() ;
+ }
+
+ @Override
public boolean equals(Object o) {
if (o instanceof ImportInfo) {
ImportInfo i = (ImportInfo) o;
--- a/langtools/test/jdk/jshell/ToolBasicTest.java Tue Mar 08 11:37:00 2016 -0800
+++ b/langtools/test/jdk/jshell/ToolBasicTest.java Tue Mar 08 11:53:35 2016 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 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
@@ -23,7 +23,7 @@
/*
* @test
- * @bug 8143037 8142447 8144095 8140265 8144906 8146138 8147887 8147886
+ * @bug 8143037 8142447 8144095 8140265 8144906 8146138 8147887 8147886 8148316 8148317
* @requires os.family != "solaris"
* @summary Tests for Basic tests for REPL tool
* @library /tools/lib
@@ -90,8 +90,7 @@
public void elideStartUpFromList() {
test(
- (a) -> assertCommandCheckOutput(a, "123", (s) ->
- assertTrue(s.contains("type int"), s)),
+ (a) -> assertCommandOutputContains(a, "123", "type int"),
(a) -> assertCommandCheckOutput(a, "/list", (s) -> {
int cnt;
try (Scanner scanner = new Scanner(s)) {
@@ -112,8 +111,7 @@
Compiler compiler = new Compiler();
Path path = compiler.getPath("myfile");
test(
- (a) -> assertCommandCheckOutput(a, "123",
- (s) -> assertTrue(s.contains("type int"), s)),
+ (a) -> assertCommandOutputContains(a, "123", "type int"),
(a) -> assertCommand(a, "/save " + path.toString(), "")
);
try (Stream<String> lines = Files.lines(path)) {
@@ -594,12 +592,12 @@
(a) -> assertMethod(a, "void f() {}", "()V", "f"),
(a) -> assertImport(a, "import java.util.stream.*;", "", "java.util.stream.*"),
(a) -> assertCommand(a, "/save " + startUpFile.toString(), null),
- (a) -> assertCommand(a, "/setstart " + startUpFile.toString(), null)
+ (a) -> assertCommand(a, "/set start " + startUpFile.toString(), null)
);
Path unknown = compiler.getPath("UNKNOWN");
test(
- (a) -> assertCommand(a, "/setstart " + unknown.toString(),
- "| File '" + unknown + "' for /setstart is not found.\n")
+ (a) -> assertCommand(a, "/set start " + unknown.toString(),
+ "| File '" + unknown + "' for /set start is not found.\n")
);
test(false, new String[0],
(a) -> {
@@ -619,7 +617,7 @@
}
private void removeStartup() {
- Preferences preferences = Preferences.userRoot().node("tool/REPL");
+ Preferences preferences = Preferences.userRoot().node("tool/JShell");
if (preferences != null) {
preferences.remove("STARTUP");
}
@@ -636,7 +634,7 @@
}
public void testNoArgument() {
- String[] commands = {"/save", "/open", "/setstart"};
+ String[] commands = {"/save", "/open", "/set start"};
test(Stream.of(commands)
.map(cmd -> {
String c = cmd;
@@ -670,8 +668,7 @@
test(
a -> assertVariable(a, "int", "x"),
a -> assertCommandCheckOutput(a, "/vars", assertVariables()),
- a -> assertCommandCheckOutput(a, "System.exit(5);", s ->
- assertTrue(s.contains("terminated"), s)),
+ a -> assertCommandOutputContains(a, "System.exit(5);", "terminated"),
a -> assertCommandCheckOutput(a, "/vars", s ->
assertTrue(s.trim().isEmpty(), s)),
a -> assertMethod(a, "void f() { }", "()void", "f"),
@@ -699,8 +696,7 @@
s -> assertEquals(s, "| No definition or id named " + arg +
" found. There are no active definitions.\n")),
a -> assertVariable(a, "int", "aardvark"),
- a -> assertCommandCheckOutput(a, "/list aardvark",
- s -> assertTrue(s.contains("aardvark"))),
+ a -> assertCommandOutputContains(a, "/list aardvark", "aardvark"),
a -> assertCommandCheckOutput(a, "/list start",
s -> checkLineToList(s, START_UP)),
a -> assertCommandCheckOutput(a, "/list all",
@@ -714,14 +710,14 @@
}
public void testFeedbackNegative() {
- test(a -> assertCommandCheckOutput(a, "/feedback aaaa",
- assertStartsWith("| Follow /feedback with of the following")));
+ test(a -> assertCommandCheckOutput(a, "/set feedback aaaa",
+ assertStartsWith("| Does not match any current feedback mode")));
}
public void testFeedbackOff() {
for (String off : new String[]{"o", "off"}) {
test(
- a -> assertCommand(a, "/feedback " + off, ""),
+ a -> assertCommand(a, "/set feedback " + off, ""),
a -> assertCommand(a, "int a", ""),
a -> assertCommand(a, "void f() {}", ""),
a -> assertCommandCheckOutput(a, "aaaa", assertStartsWith("| Error:")),
@@ -730,23 +726,6 @@
}
}
- public void testFeedbackConcise() {
- Compiler compiler = new Compiler();
- Path testConciseFile = compiler.getPath("testConciseFeedback");
- String[] sources = new String[] {"int a", "void f() {}", "class A {}", "a = 10"};
- compiler.writeToFile(testConciseFile, sources);
- for (String concise : new String[]{"c", "concise"}) {
- test(
- a -> assertCommand(a, "/feedback " + concise, ""),
- a -> assertCommand(a, sources[0], ""),
- a -> assertCommand(a, sources[1], ""),
- a -> assertCommand(a, sources[2], ""),
- a -> assertCommand(a, sources[3], "| a : 10\n"),
- a -> assertCommand(a, "/o " + testConciseFile.toString(), "| a : 10\n")
- );
- }
- }
-
public void testFeedbackNormal() {
Compiler compiler = new Compiler();
Path testNormalFile = compiler.getPath("testConciseNormal");
@@ -759,58 +738,20 @@
"| Variable a has been assigned the value 10\n"
};
compiler.writeToFile(testNormalFile, sources2);
- for (String feedback : new String[]{"/f", "/feedback"}) {
- for (String feedbackState : new String[]{"n", "normal", "v", "verbose"}) {
- String f = null;
- if (feedbackState.startsWith("n")) {
- f = "normal";
- } else if (feedbackState.startsWith("v")) {
- f = "verbose";
- }
- final String finalF = f;
+ for (String feedback : new String[]{"/set f", "/set feedback"}) {
+ for (String feedbackState : new String[]{"n", "normal", "o", "off"}) {
test(
- a -> assertCommand(a, feedback + " " + feedbackState, "| Feedback mode: " + finalF +"\n"),
+ a -> assertCommand(a, feedback + " " + feedbackState, "| Feedback mode: normal\n"),
a -> assertCommand(a, sources[0], output[0]),
a -> assertCommand(a, sources[1], output[1]),
a -> assertCommand(a, sources[2], output[2]),
a -> assertCommand(a, sources[3], output[3]),
- a -> assertCommand(a, "/o " + testNormalFile.toString(),
- "| Modified variable a of type int\n" +
- "| Modified method f()\n" +
- "| Update overwrote method f()\n" +
- "| Modified class A\n" +
- "| Update overwrote class A\n" +
- "| Variable a has been assigned the value 10\n")
+ a -> assertCommand(a, "/o " + testNormalFile.toString(), "")
);
}
}
}
- public void testFeedbackDefault() {
- Compiler compiler = new Compiler();
- Path testDefaultFile = compiler.getPath("testDefaultFeedback");
- String[] sources = new String[] {"int a", "void f() {}", "class A {}", "a = 10"};
- String[] output = new String[] {
- "| Added variable a of type int\n",
- "| Added method f()\n",
- "| Added class A\n",
- "| Variable a has been assigned the value 10\n"
- };
- compiler.writeToFile(testDefaultFile, sources);
- for (String defaultFeedback : new String[]{"", "d", "default"}) {
- test(
- a -> assertCommand(a, "/feedback o", ""),
- a -> assertCommand(a, "int x", ""),
- a -> assertCommand(a, "/feedback " + defaultFeedback, "| Feedback mode: default\n"),
- a -> assertCommand(a, sources[0], output[0]),
- a -> assertCommand(a, sources[1], output[1]),
- a -> assertCommand(a, sources[2], output[2]),
- a -> assertCommand(a, sources[3], output[3]),
- a -> assertCommand(a, "/o " + testDefaultFile.toString(), "")
- );
- }
- }
-
public void testDrop() {
test(false, new String[]{"-nostartup"},
a -> assertVariable(a, "int", "a"),
@@ -906,7 +847,7 @@
public void testCommandPrefix() {
test(a -> assertCommandCheckOutput(a, "/s",
- assertStartsWith("| Command: /s is ambiguous: /seteditor, /save, /setstart")),
+ assertStartsWith("| Command: /s is ambiguous: /save, /set")),
a -> assertCommand(a, "int var", "| Added variable var of type int\n"),
a -> assertCommandCheckOutput(a, "/va",
assertStartsWith("| int var = 0")),
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/jdk/jshell/ToolFormatTest.java Tue Mar 08 11:53:35 2016 -0800
@@ -0,0 +1,159 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @bug 8148316 8148317
+ * @summary Tests for output customization
+ * @library /tools/lib
+ * @build KullaTesting TestingInputStream ToolBox Compiler
+ * @run testng ToolFormatTest
+ */
+import org.testng.annotations.Test;
+
+@Test
+public class ToolFormatTest extends ReplToolTesting {
+
+ public void testSetFormat() {
+ 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 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) -> assertCommandOutputStartsWith(a, "/set feedback normal", "| Feedback mode: normal")
+ );
+ } finally {
+ assertCommandCheckOutput(false, "/set feedback normal", s -> {
+ });
+ }
+ }
+
+ public void testNewModeQuiet() {
+ try {
+ test(
+ (a) -> assertCommandOutputStartsWith(a, "/set newmode nmq quiet normal", "| Created new feedback mode: nmq"),
+ (a) -> assertCommand(a, "/set feedback nmq", ""),
+ (a) -> assertCommand(a, "/se ne nmq2 q nor", ""),
+ (a) -> assertCommand(a, "/se fee nmq2", ""),
+ (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")
+ );
+ } finally {
+ assertCommandCheckOutput(false, "/set feedback normal", s -> {
+ });
+ }
+ }
+
+ 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"))
+ );
+ } finally {
+ assertCommandCheckOutput(false, "/set feedback normal", s -> {
+ });
+ }
+ }
+
+ public void testSetHelp() {
+ try {
+ test(
+ (a) -> assertCommandOutputContains(a, "/help /set", "command to launch"),
+ (a) -> assertCommandOutputContains(a, "/help /set format", "vardecl"),
+ (a) -> assertCommandOutputContains(a, "/hel /se for", "vardecl"),
+ (a) -> assertCommandOutputContains(a, "/help /set editor", "temporary file")
+ );
+ } finally {
+ assertCommandCheckOutput(false, "/set feedback normal", s -> {
+ });
+ }
+ }
+
+ public void testSetHelpError() {
+ 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", "| 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")
+ );
+ } finally {
+ assertCommandCheckOutput(false, "/set feedback normal", s -> {
+ });
+ }
+ }
+}