8151755: jshell tool: properly cover resolution issues in output configuration
authorrfield
Fri, 25 Mar 2016 18:36:19 -0700
changeset 36718 bf40906bf49d
parent 36717 66bc9949f8b3
child 36719 dd54d618e1c8
8151755: jshell tool: properly cover resolution issues in output configuration 8152246: jshell tool: history overflow Reviewed-by: jlahoda
langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/Feedback.java
langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/JShellTool.java
langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/resources/l10n.properties
langtools/test/jdk/jshell/ReplToolTesting.java
langtools/test/jdk/jshell/ToolBasicTest.java
langtools/test/jdk/jshell/ToolFormatTest.java
langtools/test/jdk/jshell/ToolReloadTest.java
--- a/langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/Feedback.java	Wed Mar 23 21:44:24 2016 -0700
+++ b/langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/Feedback.java	Fri Mar 25 18:36:19 2016 -0700
@@ -28,18 +28,16 @@
 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;
+import static java.util.stream.Collectors.joining;
 
 /**
  * Feedback customization support
@@ -57,29 +55,38 @@
     // Mapping of mode names to mode modes
     private final Map<String, Mode> modeMap = new HashMap<>();
 
+    // Mapping selector enum names to enums
+    private final Map<String, Selector<?>> selectorMap = new HashMap<>();
+
+    private static final long ALWAYS = bits(FormatCase.all, FormatAction.all, FormatWhen.all,
+            FormatResolve.all, FormatUnresolved.all, FormatErrors.all);
+    private static final long ANY = 0L;
+
     public boolean shouldDisplayCommandFluff() {
         return mode.commandFluff;
     }
 
     public String getPre() {
-        return mode.pre;
+        return mode.format("pre", ANY);
     }
 
     public String getPost() {
-        return mode.post;
+        return mode.format("post", ANY);
     }
 
     public String getErrorPre() {
-        return mode.errorPre;
+        return mode.format("errorpre", ANY);
     }
 
     public String getErrorPost() {
-        return mode.errorPost;
+        return mode.format("errorpost", ANY);
     }
 
-    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 format(FormatCase fc, FormatAction fa, FormatWhen fw,
+                    FormatResolve fr, FormatUnresolved fu, FormatErrors fe,
+                    String name, String type, String value, String unresolved, List<String> errorLines) {
+        return mode.format(fc, fa, fw, fr, fu, fe,
+                name, type, value, unresolved, errorLines);
     }
 
     public String getPrompt(String nextId) {
@@ -91,184 +98,76 @@
     }
 
     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();
+        return new Setter(tool, at).setFeedback();
     }
 
     public boolean setFormat(JShellTool tool, ArgTokenizer at) {
-        return new FormatSetter(tool, at).setFormat();
+        return new Setter(tool, at).setFormat();
     }
 
     public boolean setNewMode(JShellTool tool, ArgTokenizer at) {
-        return new FormatSetter(tool, at).setNewMode();
+        return new Setter(tool, at).setNewMode();
     }
 
     public boolean setPrompt(JShellTool tool, ArgTokenizer at) {
-        return new FormatSetter(tool, at).setPrompt();
+        return new Setter(tool, at).setPrompt();
     }
 
     public void printFeedbackHelp(JShellTool tool) {
-        new FormatSetter(tool, null).printFeedbackHelp();
-    }
-
-    public void printFieldHelp(JShellTool tool) {
-        new FormatSetter(tool, null).printFieldHelp();
+        new Setter(tool, null).printFeedbackHelp();
     }
 
     public void printFormatHelp(JShellTool tool) {
-        new FormatSetter(tool, null).printFormatHelp();
+        new Setter(tool, null).printFormatHelp();
     }
 
     public void printNewModeHelp(JShellTool tool) {
-        new FormatSetter(tool, null).printNewModeHelp();
+        new Setter(tool, null).printNewModeHelp();
     }
 
     public void printPromptHelp(JShellTool tool) {
-        new FormatSetter(tool, null).printPromptHelp();
+        new Setter(tool, null).printPromptHelp();
+    }
+
+    {
+        for (FormatCase e : EnumSet.allOf(FormatCase.class))
+            selectorMap.put(e.name().toLowerCase(Locale.US), e);
+        for (FormatAction e : EnumSet.allOf(FormatAction.class))
+            selectorMap.put(e.name().toLowerCase(Locale.US), e);
+        for (FormatResolve e : EnumSet.allOf(FormatResolve.class))
+            selectorMap.put(e.name().toLowerCase(Locale.US), e);
+        for (FormatUnresolved e : EnumSet.allOf(FormatUnresolved.class))
+            selectorMap.put(e.name().toLowerCase(Locale.US), e);
+        for (FormatErrors e : EnumSet.allOf(FormatErrors.class))
+            selectorMap.put(e.name().toLowerCase(Locale.US), e);
+        for (FormatWhen e : EnumSet.allOf(FormatWhen.class))
+            selectorMap.put(e.name().toLowerCase(Locale.US), e);
     }
 
     /**
      * Holds all the context of a mode mode
      */
-    private class Mode {
+    private static class Mode {
 
-        // Use name of mode mode
-
+        // Name of 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";
+        // Event cases: class, method, expression, ...
+        final Map<String, List<Setting>> cases;
 
         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)
-                        : "";
+        static class Setting {
+            final long enumBits;
+            final String format;
+            Setting(long enumBits, String format) {
+                this.enumBits = enumBits;
+                this.format = format;
             }
-
-            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);
         }
 
         /**
@@ -280,41 +179,20 @@
         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, "");
-                    }
-                }
-            }
+            cases = new HashMap<>();
+            add("name",       new Setting(ALWAYS, "%1$s"));
+            add("type",       new Setting(ALWAYS, "%2$s"));
+            add("value",      new Setting(ALWAYS, "%3$s"));
+            add("unresolved", new Setting(ALWAYS, "%4$s"));
+            add("errors",     new Setting(ALWAYS, "%5$s"));
+            add("err",        new Setting(ALWAYS, "%6$s"));
 
-            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());
-                }
-            }
+            add("errorline",  new Setting(ALWAYS, "    {err}%n"));
 
-            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());
-            }
+            add("pre",        new Setting(ALWAYS, "|  "));
+            add("post",       new Setting(ALWAYS, "%n"));
+            add("errorpre",   new Setting(ALWAYS, "|  "));
+            add("errorpost",  new Setting(ALWAYS, "%n"));
         }
 
         /**
@@ -322,132 +200,91 @@
          *
          * @param name
          * @param commandFluff True if should display command fluff messages
-         * @param m Mode to copy
+         * @param m Mode to copy, or null for no fresh
          */
         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);
-                }
-            }
+            cases = new HashMap<>();
 
-            actions = new EnumMap<>(FormatAction.class);
-            for (FormatAction fa : FormatAction.values()) {
-                EnumMap<FormatWhen, String> afw = new EnumMap<>(m.actions.get(fa));
-                actions.put(fa, afw);
-            }
+            m.cases.entrySet().stream()
+                    .forEach(fes -> fes.getValue()
+                    .forEach(ing -> add(fes.getKey(), ing)));
 
-            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);
-                    }
-                }
+        private boolean add(String field, Setting ing) {
+            List<Setting> settings =  cases.computeIfAbsent(field, k -> new ArrayList<>());
+            if (settings == null) {
+                return false;
             }
+            settings.add(ing);
+            return true;
         }
 
