langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/JShellTool.java
changeset 43564 4287765963d5
parent 43367 7797472a9ed5
child 43757 7193f6ef25db
equal deleted inserted replaced
43379:f4aff695ffe0 43564:4287765963d5
   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) {