langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/JShellTool.java
changeset 38539 71874886920f
parent 38535 4a25025e0b0d
child 38613 2a8e50869b08
equal deleted inserted replaced
38538:8bdc63ff6961 38539:71874886920f
   115  */
   115  */
   116 public class JShellTool implements MessageHandler {
   116 public class JShellTool implements MessageHandler {
   117 
   117 
   118     private static final String LINE_SEP = System.getProperty("line.separator");
   118     private static final String LINE_SEP = System.getProperty("line.separator");
   119     private static final Pattern LINEBREAK = Pattern.compile("\\R");
   119     private static final Pattern LINEBREAK = Pattern.compile("\\R");
   120     private static final Pattern HISTORY_ALL_START_FILENAME = Pattern.compile(
       
   121             "((?<cmd>(-all|-history|-start))(\\z|\\p{javaWhitespace}+))?(?<filename>.*)");
       
   122     private static final String RECORD_SEPARATOR = "\u241E";
   120     private static final String RECORD_SEPARATOR = "\u241E";
   123     private static final String RB_NAME_PREFIX  = "jdk.internal.jshell.tool.resources";
   121     private static final String RB_NAME_PREFIX  = "jdk.internal.jshell.tool.resources";
   124     private static final String VERSION_RB_NAME = RB_NAME_PREFIX + ".version";
   122     private static final String VERSION_RB_NAME = RB_NAME_PREFIX + ".version";
   125     private static final String L10N_RB_NAME    = RB_NAME_PREFIX + ".l10n";
   123     private static final String L10N_RB_NAME    = RB_NAME_PREFIX + ".l10n";
   126 
   124 
   460             start(in, loadList);
   458             start(in, loadList);
   461         }
   459         }
   462     }
   460     }
   463 
   461 
   464     private void start(IOContext in, List<String> loadList) {
   462     private void start(IOContext in, List<String> loadList) {
       
   463         // If startup hasn't been set by command line, set from retained/default
       
   464         if (startup == null) {
       
   465             startup = prefs.get(STARTUP_KEY, null);
       
   466             if (startup == null) {
       
   467                 startup = DEFAULT_STARTUP;
       
   468             }
       
   469         }
       
   470 
   465         // Read retained editor setting (if any)
   471         // Read retained editor setting (if any)
   466         String editorString = prefs.get(EDITOR_KEY, null);
   472         String editorString = prefs.get(EDITOR_KEY, "");
   467         if (editorString != null) {
   473         if (editorString == null || editorString.isEmpty()) {
       
   474             editor = null;
       
   475         } else {
   468             editor = editorString.split(RECORD_SEPARATOR);
   476             editor = editorString.split(RECORD_SEPARATOR);
   469         }
   477         }
   470 
   478 
   471         resetState(); // Initialize
   479         resetState(); // Initialize
   472 
   480 
   646 
   654 
   647         if (cmdlineClasspath != null) {
   655         if (cmdlineClasspath != null) {
   648             state.addToClasspath(cmdlineClasspath);
   656             state.addToClasspath(cmdlineClasspath);
   649         }
   657         }
   650 
   658 
   651         String start;
   659         startUpRun(startup);
   652         if (startup == null) {
       
   653             start = startup = prefs.get(STARTUP_KEY, null);
       
   654             if (start == null) {
       
   655                 start = DEFAULT_STARTUP;
       
   656             }
       
   657         } else {
       
   658             start = startup;
       
   659         }
       
   660         startUpRun(start);
       
   661         currentNameSpace = mainNamespace;
   660         currentNameSpace = mainNamespace;
   662     }
   661     }
       
   662 
   663     //where -- one-time per run initialization of feedback modes
   663     //where -- one-time per run initialization of feedback modes
   664     private void initFeedback() {
   664     private void initFeedback() {
   665         // No fluff, no prefix, for init failures
   665         // No fluff, no prefix, for init failures
   666         MessageHandler initmh = new InitMessageHandler();
   666         MessageHandler initmh = new InitMessageHandler();
   667         // Execute the feedback initialization code in the resource file
   667         // Execute the feedback initialization code in the resource file
   668         startUpRun(getResourceString("startup.feedback"));
   668         startUpRun(getResourceString("startup.feedback"));
   669         // These predefined modes are read-only
   669         // These predefined modes are read-only
   670         feedback.markModesReadOnly();
   670         feedback.markModesReadOnly();
   671         // Restore user defined modes retained on previous run with /retain mode
   671         // Restore user defined modes retained on previous run with /retain mode
   672         String encoded = prefs.get(MODE_KEY, null);
   672         String encoded = prefs.get(MODE_KEY, null);
   673         if (encoded != null) {
   673         if (encoded != null && !encoded.isEmpty()) {
   674             feedback.restoreEncodedModes(initmh, encoded);
   674             if (!feedback.restoreEncodedModes(initmh, encoded)) {
       
   675                 // Catastrophic corruption -- remove the retained modes
       
   676                 prefs.remove(MODE_KEY);
       
   677             }
   675         }
   678         }
   676         if (commandLineFeedbackMode != null) {
   679         if (commandLineFeedbackMode != null) {
   677             // The feedback mode to use was specified on the command line, use it
   680             // The feedback mode to use was specified on the command line, use it
   678             if (!feedback.setFeedback(initmh, new ArgTokenizer("-feedback ", commandLineFeedbackMode))) {
   681             if (!feedback.setFeedback(initmh, new ArgTokenizer("-feedback", commandLineFeedbackMode))) {
   679                 regenerateOnDeath = false;
   682                 regenerateOnDeath = false;
   680             }
   683             }
   681             commandLineFeedbackMode = null;
   684             commandLineFeedbackMode = null;
   682         } else {
   685         } else {
   683             String fb = prefs.get(FEEDBACK_KEY, null);
   686             String fb = prefs.get(FEEDBACK_KEY, null);
   684             if (fb != null) {
   687             if (fb != null) {
   685                 // Restore the feedback mode to use that was retained
   688                 // Restore the feedback mode to use that was retained
   686                 // on a previous run with /retain feedback
   689                 // on a previous run with /retain feedback
   687                 feedback.setFeedback(initmh, new ArgTokenizer("/retain feedback ", fb));
   690                 feedback.retainFeedback(initmh, new ArgTokenizer("/retain feedback", fb));
   688             }
   691             }
   689         }
   692         }
   690     }
   693     }
   691 
   694 
   692     //where
   695     //where
   693     private void startUpRun(String start) {
   696     private void startUpRun(String start) {
   694         try (IOContext suin = new FileScannerIOContext(new StringReader(start))) {
   697         try (IOContext suin = new FileScannerIOContext(new StringReader(start))) {
   695             run(suin);
   698             run(suin);
   696         } catch (Exception ex) {
   699         } catch (Exception ex) {
   697             hardmsg("jshell.err.startup.unexpected.exception", ex);
   700             hardmsg("jshell.err.startup.unexpected.exception", ex);
       
   701             ex.printStackTrace(cmdout);
   698         }
   702         }
   699     }
   703     }
   700 
   704 
   701     private void closeState() {
   705     private void closeState() {
   702         live = false;
   706         live = false;
  1010 
  1014 
  1011     List<Snippet> allSnippets() {
  1015     List<Snippet> allSnippets() {
  1012         return state.snippets();
  1016         return state.snippets();
  1013     }
  1017     }
  1014 
  1018 
  1015     List<Snippet> dropableSnippets() {
  1019     List<PersistentSnippet> dropableSnippets() {
  1016         return state.snippets().stream()
  1020         return state.snippets().stream()
  1017                 .filter(sn -> state.status(sn).isActive)
  1021                 .filter(sn -> state.status(sn).isActive && sn instanceof PersistentSnippet)
       
  1022                 .map(sn -> (PersistentSnippet) sn)
  1018                 .collect(toList());
  1023                 .collect(toList());
  1019     }
  1024     }
  1020 
  1025 
  1021     List<VarSnippet> allVarSnippets() {
  1026     List<VarSnippet> allVarSnippets() {
  1022         return state.snippets().stream()
  1027         return state.snippets().stream()
  1171     }
  1176     }
  1172 
  1177 
  1173     // --- Command implementations ---
  1178     // --- Command implementations ---
  1174 
  1179 
  1175     private static final String[] SET_SUBCOMMANDS = new String[]{
  1180     private static final String[] SET_SUBCOMMANDS = new String[]{
  1176         "format", "truncation", "feedback", "newmode", "prompt", "editor", "start"};
  1181         "format", "truncation", "feedback", "mode", "prompt", "editor", "start"};
  1177 
  1182 
  1178     private static final String[] RETAIN_SUBCOMMANDS = new String[]{
  1183     private static final String[] RETAIN_SUBCOMMANDS = new String[]{
  1179         "feedback", "mode", "editor", "start"};
  1184         "feedback", "mode", "editor", "start"};
  1180 
  1185 
  1181     final boolean cmdSet(String arg) {
  1186     final boolean cmdSet(String arg) {
  1182         String cmd = "/set";
  1187         String cmd = "/set";
  1183         ArgTokenizer at = new ArgTokenizer(cmd +" ", arg.trim());
  1188         ArgTokenizer at = new ArgTokenizer(cmd, arg.trim());
  1184         String which = subCommand(cmd, at, SET_SUBCOMMANDS);
  1189         String which = subCommand(cmd, at, SET_SUBCOMMANDS);
  1185         if (which == null) {
  1190         if (which == null) {
  1186             return false;
  1191             return false;
  1187         }
  1192         }
  1188         switch (which) {
  1193         switch (which) {
  1190                 return feedback.setFormat(this, at);
  1195                 return feedback.setFormat(this, at);
  1191             case "truncation":
  1196             case "truncation":
  1192                 return feedback.setTruncation(this, at);
  1197                 return feedback.setTruncation(this, at);
  1193             case "feedback":
  1198             case "feedback":
  1194                 return feedback.setFeedback(this, at);
  1199                 return feedback.setFeedback(this, at);
  1195             case "newmode":
  1200             case "mode":
  1196                 return feedback.setNewMode(this, at);
  1201                 return feedback.setMode(this, at);
  1197             case "prompt":
  1202             case "prompt":
  1198                 return feedback.setPrompt(this, at);
  1203                 return feedback.setPrompt(this, at);
  1199             case "editor": {
  1204             case "editor":
  1200                 String prog = at.next();
  1205                 return setEditor(at, true);
  1201                 if (prog == null) {
  1206             case "start":
  1202                     errormsg("jshell.err.set.editor.arg");
  1207                 return setStart(cmd, at, true);
  1203                     return false;
       
  1204                 } else {
       
  1205                     return setEditor(cmd, prog, at);
       
  1206                 }
       
  1207             }
       
  1208             case "start": {
       
  1209                 return setStart(cmd, at.next());
       
  1210             }
       
  1211             default:
  1208             default:
  1212                 errormsg("jshell.err.arg", cmd, at.val());
  1209                 errormsg("jshell.err.arg", cmd, at.val());
  1213                 return false;
  1210                 return false;
  1214         }
  1211         }
  1215     }
  1212     }
  1216 
  1213 
  1217     final boolean cmdRetain(String arg) {
  1214     final boolean cmdRetain(String arg) {
  1218         String cmd = "/retain";
  1215         String cmd = "/retain";
  1219         ArgTokenizer at = new ArgTokenizer(cmd +" ", arg.trim());
  1216         ArgTokenizer at = new ArgTokenizer(cmd, arg.trim());
  1220         String which = subCommand(cmd, at, RETAIN_SUBCOMMANDS);
  1217         String which = subCommand(cmd, at, RETAIN_SUBCOMMANDS);
  1221         if (which == null) {
  1218         if (which == null) {
  1222             return false;
  1219             return false;
  1223         }
  1220         }
  1224         switch (which) {
  1221         switch (which) {
  1237                     // Retain this mode and all previously retained modes
  1234                     // Retain this mode and all previously retained modes
  1238                     prefs.put(MODE_KEY, retained);
  1235                     prefs.put(MODE_KEY, retained);
  1239                     return true;
  1236                     return true;
  1240                 }
  1237                 }
  1241                 return false;
  1238                 return false;
  1242             case "editor": {
  1239             case "editor":
  1243                 String prog = at.next();
  1240                 if (!setEditor(at, false)) {
  1244                 if (prog != null) {
  1241                     return false;
  1245                     // If the editor is specified, first run /set editor ...
  1242                 }
  1246                     if(!setEditor(cmd, prog, at)) {
  1243                 // retain editor setting
  1247                         return false;
  1244                 prefs.put(EDITOR_KEY, (editor == null)
  1248                     }
  1245                         ? ""
  1249                 }
  1246                         : String.join(RECORD_SEPARATOR, editor));
  1250                 if (editor != null) {
  1247                 return true;
  1251                     // If an editor has been set now, or in the past, retain it
       
  1252                     prefs.put(EDITOR_KEY, String.join(RECORD_SEPARATOR, editor));
       
  1253                     return true;
       
  1254                 }
       
  1255                 return false;
       
  1256             }
       
  1257             case "start": {
  1248             case "start": {
  1258                 String fn = at.next();
  1249                 if (!setStart(cmd, at, false)) {
  1259                 if (fn != null) {
  1250                     return false;
  1260                     if (!setStart(cmd, fn)) {
  1251                 }
  1261                         return false;
  1252                 // retain startup setting
  1262                     }
  1253                 prefs.put(STARTUP_KEY, startup);
  1263                 }
  1254                 return true;
  1264                 if (startup != null) {
       
  1265                     prefs.put(STARTUP_KEY, startup);
       
  1266                     return true;
       
  1267                 }
       
  1268                 return false;
       
  1269             }
  1255             }
  1270             default:
  1256             default:
  1271                 errormsg("jshell.err.arg", cmd, at.val());
  1257                 errormsg("jshell.err.arg", cmd, at.val());
  1272                 return false;
  1258                 return false;
  1273         }
  1259         }
  1309         }
  1295         }
  1310         return matches[0];
  1296         return matches[0];
  1311     }
  1297     }
  1312 
  1298 
  1313     // The sub-command:  /set editor <editor-command-line>>
  1299     // The sub-command:  /set editor <editor-command-line>>
  1314     boolean setEditor(String cmd, String prog, ArgTokenizer at) {
  1300     boolean setEditor(ArgTokenizer at, boolean argsRequired) {
       
  1301         at.allowedOptions("-default");
       
  1302         String prog = at.next();
  1315         List<String> ed = new ArrayList<>();
  1303         List<String> ed = new ArrayList<>();
  1316         ed.add(prog);
  1304         while (at.val() != null) {
  1317         String n;
  1305             ed.add(at.val());
  1318         while ((n = at.next()) != null) {
  1306             at.nextToken();
  1319             ed.add(n);
  1307         }
  1320         }
  1308         if (!checkOptionsAndRemainingInput(at)) {
  1321         editor = ed.toArray(new String[ed.size()]);
  1309             return false;
  1322         fluffmsg("jshell.msg.set.editor.set", prog);
  1310         }
       
  1311         boolean defaultOption = at.hasOption("-default");
       
  1312         if (prog != null) {
       
  1313             if (defaultOption) {
       
  1314                 errormsg("jshell.err.default.option.or.program", at.whole());
       
  1315                 return false;
       
  1316             }
       
  1317             editor = ed.toArray(new String[ed.size()]);
       
  1318             fluffmsg("jshell.msg.set.editor.set", prog);
       
  1319         } else if (defaultOption) {
       
  1320             editor = null;
       
  1321         } else if (argsRequired) {
       
  1322             errormsg("jshell.err.set.editor.arg");
       
  1323             return false;
       
  1324         }
  1323         return true;
  1325         return true;
  1324     }
  1326     }
  1325 
  1327 
  1326     // The sub-command:  /set start <start-file>
  1328     // The sub-command:  /set start <start-file>
  1327     boolean setStart(String cmd, String fn) {
  1329     boolean setStart(String cmd, ArgTokenizer at, boolean argsRequired) {
  1328         String init = readFile(fn, cmd + " start");
  1330         at.allowedOptions("-default", "-none");
  1329         if (init == null) {
  1331         String fn = at.next();
       
  1332         if (!checkOptionsAndRemainingInput(at)) {
  1330             return false;
  1333             return false;
  1331         } else {
  1334         }
  1332             startup = init;
  1335         int argCount = at.optionCount() + ((fn != null) ? 1 : 0);
  1333             //prefs.put(STARTUP_KEY, init);
  1336         if (argCount > 1 || argsRequired && argCount == 0) {
  1334             return true;
  1337             errormsg("jshell.err.option.or.filename", at.whole());
  1335         }
  1338             return false;
       
  1339         }
       
  1340         if (fn != null) {
       
  1341             String init = readFile(fn, cmd + " start");
       
  1342             if (init == null) {
       
  1343                 return false;
       
  1344             } else {
       
  1345                 startup = init;
       
  1346                 return true;
       
  1347             }
       
  1348         } else if (at.hasOption("-default")) {
       
  1349             startup = DEFAULT_STARTUP;
       
  1350         } else if (at.hasOption("-none")) {
       
  1351             startup = "";
       
  1352         }
       
  1353         return true;
  1336     }
  1354     }
  1337 
  1355 
  1338     boolean cmdClasspath(String arg) {
  1356     boolean cmdClasspath(String arg) {
  1339         if (arg.isEmpty()) {
  1357         if (arg.isEmpty()) {
  1340             errormsg("jshell.err.classpath.arg");
  1358             errormsg("jshell.err.classpath.arg");
  1417         fluffmsg("jshell.msg.goodbye");
  1435         fluffmsg("jshell.msg.goodbye");
  1418         return true;
  1436         return true;
  1419     }
  1437     }
  1420 
  1438 
  1421     boolean cmdHelp(String arg) {
  1439     boolean cmdHelp(String arg) {
  1422         ArgTokenizer at = new ArgTokenizer(arg);
  1440         ArgTokenizer at = new ArgTokenizer("/help", arg);
  1423         String subject = at.next();
  1441         String subject = at.next();
  1424         if (subject != null) {
  1442         if (subject != null) {
  1425             Command[] matches = commands.values().stream()
  1443             Command[] matches = commands.values().stream()
  1426                     .filter(c -> c.command.startsWith(subject))
  1444                     .filter(c -> c.command.startsWith(subject))
  1427                     .toArray(size -> new Command[size]);
  1445                     .toArray(size -> new Command[size]);
  1515         return sn instanceof DeclarationSnippet
  1533         return sn instanceof DeclarationSnippet
  1516                 && ((DeclarationSnippet) sn).name().equals(name);
  1534                 && ((DeclarationSnippet) sn).name().equals(name);
  1517     }
  1535     }
  1518 
  1536 
  1519     /**
  1537     /**
  1520      * Convert a user argument to a Stream of snippets referenced by that argument
  1538      * Convert user arguments to a Stream of snippets referenced by those
  1521      * (or lack of argument).
  1539      * arguments (or lack of arguments).
  1522      *
       
  1523      * @param snippets the base list of possible snippets
       
  1524      * @param arg the user's argument to the command, maybe be the empty string
       
  1525      * @param allowAll if true, allow the use of '-all' and '-start'
       
  1526      * @return a Stream of referenced snippets or null if no matches to specific arg
       
  1527      */
       
  1528     private <T extends Snippet> Stream<T> argToSnippets(List<T> snippets, String arg, boolean allowAll) {
       
  1529         return argToSnippets(snippets, this::mainActive, arg, allowAll);
       
  1530     }
       
  1531 
       
  1532     /**
       
  1533      * Convert a user argument to a Stream of snippets referenced by that argument
       
  1534      * (or lack of argument).
       
  1535      *
  1540      *
  1536      * @param snippets the base list of possible snippets
  1541      * @param snippets the base list of possible snippets
  1537      * @param defFilter the filter to apply to the arguments if no argument
  1542      * @param defFilter the filter to apply to the arguments if no argument
  1538      * @param arg the user's argument to the command, maybe be the empty string
  1543      * @param rawargs the user's argument to the command, maybe be the empty
  1539      * @param allowAll if true, allow the use of '-all' and '-start'
  1544      * string
  1540      * @return a Stream of referenced snippets or null if no matches to specific arg
  1545      * @return a Stream of referenced snippets or null if no matches are found
  1541      */
  1546      */
  1542     private <T extends Snippet> Stream<T> argToSnippets(List<T> snippets,
  1547     private <T extends Snippet> Stream<T> argsOptionsToSnippets(List<T> snippets,
  1543             Predicate<Snippet> defFilter, String arg, boolean allowAll) {
  1548             Predicate<Snippet> defFilter, String rawargs, String cmd) {
  1544         if (allowAll && arg.equals("-all")) {
  1549         ArgTokenizer at = new ArgTokenizer(cmd, rawargs.trim());
       
  1550         at.allowedOptions("-all", "-start");
       
  1551         List<String> args = new ArrayList<>();
       
  1552         String s;
       
  1553         while ((s = at.next()) != null) {
       
  1554             args.add(s);
       
  1555         }
       
  1556         if (!checkOptionsAndRemainingInput(at)) {
       
  1557             return null;
       
  1558         }
       
  1559         if (at.optionCount() > 0 && args.size() > 0) {
       
  1560             errormsg("jshell.err.may.not.specify.options.and.snippets", at.whole());
       
  1561             return null;
       
  1562         }
       
  1563         if (at.optionCount() > 1) {
       
  1564             errormsg("jshell.err.conflicting.options", at.whole());
       
  1565             return null;
       
  1566         }
       
  1567         if (at.hasOption("-all")) {
  1545             // all snippets including start-up, failed, and overwritten
  1568             // all snippets including start-up, failed, and overwritten
  1546             return snippets.stream();
  1569             return snippets.stream();
  1547         } else if (allowAll && arg.equals("-start")) {
  1570         }
       
  1571         if (at.hasOption("-start")) {
  1548             // start-up snippets
  1572             // start-up snippets
  1549             return snippets.stream()
  1573             return snippets.stream()
  1550                     .filter(this::inStartUp);
  1574                     .filter(this::inStartUp);
  1551         } else if (arg.isEmpty()) {
  1575         }
       
  1576         if (args.isEmpty()) {
  1552             // Default is all active user snippets
  1577             // Default is all active user snippets
  1553             return snippets.stream()
  1578             return snippets.stream()
  1554                     .filter(defFilter);
  1579                     .filter(defFilter);
  1555         } else {
  1580         }
  1556             Stream<T> result =
  1581         return argsToSnippets(snippets, args);
  1557                     nonEmptyStream(
  1582     }
  1558                             () -> snippets.stream(),
  1583 
  1559                             // look for active user declarations matching the name
  1584     /**
  1560                             sn -> isActive(sn) && matchingDeclaration(sn, arg),
  1585      * Convert user arguments to a Stream of snippets referenced by those
  1561                             // else, look for any declarations matching the name
  1586      * arguments.
  1562                             sn -> matchingDeclaration(sn, arg),
       
  1563                             // else, look for an id of this name
       
  1564                             sn -> sn.id().equals(arg)
       
  1565                     );
       
  1566             return result;
       
  1567         }
       
  1568     }
       
  1569 
       
  1570     /**
       
  1571      * Convert a user argument to a Stream of snippets referenced by that
       
  1572      * argument, printing an informative message if no matches. Allow '-all' and
       
  1573      * '-start'.
       
  1574      *
  1587      *
  1575      * @param snippets the base list of possible snippets
  1588      * @param snippets the base list of possible snippets
  1576      * @param defFilter the filter to apply to the arguments if no argument
  1589      * @param args the user's argument to the command, maybe be the empty list
  1577      * @param arg the user's argument to the command, maybe be the empty string
       
  1578      * @param cmd the name of the command (for use in a help message
       
  1579      * @return a Stream of referenced snippets or null if no matches to specific
  1590      * @return a Stream of referenced snippets or null if no matches to specific
  1580      * arg
  1591      * arg
  1581      */
  1592      */
  1582     private <T extends Snippet> Stream<T> argToSnippetsWithMessage(List<T> snippets,
  1593     private <T extends Snippet> Stream<T> argsToSnippets(List<T> snippets,
  1583             Predicate<Snippet> defFilter, String arg, String cmd) {
  1594             List<String> args) {
  1584         Stream<T> stream = argToSnippets(snippets, defFilter, arg, true);
  1595         Stream<T> result = null;
  1585         if (stream == null) {
  1596         for (String arg : args) {
  1586             errormsg("jshell.err.def.or.id.not.found", arg);
  1597             // Find the best match
  1587             // Check if there are any definitions at all
  1598             Stream<T> st = layeredSnippetSearch(snippets, arg);
  1588             if (argToSnippets(snippets, "", false).iterator().hasNext()) {
  1599             if (st == null) {
  1589                 fluffmsg("jshell.msg.try.command.without.args", cmd);
  1600                 Stream<Snippet> est = layeredSnippetSearch(state.snippets(), arg);
       
  1601                 if (est == null) {
       
  1602                     errormsg("jshell.err.no.such.snippets", arg);
       
  1603                 } else {
       
  1604                     errormsg("jshell.err.the.snippet.cannot.be.used.with.this.command",
       
  1605                             arg, est.findFirst().get().source());
       
  1606                 }
       
  1607                 return null;
       
  1608             }
       
  1609             if (result == null) {
       
  1610                 result = st;
  1590             } else {
  1611             } else {
  1591                 hardmsg("jshell.msg.no.active");
  1612                 result = Stream.concat(result, st);
  1592             }
  1613             }
  1593         }
  1614         }
  1594         return stream;
  1615         return result;
  1595     }
  1616     }
  1596 
  1617 
  1597     private boolean cmdDrop(String arg) {
  1618     private <T extends Snippet> Stream<T> layeredSnippetSearch(List<T> snippets, String arg) {
  1598         if (arg.isEmpty()) {
  1619         return nonEmptyStream(
       
  1620                 // the stream supplier
       
  1621                 () -> snippets.stream(),
       
  1622                 // look for active user declarations matching the name
       
  1623                 sn -> isActive(sn) && matchingDeclaration(sn, arg),
       
  1624                 // else, look for any declarations matching the name
       
  1625                 sn -> matchingDeclaration(sn, arg),
       
  1626                 // else, look for an id of this name
       
  1627                 sn -> sn.id().equals(arg)
       
  1628         );
       
  1629     }
       
  1630 
       
  1631     private boolean cmdDrop(String rawargs) {
       
  1632         ArgTokenizer at = new ArgTokenizer("/drop", rawargs.trim());
       
  1633         at.allowedOptions();
       
  1634         List<String> args = new ArrayList<>();
       
  1635         String s;
       
  1636         while ((s = at.next()) != null) {
       
  1637             args.add(s);
       
  1638         }
       
  1639         if (!checkOptionsAndRemainingInput(at)) {
       
  1640             return false;
       
  1641         }
       
  1642         if (args.isEmpty()) {
  1599             errormsg("jshell.err.drop.arg");
  1643             errormsg("jshell.err.drop.arg");
  1600             return false;
  1644             return false;
  1601         }
  1645         }
  1602         Stream<Snippet> stream = argToSnippets(dropableSnippets(), arg, false);
  1646         Stream<PersistentSnippet> stream = argsToSnippets(dropableSnippets(), args);
  1603         if (stream == null) {
  1647         if (stream == null) {
  1604             errormsg("jshell.err.def.or.id.not.found", arg);
  1648             // Snippet not found. Error already printed
  1605             fluffmsg("jshell.msg.see.classes.etc");
  1649             fluffmsg("jshell.msg.see.classes.etc");
  1606             return false;
  1650             return false;
  1607         }
  1651         }
  1608         List<Snippet> snippets = stream
  1652         List<PersistentSnippet> snippets = stream.collect(toList());
  1609                 .filter(sn -> state.status(sn).isActive && sn instanceof PersistentSnippet)
  1653         if (snippets.size() > args.size()) {
  1610                 .collect(toList());
  1654             // One of the args references more thean one snippet
  1611         if (snippets.isEmpty()) {
       
  1612             errormsg("jshell.err.drop.not.active");
       
  1613             return false;
       
  1614         }
       
  1615         if (snippets.size() > 1) {
       
  1616             errormsg("jshell.err.drop.ambiguous");
  1655             errormsg("jshell.err.drop.ambiguous");
  1617             fluffmsg("jshell.msg.use.one.of", snippets.stream()
  1656             fluffmsg("jshell.msg.use.one.of", snippets.stream()
  1618                     .map(sn -> String.format("\n%4s : %s", sn.id(), sn.source().replace("\n", "\n       ")))
  1657                     .map(sn -> String.format("\n/drop %-5s :   %s", sn.id(), sn.source().replace("\n", "\n       ")))
  1619                     .collect(Collectors.joining(", "))
  1658                     .collect(Collectors.joining(", "))
  1620             );
  1659             );
  1621             return false;
  1660             return false;
  1622         }
  1661         }
  1623         PersistentSnippet psn = (PersistentSnippet) snippets.get(0);
  1662         snippets.stream()
  1624         state.drop(psn).forEach(this::handleEvent);
  1663                 .forEach(sn -> state.drop(sn).forEach(this::handleEvent));
  1625         return true;
  1664         return true;
  1626     }
  1665     }
  1627 
  1666 
  1628     private boolean cmdEdit(String arg) {
  1667     private boolean cmdEdit(String arg) {
  1629         Stream<Snippet> stream = argToSnippetsWithMessage(state.snippets(),
  1668         Stream<Snippet> stream = argsOptionsToSnippets(state.snippets(),
  1630                 this::mainActive, arg, "/edit");
  1669                 this::mainActive, arg, "/edit");
  1631         if (stream == null) {
  1670         if (stream == null) {
  1632             return false;
  1671             return false;
  1633         }
  1672         }
  1634         Set<String> srcSet = new LinkedHashSet<>();
  1673         Set<String> srcSet = new LinkedHashSet<>();
  1726             return s.substring(b, e + 1);
  1765             return s.substring(b, e + 1);
  1727         }
  1766         }
  1728     }
  1767     }
  1729 
  1768 
  1730     private boolean cmdList(String arg) {
  1769     private boolean cmdList(String arg) {
  1731         if (arg.equals("-history")) {
  1770         if (arg.length() >= 2 && "-history".startsWith(arg)) {
  1732             return cmdHistory();
  1771             return cmdHistory();
  1733         }
  1772         }
  1734         Stream<Snippet> stream = argToSnippetsWithMessage(state.snippets(),
  1773         Stream<Snippet> stream = argsOptionsToSnippets(state.snippets(),
  1735                 this::mainActive, arg, "/list");
  1774                 this::mainActive, arg, "/list");
  1736         if (stream == null) {
  1775         if (stream == null) {
  1737             return false;
  1776             return false;
  1738         }
  1777         }
  1739 
  1778 
  1799         live = false;
  1838         live = false;
  1800         fluffmsg("jshell.msg.resetting.state");
  1839         fluffmsg("jshell.msg.resetting.state");
  1801         return true;
  1840         return true;
  1802     }
  1841     }
  1803 
  1842 
  1804     private boolean cmdReload(String arg) {
  1843     private boolean cmdReload(String rawargs) {
  1805         Iterable<String> history = replayableHistory;
  1844         ArgTokenizer at = new ArgTokenizer("/reload", rawargs.trim());
  1806         boolean echo = true;
  1845         at.allowedOptions("-restore", "-quiet");
  1807         if (arg.length() > 0) {
  1846         if (!checkOptionsAndRemainingInput(at)) {
  1808             if ("-restore".startsWith(arg)) {
  1847             return false;
  1809                 if (replayableHistoryPrevious == null) {
  1848         }
  1810                     errormsg("jshell.err.reload.no.previous");
  1849         Iterable<String> history;
  1811                     return false;
  1850         if (at.hasOption("-restore")) {
  1812                 }
  1851             if (replayableHistoryPrevious == null) {
  1813                 history = replayableHistoryPrevious;
  1852                 errormsg("jshell.err.reload.no.previous");
  1814             } else if ("-quiet".startsWith(arg)) {
       
  1815                 echo = false;
       
  1816             } else {
       
  1817                 errormsg("jshell.err.arg", "/reload", arg);
       
  1818                 return false;
  1853                 return false;
  1819             }
  1854             }
  1820         }
  1855             history = replayableHistoryPrevious;
  1821         fluffmsg(history == replayableHistoryPrevious
  1856             fluffmsg("jshell.err.reload.restarting.previous.state");
  1822                         ? "jshell.err.reload.restarting.previous.state"
  1857         } else {
  1823                         : "jshell.err.reload.restarting.state");
  1858             history = replayableHistory;
       
  1859             fluffmsg("jshell.err.reload.restarting.state");
       
  1860         }
       
  1861         boolean echo = !at.hasOption("-quiet");
  1824         resetState();
  1862         resetState();
  1825         run(new ReloadIOContext(history,
  1863         run(new ReloadIOContext(history,
  1826                 echo? cmdout : null));
  1864                 echo ? cmdout : null));
  1827         return true;
  1865         return true;
  1828     }
  1866     }
  1829 
  1867 
  1830     private boolean cmdSave(String arg_filename) {
  1868     private boolean cmdSave(String rawargs) {
  1831         Matcher mat = HISTORY_ALL_START_FILENAME.matcher(arg_filename);
  1869         ArgTokenizer at = new ArgTokenizer("/save", rawargs.trim());
  1832         if (!mat.find()) {
  1870         at.allowedOptions("-all", "-start", "-history");
  1833             errormsg("jshell.err.arg", arg_filename);
  1871         String filename = at.next();
       
  1872         if (filename == null) {
       
  1873             errormsg("jshell.err.file.filename", "/save");
  1834             return false;
  1874             return false;
  1835         }
  1875         }
  1836         boolean useHistory = false;
  1876         if (!checkOptionsAndRemainingInput(at)) {
  1837         String saveAll = "";
  1877             return false;
  1838         boolean saveStart = false;
  1878         }
  1839         String cmd = mat.group("cmd");
  1879         if (at.optionCount() > 1) {
  1840         if (cmd != null) switch (cmd) {
  1880             errormsg("jshell.err.conflicting.options", at.whole());
  1841             case "-all":
       
  1842                 saveAll = "-all";
       
  1843                 break;
       
  1844             case "-history":
       
  1845                 useHistory = true;
       
  1846                 break;
       
  1847             case "-start":
       
  1848                 saveStart = true;
       
  1849                 break;
       
  1850         }
       
  1851         String filename = mat.group("filename");
       
  1852         if (filename == null ||filename.isEmpty()) {
       
  1853             errormsg("jshell.err.file.filename", "/save");
       
  1854             return false;
  1881             return false;
  1855         }
  1882         }
  1856         try (BufferedWriter writer = Files.newBufferedWriter(toPathResolvingUserHome(filename),
  1883         try (BufferedWriter writer = Files.newBufferedWriter(toPathResolvingUserHome(filename),
  1857                 Charset.defaultCharset(),
  1884                 Charset.defaultCharset(),
  1858                 CREATE, TRUNCATE_EXISTING, WRITE)) {
  1885                 CREATE, TRUNCATE_EXISTING, WRITE)) {
  1859             if (useHistory) {
  1886             if (at.hasOption("-history")) {
  1860                 for (String s : input.currentSessionHistory()) {
  1887                 for (String s : input.currentSessionHistory()) {
  1861                     writer.write(s);
  1888                     writer.write(s);
  1862                     writer.write("\n");
  1889                     writer.write("\n");
  1863                 }
  1890                 }
  1864             } else if (saveStart) {
  1891             } else if (at.hasOption("-start")) {
  1865                 writer.append(DEFAULT_STARTUP);
  1892                 writer.append(startup);
  1866             } else {
  1893             } else {
  1867                 Stream<Snippet> stream = argToSnippets(state.snippets(), saveAll, true);
  1894                 List<Snippet> sns = at.hasOption("-all")
  1868                 if (stream != null) {
  1895                         ? state.snippets()
  1869                     for (Snippet sn : stream.collect(toList())) {
  1896                         : state.snippets().stream().filter(this::mainActive).collect(toList());
  1870                         writer.write(sn.source());
  1897                 for (Snippet sn : sns) {
  1871                         writer.write("\n");
  1898                     writer.write(sn.source());
  1872                     }
  1899                     writer.write("\n");
  1873                 }
  1900                 }
  1874             }
  1901             }
  1875         } catch (FileNotFoundException e) {
  1902         } catch (FileNotFoundException e) {
  1876             errormsg("jshell.err.file.not.found", "/save", filename, e.getMessage());
  1903             errormsg("jshell.err.file.not.found", "/save", filename, e.getMessage());
  1877             return false;
  1904             return false;
  1881         }
  1908         }
  1882         return true;
  1909         return true;
  1883     }
  1910     }
  1884 
  1911 
  1885     private boolean cmdVars(String arg) {
  1912     private boolean cmdVars(String arg) {
  1886         Stream<VarSnippet> stream = argToSnippetsWithMessage(allVarSnippets(),
  1913         Stream<VarSnippet> stream = argsOptionsToSnippets(allVarSnippets(),
  1887                 this::isActive, arg, "/vars");
  1914                 this::isActive, arg, "/vars");
  1888         if (stream == null) {
  1915         if (stream == null) {
  1889             return false;
  1916             return false;
  1890         }
  1917         }
  1891         stream.forEachOrdered(vk ->
  1918         stream.forEachOrdered(vk ->
  1897         });
  1924         });
  1898         return true;
  1925         return true;
  1899     }
  1926     }
  1900 
  1927 
  1901     private boolean cmdMethods(String arg) {
  1928     private boolean cmdMethods(String arg) {
  1902         Stream<MethodSnippet> stream = argToSnippetsWithMessage(allMethodSnippets(),
  1929         Stream<MethodSnippet> stream = argsOptionsToSnippets(allMethodSnippets(),
  1903                 this::isActive, arg, "/methods");
  1930                 this::isActive, arg, "/methods");
  1904         if (stream == null) {
  1931         if (stream == null) {
  1905             return false;
  1932             return false;
  1906         }
  1933         }
  1907         stream.forEachOrdered(mk
  1934         stream.forEachOrdered(mk
  1909         );
  1936         );
  1910         return true;
  1937         return true;
  1911     }
  1938     }
  1912 
  1939 
  1913     private boolean cmdTypes(String arg) {
  1940     private boolean cmdTypes(String arg) {
  1914         Stream<TypeDeclSnippet> stream = argToSnippetsWithMessage(allTypeSnippets(),
  1941         Stream<TypeDeclSnippet> stream = argsOptionsToSnippets(allTypeSnippets(),
  1915                 this::isActive, arg, "/types");
  1942                 this::isActive, arg, "/types");
  1916         if (stream == null) {
  1943         if (stream == null) {
  1917             return false;
  1944             return false;
  1918         }
  1945         }
  1919         stream.forEachOrdered(ck
  1946         stream.forEachOrdered(ck
  1958         if (index >= 0 && index < keys.size()) {
  1985         if (index >= 0 && index < keys.size()) {
  1959             rerunSnippet(keys.get(index));
  1986             rerunSnippet(keys.get(index));
  1960         } else {
  1987         } else {
  1961             errormsg("jshell.err.out.of.range");
  1988             errormsg("jshell.err.out.of.range");
  1962             return false;
  1989             return false;
       
  1990         }
       
  1991         return true;
       
  1992     }
       
  1993 
       
  1994     boolean checkOptionsAndRemainingInput(ArgTokenizer at) {
       
  1995         String junk = at.remainder();
       
  1996         if (!junk.isEmpty()) {
       
  1997             errormsg("jshell.err.unexpected.at.end", junk, at.whole());
       
  1998             return false;
       
  1999         } else {
       
  2000             String bad = at.badOptions();
       
  2001             if (!bad.isEmpty()) {
       
  2002                 errormsg("jshell.err.unknown.option", bad, at.whole());
       
  2003                 return false;
       
  2004             }
  1963         }
  2005         }
  1964         return true;
  2006         return true;
  1965     }
  2007     }
  1966 
  2008 
  1967     private boolean rerunHistoryEntryById(String id) {
  2009     private boolean rerunHistoryEntryById(String id) {