langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/JShellTool.java
changeset 41635 cb3d04878117
parent 41514 a75c2b869d8d
child 41641 a628785b9dd9
equal deleted inserted replaced
41634:3f9c491b05aa 41635:cb3d04878117
   202 
   202 
   203     SourceCodeAnalysis analysis;
   203     SourceCodeAnalysis analysis;
   204     JShell state = null;
   204     JShell state = null;
   205     Subscription shutdownSubscription = null;
   205     Subscription shutdownSubscription = null;
   206 
   206 
       
   207     static final EditorSetting BUILT_IN_EDITOR = new EditorSetting(null, false);
       
   208 
   207     private boolean debug = false;
   209     private boolean debug = false;
   208     public boolean testPrompt = false;
   210     public boolean testPrompt = false;
   209     private String cmdlineClasspath = null;
   211     private String cmdlineClasspath = null;
   210     private String startup = null;
   212     private String startup = null;
   211     private String[] editor = null;
   213     private EditorSetting editor = BUILT_IN_EDITOR;
   212     private boolean editorWait = false;
       
   213 
   214 
   214     // Commands and snippets which should be replayed
   215     // Commands and snippets which should be replayed
   215     private List<String> replayableHistory;
   216     private List<String> replayableHistory;
   216     private List<String> replayableHistoryPrevious;
   217     private List<String> replayableHistoryPrevious;
   217 
   218 
   271      * Must show command output
   272      * Must show command output
   272      *
   273      *
   273      * @param format printf format
   274      * @param format printf format
   274      * @param args printf args
   275      * @param args printf args
   275      */
   276      */
   276     void hard(String format, Object... args) {
   277     @Override
       
   278     public void hard(String format, Object... args) {
   277         rawout(feedback.getPre() + format + feedback.getPost(), args);
   279         rawout(feedback.getPre() + format + feedback.getPost(), args);
   278     }
   280     }
   279 
   281 
   280     /**
   282     /**
   281      * Error command output
   283      * Error command output
   286     void error(String format, Object... args) {
   288     void error(String format, Object... args) {
   287         rawout(feedback.getErrorPre() + format + feedback.getErrorPost(), args);
   289         rawout(feedback.getErrorPre() + format + feedback.getErrorPost(), args);
   288     }
   290     }
   289 
   291 
   290     /**
   292     /**
       
   293      * Should optional informative be displayed?
       
   294      * @return true if they should be displayed
       
   295      */
       
   296     @Override
       
   297     public boolean showFluff() {
       
   298         return feedback.shouldDisplayCommandFluff() && interactive();
       
   299     }
       
   300 
       
   301     /**
   291      * Optional output
   302      * Optional output
   292      *
   303      *
   293      * @param format printf format
   304      * @param format printf format
   294      * @param args printf args
   305      * @param args printf args
   295      */
   306      */
   296     @Override
   307     @Override
   297     public void fluff(String format, Object... args) {
   308     public void fluff(String format, Object... args) {
   298         if (feedback.shouldDisplayCommandFluff() && interactive()) {
   309         if (showFluff()) {
   299             hard(format, args);
   310             hard(format, args);
   300         }
   311         }
   301     }
   312     }
   302 
   313 
   303     /**
   314     /**
   305      *
   316      *
   306      * @param format printf format
   317      * @param format printf format
   307      * @param args printf args
   318      * @param args printf args
   308      */
   319      */
   309     void fluffRaw(String format, Object... args) {
   320     void fluffRaw(String format, Object... args) {
   310         if (feedback.shouldDisplayCommandFluff() && interactive()) {
   321         if (showFluff()) {
   311             rawout(format, args);
   322             rawout(format, args);
   312         }
   323         }
   313     }
   324     }
   314 
   325 
   315     /**
   326     /**
   387      * postfix
   398      * postfix
   388      *
   399      *
   389      * @param key the resource key
   400      * @param key the resource key
   390      * @param args
   401      * @param args
   391      */
   402      */
   392     void hardmsg(String key, Object... args) {
   403     @Override
       
   404     public void hardmsg(String key, Object... args) {
   393         cmdout.println(prefix(messageFormat(key, args)));
   405         cmdout.println(prefix(messageFormat(key, args)));
   394     }
   406     }
   395 
   407 
   396     /**
   408     /**
   397      * Print error using resource bundle look-up, MessageFormat, and add prefix
   409      * Print error using resource bundle look-up, MessageFormat, and add prefix
   426      * @param key the resource key
   438      * @param key the resource key
   427      * @param args
   439      * @param args
   428      */
   440      */
   429     @Override
   441     @Override
   430     public void fluffmsg(String key, Object... args) {
   442     public void fluffmsg(String key, Object... args) {
   431         if (feedback.shouldDisplayCommandFluff() && interactive()) {
   443         if (showFluff()) {
   432             hardmsg(key, args);
   444             hardmsg(key, args);
   433         }
   445         }
   434     }
   446     }
   435 
   447 
   436     <T> void hardPairs(Stream<T> stream, Function<T, String> a, Function<T, String> b) {
   448     <T> void hardPairs(Stream<T> stream, Function<T, String> a, Function<T, String> b) {
   500                 startup = DEFAULT_STARTUP;
   512                 startup = DEFAULT_STARTUP;
   501             }
   513             }
   502         }
   514         }
   503 
   515 
   504         // Read retained editor setting (if any)
   516         // Read retained editor setting (if any)
   505         String editorString = prefs.get(EDITOR_KEY, "");
   517         editor = EditorSetting.fromPrefs(prefs);
   506         if (editorString == null || editorString.isEmpty()) {
   518         if (editor == null) {
   507             editor = null;
   519             editor = BUILT_IN_EDITOR;
   508         } else {
       
   509             char waitMarker = editorString.charAt(0);
       
   510             if (waitMarker == '-' || waitMarker == '*') {
       
   511                 editorWait = waitMarker == '-';
       
   512                 editorString = editorString.substring(1);
       
   513             }
       
   514             editor = editorString.split(RECORD_SEPARATOR);
       
   515         }
   520         }
   516 
   521 
   517         resetState(); // Initialize
   522         resetState(); // Initialize
   518 
   523 
   519         // Read replay history from last jshell session into previous history
   524         // Read replay history from last jshell session into previous history
   679         public void fluffmsg(String messageKey, Object... args) {
   684         public void fluffmsg(String messageKey, Object... args) {
   680             //ignore
   685             //ignore
   681         }
   686         }
   682 
   687 
   683         @Override
   688         @Override
       
   689         public void hard(String format, Object... args) {
       
   690             //ignore
       
   691         }
       
   692 
       
   693         @Override
       
   694         public void hardmsg(String messageKey, Object... args) {
       
   695             //ignore
       
   696         }
       
   697 
       
   698         @Override
   684         public void errormsg(String messageKey, Object... args) {
   699         public void errormsg(String messageKey, Object... args) {
   685             startmsg(messageKey, args);
   700             startmsg(messageKey, args);
       
   701         }
       
   702 
       
   703         @Override
       
   704         public boolean showFluff() {
       
   705             return false;
   686         }
   706         }
   687     }
   707     }
   688 
   708 
   689     private void resetState() {
   709     private void resetState() {
   690         closeState();
   710         closeState();
   743         MessageHandler initmh = new InitMessageHandler();
   763         MessageHandler initmh = new InitMessageHandler();
   744         // Execute the feedback initialization code in the resource file
   764         // Execute the feedback initialization code in the resource file
   745         startUpRun(getResourceString("startup.feedback"));
   765         startUpRun(getResourceString("startup.feedback"));
   746         // These predefined modes are read-only
   766         // These predefined modes are read-only
   747         feedback.markModesReadOnly();
   767         feedback.markModesReadOnly();
   748         // Restore user defined modes retained on previous run with /retain mode
   768         // Restore user defined modes retained on previous run with /set mode -retain
   749         String encoded = prefs.get(MODE_KEY, null);
   769         String encoded = prefs.get(MODE_KEY, null);
   750         if (encoded != null && !encoded.isEmpty()) {
   770         if (encoded != null && !encoded.isEmpty()) {
   751             if (!feedback.restoreEncodedModes(initmh, encoded)) {
   771             if (!feedback.restoreEncodedModes(initmh, encoded)) {
   752                 // Catastrophic corruption -- remove the retained modes
   772                 // Catastrophic corruption -- remove the retained modes
   753                 prefs.remove(MODE_KEY);
   773                 prefs.remove(MODE_KEY);
   754             }
   774             }
   755         }
   775         }
   756         if (commandLineFeedbackMode != null) {
   776         if (commandLineFeedbackMode != null) {
   757             // The feedback mode to use was specified on the command line, use it
   777             // The feedback mode to use was specified on the command line, use it
   758             if (!feedback.setFeedback(initmh, new ArgTokenizer("--feedback", commandLineFeedbackMode))) {
   778             if (!setFeedback(initmh, new ArgTokenizer("--feedback", commandLineFeedbackMode))) {
   759                 regenerateOnDeath = false;
   779                 regenerateOnDeath = false;
   760             }
   780             }
   761             commandLineFeedbackMode = null;
   781             commandLineFeedbackMode = null;
   762         } else {
   782         } else {
   763             String fb = prefs.get(FEEDBACK_KEY, null);
   783             String fb = prefs.get(FEEDBACK_KEY, null);
   764             if (fb != null) {
   784             if (fb != null) {
   765                 // Restore the feedback mode to use that was retained
   785                 // Restore the feedback mode to use that was retained
   766                 // on a previous run with /retain feedback
   786                 // on a previous run with /set feedback -retain
   767                 feedback.retainFeedback(initmh, new ArgTokenizer("/retain feedback", fb));
   787                 setFeedback(initmh, new ArgTokenizer("previous retain feedback", "-retain " + fb));
   768             }
   788             }
   769         }
   789         }
   770     }
   790     }
   771 
   791 
   772     //where
   792     //where
  1225                                 SET_MODE_OPTIONS_COMPLETION_PROVIDER)),
  1245                                 SET_MODE_OPTIONS_COMPLETION_PROVIDER)),
  1226                         "prompt", feedback.modeCompletions(),
  1246                         "prompt", feedback.modeCompletions(),
  1227                         "editor", fileCompletions(Files::isExecutable),
  1247                         "editor", fileCompletions(Files::isExecutable),
  1228                         "start", FILE_COMPLETION_PROVIDER),
  1248                         "start", FILE_COMPLETION_PROVIDER),
  1229                         STARTSWITH_MATCHER)));
  1249                         STARTSWITH_MATCHER)));
  1230         registerCommand(new Command("/retain",
       
  1231                 arg -> cmdRetain(arg),
       
  1232                 new ContinuousCompletionProvider(Map.of(
       
  1233                         "feedback", feedback.modeCompletions(),
       
  1234                         "mode", feedback.modeCompletions(),
       
  1235                         "editor", fileCompletions(Files::isExecutable),
       
  1236                         "start", FILE_COMPLETION_PROVIDER),
       
  1237                         STARTSWITH_MATCHER)));
       
  1238         registerCommand(new Command("/?",
  1250         registerCommand(new Command("/?",
  1239                 "help.quest",
  1251                 "help.quest",
  1240                 arg -> cmdHelp(arg),
  1252                 arg -> cmdHelp(arg),
  1241                 EMPTY_COMPLETION_PROVIDER,
  1253                 EMPTY_COMPLETION_PROVIDER,
  1242                 CommandKind.NORMAL));
  1254                 CommandKind.NORMAL));
  1291     // --- Command implementations ---
  1303     // --- Command implementations ---
  1292 
  1304 
  1293     private static final String[] SET_SUBCOMMANDS = new String[]{
  1305     private static final String[] SET_SUBCOMMANDS = new String[]{
  1294         "format", "truncation", "feedback", "mode", "prompt", "editor", "start"};
  1306         "format", "truncation", "feedback", "mode", "prompt", "editor", "start"};
  1295 
  1307 
  1296     private static final String[] RETAIN_SUBCOMMANDS = new String[]{
       
  1297         "feedback", "mode", "editor", "start"};
       
  1298 
       
  1299     final boolean cmdSet(String arg) {
  1308     final boolean cmdSet(String arg) {
  1300         String cmd = "/set";
  1309         String cmd = "/set";
  1301         ArgTokenizer at = new ArgTokenizer(cmd, arg.trim());
  1310         ArgTokenizer at = new ArgTokenizer(cmd, arg.trim());
  1302         String which = subCommand(cmd, at, SET_SUBCOMMANDS);
  1311         String which = subCommand(cmd, at, SET_SUBCOMMANDS);
  1303         if (which == null) {
  1312         if (which == null) {
  1304             return false;
  1313             return false;
  1305         }
  1314         }
  1306         switch (which) {
  1315         switch (which) {
       
  1316             case "_retain": {
       
  1317                 errormsg("jshell.err.setting.to.retain.must.be.specified", at.whole());
       
  1318                 return false;
       
  1319             }
       
  1320             case "_blank": {
       
  1321                 // show top-level settings
       
  1322                 new SetEditor().set();
       
  1323                 showSetStart();
       
  1324                 setFeedback(this, at); // no args so shows feedback setting
       
  1325                 hardmsg("jshell.msg.set.show.mode.settings");
       
  1326                 return true;
       
  1327             }
  1307             case "format":
  1328             case "format":
  1308                 return feedback.setFormat(this, at);
  1329                 return feedback.setFormat(this, at);
  1309             case "truncation":
  1330             case "truncation":
  1310                 return feedback.setTruncation(this, at);
  1331                 return feedback.setTruncation(this, at);
  1311             case "feedback":
  1332             case "feedback":
  1312                 return feedback.setFeedback(this, at);
  1333                 return setFeedback(this, at);
  1313             case "mode":
  1334             case "mode":
  1314                 return feedback.setMode(this, at);
  1335                 return feedback.setMode(this, at,
       
  1336                         retained -> prefs.put(MODE_KEY, retained));
  1315             case "prompt":
  1337             case "prompt":
  1316                 return feedback.setPrompt(this, at);
  1338                 return feedback.setPrompt(this, at);
  1317             case "editor":
  1339             case "editor":
  1318                 return setEditor(at, true);
  1340                 return new SetEditor(at).set();
  1319             case "start":
  1341             case "start":
  1320                 return setStart(cmd, at, true);
  1342                 return setStart(at);
  1321             default:
  1343             default:
  1322                 errormsg("jshell.err.arg", cmd, at.val());
  1344                 errormsg("jshell.err.arg", cmd, at.val());
  1323                 return false;
  1345                 return false;
  1324         }
  1346         }
  1325     }
  1347     }
  1326 
  1348 
  1327     final boolean cmdRetain(String arg) {
  1349     boolean setFeedback(MessageHandler messageHandler, ArgTokenizer at) {
  1328         String cmd = "/retain";
  1350         return feedback.setFeedback(messageHandler, at,
  1329         ArgTokenizer at = new ArgTokenizer(cmd, arg.trim());
  1351                 fb -> prefs.put(FEEDBACK_KEY, fb));
  1330         String which = subCommand(cmd, at, RETAIN_SUBCOMMANDS);
  1352     }
  1331         if (which == null) {
  1353 
  1332             return false;
  1354     // Find which, if any, sub-command matches.
  1333         }
  1355     // Return null on error
  1334         switch (which) {
       
  1335             case "feedback": {
       
  1336                 String fb = feedback.retainFeedback(this, at);
       
  1337                 if (fb != null) {
       
  1338                     // If a feedback mode has been set now, or in the past, retain it
       
  1339                     prefs.put(FEEDBACK_KEY, fb);
       
  1340                     return true;
       
  1341                 }
       
  1342                 return false;
       
  1343             }
       
  1344             case "mode":
       
  1345                 String retained = feedback.retainMode(this, at);
       
  1346                 if (retained != null) {
       
  1347                     // Retain this mode and all previously retained modes
       
  1348                     prefs.put(MODE_KEY, retained);
       
  1349                     return true;
       
  1350                 }
       
  1351                 return false;
       
  1352             case "editor":
       
  1353                 if (!setEditor(at, false)) {
       
  1354                     return false;
       
  1355                 }
       
  1356                 // retain editor setting
       
  1357                 prefs.put(EDITOR_KEY, (editor == null)
       
  1358                         ? ""
       
  1359                         : (editorWait? "-" : "*") + String.join(RECORD_SEPARATOR, editor));
       
  1360                 return true;
       
  1361             case "start": {
       
  1362                 if (!setStart(cmd, at, false)) {
       
  1363                     return false;
       
  1364                 }
       
  1365                 // retain startup setting
       
  1366                 prefs.put(STARTUP_KEY, startup);
       
  1367                 return true;
       
  1368             }
       
  1369             default:
       
  1370                 errormsg("jshell.err.arg", cmd, at.val());
       
  1371                 return false;
       
  1372         }
       
  1373     }
       
  1374 
       
  1375     // Print the help doc for the specified sub-command
       
  1376     boolean printSubCommandHelp(String cmd, ArgTokenizer at, String helpPrefix, String[] subs) {
       
  1377         String which = subCommand(cmd, at, subs);
       
  1378         if (which == null) {
       
  1379             return false;
       
  1380         }
       
  1381         hardrb(helpPrefix + which);
       
  1382         return true;
       
  1383     }
       
  1384 
       
  1385     // Find which, if any, sub-command matches
       
  1386     String subCommand(String cmd, ArgTokenizer at, String[] subs) {
  1356     String subCommand(String cmd, ArgTokenizer at, String[] subs) {
  1387         String[] matches = at.next(subs);
  1357         at.allowedOptions("-retain");
  1388         if (matches == null) {
  1358         String sub = at.next();
       
  1359         if (sub == null) {
  1389             // No sub-command was given
  1360             // No sub-command was given
  1390             errormsg("jshell.err.sub.arg", cmd);
  1361             return at.hasOption("-retain")
  1391             return null;
  1362                     ? "_retain"
  1392         }
  1363                     : "_blank";
       
  1364         }
       
  1365         String[] matches = Arrays.stream(subs)
       
  1366                 .filter(s -> s.startsWith(sub))
       
  1367                 .toArray(size -> new String[size]);
  1393         if (matches.length == 0) {
  1368         if (matches.length == 0) {
  1394             // There are no matching sub-commands
  1369             // There are no matching sub-commands
  1395             errormsg("jshell.err.arg", cmd, at.val());
  1370             errormsg("jshell.err.arg", cmd, sub);
  1396             fluffmsg("jshell.msg.use.one.of", Arrays.stream(subs)
  1371             fluffmsg("jshell.msg.use.one.of", Arrays.stream(subs)
  1397                     .collect(Collectors.joining(", "))
  1372                     .collect(Collectors.joining(", "))
  1398             );
  1373             );
  1399             return null;
  1374             return null;
  1400         }
  1375         }
  1401         if (matches.length > 1) {
  1376         if (matches.length > 1) {
  1402             // More than one sub-command matches the initial characters provided
  1377             // More than one sub-command matches the initial characters provided
  1403             errormsg("jshell.err.sub.ambiguous", cmd, at.val());
  1378             errormsg("jshell.err.sub.ambiguous", cmd, sub);
  1404             fluffmsg("jshell.msg.use.one.of", Arrays.stream(matches)
  1379             fluffmsg("jshell.msg.use.one.of", Arrays.stream(matches)
  1405                     .collect(Collectors.joining(", "))
  1380                     .collect(Collectors.joining(", "))
  1406             );
  1381             );
  1407             return null;
  1382             return null;
  1408         }
  1383         }
  1409         return matches[0];
  1384         return matches[0];
  1410     }
  1385     }
  1411 
  1386 
  1412     // The sub-command:  /set editor <editor-command-line>>
  1387     static class EditorSetting {
  1413     boolean setEditor(ArgTokenizer at, boolean argsRequired) {
  1388 
  1414         at.allowedOptions("-default", "-wait");
  1389         static String BUILT_IN_REP = "-default";
  1415         String prog = at.next();
  1390         static char WAIT_PREFIX = '-';
  1416         List<String> ed = new ArrayList<>();
  1391         static char NORMAL_PREFIX = '*';
  1417         while (at.val() != null) {
  1392 
  1418             ed.add(at.val());
  1393         final String[] cmd;
  1419             at.nextToken();
  1394         final boolean wait;
  1420         }
  1395 
  1421         if (!checkOptionsAndRemainingInput(at)) {
  1396         EditorSetting(String[] cmd, boolean wait) {
  1422             return false;
  1397             this.wait = wait;
  1423         }
  1398             this.cmd = cmd;
  1424         boolean defaultOption = at.hasOption("-default");
  1399         }
  1425         boolean waitOption = at.hasOption("-wait");
  1400 
  1426         if (prog != null) {
  1401         // returns null if not stored in preferences
  1427             if (defaultOption) {
  1402         static EditorSetting fromPrefs(Preferences prefs) {
       
  1403             // Read retained editor setting (if any)
       
  1404             String editorString = prefs.get(EDITOR_KEY, "");
       
  1405             if (editorString == null || editorString.isEmpty()) {
       
  1406                 return null;
       
  1407             } else if (editorString.equals(BUILT_IN_REP)) {
       
  1408                 return BUILT_IN_EDITOR;
       
  1409             } else {
       
  1410                 boolean wait = false;
       
  1411                 char waitMarker = editorString.charAt(0);
       
  1412                 if (waitMarker == WAIT_PREFIX || waitMarker == NORMAL_PREFIX) {
       
  1413                     wait = waitMarker == WAIT_PREFIX;
       
  1414                     editorString = editorString.substring(1);
       
  1415                 }
       
  1416                 String[] cmd = editorString.split(RECORD_SEPARATOR);
       
  1417                 return new EditorSetting(cmd, wait);
       
  1418             }
       
  1419         }
       
  1420 
       
  1421         void toPrefs(Preferences prefs) {
       
  1422             prefs.put(EDITOR_KEY, (this == BUILT_IN_EDITOR)
       
  1423                     ? BUILT_IN_REP
       
  1424                     : (wait ? WAIT_PREFIX : NORMAL_PREFIX) + String.join(RECORD_SEPARATOR, cmd));
       
  1425         }
       
  1426 
       
  1427         @Override
       
  1428         public boolean equals(Object o) {
       
  1429             if (o instanceof EditorSetting) {
       
  1430                 EditorSetting ed = (EditorSetting) o;
       
  1431                 return Arrays.equals(cmd, ed.cmd) && wait == ed.wait;
       
  1432             } else {
       
  1433                 return false;
       
  1434             }
       
  1435         }
       
  1436 
       
  1437         @Override
       
  1438         public int hashCode() {
       
  1439             int hash = 7;
       
  1440             hash = 71 * hash + Arrays.deepHashCode(this.cmd);
       
  1441             hash = 71 * hash + (this.wait ? 1 : 0);
       
  1442             return hash;
       
  1443         }
       
  1444     }
       
  1445 
       
  1446     class SetEditor {
       
  1447 
       
  1448         private final ArgTokenizer at;
       
  1449         private final String[] command;
       
  1450         private final boolean hasCommand;
       
  1451         private final boolean defaultOption;
       
  1452         private final boolean waitOption;
       
  1453         private final boolean retainOption;
       
  1454 
       
  1455         SetEditor(ArgTokenizer at) {
       
  1456             at.allowedOptions("-default", "-wait", "-retain");
       
  1457             String prog = at.next();
       
  1458             List<String> ed = new ArrayList<>();
       
  1459             while (at.val() != null) {
       
  1460                 ed.add(at.val());
       
  1461                 at.nextToken();  // so that options are not interpreted as jshell options
       
  1462             }
       
  1463             this.at = at;
       
  1464             this.command = ed.toArray(new String[ed.size()]);
       
  1465             this.hasCommand = command.length > 0;
       
  1466             this.defaultOption = at.hasOption("-default");
       
  1467             this.waitOption = at.hasOption("-wait");
       
  1468             this.retainOption = at.hasOption("-retain");
       
  1469         }
       
  1470 
       
  1471         SetEditor() {
       
  1472             this(new ArgTokenizer("", ""));
       
  1473         }
       
  1474 
       
  1475         boolean set() {
       
  1476             if (!check()) {
       
  1477                 return false;
       
  1478             }
       
  1479             if (!hasCommand && !defaultOption && !retainOption) {
       
  1480                 // No settings or -retain, so this is a query
       
  1481                 EditorSetting retained = EditorSetting.fromPrefs(prefs);
       
  1482                 if (retained != null) {
       
  1483                     // retained editor is set
       
  1484                     hard("/set editor -retain %s", format(retained));
       
  1485                 }
       
  1486                 if (retained == null || !retained.equals(editor)) {
       
  1487                     // editor is not retained or retained is different from set
       
  1488                     hard("/set editor %s", format(editor));
       
  1489                 }
       
  1490                 return true;
       
  1491             }
       
  1492             install();
       
  1493             if (retainOption) {
       
  1494                 editor.toPrefs(prefs);
       
  1495                 fluffmsg("jshell.msg.set.editor.retain", format(editor));
       
  1496             }
       
  1497             return true;
       
  1498         }
       
  1499 
       
  1500         private boolean check() {
       
  1501             if (!checkOptionsAndRemainingInput(at)) {
       
  1502                 return false;
       
  1503             }
       
  1504             if (hasCommand && defaultOption) {
  1428                 errormsg("jshell.err.default.option.or.program", at.whole());
  1505                 errormsg("jshell.err.default.option.or.program", at.whole());
  1429                 return false;
  1506                 return false;
  1430             }
  1507             }
  1431             editor = ed.toArray(new String[ed.size()]);
  1508             if (waitOption && !hasCommand) {
  1432             editorWait = waitOption;
       
  1433             fluffmsg("jshell.msg.set.editor.set", prog);
       
  1434         } else if (defaultOption) {
       
  1435             if (waitOption) {
       
  1436                 errormsg("jshell.err.wait.applies.to.external.editor", at.whole());
  1509                 errormsg("jshell.err.wait.applies.to.external.editor", at.whole());
  1437                 return false;
  1510                 return false;
  1438             }
  1511             }
  1439             editor = null;
  1512             return true;
  1440         } else if (argsRequired) {
  1513         }
  1441             errormsg("jshell.err.set.editor.arg");
  1514 
  1442             return false;
  1515         private void install() {
  1443         }
  1516             if (hasCommand) {
  1444         return true;
  1517                 editor = new EditorSetting(command, waitOption);
       
  1518             } else if (defaultOption) {
       
  1519                 editor = BUILT_IN_EDITOR;
       
  1520             } else {
       
  1521                 return;
       
  1522             }
       
  1523             fluffmsg("jshell.msg.set.editor.set", format(editor));
       
  1524         }
       
  1525 
       
  1526         private String format(EditorSetting ed) {
       
  1527             if (ed == BUILT_IN_EDITOR) {
       
  1528                 return "-default";
       
  1529             } else {
       
  1530                 Stream<String> elems = Arrays.stream(ed.cmd);
       
  1531                 if (ed.wait) {
       
  1532                     elems = Stream.concat(Stream.of("-wait"), elems);
       
  1533                 }
       
  1534                 return elems.collect(joining(" "));
       
  1535             }
       
  1536         }
  1445     }
  1537     }
  1446 
  1538 
  1447     // The sub-command:  /set start <start-file>
  1539     // The sub-command:  /set start <start-file>
  1448     boolean setStart(String cmd, ArgTokenizer at, boolean argsRequired) {
  1540     boolean setStart(ArgTokenizer at) {
  1449         at.allowedOptions("-default", "-none");
  1541         at.allowedOptions("-default", "-none", "-retain");
  1450         String fn = at.next();
  1542         String fn = at.next();
  1451         if (!checkOptionsAndRemainingInput(at)) {
  1543         if (!checkOptionsAndRemainingInput(at)) {
  1452             return false;
  1544             return false;
  1453         }
  1545         }
  1454         int argCount = at.optionCount() + ((fn != null) ? 1 : 0);
  1546         boolean defaultOption = at.hasOption("-default");
  1455         if (argCount > 1 || argsRequired && argCount == 0) {
  1547         boolean noneOption = at.hasOption("-none");
       
  1548         boolean retainOption = at.hasOption("-retain");
       
  1549         boolean hasFile = fn != null;
       
  1550 
       
  1551         int argCount = (defaultOption ? 1 : 0) + (noneOption ? 1 : 0) + (hasFile ? 1 : 0);
       
  1552         if (argCount > 1) {
  1456             errormsg("jshell.err.option.or.filename", at.whole());
  1553             errormsg("jshell.err.option.or.filename", at.whole());
  1457             return false;
  1554             return false;
  1458         }
  1555         }
  1459         if (fn != null) {
  1556         if (argCount == 0 && !retainOption) {
  1460             String init = readFile(fn, cmd + " start");
  1557             // no options or filename, show current setting
       
  1558             showSetStart();
       
  1559             return true;
       
  1560         }
       
  1561         if (hasFile) {
       
  1562             String init = readFile(fn, "/set start");
  1461             if (init == null) {
  1563             if (init == null) {
  1462                 return false;
  1564                 return false;
  1463             } else {
  1565             }
  1464                 startup = init;
  1566             startup = init;
  1465                 return true;
  1567         } else if (defaultOption) {
  1466             }
       
  1467         } else if (at.hasOption("-default")) {
       
  1468             startup = DEFAULT_STARTUP;
  1568             startup = DEFAULT_STARTUP;
  1469         } else if (at.hasOption("-none")) {
  1569         } else if (noneOption) {
  1470             startup = "";
  1570             startup = "";
  1471         }
  1571         }
       
  1572         if (retainOption) {
       
  1573             // retain startup setting
       
  1574             prefs.put(STARTUP_KEY, startup);
       
  1575         }
  1472         return true;
  1576         return true;
       
  1577     }
       
  1578 
       
  1579     void showSetStart() {
       
  1580         String retained = prefs.get(STARTUP_KEY, null);
       
  1581         if (retained != null) {
       
  1582             showSetStart(true, retained);
       
  1583         }
       
  1584         if (retained == null || !startup.equals(retained)) {
       
  1585             showSetStart(false, startup);
       
  1586         }
       
  1587     }
       
  1588 
       
  1589     void showSetStart(boolean isRetained, String start) {
       
  1590         String cmd = "/set start" + (isRetained ? " -retain " : " ");
       
  1591         String stset;
       
  1592         if (start.equals(DEFAULT_STARTUP)) {
       
  1593             stset = cmd + "-default";
       
  1594         } else if (start.isEmpty()) {
       
  1595             stset = cmd + "-none";
       
  1596         } else {
       
  1597             stset = prefix("startup.jsh:\n" + start + "\n" + cmd + "startup.jsh", "");
       
  1598         }
       
  1599         hard(stset);
  1473     }
  1600     }
  1474 
  1601 
  1475     boolean cmdClasspath(String arg) {
  1602     boolean cmdClasspath(String arg) {
  1476         if (arg.isEmpty()) {
  1603         if (arg.isEmpty()) {
  1477             errormsg("jshell.err.classpath.arg");
  1604             errormsg("jshell.err.classpath.arg");
  1557         String subject = at.next();
  1684         String subject = at.next();
  1558         if (subject != null) {
  1685         if (subject != null) {
  1559             Command[] matches = commands.values().stream()
  1686             Command[] matches = commands.values().stream()
  1560                     .filter(c -> c.command.startsWith(subject))
  1687                     .filter(c -> c.command.startsWith(subject))
  1561                     .toArray(size -> new Command[size]);
  1688                     .toArray(size -> new Command[size]);
  1562             at.mark();
  1689             if (matches.length == 1) {
  1563             String sub = at.next();
       
  1564             if (sub != null && matches.length == 1) {
       
  1565                 String cmd = matches[0].command;
  1690                 String cmd = matches[0].command;
  1566                 switch (cmd) {
  1691                 if (cmd.equals("/set")) {
  1567                     case "/set":
  1692                     // Print the help doc for the specified sub-command
  1568                         at.rewind();
  1693                     String which = subCommand(cmd, at, SET_SUBCOMMANDS);
  1569                         return printSubCommandHelp(cmd, at, "help.set.", SET_SUBCOMMANDS);
  1694                     if (which == null) {
  1570                     case "/retain":
  1695                         return false;
  1571                         at.rewind();
  1696                     }
  1572                         return printSubCommandHelp(cmd, at, "help.retain.", RETAIN_SUBCOMMANDS);
  1697                     if (!which.equals("_blank")) {
       
  1698                         hardrb("help.set." + which);
       
  1699                         return true;
       
  1700                     }
  1573                 }
  1701                 }
  1574             }
  1702             }
  1575             if (matches.length > 0) {
  1703             if (matches.length > 0) {
  1576                 for (Command c : matches) {
  1704                 for (Command c : matches) {
  1577                     hard("");
  1705                     hard("");
  1811             sb.append('\n');
  1939             sb.append('\n');
  1812         }
  1940         }
  1813         String src = sb.toString();
  1941         String src = sb.toString();
  1814         Consumer<String> saveHandler = new SaveHandler(src, srcSet);
  1942         Consumer<String> saveHandler = new SaveHandler(src, srcSet);
  1815         Consumer<String> errorHandler = s -> hard("Edit Error: %s", s);
  1943         Consumer<String> errorHandler = s -> hard("Edit Error: %s", s);
  1816         if (editor == null) {
  1944         if (editor == BUILT_IN_EDITOR) {
  1817             try {
  1945             try {
  1818                 EditPad.edit(errorHandler, src, saveHandler);
  1946                 EditPad.edit(errorHandler, src, saveHandler);
  1819             } catch (RuntimeException ex) {
  1947             } catch (RuntimeException ex) {
  1820                 errormsg("jshell.err.cant.launch.editor", ex);
  1948                 errormsg("jshell.err.cant.launch.editor", ex);
  1821                 fluffmsg("jshell.msg.try.set.editor");
  1949                 fluffmsg("jshell.msg.try.set.editor");
  1822                 return false;
  1950                 return false;
  1823             }
  1951             }
  1824         } else {
  1952         } else {
  1825             ExternalEditor.edit(editor, errorHandler, src, saveHandler, input,
  1953             ExternalEditor.edit(editor.cmd, errorHandler, src, saveHandler, input,
  1826                     editorWait, this::hardrb);
  1954                     editor.wait, this::hardrb);
  1827         }
  1955         }
  1828         return true;
  1956         return true;
  1829     }
  1957     }
  1830     //where
  1958     //where
  1831     // receives editor requests to save
  1959     // receives editor requests to save