195 |
195 |
196 static final EditorSetting BUILT_IN_EDITOR = new EditorSetting(null, false); |
196 static final EditorSetting BUILT_IN_EDITOR = new EditorSetting(null, false); |
197 |
197 |
198 private boolean debug = false; |
198 private boolean debug = false; |
199 public boolean testPrompt = false; |
199 public boolean testPrompt = false; |
200 private String defaultStartup = null; |
|
201 private Startup startup = null; |
200 private Startup startup = null; |
202 private String executionControlSpec = null; |
201 private String executionControlSpec = null; |
203 private EditorSetting editor = BUILT_IN_EDITOR; |
202 private EditorSetting editor = BUILT_IN_EDITOR; |
204 |
203 |
205 private static final String[] EDITOR_ENV_VARS = new String[] { |
204 private static final String[] EDITOR_ENV_VARS = new String[] { |
206 "JSHELLEDITOR", "VISUAL", "EDITOR"}; |
205 "JSHELLEDITOR", "VISUAL", "EDITOR"}; |
207 |
206 |
208 // Commands and snippets which should be replayed |
207 // Commands and snippets which can be replayed |
209 private List<String> replayableHistory; |
208 private ReplayableHistory replayableHistory; |
210 private List<String> replayableHistoryPrevious; |
209 private ReplayableHistory replayableHistoryPrevious; |
211 |
210 |
212 static final String STARTUP_KEY = "STARTUP"; |
211 static final String STARTUP_KEY = "STARTUP"; |
213 static final String EDITOR_KEY = "EDITOR"; |
212 static final String EDITOR_KEY = "EDITOR"; |
214 static final String FEEDBACK_KEY = "FEEDBACK"; |
213 static final String FEEDBACK_KEY = "FEEDBACK"; |
215 static final String MODE_KEY = "MODE"; |
214 static final String MODE_KEY = "MODE"; |
505 return super.parse(options); |
504 return super.parse(options); |
506 } |
505 } |
507 } |
506 } |
508 |
507 |
509 /** |
508 /** |
|
509 * Encapsulate a history of snippets and commands which can be replayed. |
|
510 */ |
|
511 private static class ReplayableHistory { |
|
512 |
|
513 // the history |
|
514 private List<String> hist; |
|
515 |
|
516 // the length of the history as of last save |
|
517 private int lastSaved; |
|
518 |
|
519 private ReplayableHistory(List<String> hist) { |
|
520 this.hist = hist; |
|
521 this.lastSaved = 0; |
|
522 } |
|
523 |
|
524 // factory for empty histories |
|
525 static ReplayableHistory emptyHistory() { |
|
526 return new ReplayableHistory(new ArrayList<>()); |
|
527 } |
|
528 |
|
529 // factory for history stored in persistent storage |
|
530 static ReplayableHistory fromPrevious(PersistentStorage prefs) { |
|
531 // Read replay history from last jshell session |
|
532 String prevReplay = prefs.get(REPLAY_RESTORE_KEY); |
|
533 if (prevReplay == null) { |
|
534 return null; |
|
535 } else { |
|
536 return new ReplayableHistory(Arrays.asList(prevReplay.split(RECORD_SEPARATOR))); |
|
537 } |
|
538 |
|
539 } |
|
540 |
|
541 // store the history in persistent storage |
|
542 void storeHistory(PersistentStorage prefs) { |
|
543 if (hist.size() > lastSaved) { |
|
544 // Prevent history overflow by calculating what will fit, starting |
|
545 // with most recent |
|
546 int sepLen = RECORD_SEPARATOR.length(); |
|
547 int length = 0; |
|
548 int first = hist.size(); |
|
549 while (length < Preferences.MAX_VALUE_LENGTH && --first >= 0) { |
|
550 length += hist.get(first).length() + sepLen; |
|
551 } |
|
552 if (first >= 0) { |
|
553 hist = hist.subList(first + 1, hist.size()); |
|
554 } |
|
555 String shist = String.join(RECORD_SEPARATOR, hist); |
|
556 prefs.put(REPLAY_RESTORE_KEY, shist); |
|
557 markSaved(); |
|
558 } |
|
559 prefs.flush(); |
|
560 } |
|
561 |
|
562 // add a snippet or command to the history |
|
563 void add(String s) { |
|
564 hist.add(s); |
|
565 } |
|
566 |
|
567 // return history to reloaded |
|
568 Iterable<String> iterable() { |
|
569 return hist; |
|
570 } |
|
571 |
|
572 // mark that persistent storage and current history are in sync |
|
573 void markSaved() { |
|
574 lastSaved = hist.size(); |
|
575 } |
|
576 } |
|
577 |
|
578 /** |
510 * Is the input/output currently interactive |
579 * Is the input/output currently interactive |
511 * |
580 * |
512 * @return true if console |
581 * @return true if console |
513 */ |
582 */ |
514 boolean interactive() { |
583 boolean interactive() { |
754 // initialize editor settings |
823 // initialize editor settings |
755 configEditor(); |
824 configEditor(); |
756 // initialize JShell instance |
825 // initialize JShell instance |
757 resetState(); |
826 resetState(); |
758 // Read replay history from last jshell session into previous history |
827 // Read replay history from last jshell session into previous history |
759 String prevReplay = prefs.get(REPLAY_RESTORE_KEY); |
828 replayableHistoryPrevious = ReplayableHistory.fromPrevious(prefs); |
760 if (prevReplay != null) { |
|
761 replayableHistoryPrevious = Arrays.asList(prevReplay.split(RECORD_SEPARATOR)); |
|
762 } |
|
763 // load snippet/command files given on command-line |
829 // load snippet/command files given on command-line |
764 for (String loadFile : commandLineArgs.nonOptions()) { |
830 for (String loadFile : commandLineArgs.nonOptions()) { |
765 runFile(loadFile, "jshell"); |
831 runFile(loadFile, "jshell"); |
766 } |
832 } |
767 // if we survived that... |
833 // if we survived that... |
773 if (regenerateOnDeath) { |
839 if (regenerateOnDeath) { |
774 // if we haven't died, and the feedback mode wants fluff, print welcome |
840 // if we haven't died, and the feedback mode wants fluff, print welcome |
775 if (feedback.shouldDisplayCommandFluff()) { |
841 if (feedback.shouldDisplayCommandFluff()) { |
776 hardmsg("jshell.msg.welcome", version()); |
842 hardmsg("jshell.msg.welcome", version()); |
777 } |
843 } |
|
844 // Be sure history is always saved so that user code isn't lost |
|
845 Runtime.getRuntime().addShutdownHook(new Thread() { |
|
846 @Override |
|
847 public void run() { |
|
848 replayableHistory.storeHistory(prefs); |
|
849 } |
|
850 }); |
778 // execute from user input |
851 // execute from user input |
779 try (IOContext in = new ConsoleIOContext(this, cmdin, console)) { |
852 try (IOContext in = new ConsoleIOContext(this, cmdin, console)) { |
780 start(in); |
853 start(in); |
781 } |
854 } |
782 } |
855 } |
866 mapSnippet = new LinkedHashMap<>(); |
939 mapSnippet = new LinkedHashMap<>(); |
867 currentNameSpace = startNamespace; |
940 currentNameSpace = startNamespace; |
868 |
941 |
869 // Reset the replayable history, saving the old for restore |
942 // Reset the replayable history, saving the old for restore |
870 replayableHistoryPrevious = replayableHistory; |
943 replayableHistoryPrevious = replayableHistory; |
871 replayableHistory = new ArrayList<>(); |
944 replayableHistory = ReplayableHistory.emptyHistory(); |
872 JShell.Builder builder = |
945 JShell.Builder builder = |
873 JShell.builder() |
946 JShell.builder() |
874 .in(userin) |
947 .in(userin) |
875 .out(userout) |
948 .out(userout) |
876 .err(usererr) |
949 .err(usererr) |
1931 } |
2004 } |
1932 |
2005 |
1933 private boolean cmdExit() { |
2006 private boolean cmdExit() { |
1934 regenerateOnDeath = false; |
2007 regenerateOnDeath = false; |
1935 live = false; |
2008 live = false; |
1936 if (!replayableHistory.isEmpty()) { |
2009 replayableHistory.storeHistory(prefs); |
1937 // Prevent history overflow by calculating what will fit, starting |
|
1938 // with most recent |
|
1939 int sepLen = RECORD_SEPARATOR.length(); |
|
1940 int length = 0; |
|
1941 int first = replayableHistory.size(); |
|
1942 while(length < Preferences.MAX_VALUE_LENGTH && --first >= 0) { |
|
1943 length += replayableHistory.get(first).length() + sepLen; |
|
1944 } |
|
1945 String hist = String.join(RECORD_SEPARATOR, |
|
1946 replayableHistory.subList(first + 1, replayableHistory.size())); |
|
1947 prefs.put(REPLAY_RESTORE_KEY, hist); |
|
1948 } |
|
1949 prefs.flush(); |
|
1950 fluffmsg("jshell.msg.goodbye"); |
2010 fluffmsg("jshell.msg.goodbye"); |
1951 return true; |
2011 return true; |
1952 } |
2012 } |
1953 |
2013 |
1954 boolean cmdHelp(String arg) { |
2014 boolean cmdHelp(String arg) { |
2418 private boolean cmdReload(String rawargs) { |
2478 private boolean cmdReload(String rawargs) { |
2419 OptionParserReload ap = new OptionParserReload(); |
2479 OptionParserReload ap = new OptionParserReload(); |
2420 if (!parseCommandLineLikeFlags(rawargs, ap)) { |
2480 if (!parseCommandLineLikeFlags(rawargs, ap)) { |
2421 return false; |
2481 return false; |
2422 } |
2482 } |
2423 Iterable<String> history; |
2483 ReplayableHistory history; |
2424 if (ap.restore()) { |
2484 if (ap.restore()) { |
2425 if (replayableHistoryPrevious == null) { |
2485 if (replayableHistoryPrevious == null) { |
2426 errormsg("jshell.err.reload.no.previous"); |
2486 errormsg("jshell.err.reload.no.previous"); |
2427 return false; |
2487 return false; |
2428 } |
2488 } |
2430 fluffmsg("jshell.err.reload.restarting.previous.state"); |
2490 fluffmsg("jshell.err.reload.restarting.previous.state"); |
2431 } else { |
2491 } else { |
2432 history = replayableHistory; |
2492 history = replayableHistory; |
2433 fluffmsg("jshell.err.reload.restarting.state"); |
2493 fluffmsg("jshell.err.reload.restarting.state"); |
2434 } |
2494 } |
2435 return doReload(history, !ap.quiet()); |
2495 boolean success = doReload(history, !ap.quiet()); |
|
2496 if (success && ap.restore()) { |
|
2497 // if we are restoring from previous, then if nothing was added |
|
2498 // before time of exit, there is nothing to save |
|
2499 replayableHistory.markSaved(); |
|
2500 } |
|
2501 return success; |
2436 } |
2502 } |
2437 |
2503 |
2438 private boolean cmdEnv(String rawargs) { |
2504 private boolean cmdEnv(String rawargs) { |
2439 if (rawargs.trim().isEmpty()) { |
2505 if (rawargs.trim().isEmpty()) { |
2440 // No arguments, display current settings (as option flags) |
2506 // No arguments, display current settings (as option flags) |
2458 } |
2524 } |
2459 fluffmsg("jshell.msg.set.restore"); |
2525 fluffmsg("jshell.msg.set.restore"); |
2460 return doReload(replayableHistory, false); |
2526 return doReload(replayableHistory, false); |
2461 } |
2527 } |
2462 |
2528 |
2463 private boolean doReload(Iterable<String> history, boolean echo) { |
2529 private boolean doReload(ReplayableHistory history, boolean echo) { |
2464 resetState(); |
2530 resetState(); |
2465 run(new ReloadIOContext(history, |
2531 run(new ReloadIOContext(history.iterable(), |
2466 echo ? cmdout : null)); |
2532 echo ? cmdout : null)); |
2467 return true; |
2533 return true; |
2468 } |
2534 } |
2469 |
2535 |
2470 private boolean parseCommandLineLikeFlags(String rawargs, OptionParserBase ap) { |
2536 private boolean parseCommandLineLikeFlags(String rawargs, OptionParserBase ap) { |