-        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 set(String field,
+                Collection<FormatCase> cc, Collection<FormatAction> ca, Collection<FormatWhen> cw,
+                Collection<FormatResolve> cr, Collection<FormatUnresolved> cu, Collection<FormatErrors> ce,
+                String format) {
+            long bits = bits(cc, ca, cw, cr, cu, ce);
+            set(field, bits, 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 set(String field, long bits, String format) {
+            add(field, new Setting(bits, 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;
+        /**
+         * Lookup format Replace fields with context specific formats.
+         *
+         * @return format string
+         */
+        String format(String field, long bits) {
+            List<Setting> settings = cases.get(field);
+            if (settings == null) {
+                return ""; //TODO error?
+            }
+            String format = null;
+            for (int i = settings.size() - 1; i >= 0; --i) {
+                Setting ing = settings.get(i);
+                long mask = ing.enumBits;
+                if ((bits & mask) == bits) {
+                    format = ing.format;
+                    break;
+                }
+            }
+            if (format == null || format.isEmpty()) {
+                return "";
+            }
+            Matcher m = FIELD_PATTERN.matcher(format);
+            StringBuffer sb = new StringBuffer(format.length());
+            while (m.find()) {
+                String fieldName = m.group(1);
+                String sub = format(fieldName, bits);
+                m.appendReplacement(sb, Matcher.quoteReplacement(sub));
+            }
+            m.appendTail(sb);
+            return sb.toString();
         }
 
-        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;
+        String format(FormatCase fc, FormatAction fa, FormatWhen fw,
+                    FormatResolve fr, FormatUnresolved fu, FormatErrors fe,
+                    String name, String type, String value, String unresolved, List<String> errorLines) {
+            long bits = bits(fc, fa, fw, fr, fu, fe);
+            String fname = name==null? "" : name;
+            String ftype = type==null? "" : type;
+            String fvalue = value==null? "" : value;
+            String funresolved = unresolved==null? "" : unresolved;
+            String errors = errorLines.stream()
+                    .map(el -> String.format(
+                            format("errorline", bits),
+                            fname, ftype, fvalue, funresolved, "*cannot-use-errors-here*", el))
+                    .collect(joining());
+            return String.format(
+                    format("display", bits),
+                    fname, ftype, fvalue, funresolved, errors, "*cannot-use-err-here*");
         }
 
         void setPrompts(String prompt, String continuationPrompt) {
@@ -464,50 +301,82 @@
         }
     }
 
-    /**
-     * 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;
+    // Representation of one instance of all the enum values as bits in a long
+    private static long bits(FormatCase fc, FormatAction fa, FormatWhen fw,
+            FormatResolve fr, FormatUnresolved fu, FormatErrors fe) {
+        long res = 0L;
+        res |= 1 << fc.ordinal();
+        res <<= FormatAction.count;
+        res |= 1 << fa.ordinal();
+        res <<= FormatWhen.count;
+        res |= 1 << fw.ordinal();
+        res <<= FormatResolve.count;
+        res |= 1 << fr.ordinal();
+        res <<= FormatUnresolved.count;
+        res |= 1 << fu.ordinal();
+        res <<= FormatErrors.count;
+        res |= 1 << fe.ordinal();
+        return res;
+    }
 
-        FormatField(String s) {
-            this.form = s;
-        }
+    // Representation of a space of enum values as or'edbits in a long
+    private static long bits(Collection<FormatCase> cc, Collection<FormatAction> ca, Collection<FormatWhen> cw,
+                Collection<FormatResolve> cr, Collection<FormatUnresolved> cu, Collection<FormatErrors> ce) {
+        long res = 0L;
+        for (FormatCase fc : cc)
+            res |= 1 << fc.ordinal();
+        res <<= FormatAction.count;
+        for (FormatAction fa : ca)
+            res |= 1 << fa.ordinal();
+        res <<= FormatWhen.count;
+        for (FormatWhen fw : cw)
+            res |= 1 << fw.ordinal();
+        res <<= FormatResolve.count;
+        for (FormatResolve fr : cr)
+            res |= 1 << fr.ordinal();
+        res <<= FormatUnresolved.count;
+        for (FormatUnresolved fu : cu)
+            res |= 1 << fu.ordinal();
+        res <<= FormatErrors.count;
+        for (FormatErrors fe : ce)
+            res |= 1 << fe.ordinal();
+        return res;
+    }
 
-        FormatField() {
-            this.form = null;
-        }
+    interface Selector<E extends Enum<E> & Selector<E>> {
+        SelectorCollector<E> collector(Setter.SelectorList sl);
+        String doc();
     }
 
     /**
      * 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}");
+    public enum FormatCase implements Selector<FormatCase> {
+        IMPORT("import declaration"),
+        CLASS("class declaration"),
+        INTERFACE("interface declaration"),
+        ENUM("enum declaration"),
+        ANNOTATION("annotation interface declaration"),
+        METHOD("method declaration -- note: {type}==parameter-types"),
+        VARDECL("variable declaration without init"),
+        VARINIT("variable declaration with init"),
+        EXPRESSION("expression -- note: {name}==scratch-variable-name"),
+        VARVALUE("variable value expression"),
+        ASSIGNMENT("assign variable"),
+        STATEMENT("statement");
         String doc;
+        static final EnumSet<FormatCase> all = EnumSet.allOf(FormatCase.class);
+        static final int count = all.size();
+
+        @Override
+        public SelectorCollector<FormatCase> collector(Setter.SelectorList sl) {
+            return sl.cases;
+        }
+
+        @Override
+        public String doc() {
+            return doc;
+        }
 
         private FormatCase(String doc) {
             this.doc = doc;
@@ -517,14 +386,26 @@
     /**
      * The event actions
      */
-    public enum FormatAction {
+    public enum FormatAction implements Selector<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");
+        USED("snippet was used when it cannot be");
         String doc;
+        static final EnumSet<FormatAction> all = EnumSet.allOf(FormatAction.class);
+        static final int count = all.size();
+
+        @Override
+        public SelectorCollector<FormatAction> collector(Setter.SelectorList sl) {
+            return sl.actions;
+        }
+
+        @Override
+        public String doc() {
+            return doc;
+        }
 
         private FormatAction(String doc) {
             this.doc = doc;
@@ -534,10 +415,22 @@
     /**
      * When the event occurs: primary or update
      */
-    public enum FormatWhen {
+    public enum FormatWhen implements Selector<FormatWhen> {
         PRIMARY("the entered snippet"),
         UPDATE("an update to a dependent snippet");
         String doc;
+        static final EnumSet<FormatWhen> all = EnumSet.allOf(FormatWhen.class);
+        static final int count = all.size();
+
+        @Override
+        public SelectorCollector<FormatWhen> collector(Setter.SelectorList sl) {
+            return sl.whens;
+        }
+
+        @Override
+        public String doc() {
+            return doc;
+        }
 
         private FormatWhen(String doc) {
             this.doc = doc;
@@ -545,46 +438,119 @@
     }
 
     /**
-     * Resolution problems with event
+     * Resolution problems
      */
-    public enum FormatResolve {
+    public enum FormatResolve implements Selector<FormatResolve> {
         OK("resolved correctly"),
         DEFINED("defined despite recoverably unresolved references"),
         NOTDEFINED("not defined because of recoverably unresolved references");
         String doc;
+        static final EnumSet<FormatResolve> all = EnumSet.allOf(FormatResolve.class);
+        static final int count = all.size();
+
+        @Override
+        public SelectorCollector<FormatResolve> collector(Setter.SelectorList sl) {
+            return sl.resolves;
+        }
+
+        @Override
+        public String doc() {
+            return doc;
+        }
 
         private FormatResolve(String doc) {
             this.doc = doc;
         }
     }
 
+    /**
+     * Count of unresolved references
+     */
+    public enum FormatUnresolved implements Selector<FormatUnresolved> {
+        UNRESOLVED0("no names are unresolved"),
+        UNRESOLVED1("one name is unresolved"),
+        UNRESOLVED2("two or more names are unresolved");
+        String doc;
+        static final EnumSet<FormatUnresolved> all = EnumSet.allOf(FormatUnresolved.class);
+        static final int count = all.size();
+
+        @Override
+        public SelectorCollector<FormatUnresolved> collector(Setter.SelectorList sl) {
+            return sl.unresolvedCounts;
+        }
+
+        @Override
+        public String doc() {
+            return doc;
+        }
+
+        private FormatUnresolved(String doc) {
+            this.doc = doc;
+        }
+    }
+
+    /**
+     * Count of unresolved references
+     */
+    public enum FormatErrors implements Selector<FormatErrors> {
+        ERROR0("no errors"),
+        ERROR1("one error"),
+        ERROR2("two or more errors");
+        String doc;
+        static final EnumSet<FormatErrors> all = EnumSet.allOf(FormatErrors.class);
+        static final int count = all.size();
+
+        @Override
+        public SelectorCollector<FormatErrors> collector(Setter.SelectorList sl) {
+            return sl.errorCounts;
+        }
+
+        @Override
+        public String doc() {
+            return doc;
+        }
+
+        private FormatErrors(String doc) {
+            this.doc = doc;
+        }
+    }
+
+    class SelectorCollector<E extends Enum<E> & Selector<E>> {
+        final EnumSet<E> all;
+        EnumSet<E> set = null;
+        SelectorCollector(EnumSet<E> all) {
+            this.all = all;
+        }
+        void add(Object o) {
+            @SuppressWarnings("unchecked")
+            E e = (E) o;
+            if (set == null) {
+                set = EnumSet.of(e);
+            } else {
+                set.add(e);
+            }
+        }
+
+        boolean isEmpty() {
+            return set == null;
+        }
+
+        EnumSet<E> getSet() {
+            return set == null
+                    ? all
+                    : set;
+        }
+    }
+
     // 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 {
+    // For both /set format  -- Parse arguments, setting custom format, or printing error
+    private class Setter {
 
         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) {
+        Setter(JShellTool tool, ArgTokenizer at) {
             this.tool = tool;
             this.at = at;
         }
@@ -593,6 +559,10 @@
             tool.hard(format, args);
         }
 
+        void hardrb(String key) {
+            tool.hardrb(key);
+        }
+
         <E extends Enum<E>> void hardEnums(EnumSet<E> es, Function<E, String> e2s) {
             hardPairs(es.stream(), ev -> ev.name().toLowerCase(Locale.US), e2s);
         }
@@ -681,137 +651,31 @@
         // 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));
-                }
+            String field = at.next();
+            if (field == null || at.isQuoted()) {
+                errorat("Expected field name missing");
+                valid = false;
             }
-            if (!valid) {
-                fluff("See '/help /set format' for help");
+            String format = valid? nextFormat() : null;
+            String slRaw;
+            List<SelectorList> slList = new ArrayList<>();
+            while (valid && (slRaw = at.next()) != null) {
+                SelectorList sl = new SelectorList();
+                sl.parseSelectorList(slRaw);
+                slList.add(sl);
             }
-            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 (slList.isEmpty()) {
+                    m.set(field, ALWAYS, format);
+                } else {
+                    slList.stream()
+                            .forEach(sl -> m.set(field,
+                                sl.cases.getSet(), sl.actions.getSet(), sl.whens.getSet(),
+                                sl.resolves.getSet(), sl.unresolvedCounts.getSet(), sl.errorCounts.getSet(),
+                                format));
                 }
-            }
-            if (!valid) {
-                fluff("See '/help /set field' for help");
+            } else {
+                fluff("See '/help /set format' for help");
             }
             return valid;
         }
@@ -843,12 +707,11 @@
                 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);
+                    errorat("Matches 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;
             }
         }
@@ -869,181 +732,75 @@
             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");
-        }
+        class SelectorList {
 
-        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");
-        }
+            SelectorCollector<FormatCase> cases = new SelectorCollector<>(FormatCase.all);
+            SelectorCollector<FormatAction> actions = new SelectorCollector<>(FormatAction.all);
+            SelectorCollector<FormatWhen> whens = new SelectorCollector<>(FormatWhen.all);
+            SelectorCollector<FormatResolve> resolves = new SelectorCollector<>(FormatResolve.all);
+            SelectorCollector<FormatUnresolved> unresolvedCounts = new SelectorCollector<>(FormatUnresolved.all);
+            SelectorCollector<FormatErrors> errorCounts = new SelectorCollector<>(FormatErrors.all);
 
-        /**
-         * 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));
+            final void parseSelectorList(String sl) {
+                for (String s : sl.split("-")) {
+                    SelectorCollector<?> lastCollector = null;
+                    for (String as : s.split(",")) {
+                        if (!as.isEmpty()) {
+                            Selector<?> sel = selectorMap.get(as);
+                            if (sel == null) {
+                                errorat("Not a valid selector %s in %s", as, s);
+                                valid = false;
+                                return;
+                            }
+                            SelectorCollector<?> collector = sel.collector(this);
+                            if (lastCollector == null) {
+                                if (!collector.isEmpty()) {
+                                    errorat("Selector kind in multiple sections of selector list %s in %s", as, s);
+                                    valid = false;
+                                    return;
+                                }
+                            } else if (collector != lastCollector) {
+                                errorat("Different selector kinds in same sections of selector list %s in %s", as, s);
+                                valid = false;
+                                return;
+                            }
+                            collector.add(sel);
+                            lastCollector = collector;
+                        }
+                    }
                 }
             }
-            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:");
+            hardrb("help.set.format");
+            hardrb("help.set.format.case");
             hardEnums(EnumSet.allOf(FormatCase.class), ev -> ev.doc);
-            hard("For action, the field values are:");
+            hardrb("help.set.format.action");
             hardEnums(EnumSet.allOf(FormatAction.class), ev -> ev.doc);
-            hard("For when, the field values are:");
+            hardrb("help.set.format.when");
             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:");
+            hardrb("help.set.format.resolve");
             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");
+            hardrb("help.set.format.unresolved");
+            hardEnums(EnumSet.allOf(FormatUnresolved.class), ev -> ev.doc);
+            hardrb("help.set.format.errors");
+            hardEnums(EnumSet.allOf(FormatErrors.class), ev -> ev.doc);
+            hardrb("help.set.format.end");
         }
 
         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:");
+            hardrb("help.set.feedback");
             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.");
+            hardrb("help.set.newmode");
         }
 
         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.");
+            hardrb("help.set.prompt");
         }
     }
 }
--- a/langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/JShellTool.java	Wed Mar 23 21:44:24 2016 -0700
+++ b/langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/JShellTool.java	Fri Mar 25 18:36:19 2016 -0700
@@ -75,7 +75,6 @@
 import jdk.jshell.PersistentSnippet;
 import jdk.jshell.Snippet;
 import jdk.jshell.Snippet.Status;
-import jdk.jshell.Snippet.SubKind;
 import jdk.jshell.SnippetEvent;
 import jdk.jshell.SourceCodeAnalysis;
 import jdk.jshell.SourceCodeAnalysis.CompletionInfo;
@@ -95,10 +94,11 @@
 import java.util.function.Supplier;
 import jdk.internal.jshell.tool.Feedback.FormatAction;
 import jdk.internal.jshell.tool.Feedback.FormatCase;
+import jdk.internal.jshell.tool.Feedback.FormatErrors;
 import jdk.internal.jshell.tool.Feedback.FormatResolve;
+import jdk.internal.jshell.tool.Feedback.FormatUnresolved;
 import jdk.internal.jshell.tool.Feedback.FormatWhen;
 import static java.util.stream.Collectors.toList;
-import static jdk.jshell.Snippet.Kind.METHOD;
 import static java.util.stream.Collectors.toMap;
 import static jdk.jshell.Snippet.SubKind.VAR_VALUE_SUBKIND;
 
@@ -113,6 +113,12 @@
     private static final Pattern HISTORY_ALL_START_FILENAME = Pattern.compile(
             "((?<cmd>(all|history|start))(\\z|\\p{javaWhitespace}+))?(?<filename>.*)");
     private static final String RECORD_SEPARATOR = "\u241E";
+    private static final String RB_NAME_PREFIX  = "jdk.internal.jshell.tool.resources";
+    private static final String VERSION_RB_NAME = RB_NAME_PREFIX + ".version";
+    private static final String L10N_RB_NAME    = RB_NAME_PREFIX + ".l10n";
+    private static ResourceBundle versionRB = null;
+    private static ResourceBundle outputRB  = null;
+
 
     final InputStream cmdin;
     final PrintStream cmdout;
@@ -148,82 +154,18 @@
         this.userout = userout;
         this.usererr = usererr;
         this.prefs = prefs;
-        initializeFeedbackModes();
-    }
-
-    /**
-     * Create the default set of feedback modes
-     */
-    final void initializeFeedbackModes() {
-        // Initialize normal feedback mode
-        cmdSet("newmode normal command");
-        cmdSet("prompt normal '\n-> ' '>> '");
-        cmdSet("field normal pre '|  '");
-        cmdSet("field normal post '%n'");
-        cmdSet("field normal errorpre '|  '");
-        cmdSet("field normal errorpost '%n'");
-        cmdSet("field normal action 'Added' added-primary");
-        cmdSet("field normal action 'Modified' modified-primary");
-        cmdSet("field normal action 'Replaced' replaced-primary");
-        cmdSet("field normal action 'Overwrote' overwrote-primary");
-        cmdSet("field normal action 'Dropped' dropped-primary");
-        cmdSet("field normal action 'Rejected' rejected-primary");
-        cmdSet("field normal action '  Update added' added-update");
-        cmdSet("field normal action '  Update modified' modified-update");
-        cmdSet("field normal action '  Update replaced' replaced-update");
-        cmdSet("field normal action '  Update overwrote' overwrote-update");
-        cmdSet("field normal action '  Update dropped' dropped-update");
-        cmdSet("field normal action '  Update rejected' rejected-update");
-        cmdSet("field normal resolve '' ok-*");
-        cmdSet("field normal resolve ', however, it cannot be invoked until%s is declared' defined-primary");
-        cmdSet("field normal resolve ', however, it cannot be referenced until%s is declared' notdefined-primary");
-        cmdSet("field normal resolve ' which cannot be invoked until%s is declared' defined-update");
-        cmdSet("field normal resolve ' which cannot be referenced until%s is declared' notdefined-update");
-        cmdSet("field normal name '%s'");
-        cmdSet("field normal type '%s'");
-        cmdSet("field normal result '%s'");
-
-        cmdSet("format normal '' *-*-*");
-
-        cmdSet("format normal '{pre}{action} class {name}{resolve}{post}' class");
-        cmdSet("format normal '{pre}{action} interface {name}{resolve}{post}' interface");
-        cmdSet("format normal '{pre}{action} enum {name}{resolve}{post}' enum");
-        cmdSet("format normal '{pre}{action} annotation interface {name}{resolve}{post}' annotation");
-
-        cmdSet("format normal '{pre}{action} method {name}({type}){resolve}{post}' method");
-
-        cmdSet("format normal '{pre}{action} variable {name} of type {type}{resolve}{post}' vardecl");
-        cmdSet("format normal '{pre}{action} variable {name} of type {type} with initial value {result}{resolve}{post}' varinit");
-        cmdSet("format normal '{pre}{action} variable {name}{resolve}{post}' vardeclrecoverable");
-        cmdSet("format normal '{pre}{action} variable {name}, reset to null{post}' varreset-*-update");
-
-        cmdSet("format normal '{pre}Expression value is: {result}{post}" +
-                "{pre}  assigned to temporary variable {name} of type {type}{post}' expression");
-        cmdSet("format normal '{pre}Variable {name} of type {type} has value {result}{post}' varvalue");
-        cmdSet("format normal '{pre}Variable {name} has been assigned the value {result}{post}' assignment");
-
-        cmdSet("feedback normal");
-
-        // Initialize off feedback mode
-        cmdSet("newmode off quiet");
-        cmdSet("prompt off '-> ' '>> '");
-        cmdSet("field off pre '|  '");
-        cmdSet("field off post '%n'");
-        cmdSet("field off errorpre '|  '");
-        cmdSet("field off errorpost '%n'");
-        cmdSet("format off '' *-*-*");
     }
 
     private IOContext input = null;
     private boolean regenerateOnDeath = true;
     private boolean live = false;
+    private boolean feedbackInitialized = false;
 
     SourceCodeAnalysis analysis;
     JShell state = null;
     Subscription shutdownSubscription = null;
 
     private boolean debug = false;
-    private boolean displayPrompt = true;
     public boolean testPrompt = false;
     private String cmdlineClasspath = null;
     private String cmdlineStartup = null;
@@ -326,6 +268,43 @@
         }
     }
 
+    /**
+     * Print using resource bundle look-up and adding prefix and postfix
+     *
+     * @param key the resource key
+     */
+    String getResourceString(String key) {
+        if (outputRB == null) {
+            try {
+                outputRB = ResourceBundle.getBundle(L10N_RB_NAME);
+            } catch (MissingResourceException mre) {
+                error("Cannot find ResourceBundle: %s", L10N_RB_NAME);
+                return "";
+            }
+        }
+        String s;
+        try {
+            s = outputRB.getString(key);
+        } catch (MissingResourceException mre) {
+            error("Missing resource: %s in %s", key, L10N_RB_NAME);
+            return "";
+        }
+        return s;
+    }
+
+    /**
+     * Print using resource bundle look-up and adding prefix and postfix
+     *
+     * @param key the resource key
+     */
+    void hardrb(String key) {
+        String s = getResourceString(key);
+        String out = feedback.getPre()
+                + s.substring(0, s.length() - 1).replaceAll("\\R", "\n" + feedback.getPre())
+                + s.substring(s.length() - 1, s.length());
+        cmdout.print(out);
+    }
+
     <T> void hardPairs(Stream<T> stream, Function<T, String> a, Function<T, String> b) {
         Map<String, String> a2b = stream.collect(toMap(a, b,
                 (m1, m2) -> m1,
@@ -343,26 +322,6 @@
     }
 
     /**
-     * User custom feedback mode only
-     *
-     * @param fcase Event to report
-     * @param update Is this an update (rather than primary)
-     * @param fa Action
-     * @param fr Resolution status
-     * @param name Name string
-     * @param type Type string or null
-     * @param result Result value or null
-     * @param unresolved The unresolved symbols
-     */
-    void custom(FormatCase fcase, boolean update, FormatAction fa, FormatResolve fr,
-            String name, String type, String unresolved, String result) {
-        String format = feedback.getFormat(fcase,
-                (update ? FormatWhen.UPDATE : FormatWhen.PRIMARY), fa, fr,
-                name != null, type != null, result != null);
-        fluffRaw(format, unresolved, name, type, result);
-    }
-
-    /**
      * Trim whitespace off end of string
      *
      * @param s
@@ -544,7 +503,6 @@
                         ? currentNameSpace.tid(sn)
                         : errorNamespace.tid(sn))
                 .build();
-        analysis = state.sourceCodeAnalysis();
         shutdownSubscription = state.onShutdown((JShell deadState) -> {
             if (deadState == state) {
                 hard("State engine terminated.");
@@ -552,7 +510,12 @@
                 live = false;
             }
         });
+        analysis = state.sourceCodeAnalysis();
         live = true;
+        if (!feedbackInitialized) {
+            startUpRun(getResourceString("startup.feedback"));
+            feedbackInitialized = true;
+        }
 
         if (cmdlineClasspath != null) {
             state.addToClasspath(cmdlineClasspath);
@@ -568,12 +531,16 @@
         } else {
             start = cmdlineStartup;
         }
+        startUpRun(start);
+        currentNameSpace = mainNamespace;
+    }
+    //where
+    private void startUpRun(String start) {
         try (IOContext suin = new FileScannerIOContext(new StringReader(start))) {
             run(suin);
         } catch (Exception ex) {
             hard("Unexpected exception reading start-up: %s\n", ex);
         }
-        currentNameSpace = mainNamespace;
     }
 
     private void closeState() {
@@ -596,7 +563,7 @@
             String incomplete = "";
             while (live) {
                 String prompt;
-                if (displayPrompt) {
+                if (currentNameSpace == mainNamespace) {
                     prompt = testPrompt
                                     ? incomplete.isEmpty()
                                             ? "\u0005" //ENQ
@@ -679,21 +646,22 @@
             cmd = cmd.substring(0, idx);
         }
         Command[] candidates = findCommand(cmd, c -> c.kind.isRealCommand);
-        if (candidates.length == 0) {
-            if (!rerunHistoryEntryById(cmd.substring(1))) {
-                error("No such command or snippet id: %s", cmd);
+        switch (candidates.length) {
+            case 0:
+                if (!rerunHistoryEntryById(cmd.substring(1))) {
+                    error("No such command or snippet id: %s", cmd);
+                    fluff("Type /help for help.");
+                }   break;
+            case 1:
+                Command command = candidates[0];
+                // If comand was successful and is of a replayable kind, add it the replayable history
+                if (command.run.apply(arg) && command.kind == CommandKind.REPLAY) {
+                    addToReplayHistory((command.command + " " + arg).trim());
+                }   break;
+            default:
+                error("Command: %s is ambiguous: %s", cmd, Arrays.stream(candidates).map(c -> c.command).collect(Collectors.joining(", ")));
                 fluff("Type /help for help.");
-            }
-        } else if (candidates.length == 1) {
-            Command command = candidates[0];
-
-            // If comand was successful and is of a replayable kind, add it the replayable history
-            if (command.run.apply(arg) && command.kind == CommandKind.REPLAY) {
-                addToReplayHistory((command.command + " " + arg).trim());
-            }
-        } else {
-            error("Command: %s is ambiguous: %s", cmd, Arrays.stream(candidates).map(c -> c.command).collect(Collectors.joining(", ")));
-            fluff("Type /help for help.");
+                break;
         }
     }
 
@@ -1022,7 +990,7 @@
                 "  -- Display information about the specified help subject. Example: /help intro\n",
                 arg -> cmdHelp(arg),
                 EMPTY_COMPLETION_PROVIDER));
-        registerCommand(new Command("/set", "editor|start|feedback|newmode|prompt|format|field ...", "set jshell configuration information",
+        registerCommand(new Command("/set", "editor|start|feedback|newmode|prompt|format ...", "set jshell configuration information",
                 "Set jshell configuration information, including:\n" +
                 "the external editor to use, the start-up definitions to use, a new feedback mode,\n" +
                 "the command prompt, the feedback mode to use, or the format of output.\n" +
@@ -1043,16 +1011,13 @@
                 "/set prompt <mode> \"<prompt>\" \"<continuation-prompt>\"\n" +
                 "  -- Set the displayed prompts for a given feedback mode.\n" +
                 "\n" +
-                "/set format <mode> \"<format>\" <selector>...\n" +
-                "  -- Configure a feedback mode by setting the format to use in a specified set of cases.\n" +
-                "\n" +
-                "/set field name|type|result|when|action|resolve|pre|post|errorpre|errorpost \"<format>\"  <format-case>...\n" +
-                "  -- Set the format of a field within the <format-string> of a \"/set format\" command\n" +
+                "/set format <mode> <field> \"<format>\" <selector>...\n" +
+                "  -- Configure a feedback mode by setting the format of a field when the selector matchs.\n" +
                 "\n" +
                 "To get more information about one of these forms, use /help with the form specified.\n" +
                 "For example:   /help /set format\n",
                 arg -> cmdSet(arg),
-                new FixedCompletionProvider("format", "field", "feedback", "prompt", "newmode", "start", "editor")));
+                new FixedCompletionProvider("format", "feedback", "prompt", "newmode", "start", "editor")));
         registerCommand(new Command("/?", "", "get information about jshell",
                 "Display information about jshell (abbreviation for /help).\n" +
                 "/?\n" +
@@ -1153,11 +1118,9 @@
 
     // --- Command implementations ---
 
-    private static final String[] setSub = new String[]{
-        "format", "field", "feedback", "newmode", "prompt", "editor", "start"};
+    private static final String[] SET_SUBCOMMANDS = new String[]{
+        "format", "feedback", "newmode", "prompt", "editor", "start"};
 
-    // The /set command.  Currently /set format, /set field and /set feedback.
-    // Other commands will fold here, see: 8148317
     final boolean cmdSet(String arg) {
         ArgTokenizer at = new ArgTokenizer(arg.trim());
         String which = setSubCommand(at);
@@ -1167,8 +1130,6 @@
         switch (which) {
             case "format":
                 return feedback.setFormat(this, at);
-            case "field":
-                return feedback.setField(this, at);
             case "feedback":
                 return feedback.setFeedback(this, at);
             case "newmode":
@@ -1229,9 +1190,6 @@
             case "format":
                 feedback.printFormatHelp(this);
                 return true;
-            case "field":
-                feedback.printFieldHelp(this);
-                return true;
             case "feedback":
                 feedback.printFeedbackHelp(this);
                 return true;
@@ -1265,13 +1223,13 @@
     }
 
     String setSubCommand(ArgTokenizer at) {
-        String[] matches = at.next(setSub);
+        String[] matches = at.next(SET_SUBCOMMANDS);
         if (matches == null) {
             error("The /set command requires arguments. See: /help /set");
             return null;
         } else if (matches.length == 0) {
             error("Not a valid argument to /set: %s", at.val());
-            fluff("/set is followed by one of: %s", Arrays.stream(setSub)
+            fluff("/set is followed by one of: %s", Arrays.stream(SET_SUBCOMMANDS)
                     .collect(Collectors.joining(", "))
             );
             return null;
@@ -1349,8 +1307,20 @@
         regenerateOnDeath = false;
         live = false;
         if (!replayableHistory.isEmpty()) {
-            prefs.put(REPLAY_RESTORE_KEY, replayableHistory.stream().reduce(
-                    (a, b) -> a + RECORD_SEPARATOR + b).get());
+            // Prevent history overflow by calculating what will fit, starting
+            // with must recent
+            int sepLen = RECORD_SEPARATOR.length();
+            int length = 0;
+            int first = replayableHistory.size();
+            while(length < Preferences.MAX_VALUE_LENGTH && --first >= 0) {
+                length += replayableHistory.get(first).length() + sepLen;
+            }
+            String hist = replayableHistory
+                    .subList(first + 1, replayableHistory.size())
+                    .stream()
+                    .reduce( (a, b) -> a + RECORD_SEPARATOR + b)
+                    .get();
+            prefs.put(REPLAY_RESTORE_KEY, hist);
         }
         fluff("Goodbye\n");
         return true;
@@ -1841,68 +1811,55 @@
                 .collect(toList());
     }
 
-    void printDiagnostics(String source, List<Diag> diagnostics, boolean embed) {
-        String padding = embed? "    " : "";
-        for (Diag diag : diagnostics) {
-            //assert diag.getSource().equals(source);
+    void displayDiagnostics(String source, Diag diag, List<String> toDisplay) {
+        for (String line : diag.getMessage(null).split("\\r?\\n")) { // TODO: Internationalize
+            if (!line.trim().startsWith("location:")) {
+                toDisplay.add(line);
+            }
+        }
 
-            if (!embed) {
-                if (diag.isError()) {
-                    hard("Error:");
-                } else {
-                    hard("Warning:");
-                }
+        int pstart = (int) diag.getStartPosition();
+        int pend = (int) diag.getEndPosition();
+        Matcher m = LINEBREAK.matcher(source);
+        int pstartl = 0;
+        int pendl = -2;
+        while (m.find(pstartl)) {
+            pendl = m.start();
+            if (pendl >= pstart) {
+                break;
+            } else {
+                pstartl = m.end();
             }
-
-            for (String line : diag.getMessage(null).split("\\r?\\n")) { // TODO: Internationalize
-                if (!line.trim().startsWith("location:")) {
-                    hard("%s%s", padding, line);
-                }
-            }
+        }
+        if (pendl < pstart) {
+            pendl = source.length();
+        }
+        toDisplay.add(source.substring(pstartl, pendl));
 
-            int pstart = (int) diag.getStartPosition();
-            int pend = (int) diag.getEndPosition();
-            Matcher m = LINEBREAK.matcher(source);
-            int pstartl = 0;
-            int pendl = -2;
-            while (m.find(pstartl)) {
-                pendl = m.start();
-                if (pendl >= pstart) {
-                    break;
-                } else {
-                    pstartl = m.end();
-                }
+        StringBuilder sb = new StringBuilder();
+        int start = pstart - pstartl;
+        for (int i = 0; i < start; ++i) {
+            sb.append(' ');
+        }
+        sb.append('^');
+        boolean multiline = pend > pendl;
+        int end = (multiline ? pendl : pend) - pstartl - 1;
+        if (end > start) {
+            for (int i = start + 1; i < end; ++i) {
+                sb.append('-');
             }
-            if (pendl < pstart) {
-                pendl = source.length();
+            if (multiline) {
+                sb.append("-...");
+            } else {
+                sb.append('^');
             }
-            fluff("%s%s", padding, source.substring(pstartl, pendl));
+        }
+        toDisplay.add(sb.toString());
 
-            StringBuilder sb = new StringBuilder();
-            int start = pstart - pstartl;
-            for (int i = 0; i < start; ++i) {
-                sb.append(' ');
-            }
-            sb.append('^');
-            boolean multiline = pend > pendl;
-            int end = (multiline ? pendl : pend) - pstartl - 1;
-            if (end > start) {
-                for (int i = start + 1; i < end; ++i) {
-                    sb.append('-');
-                }
-                if (multiline) {
-                    sb.append("-...");
-                } else {
-                    sb.append('^');
-                }
-            }
-            fluff("%s%s", padding, sb.toString());
-
-            debug("printDiagnostics start-pos = %d ==> %d -- wrap = %s", diag.getStartPosition(), start, this);
-            debug("Code: %s", diag.getCode());
-            debug("Pos: %d (%d - %d)", diag.getPosition(),
-                    diag.getStartPosition(), diag.getEndPosition());
-        }
+        debug("printDiagnostics start-pos = %d ==> %d -- wrap = %s", diag.getStartPosition(), start, this);
+        debug("Code: %s", diag.getCode());
+        debug("Pos: %d (%d - %d)", diag.getPosition(),
+                diag.getStartPosition(), diag.getEndPosition());
     }
 
     private String processSource(String srcInput) throws IllegalStateException {
@@ -1943,6 +1900,7 @@
         return failed;
     }
 
+    // Handle incoming snippet events -- return true on failure
     private boolean handleEvent(SnippetEvent ste) {
         Snippet sn = ste.snippet();
         if (sn == null) {
@@ -1953,162 +1911,49 @@
         String source = sn.source();
         if (ste.causeSnippet() == null) {
             // main event
-            printDiagnostics(source, diagnostics, false);
-            if (ste.status().isActive) {
+            for (Diag d : diagnostics) {
+                if (d.isError()) {
+                    hard("Error:");
+                } else {
+                    hard("Warning:");
+                }
+                List<String> disp = new ArrayList<>();
+                displayDiagnostics(source, d, disp);
+                disp.stream()
+                        .forEach(l -> hard(l));
+            }
+
+            if (ste.status() != Status.REJECTED) {
                 if (ste.exception() != null) {
                     if (ste.exception() instanceof EvalException) {
                         printEvalException((EvalException) ste.exception());
                         return true;
                     } else if (ste.exception() instanceof UnresolvedReferenceException) {
-                        printUnresolved((UnresolvedReferenceException) ste.exception());
+                        printUnresolvedException((UnresolvedReferenceException) ste.exception());
                     } else {
                         hard("Unexpected execution exception: %s", ste.exception());
                         return true;
                     }
                 } else {
-                    displayDeclarationAndValue(ste, false, ste.value());
+                    new DisplayEvent(ste, false, ste.value(), diagnostics).displayDeclarationAndValue();
                 }
-            } else if (ste.status() == Status.REJECTED) {
+            } else {
                 if (diagnostics.isEmpty()) {
                     hard("Failed.");
                 }
                 return true;
             }
-        } else if (ste.status() == Status.REJECTED) {
-            //TODO -- I don't believe updates can cause failures any more
-            hard("Caused failure of dependent %s --", ((DeclarationSnippet) sn).name());
-            printDiagnostics(source, diagnostics, true);
         } else {
             // Update
             if (sn instanceof DeclarationSnippet) {
-                // display update information
-                displayDeclarationAndValue(ste, true, ste.value());
+                List<Diag> other = errorsOnly(diagnostics);
 
-                List<Diag> other = errorsOnly(diagnostics);
-                if (other.size() > 0) {
-                    printDiagnostics(source, other, true);
-                }
+                // display update information
+                new DisplayEvent(ste, true, ste.value(), other).displayDeclarationAndValue();
             }
         }
         return false;
     }
-
-    @SuppressWarnings("fallthrough")
-    private void displayDeclarationAndValue(SnippetEvent ste, boolean update, String value) {
-        Snippet key = ste.snippet();
-        FormatAction action;
-        Status status = ste.status();
-        switch (status) {
-            case VALID:
-            case RECOVERABLE_DEFINED:
-            case RECOVERABLE_NOT_DEFINED:
-                if (ste.previousStatus().isActive) {
-                    action = ste.isSignatureChange()
-                        ? FormatAction.REPLACED
-                        : FormatAction.MODIFIED;
-                } else {
-                    action = FormatAction.ADDED;
-                }
-                break;
-            case OVERWRITTEN:
-                action = FormatAction.OVERWROTE;
-                break;
-            case DROPPED:
-                action = FormatAction.DROPPED;
-                break;
-            case REJECTED:
-                action = FormatAction.REJECTED;
-                break;
-            case NONEXISTENT:
-            default:
-                // Should not occur
-                error("Unexpected status: " + ste.previousStatus().toString() + "=>" + status.toString());
-                return;
-        }
-        FormatResolve resolution;
-        String unresolved;
-        if (key instanceof DeclarationSnippet && (status == Status.RECOVERABLE_DEFINED || status == Status.RECOVERABLE_NOT_DEFINED)) {
-            resolution = (status == Status.RECOVERABLE_NOT_DEFINED)
-                    ? FormatResolve.NOTDEFINED
-                    : FormatResolve.DEFINED;
-            unresolved = unresolved((DeclarationSnippet) key);
-        } else {
-            resolution = FormatResolve.OK;
-            unresolved = "";
-        }
-        switch (key.subKind()) {
-            case CLASS_SUBKIND:
-                custom(FormatCase.CLASS, update, action, resolution,
-                        ((TypeDeclSnippet) key).name(), null, unresolved, null);
-                break;
-            case INTERFACE_SUBKIND:
-                custom(FormatCase.INTERFACE, update, action, resolution,
-                        ((TypeDeclSnippet) key).name(), null, unresolved, null);
-                break;
-            case ENUM_SUBKIND:
-                custom(FormatCase.ENUM, update, action, resolution,
-                        ((TypeDeclSnippet) key).name(), null, unresolved, null);
-                break;
-            case ANNOTATION_TYPE_SUBKIND:
-                custom(FormatCase.ANNOTATION, update, action, resolution,
-                        ((TypeDeclSnippet) key).name(), null, unresolved, null);
-                break;
-            case METHOD_SUBKIND:
-                custom(FormatCase.METHOD, update, action, resolution,
-                        ((MethodSnippet) key).name(), ((MethodSnippet) key).parameterTypes(), unresolved, null);
-                break;
-            case VAR_DECLARATION_SUBKIND:
-            case VAR_DECLARATION_WITH_INITIALIZER_SUBKIND: {
-                VarSnippet vk = (VarSnippet) key;
-                if (status == Status.RECOVERABLE_NOT_DEFINED) {
-                    custom(FormatCase.VARDECLRECOVERABLE, update, action, resolution,
-                            vk.name(), null, unresolved, null);
-                } else if (update && ste.isSignatureChange()) {
-                    custom(FormatCase.VARRESET, update, action, resolution,
-                            vk.name(), null, unresolved, value);
-                } else if (key.subKind() == SubKind.VAR_DECLARATION_WITH_INITIALIZER_SUBKIND) {
-                    custom(FormatCase.VARINIT, update, action, resolution,
-                            vk.name(), vk.typeName(), unresolved, value);
-                } else {
-                    custom(FormatCase.VARDECL, update, action, resolution,
-                            vk.name(), vk.typeName(), unresolved, value);
-                }
-                break;
-            }
-            case TEMP_VAR_EXPRESSION_SUBKIND: {
-                VarSnippet vk = (VarSnippet) key;
-                custom(FormatCase.EXPRESSION, update, action, resolution,
-                        vk.name(), vk.typeName(), null, value);
-                break;
-            }
-            case OTHER_EXPRESSION_SUBKIND:
-                error("Unexpected expression form -- value is: %s", (value));
-                break;
-            case VAR_VALUE_SUBKIND: {
-                ExpressionSnippet ek = (ExpressionSnippet) key;
-                custom(FormatCase.VARVALUE, update, action, resolution,
-                        ek.name(), ek.typeName(), null, value);
-                break;
-            }
-            case ASSIGNMENT_SUBKIND: {
-                ExpressionSnippet ek = (ExpressionSnippet) key;
-                custom(FormatCase.ASSIGNMENT, update, action, resolution,
-                        ek.name(), ek.typeName(), null, value);
-                break;
-            }
-            case SINGLE_TYPE_IMPORT_SUBKIND:
-            case TYPE_IMPORT_ON_DEMAND_SUBKIND:
-            case SINGLE_STATIC_IMPORT_SUBKIND:
-            case STATIC_IMPORT_ON_DEMAND_SUBKIND:
-                custom(FormatCase.IMPORT, update, action, resolution,
-                        ((ImportSnippet) key).name(), null, null, null);
-                break;
-            case STATEMENT_SUBKIND:
-                custom(FormatCase.STATEMENT, update, action, resolution,
-                        null, null, null, null);
-                break;
-        }
-    }
     //where
     void printStackTrace(StackTraceElement[] stes) {
         for (StackTraceElement ste : stes) {
@@ -2141,31 +1986,11 @@
         }
     }
     //where
-    void printUnresolved(UnresolvedReferenceException ex) {
+    void printUnresolvedException(UnresolvedReferenceException ex) {
         DeclarationSnippet corralled =  ex.getSnippet();
         List<Diag> otherErrors = errorsOnly(state.diagnostics(corralled));
-        StringBuilder sb = new StringBuilder();
-        if (otherErrors.size() > 0) {
-            if (state.unresolvedDependencies(corralled).size() > 0) {
-                sb.append(" and");
-            }
-            if (otherErrors.size() == 1) {
-                sb.append(" this error is addressed --");
-            } else {
-                sb.append(" these errors are addressed --");
-            }
-        } else {
-            sb.append(".");
-        }
-
-        String format = corralled.kind() == METHOD
-                ? "Attempted to call %s which cannot be invoked until%s"
-                : "Attempted to use %s which cannot be accessed until%s";
-        hard(format, corralled.name(),
-                unresolved(corralled), sb.toString());
-        if (otherErrors.size() > 0) {
-            printDiagnostics(corralled.source(), otherErrors, true);
-        }
+        new DisplayEvent(corralled, state.status(corralled), FormatAction.USED, true, null, otherErrors)
+                .displayDeclarationAndValue();
     }
     //where
     void printEvalException(EvalException ex) {
@@ -2176,26 +2001,180 @@
         }
         printStackTrace(ex.getStackTrace());
     }
-    //where
-    String unresolved(DeclarationSnippet key) {
-        List<String> unr = state.unresolvedDependencies(key);
-        StringBuilder sb = new StringBuilder();
-        int fromLast = unr.size();
-        if (fromLast > 0) {
-            sb.append(" ");
+
+    private FormatAction toAction(Status status, Status previousStatus, boolean isSignatureChange) {
+        FormatAction act;
+        switch (status) {
+            case VALID:
+            case RECOVERABLE_DEFINED:
+            case RECOVERABLE_NOT_DEFINED:
+                if (previousStatus.isActive) {
+                    act = isSignatureChange
+                            ? FormatAction.REPLACED
+                            : FormatAction.MODIFIED;
+                } else {
+                    act = FormatAction.ADDED;
+                }
+                break;
+            case OVERWRITTEN:
+                act = FormatAction.OVERWROTE;
+                break;
+            case DROPPED:
+                act = FormatAction.DROPPED;
+                break;
+            case REJECTED:
+            case NONEXISTENT:
+            default:
+                // Should not occur
+                error("Unexpected status: " + previousStatus.toString() + "=>" + status.toString());
+                act = FormatAction.DROPPED;
+        }
+        return act;
+    }
+
+    class DisplayEvent {
+        private final Snippet sn;
+        private final FormatAction action;
+        private final boolean update;
+        private final String value;
+        private final List<String> errorLines;
+        private final FormatResolve resolution;
+        private final String unresolved;
+        private final FormatUnresolved unrcnt;
+        private final FormatErrors errcnt;
+
+        DisplayEvent(SnippetEvent ste, boolean update, String value, List<Diag> errors) {
+            this(ste.snippet(), ste.status(), toAction(ste.status(), ste.previousStatus(), ste.isSignatureChange()), update, value, errors);
         }
-        for (String u : unr) {
-            --fromLast;
-            sb.append(u);
-            if (fromLast == 0) {
-                // No suffix
-            } else if (fromLast == 1) {
-                sb.append(", and ");
+
+        DisplayEvent(Snippet sn, Status status, FormatAction action, boolean update, String value, List<Diag> errors) {
+            this.sn = sn;
+            this.action = action;
+            this.update = update;
+            this.value = value;
+            this.errorLines = new ArrayList<>();
+            for (Diag d : errors) {
+                displayDiagnostics(sn.source(), d, errorLines);
+            }
+            int unresolvedCount;
+            if (sn instanceof DeclarationSnippet && (status == Status.RECOVERABLE_DEFINED || status == Status.RECOVERABLE_NOT_DEFINED)) {
+                resolution = (status == Status.RECOVERABLE_NOT_DEFINED)
+                        ? FormatResolve.NOTDEFINED
+                        : FormatResolve.DEFINED;
+                unresolved = unresolved((DeclarationSnippet) sn);
+                unresolvedCount = state.unresolvedDependencies((DeclarationSnippet) sn).size();
             } else {
-                sb.append(", ");
+                resolution = FormatResolve.OK;
+                unresolved = "";
+                unresolvedCount = 0;
+            }
+            unrcnt = unresolvedCount == 0
+                    ? FormatUnresolved.UNRESOLVED0
+                    : unresolvedCount == 1
+                        ? FormatUnresolved.UNRESOLVED1
+                        : FormatUnresolved.UNRESOLVED2;
+            errcnt = errors.isEmpty()
+                    ? FormatErrors.ERROR0
+                    : errors.size() == 1
+                        ? FormatErrors.ERROR1
+                        : FormatErrors.ERROR2;
+        }
+
+        private String unresolved(DeclarationSnippet key) {
+            List<String> unr = state.unresolvedDependencies(key);
+            StringBuilder sb = new StringBuilder();
+            int fromLast = unr.size();
+            if (fromLast > 0) {
+                sb.append(" ");
+            }
+            for (String u : unr) {
+                --fromLast;
+                sb.append(u);
+                switch (fromLast) {
+                    // No suffix
+                    case 0:
+                        break;
+                    case 1:
+                        sb.append(", and ");
+                        break;
+                    default:
+                        sb.append(", ");
+                        break;
+                }
+            }
+            return sb.toString();
+        }
+
+        private void custom(FormatCase fcase, String name) {
+            custom(fcase, name, null);
+        }
+
+        private void custom(FormatCase fcase, String name, String type) {
+            String display = feedback.format(fcase, action, (update ? FormatWhen.UPDATE : FormatWhen.PRIMARY),
+                    resolution, unrcnt, errcnt,
+                    name, type, value, unresolved, errorLines);
+            if (interactive()) {
+                cmdout.print(display);
             }
         }
-        return sb.toString();
+
+        @SuppressWarnings("fallthrough")
+        private void displayDeclarationAndValue() {
+            switch (sn.subKind()) {
+                case CLASS_SUBKIND:
+                    custom(FormatCase.CLASS, ((TypeDeclSnippet) sn).name());
+                    break;
+                case INTERFACE_SUBKIND:
+                    custom(FormatCase.INTERFACE, ((TypeDeclSnippet) sn).name());
+                    break;
+                case ENUM_SUBKIND:
+                    custom(FormatCase.ENUM, ((TypeDeclSnippet) sn).name());
+                    break;
+                case ANNOTATION_TYPE_SUBKIND:
+                    custom(FormatCase.ANNOTATION, ((TypeDeclSnippet) sn).name());
+                    break;
+                case METHOD_SUBKIND:
+                    custom(FormatCase.METHOD, ((MethodSnippet) sn).name(), ((MethodSnippet) sn).parameterTypes());
+                    break;
+                case VAR_DECLARATION_SUBKIND: {
+                    VarSnippet vk = (VarSnippet) sn;
+                    custom(FormatCase.VARDECL, vk.name(), vk.typeName());
+                    break;
+                }
+                case VAR_DECLARATION_WITH_INITIALIZER_SUBKIND: {
+                    VarSnippet vk = (VarSnippet) sn;
+                    custom(FormatCase.VARINIT, vk.name(), vk.typeName());
+                    break;
+                }
+                case TEMP_VAR_EXPRESSION_SUBKIND: {
+                    VarSnippet vk = (VarSnippet) sn;
+                    custom(FormatCase.EXPRESSION, vk.name(), vk.typeName());
+                    break;
+                }
+                case OTHER_EXPRESSION_SUBKIND:
+                    error("Unexpected expression form -- value is: %s", (value));
+                    break;
+                case VAR_VALUE_SUBKIND: {
+                    ExpressionSnippet ek = (ExpressionSnippet) sn;
+                    custom(FormatCase.VARVALUE, ek.name(), ek.typeName());
+                    break;
+                }
+                case ASSIGNMENT_SUBKIND: {
+                    ExpressionSnippet ek = (ExpressionSnippet) sn;
+                    custom(FormatCase.ASSIGNMENT, ek.name(), ek.typeName());
+                    break;
+                }
+                case SINGLE_TYPE_IMPORT_SUBKIND:
+                case TYPE_IMPORT_ON_DEMAND_SUBKIND:
+                case SINGLE_STATIC_IMPORT_SUBKIND:
+                case STATIC_IMPORT_ON_DEMAND_SUBKIND:
+                    custom(FormatCase.IMPORT, ((ImportSnippet) sn).name());
+                    break;
+                case STATEMENT_SUBKIND:
+                    custom(FormatCase.STATEMENT, null);
+                    break;
+            }
+        }
     }
 
     /** The current version number as a string.
@@ -2210,13 +2189,10 @@
         return version("full"); // mm.mm.oo[-milestone]-build
     }
 
-    private static final String versionRBName = "jdk.internal.jshell.tool.resources.version";
-    private static ResourceBundle versionRB;
-
     private static String version(String key) {
         if (versionRB == null) {
             try {
-                versionRB = ResourceBundle.getBundle(versionRBName);
+                versionRB = ResourceBundle.getBundle(VERSION_RB_NAME);
             } catch (MissingResourceException e) {
                 return "(version info not available)";
             }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/resources/l10n.properties	Fri Mar 25 18:36:19 2016 -0700
@@ -0,0 +1,179 @@
+#
+# Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# This code is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License version 2 only, as
+# published by the Free Software Foundation.  Oracle designates this
+# particular file as subject to the "Classpath" exception as provided
+# by Oracle in the LICENSE file that accompanied this code.
+#
+# This code is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+# version 2 for more details (a copy is included in the LICENSE file that
+# accompanied this code).
+#
+# You should have received a copy of the GNU General Public License version
+# 2 along with this work; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+# or visit www.oracle.com if you need additional information or have any
+# questions.
+#
+
+help.set.format = \
+Set the format for reporting a snippet event.\n\
+\n\
+/set format <mode> <field> "<format>" <selector>...\n\
+\n\
+Where <mode> is the name of a previously defined feedback mode -- see '/help /set newmode'.\n\
+Where <field> is the name of context-specific format to define.\n\
+Where <format> is a quoted string which will be the value of the field if one of\n\
+the selectors matches (or there are no selectors). When the format is used,\n\
+field names enclosed in braces are replaced with the value of the field at that\n\
+time. These fields may have been previously defined with this command or may be\n\
+one of these predefined fields specific to the context:\n\t\
+{name}       == The name, e.g.: the variable name, ...\n\t\
+{type}       == The type name. The type of a variable or expression, the\n\t\t\t\
+                 parameter types of a method\n\t\
+{value}      == The result value of an expression or variable initialization\n\t\
+{unresolved} == The list of unresolved references\n\t\
+{errors}     == The list of recoverable errors (during the processing of the\n\t\t\t\
+                "display" field only)\n\t\
+{err}        == An unformatted error line (during the processing of the\n\t\t\t\
+                "errorline" field only)\n\
+The following fields are accessed by the tool to determine the displayed feedback:\n\t\
+{display}    == The displayed message for a snippet event\n\t\
+{errorline}  == The format of one error line within the "errors" field\n\t\
+{pre}        == The feedback prefix (begins command feedback)\n\t\
+{post}       == The feedback postfix (ends command feedback)\n\t\
+{errorpre}   == The error prefix (begins error feedback)\n\t\
+{errorpost}  == The error postfix (ends error feedback)\n\
+These fields have default settings (which may be overwritten).\n\
+Where <selector> is the context in which the format is applied.\n\
+The structure of selector is a hyphen separated list of selector kind lists.\n\
+A selector kind list is a comma separated list of values of one selector kind.\n\
+A selector matches if each selector kind list matches; A selector kind list\n\
+matches if one of the values matches.\n
+help.set.format.case = The case selector kind describes the kind of snippet.  The values are:\n
+help.set.format.action = The action selector kind describes what happened to the snippet.  The values are:\n
+help.set.format.when = The when-did-it-occur selector kind describes if this is a direct or indirect action.  The values are:\n
+help.set.format.resolve = The resolution-state selector kind describes the state of resolution/definition of the snippet.  The values are:\n
+help.set.format.unresolved = The unresolved-count selector kind describes the number of unresolved references.  The values are:\n
+help.set.format.errors = The errors-count selector kind describes the number of errors.  The values are:\n
+help.set.format.end = \n\
+Examples:\n\t\
+/set format myformat action 'Created' added-primary\n\t\
+/set format myformat action 'Update replaced' replaced-update\n\t\
+/set format myformat display '{pre}{action} class {name}{post}' class-ok\n\t\
+/set format myformat display '{pre}{action} variable {name}, reset to null{post}' replaced-vardecl,varinit-ok-update\n\n\
+Note that subsequent selectors for a field may overwrite some or all of previous used selectors -- last one wins\n
+
+help.set.feedback = \
+Set the feedback mode describing displayed feedback for entered snippets and commands.\n\
+\n\
+/set feedback <mode>\n\
+\n\
+Where <mode> is the name of a previously defined feedback mode.\n\
+You may use just enough letters to make it unique.\n\
+User-defined modes can be added, see '/help /set newmode'\n\
+Currently defined feedback modes:\n
+
+help.set.newmode = \
+Create a user-defined feedback mode, optionally copying from an existing mode.\n\
+\n\
+/set newmode <new-mode> [command|quiet [<old-mode>]]\n\
+\n\
+Where <new-mode> is the name of a mode you wish to create.\n\
+Where <old-mode> is the name of a previously defined feedback mode.\n\
+If <old-mode> is present, its settings are copied to the new mode.\n\
+'command' vs 'quiet' determines if informative/verifying command feedback is displayed.\n\
+\n\
+Once the new mode is created, use '/set format' and '/set prompt' to configure it.\n\
+Use '/set feedback' to use the new mode.\n\
+
+help.set.prompt = \
+Set the prompts.  Both the normal prompt and the continuation-prompt must be set.\n\
+\n\
+/set prompt <mode> \"<prompt>\" \"<continuation-propmt>\"\n\
+\n\
+Where <mode> is the name of a previously defined feedback mode.\n\
+Where <prompt> and <continuation-propmt> are quoted strings printed as input prompts;\n\
+Both may optionally contain '%s' which will be substituted with the next snippet id --\n\
+note that what is entered may not be assigned that id, for example it may be an error or command.\n\
+The continuation-prompt is used on the second and subsequent lines of a multi-line snippet.\n
+
+startup.feedback = \
+/set newmode normal command    \n\
+/set prompt normal '\\n-> ' '>> '    \n\
+/set format normal pre '|  '    \n\
+/set format normal post '%n'    \n\
+/set format normal errorpre '|  '    \n\
+/set format normal errorpost '%n'    \n\
+    \n\
+/set format normal errorline '{post}{pre}    {err}'    \n\
+    \n\
+/set format normal action 'Added' added-primary    \n\
+/set format normal action 'Modified' modified-primary    \n\
+/set format normal action 'Replaced' replaced-primary    \n\
+/set format normal action 'Overwrote' overwrote-primary    \n\
+/set format normal action 'Dropped' dropped-primary    \n\
+/set format normal action '  Update added' added-update    \n\
+/set format normal action '  Update modified' modified-update    \n\
+/set format normal action '  Update replaced' replaced-update    \n\
+/set format normal action '  Update overwrote' overwrote-update    \n\
+/set format normal action '  Update dropped' dropped-update    \n\
+    \n\
+/set format normal until ', however, it cannot be instanciated or its methods invoked until'  defined-class-primary    \n\
+/set format normal until ', however, its methods cannot be invoked until'                     defined-interface-primary    \n\
+/set format normal until ', however, it cannot be used until'                                 defined-enum,annotation-primary    \n\
+/set format normal until ', however, it cannot be invoked until'                              defined-method-primary    \n\
+/set format normal until ', however, it cannot be referenced until'                           notdefined-primary    \n\
+/set format normal until ' which cannot be instanciated or its methods invoked until'         defined-class-update    \n\
+/set format normal until ' whose methods cannot be invoked until'                             defined-interface-update    \n\
+/set format normal until ' which cannot be invoked until'                                     defined-method-update    \n\
+/set format normal until ' which cannot be referenced until'                                  notdefined-update    \n\
+    \n\
+/set format normal unrerr '{unresolved} is declared'                                           unresolved1-error0    \n\
+/set format normal unrerr '{unresolved} are declared'                                          unresolved2-error0    \n\
+/set format normal unrerr ' this error is corrected: {errors}'                                 unresolved0-error1    \n\
+/set format normal unrerr '{unresolved} is declared and this error is corrected: {errors}'     unresolved1-error1    \n\
+/set format normal unrerr '{unresolved} are declared and this error is corrected: {errors}'    unresolved2-error1    \n\
+/set format normal unrerr ' these errors are corrected: {errors}'                              unresolved0-error2    \n\
+/set format normal unrerr '{unresolved} is declared and these errors are corrected: {errors}'  unresolved1-error2    \n\
+/set format normal unrerr '{unresolved} are declared and these errors are corrected: {errors}' unresolved2-error2    \n\
+    \n\
+/set format normal resolve '{until}{unrerr}'                                                added,modified,replaced,used    \n\
+    \n\
+/set format normal typeKind 'class'                  class    \n\
+/set format normal typeKind 'interface'              interface    \n\
+/set format normal typeKind 'enum'                   enum    \n\
+/set format normal typeKind 'annotation interface'   annotation    \n\
+    \n\
+/set format normal display '{pre}{action} {typeKind} {name}{resolve}{post}'                 class,interface,enum,annotation    \n\
+/set format normal display '{pre}{action} method {name}({type}){resolve}{post}'             method    \n\
+    \n\
+/set format normal display '{pre}{action} variable {name} of type {type}{resolve}{post}'    vardecl    \n\
+/set format normal display '{pre}{action} variable {name} of type {type} with initial value {value}{resolve}{post}'    varinit    \n\
+/set format normal display '{pre}{action} variable {name}, reset to null{post}'             replaced-vardecl,varinit-ok-update    \n\
+/set format normal display '{pre}{action} variable {name}{resolve}{post}'                   vardecl,varinit-notdefined    \n\
+/set format normal display '{pre}{action} variable {name}{post}'                            overwrote,dropped-vardecl,varinit    \n\
+    \n\
+/set format normal display '{pre}Expression value is: {value}{post}{pre}  assigned to temporary variable {name} of type {type}{post}' expression    \n\
+/set format normal display '{pre}Variable {name} of type {type} has value {value}{post}'    varvalue    \n\
+/set format normal display '{pre}Variable {name} has been assigned the value {value}{post}' assignment    \n\
+    \n\
+/set format normal display '{pre}Attempted to use {typeKind} {name}{resolve}{post}'         used-class,interface,enum,annotation    \n\
+/set format normal display '{pre}Attempted to call method {name}({type}){resolve}{post}'    used-method    \n\
+    \n\
+/set feedback normal    \n\
+    \n\
+/set newmode off quiet    \n\
+/set prompt off '-> ' '>> '    \n\
+/set format off pre '|  '    \n\
+/set format off post '%n'    \n\
+/set format off errorpre '|  '    \n\
+/set format off errorpost '%n'    \n\
+/set format off display ''    \n
--- a/langtools/test/jdk/jshell/ReplToolTesting.java	Wed Mar 23 21:44:24 2016 -0700
+++ b/langtools/test/jdk/jshell/ReplToolTesting.java	Fri Mar 25 18:36:19 2016 -0700
@@ -374,8 +374,8 @@
         }
     }
 
-    private void dropKey(boolean after, String cmd, String name, Map<String, ? extends MemberInfo> map) {
-        assertCommand(after, cmd, "");
+    private void dropKey(boolean after, String cmd, String name, Map<String, ? extends MemberInfo> map, String output) {
+        assertCommand(after, cmd, output);
         if (after) {
             map.remove(name);
             for (int i = 0; i < keys.size(); ++i) {
@@ -389,20 +389,20 @@
         }
     }
 
-    public void dropVariable(boolean after, String cmd, String name) {
-        dropKey(after, cmd, name, variables);
+    public void dropVariable(boolean after, String cmd, String name, String output) {
+        dropKey(after, cmd, name, variables, output);
     }
 
-    public void dropMethod(boolean after, String cmd, String name) {
-        dropKey(after, cmd, name, methods);
+    public void dropMethod(boolean after, String cmd, String name, String output) {
+        dropKey(after, cmd, name, methods, output);
     }
 
-    public void dropClass(boolean after, String cmd, String name) {
-        dropKey(after, cmd, name, classes);
+    public void dropClass(boolean after, String cmd, String name, String output) {
+        dropKey(after, cmd, name, classes, output);
     }
 
-    public void dropImport(boolean after, String cmd, String name) {
-        dropKey(after, cmd, name, imports);
+    public void dropImport(boolean after, String cmd, String name, String output) {
+        dropKey(after, cmd, name, imports, output);
     }
 
     public void assertCommand(boolean after, String cmd, String out) {
@@ -436,10 +436,10 @@
             }
             setCommandInput(cmd + "\n");
         } else {
-            assertOutput(getCommandOutput(), out, "command");
-            assertOutput(getCommandErrorOutput(), err, "command error");
-            assertOutput(getUserOutput(), print, "user");
-            assertOutput(getUserErrorOutput(), usererr, "user error");
+            assertOutput(getCommandOutput(), out, "command output: " + cmd);
+            assertOutput(getCommandErrorOutput(), err, "command error: " + cmd);
+            assertOutput(getUserOutput(), print, "user output: " + cmd);
+            assertOutput(getUserErrorOutput(), usererr, "user error: " + cmd);
         }
     }
 
@@ -476,9 +476,9 @@
         return (output) -> assertTrue(output.startsWith(prefix), "Output: \'" + output + "' does not start with: " + prefix);
     }
 
-    public void assertOutput(String got, String expected, String kind) {
+    public void assertOutput(String got, String expected, String display) {
         if (expected != null) {
-            assertEquals(got, expected, "Kind: " + kind + ".\n");
+            assertEquals(got, expected, display + ".\n");
         }
     }
 
--- a/langtools/test/jdk/jshell/ToolBasicTest.java	Wed Mar 23 21:44:24 2016 -0700
+++ b/langtools/test/jdk/jshell/ToolBasicTest.java	Fri Mar 25 18:36:19 2016 -0700
@@ -755,13 +755,13 @@
     public void testDrop() {
         test(false, new String[]{"-nostartup"},
                 a -> assertVariable(a, "int", "a"),
-                a -> dropVariable(a, "/drop 1", "int a = 0"),
+                a -> dropVariable(a, "/drop 1", "int a = 0", "|  Dropped variable a\n"),
                 a -> assertMethod(a, "int b() { return 0; }", "()I", "b"),
-                a -> dropMethod(a, "/drop 2", "b ()I"),
+                a -> dropMethod(a, "/drop 2", "b ()I", "|  Dropped method b()\n"),
                 a -> assertClass(a, "class A {}", "class", "A"),
-                a -> dropClass(a, "/drop 3", "class A"),
+                a -> dropClass(a, "/drop 3", "class A", "|  Dropped class A\n"),
                 a -> assertImport(a, "import java.util.stream.*;", "", "java.util.stream.*"),
-                a -> dropImport(a, "/drop 4", "import java.util.stream.*"),
+                a -> dropImport(a, "/drop 4", "import java.util.stream.*", ""),
                 a -> assertCommandCheckOutput(a, "/vars", assertVariables()),
                 a -> assertCommandCheckOutput(a, "/methods", assertMethods()),
                 a -> assertCommandCheckOutput(a, "/classes", assertClasses()),
@@ -769,11 +769,11 @@
         );
         test(false, new String[]{"-nostartup"},
                 a -> assertVariable(a, "int", "a"),
-                a -> dropVariable(a, "/drop a", "int a = 0"),
+                a -> dropVariable(a, "/drop a", "int a = 0", "|  Dropped variable a\n"),
                 a -> assertMethod(a, "int b() { return 0; }", "()I", "b"),
-                a -> dropMethod(a, "/drop b", "b ()I"),
+                a -> dropMethod(a, "/drop b", "b ()I", "|  Dropped method b()\n"),
                 a -> assertClass(a, "class A {}", "class", "A"),
-                a -> dropClass(a, "/drop A", "class A"),
+                a -> dropClass(a, "/drop A", "class A", "|  Dropped class A\n"),
                 a -> assertCommandCheckOutput(a, "/vars", assertVariables()),
                 a -> assertCommandCheckOutput(a, "/methods", assertMethods()),
                 a -> assertCommandCheckOutput(a, "/classes", assertClasses()),
--- a/langtools/test/jdk/jshell/ToolFormatTest.java	Wed Mar 23 21:44:24 2016 -0700
+++ b/langtools/test/jdk/jshell/ToolFormatTest.java	Fri Mar 25 18:36:19 2016 -0700
@@ -23,7 +23,7 @@
 
 /*
  * @test
- * @bug 8148316 8148317
+ * @bug 8148316 8148317 8151755 8152246
  * @summary Tests for output customization
  * @library /tools/lib
  * @modules jdk.compiler/com.sun.tools.javac.api
@@ -33,6 +33,8 @@
  * @build KullaTesting TestingInputStream ToolBox Compiler
  * @run testng ToolFormatTest
  */
+import java.util.ArrayList;
+import java.util.List;
 import org.testng.annotations.Test;
 
 @Test
@@ -42,28 +44,34 @@
         try {
             test(
                     (a) -> assertCommandOutputStartsWith(a, "/set newmode test command", "|  Created new feedback mode: test"),
-                    (a) -> assertCommand(a, "/set field test pre '$ '", ""),
-                    (a) -> assertCommand(a, "/set field test post ''", ""),
-                    (a) -> assertCommand(a, "/set field test action 'ADD ' added-primary", ""),
-                    (a) -> assertCommand(a, "/set field test action 'MOD ' modified-primary", ""),
-                    (a) -> assertCommand(a, "/set field test action 'REP ' replaced-primary", ""),
-                    (a) -> assertCommand(a, "/set field test action 'UP-ADD ' added-update", ""),
-                    (a) -> assertCommand(a, "/set field test action 'UP-MOD ' modified-update", ""),
-                    (a) -> assertCommand(a, "/set field test action 'UP-REP ' replaced-update", ""),
-                    (a) -> assertCommand(a, "/set field test resolve 'OK' ok-*", ""),
-                    (a) -> assertCommand(a, "/set field test resolve 'DEF' defined-*", ""),
-                    (a) -> assertCommand(a, "/set field test resolve 'NODEF' notdefined-*", ""),
-                    (a) -> assertCommand(a, "/set field test name ':%s ' ", ""),
-                    (a) -> assertCommand(a, "/set field test type '[%s]' ", ""),
-                    (a) -> assertCommand(a, "/set field test result '=%s ' ", ""),
-                    (a) -> assertCommand(a, "/set format test '{pre}{action}{type}{name}{result}{resolve}' *-*-*", ""),
-                    (a) -> assertCommand(a, "/set format test '{pre}HI this is enum' enum", ""),
+                    (a) -> assertCommand(a, "/set format test pre '$ '", ""),
+                    (a) -> assertCommand(a, "/set format test post ''", ""),
+                    (a) -> assertCommand(a, "/set format test act 'ADD' added", ""),
+                    (a) -> assertCommand(a, "/set format test act 'MOD' modified", ""),
+                    (a) -> assertCommand(a, "/set format test act 'REP' replaced", ""),
+                    (a) -> assertCommand(a, "/set format test act 'OVR' overwrote", ""),
+                    (a) -> assertCommand(a, "/set format test act 'USE' used", ""),
+                    (a) -> assertCommand(a, "/set format test act 'DRP' dropped", ""),
+                    (a) -> assertCommand(a, "/set format test up 'UP-' update", ""),
+                    (a) -> assertCommand(a, "/set format test action '{up}{act} '", ""),
+                    (a) -> assertCommand(a, "/set format test resolve 'OK' ok", ""),
+                    (a) -> assertCommand(a, "/set format test resolve 'DEF' defined", ""),
+                    (a) -> assertCommand(a, "/set format test resolve 'NODEF' notdefined", ""),
+                    (a) -> assertCommand(a, "/set format test fname ':{name} ' ", ""),
+                    (a) -> assertCommand(a, "/set format test ftype '[{type}]' method,expression", ""),
+                    (a) -> assertCommand(a, "/set format test result '={value} ' expression", ""),
+                    (a) -> assertCommand(a, "/set format test display '{pre}{action}{ftype}{fname}{result}{resolve}'", ""),
+                    (a) -> assertCommand(a, "/set format test display '{pre}HI this is enum' enum", ""),
                     (a) -> assertCommand(a, "/set feedback test", "$ Feedback mode: test"),
                     (a) -> assertCommand(a, "class D {}", "$ ADD :D OK"),
                     (a) -> assertCommand(a, "void m() {}", "$ ADD []:m OK"),
                     (a) -> assertCommand(a, "interface EX extends EEX {}", "$ ADD :EX NODEF"),
                     (a) -> assertCommand(a, "56", "$ ADD [int]:$4 =56 OK"),
-                    (a) -> assertCommand(a, "class D { int hh; }", "$ REP :D OK$ OVERWROTE-UPDATE:D OK"),
+                    (a) -> assertCommand(a, "class D { int hh; }", "$ REP :D OK$ UP-OVR :D OK"),
+                    (a) -> assertCommand(a, "enum E {A,B}", "$ HI this is enum"),
+                    (a) -> assertCommand(a, "int z() { return f(); }", "$ ADD []:z DEF"),
+                    (a) -> assertCommand(a, "z()", "$ UP-USE []:z DEF"),
+                    (a) -> assertCommand(a, "/drop z", "$ DRP []:z OK"),
                     (a) -> assertCommandOutputStartsWith(a, "/set feedback normal", "|  Feedback mode: normal")
             );
         } finally {
@@ -72,7 +80,82 @@
         }
     }
 
-    public void testNewModeQuiet() {
+    public void testSetFormatSelector() {
+        List<ReplTest> tests = new ArrayList<>();
+        tests.add((a) -> assertCommandOutputStartsWith(a, "/set newmode ate quiet",
+                            "|  Created new feedback mode: ate"));
+        tests.add((a) -> assertCommand(a, "/set feedback ate", ""));
+        StringBuilder sb = new StringBuilder();
+        class KindList {
+            final String[] values;
+            final int matchIndex;
+            int current;
+            boolean match;
+            KindList(String[] values, int matchIndex) {
+                this.values = values;
+                this.matchIndex = matchIndex;
+                this.current = 1 << values.length;
+            }
+            boolean next() {
+                if (current <= 0) {
+                    return false;
+                }
+                --current;
+                return true;
+            }
+            boolean append(boolean ahead) {
+                boolean any = false;
+                match = false;
+                for (int i = values.length - 1; i >= 0 ; --i) {
+                    if ((current & (1 << i)) != 0) {
+                        match |= i == matchIndex;
+                        if (any) {
+                            sb.append(",");
+                        } else {
+                            if (ahead) {
+                                sb.append("-");
+                            }
+                        }
+                        sb.append(values[i]);
+                        any = true;
+                    }
+                }
+                match |= !any;
+                return ahead || any;
+            }
+        }
+        KindList klcase = new KindList(new String[] {"class", "method", "expression", "vardecl"}, 2);
+        while (klcase.next()) {
+            KindList klact  = new KindList(new String[] {"added", "modified", "replaced"}, 0);
+            while (klact.next()) {
+                KindList klwhen = new KindList(new String[] {"update", "primary"}, 1);
+                while (klwhen.next()) {
+                    sb.setLength(0);
+                    klwhen.append(
+                        klact.append(
+                            klcase.append(false)));
+                    boolean match = klcase.match && klact.match && klwhen.match;
+                    String select = sb.toString();
+                    String yes = "+++" + select + "+++";
+                    String no  = "---" + select + "---";
+                    String expect = match? yes : no;
+                    tests.add((a) -> assertCommand(a, "/set format ate display '" + no  + "'", ""));
+                    tests.add((a) -> assertCommand(a, "/set format ate display '" + yes + "' " + select, ""));
+                    tests.add((a) -> assertCommand(a, "\"" + select + "\"", expect));
+                }
+            }
+        }
+        tests.add((a) -> assertCommandOutputStartsWith(a, "/set feedback normal", "|  Feedback mode: normal"));
+
+        try {
+            test(tests.toArray(new ReplTest[tests.size()]));
+        } finally {
+            assertCommandCheckOutput(false, "/set feedback normal", s -> {
+            });
+        }
+    }
+
+    public void testSetNewModeQuiet() {
         try {
             test(
                     (a) -> assertCommandOutputStartsWith(a, "/set newmode nmq quiet normal", "|  Created new feedback mode: nmq"),
@@ -82,7 +165,8 @@
                     (a) -> assertCommand(a, "/set newmode nmc command normal", ""),
                     (a) -> assertCommandOutputStartsWith(a, "/set feedback nmc", "|  Feedback mode: nmc"),
                     (a) -> assertCommandOutputStartsWith(a, "/set newmode nm", "|  Created new feedback mode: nm"),
-                    (a) -> assertCommandOutputStartsWith(a, "/set feedback nm", "|  Feedback mode: nm")
+                    (a) -> assertCommandOutputStartsWith(a, "/set feedback nm", "|  Feedback mode: nm"),
+                    (a) -> assertCommandOutputStartsWith(a, "/set feedback normal", "|  Feedback mode: normal")
             );
         } finally {
             assertCommandCheckOutput(false, "/set feedback normal", s -> {
@@ -93,38 +177,75 @@
     public void testSetError() {
         try {
             test(
-                    (a) -> assertCommandOutputStartsWith(a, "/set newmode te command normal", "|  Created new feedback mode: te"),
-                    (a) -> assertCommand(a, "/set field te errorpre 'ERROR: '", ""),
-                    (a) -> assertCommandOutputStartsWith(a, "/set feedback te", ""),
-                    (a) -> assertCommandCheckOutput(a, "/set ", assertStartsWith("ERROR: The /set command requires arguments")),
-                    (a) -> assertCommandCheckOutput(a, "/set xyz", assertStartsWith("ERROR: Not a valid argument to /set")),
-                    (a) -> assertCommandCheckOutput(a, "/set f", assertStartsWith("ERROR: Ambiguous argument to /set")),
-                    (a) -> assertCommandCheckOutput(a, "/set feedback", assertStartsWith("ERROR: Expected a feedback mode")),
-                    (a) -> assertCommandCheckOutput(a, "/set feedback xyz", assertStartsWith("ERROR: Does not match any current feedback mode")),
-                    (a) -> assertCommandCheckOutput(a, "/set format", assertStartsWith("ERROR: Expected a feedback mode")),
-                    (a) -> assertCommandCheckOutput(a, "/set format xyz", assertStartsWith("ERROR: Does not match any current feedback mode")),
-                    (a) -> assertCommandCheckOutput(a, "/set format te", assertStartsWith("ERROR: Expected format missing")),
-                    (a) -> assertCommandCheckOutput(a, "/set format te aaa", assertStartsWith("ERROR: Format 'aaa' must be quoted")),
-                    (a) -> assertCommandCheckOutput(a, "/set format te 'aaa'", assertStartsWith("ERROR: At least one selector required")),
-                    (a) -> assertCommandCheckOutput(a, "/set format te 'aaa' frog", assertStartsWith("ERROR: Not a valid case")),
-                    (a) -> assertCommandCheckOutput(a, "/set format te 'aaa' import-frog", assertStartsWith("ERROR: Not a valid action")),
-                    (a) -> assertCommandCheckOutput(a, "/set newmode", assertStartsWith("ERROR: Expected new feedback mode")),
-                    (a) -> assertCommandCheckOutput(a, "/set newmode te", assertStartsWith("ERROR: Expected a new feedback mode name")),
-                    (a) -> assertCommandCheckOutput(a, "/set newmode x xyz", assertStartsWith("ERROR: Specify either 'command' or 'quiet'")),
-                    (a) -> assertCommandCheckOutput(a, "/set newmode x quiet y", assertStartsWith("ERROR: Does not match any current feedback mode")),
-                    (a) -> assertCommandCheckOutput(a, "/set prompt", assertStartsWith("ERROR: Expected a feedback mode")),
-                    (a) -> assertCommandCheckOutput(a, "/set prompt te", assertStartsWith("ERROR: Expected format missing")),
-                    (a) -> assertCommandCheckOutput(a, "/set prompt te aaa xyz", assertStartsWith("ERROR: Format 'aaa' must be quoted")),
-                    (a) -> assertCommandCheckOutput(a, "/set prompt te 'aaa' xyz", assertStartsWith("ERROR: Format 'xyz' must be quoted")),
-                    (a) -> assertCommandCheckOutput(a, "/set prompt", assertStartsWith("ERROR: Expected a feedback mode")),
-                    (a) -> assertCommandCheckOutput(a, "/set prompt te", assertStartsWith("ERROR: Expected format missing")),
-                    (a) -> assertCommandCheckOutput(a, "/set prompt te aaa", assertStartsWith("ERROR: Format 'aaa' must be quoted")),
-                    (a) -> assertCommandCheckOutput(a, "/set prompt te 'aaa'", assertStartsWith("ERROR: Expected format missing")),
-                    (a) -> assertCommandCheckOutput(a, "/set field", assertStartsWith("ERROR: Expected a feedback mode")),
-                    (a) -> assertCommandCheckOutput(a, "/set field xyz", assertStartsWith("ERROR: Does not match any current feedback mode: xyz")),
-                    (a) -> assertCommandCheckOutput(a, "/set field te xyz", assertStartsWith("ERROR: Not a valid field: xyz, must be one of: when")),
-                    (a) -> assertCommandCheckOutput(a, "/set field te action", assertStartsWith("ERROR: Expected format missing")),
-                    (a) -> assertCommandCheckOutput(a, "/set field te action 'act'", assertStartsWith("ERROR: At least one selector required"))
+                    (a) -> assertCommandOutputStartsWith(a, "/set newmode tee command foo",
+                            "|  Does not match any current feedback mode: foo"),
+                    (a) -> assertCommandOutputStartsWith(a, "/set newmode tee flurb",
+                            "|  Specify either 'command' or 'quiet'"),
+                    (a) -> assertCommandOutputStartsWith(a, "/set newmode te2",
+                            "|  Created new feedback mode: te2"),
+                    (a) -> assertCommandOutputStartsWith(a, "/set newmode te2 command",
+                            "|  Expected a new feedback mode name. te2 is a known feedback mode"),
+                    (a) -> assertCommandOutputStartsWith(a, "/set newmode te command normal",
+                            "|  Created new feedback mode: te"),
+                    (a) -> assertCommand(a, "/set format te errorpre 'ERROR: '", ""),
+                    (a) -> assertCommandOutputStartsWith(a, "/set feedback te",
+                            ""),
+                    (a) -> assertCommandOutputStartsWith(a, "/set ",
+                            "ERROR: The /set command requires arguments"),
+                    (a) -> assertCommandOutputStartsWith(a, "/set xyz",
+                            "ERROR: Not a valid argument to /set"),
+                    (a) -> assertCommandOutputStartsWith(a, "/set f",
+                            "ERROR: Ambiguous argument to /set"),
+                    (a) -> assertCommandOutputStartsWith(a, "/set feedback",
+                            "ERROR: Expected a feedback mode"),
+                    (a) -> assertCommandOutputStartsWith(a, "/set feedback xyz",
+                            "ERROR: Does not match any current feedback mode"),
+                    (a) -> assertCommandOutputStartsWith(a, "/set format",
+                            "ERROR: Expected a feedback mode"),
+                    (a) -> assertCommandOutputStartsWith(a, "/set format xyz",
+                            "ERROR: Does not match any current feedback mode"),
+                    (a) -> assertCommandOutputStartsWith(a, "/set format t",
+                            "ERROR: Matches more then one current feedback mode: t"),
+                    (a) -> assertCommandOutputStartsWith(a, "/set format te",
+                            "ERROR: Expected field name missing"),
+                    (a) -> assertCommandOutputStartsWith(a, "/set format te fld",
+                            "ERROR: Expected format missing"),
+                    (a) -> assertCommandOutputStartsWith(a, "/set format te fld aaa",
+                            "ERROR: Format 'aaa' must be quoted"),
+                    (a) -> assertCommandOutputStartsWith(a, "/set format te fld 'aaa' frog",
+                            "ERROR: Not a valid selector"),
+                    (a) -> assertCommandOutputStartsWith(a, "/set format te fld 'aaa' import-frog",
+                            "ERROR: Not a valid selector"),
+                    (a) -> assertCommandOutputStartsWith(a, "/set format te fld 'aaa' import-import",
+                            "ERROR: Selector kind in multiple sections of"),
+                    (a) -> assertCommandOutputStartsWith(a, "/set format te fld 'aaa' import,added",
+                            "ERROR: Different selector kinds in same sections of"),
+                    (a) -> assertCommandOutputStartsWith(a, "/set newmode",
+                            "ERROR: Expected new feedback mode"),
+                    (a) -> assertCommandOutputStartsWith(a, "/set newmode te",
+                            "ERROR: Expected a new feedback mode name"),
+                    (a) -> assertCommandOutputStartsWith(a, "/set newmode x xyz",
+                            "ERROR: Specify either 'command' or 'quiet'"),
+                    (a) -> assertCommandOutputStartsWith(a, "/set newmode x quiet y",
+                            "ERROR: Does not match any current feedback mode"),
+                    (a) -> assertCommandOutputStartsWith(a, "/set prompt",
+                            "ERROR: Expected a feedback mode"),
+                    (a) -> assertCommandOutputStartsWith(a, "/set prompt te",
+                            "ERROR: Expected format missing"),
+                    (a) -> assertCommandOutputStartsWith(a, "/set prompt te aaa xyz",
+                            "ERROR: Format 'aaa' must be quoted"),
+                    (a) -> assertCommandOutputStartsWith(a, "/set prompt te 'aaa' xyz",
+                            "ERROR: Format 'xyz' must be quoted"),
+                    (a) -> assertCommandOutputStartsWith(a, "/set prompt",
+                            "ERROR: Expected a feedback mode"),
+                    (a) -> assertCommandOutputStartsWith(a, "/set prompt te",
+                            "ERROR: Expected format missing"),
+                    (a) -> assertCommandOutputStartsWith(a, "/set prompt te aaa",
+                            "ERROR: Format 'aaa' must be quoted"),
+                    (a) -> assertCommandOutputStartsWith(a, "/set prompt te 'aaa'",
+                            "ERROR: Expected format missing"),
+                    (a) -> assertCommandOutputStartsWith(a, "/set feedback normal",
+                            "|  Feedback mode: normal")
             );
         } finally {
             assertCommandCheckOutput(false, "/set feedback normal", s -> {
@@ -136,7 +257,7 @@
         try {
             test(
                     (a) -> assertCommandOutputContains(a, "/help /set", "command to launch"),
-                    (a) -> assertCommandOutputContains(a, "/help /set format", "vardecl"),
+                    (a) -> assertCommandOutputContains(a, "/help /set format", "display"),
                     (a) -> assertCommandOutputContains(a, "/hel /se for", "vardecl"),
                     (a) -> assertCommandOutputContains(a, "/help /set editor", "temporary file")
             );
@@ -150,7 +271,7 @@
         try {
             test(
                     (a) -> assertCommandOutputStartsWith(a, "/set newmode te command normal", "|  Created new feedback mode: te"),
-                    (a) -> assertCommand(a, "/set field te errorpre 'ERROR: '", ""),
+                    (a) -> assertCommand(a, "/set format te errorpre 'ERROR: '", ""),
                     (a) -> assertCommandOutputStartsWith(a, "/set feedback te", "|  Feedback mode: te"),
                     (a) -> assertCommandOutputContains(a, "/help /set xyz", "ERROR: Not a valid argument to /set: xyz"),
                     (a) -> assertCommandOutputContains(a, "/help /set f", "ERROR: Ambiguous argument to /set: f")
--- a/langtools/test/jdk/jshell/ToolReloadTest.java	Wed Mar 23 21:44:24 2016 -0700
+++ b/langtools/test/jdk/jshell/ToolReloadTest.java	Fri Mar 25 18:36:19 2016 -0700
@@ -92,11 +92,11 @@
     public void testReloadDrop() {
         test(false, new String[]{"-nostartup"},
                 a -> assertVariable(a, "int", "a"),
-                a -> dropVariable(a, "/dr 1", "int a = 0"),
+                a -> dropVariable(a, "/dr 1", "int a = 0", "|  Dropped variable a\n"),
                 a -> assertMethod(a, "int b() { return 0; }", "()I", "b"),
-                a -> dropMethod(a, "/drop b", "b ()I"),
+                a -> dropMethod(a, "/drop b", "b ()I", "|  Dropped method b()\n"),
                 a -> assertClass(a, "class A {}", "class", "A"),
-                a -> dropClass(a, "/dr A", "class A"),
+                a -> dropClass(a, "/dr A", "class A", "|  Dropped class A\n"),
                 a -> assertCommand(a, "/reload",
                         "|  Restarting and restoring state.\n" +
                         "-: int a;\n" +
@@ -115,11 +115,11 @@
     public void testReloadQuiet() {
         test(false, new String[]{"-nostartup"},
                 a -> assertVariable(a, "int", "a"),
-                a -> dropVariable(a, "/dr 1", "int a = 0"),
+                a -> dropVariable(a, "/dr 1", "int a = 0", "|  Dropped variable a\n"),
                 a -> assertMethod(a, "int b() { return 0; }", "()I", "b"),
-                a -> dropMethod(a, "/drop b", "b ()I"),
+                a -> dropMethod(a, "/drop b", "b ()I", "|  Dropped method b()\n"),
                 a -> assertClass(a, "class A {}", "class", "A"),
-                a -> dropClass(a, "/dr A", "class A"),
+                a -> dropClass(a, "/dr A", "class A", "|  Dropped class A\n"),
                 a -> assertCommand(a, "/reload quiet",
                         "|  Restarting and restoring state.\n"),
                 a -> assertCommandCheckOutput(a, "/vars", assertVariables()),