8158738: jshell tool: Save does not affect jshell if started from another editor
authorrfield
Fri, 26 Aug 2016 11:36:08 -0700
changeset 40598 821b945fc942
parent 40597 144acbd86b89
child 40599 be40838eb215
8158738: jshell tool: Save does not affect jshell if started from another editor Reviewed-by: jlahoda
langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/ExternalEditor.java
langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/JShellTool.java
langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/resources/l10n.properties
langtools/test/jdk/jshell/ToolCommandOptionTest.java
--- a/langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/ExternalEditor.java	Thu Aug 25 21:58:13 2016 -0700
+++ b/langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/ExternalEditor.java	Fri Aug 26 11:36:08 2016 -0700
@@ -34,6 +34,7 @@
 import java.nio.file.WatchKey;
 import java.nio.file.WatchService;
 import java.util.Arrays;
+import java.util.Scanner;
 import java.util.function.Consumer;
 import java.util.stream.Collectors;
 import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE;
@@ -46,17 +47,22 @@
 public class ExternalEditor {
     private final Consumer<String> errorHandler;
     private final Consumer<String> saveHandler;
+    private final Consumer<String> printHandler;
     private final IOContext input;
+    private final boolean wait;
 
     private WatchService watcher;
     private Thread watchedThread;
     private Path dir;
     private Path tmpfile;
 
-    ExternalEditor(Consumer<String> errorHandler, Consumer<String> saveHandler, IOContext input) {
+    ExternalEditor(Consumer<String> errorHandler, Consumer<String> saveHandler,
+            IOContext input, boolean wait, Consumer<String> printHandler) {
         this.errorHandler = errorHandler;
         this.saveHandler = saveHandler;
+        this.printHandler = printHandler;
         this.input = input;
+        this.wait = wait;
     }
 
     private void edit(String[] cmd, String initialText) {
@@ -121,7 +127,16 @@
         try {
             input.suspend();
             Process process = pb.start();
-            process.waitFor();
+            // wait to exit edit mode in one of these ways...
+            if (wait) {
+                // -wait option -- ignore process exit, wait for carriage-return
+                Scanner scanner = new Scanner(System.in);
+                printHandler.accept("jshell.msg.press.return.to.leave.edit.mode");
+                scanner.nextLine();
+            } else {
+                // wait for process to exit
+                process.waitFor();
+            }
         } catch (IOException ex) {
             errorHandler.accept("process IO failure: " + ex.getMessage());
         } catch (InterruptedException ex) {
@@ -148,8 +163,8 @@
     }
 
     static void edit(String[] cmd, Consumer<String> errorHandler, String initialText,
-            Consumer<String> saveHandler, IOContext input) {
-        ExternalEditor ed = new ExternalEditor(errorHandler,  saveHandler, input);
+            Consumer<String> saveHandler, IOContext input, boolean wait, Consumer<String> printHandler) {
+        ExternalEditor ed = new ExternalEditor(errorHandler, saveHandler, input, wait, printHandler);
         ed.edit(cmd, initialText);
     }
 }
--- a/langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/JShellTool.java	Thu Aug 25 21:58:13 2016 -0700
+++ b/langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/JShellTool.java	Fri Aug 26 11:36:08 2016 -0700
@@ -185,6 +185,7 @@
     private String cmdlineClasspath = null;
     private String startup = null;
     private String[] editor = null;
+    private boolean editorWait = false;
 
     // Commands and snippets which should be replayed
     private List<String> replayableHistory;
@@ -481,6 +482,11 @@
         if (editorString == null || editorString.isEmpty()) {
             editor = null;
         } else {
+            char waitMarker = editorString.charAt(0);
+            if (waitMarker == '-' || waitMarker == '*') {
+                editorWait = waitMarker == '-';
+                editorString = editorString.substring(1);
+            }
             editor = editorString.split(RECORD_SEPARATOR);
         }
 
@@ -1260,7 +1266,7 @@
                 // retain editor setting
                 prefs.put(EDITOR_KEY, (editor == null)
                         ? ""
-                        : String.join(RECORD_SEPARATOR, editor));
+                        : (editorWait? "-" : "*") + String.join(RECORD_SEPARATOR, editor));
                 return true;
             case "start": {
                 if (!setStart(cmd, at, false)) {
@@ -1315,7 +1321,7 @@
 
     // The sub-command:  /set editor <editor-command-line>>
     boolean setEditor(ArgTokenizer at, boolean argsRequired) {
-        at.allowedOptions("-default");
+        at.allowedOptions("-default", "-wait");
         String prog = at.next();
         List<String> ed = new ArrayList<>();
         while (at.val() != null) {
@@ -1326,14 +1332,20 @@
             return false;
         }
         boolean defaultOption = at.hasOption("-default");
+        boolean waitOption = at.hasOption("-wait");
         if (prog != null) {
             if (defaultOption) {
                 errormsg("jshell.err.default.option.or.program", at.whole());
                 return false;
             }
             editor = ed.toArray(new String[ed.size()]);
+            editorWait = waitOption;
             fluffmsg("jshell.msg.set.editor.set", prog);
         } else if (defaultOption) {
+            if (waitOption) {
+                errormsg("jshell.err.wait.applies.to.external.editor", at.whole());
+                return false;
+            }
             editor = null;
         } else if (argsRequired) {
             errormsg("jshell.err.set.editor.arg");
@@ -1720,7 +1732,8 @@
                 return false;
             }
         } else {
-            ExternalEditor.edit(editor, errorHandler, src, saveHandler, input);
+            ExternalEditor.edit(editor, errorHandler, src, saveHandler, input,
+                    editorWait, this::hardrb);
         }
         return true;
     }
--- a/langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/resources/l10n.properties	Thu Aug 25 21:58:13 2016 -0700
+++ b/langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/resources/l10n.properties	Fri Aug 26 11:36:08 2016 -0700
@@ -56,6 +56,8 @@
 jshell.msg.set.editor.set = Editor set to: {0}
 jshell.err.cant.launch.editor = Cannot launch editor -- unexpected exception: {0}
 jshell.msg.try.set.editor = Try /set editor to use external editor.
+jshell.msg.press.return.to.leave.edit.mode = Press return to leave edit mode.
+jshell.err.wait.applies.to.external.editor = -wait applies to external editors, cannot be used with -default
 
 jshell.msg.try.command.without.args = Try ''{0}'' without arguments.
 jshell.msg.no.active = There are no active definitions.
@@ -358,7 +360,7 @@
 the external editor to use, the start-up definitions to use, a new feedback mode,\n\
 the command prompt, the feedback mode to use, or the format of output.\n\
 \n\
-/set editor <command> <optional-arg>...\n\t\
+/set editor [-wait] <command> <optional-arg>...\n\t\
      Specify the command to launch for the /edit command.\n\t\
      The <command> is an operating system dependent string.\n\n\
 /set start <file>\n\t\
@@ -602,12 +604,19 @@
 help.set.editor =\
 Specify the command to launch for the /edit command.\n\
 \n\t\
-/set editor <command>|-default\n\
+/set editor [-wait] <command>|-default\n\
 \n\
 The <command> is an operating system dependent string.\n\
-The <command> may include space-separated arguments (such as flags)\n\
-When /edit is used, the temporary file to edit will be appended as the last argument.\n\
-If instead the -default option is specified, the built-in default editor will be used.
+The <command> may include space-separated arguments (such as flags)\n\n\
+If the -default option is specified, the built-in default editor will be used.\n\n\
+Otherwise an external editor should be specified in <command>. When <command>\n\
+is used, the temporary file to edit will be appended as the last argument.\n\
+Normally, edit mode will last until the external editor exits. Some external editors\n\
+will exit immediately (for example, if the edit window exists) either external editor\n\
+flags should be used to prevent immediate exit, or the -wait option should be used to\n\
+prompt the user to indicate when edit mode should end.\n\n\
+Note: while in edit mode no command inputs are seen.  After leaving edit mode changes\n\
+to the edited snippets are not seen.
 
 help.set.start =\
 Set the start-up configuration -- a sequence of snippets and commands read at start-up.\n\
--- a/langtools/test/jdk/jshell/ToolCommandOptionTest.java	Thu Aug 25 21:58:13 2016 -0700
+++ b/langtools/test/jdk/jshell/ToolCommandOptionTest.java	Fri Aug 26 11:36:08 2016 -0700
@@ -23,7 +23,7 @@
 
  /*
  * @test
- * @bug 8157395 8157393 8157517
+ * @bug 8157395 8157393 8157517 8158738
  * @summary Tests of jshell comand options, and undoing operations
  * @modules jdk.jshell/jdk.internal.jshell.tool
  * @build ToolCommandOptionTest ReplToolTesting
@@ -148,6 +148,8 @@
                         "|  Unknown option: -furball -mattress -- /retain editor -furball -mattress"),
                 (a) -> assertCommand(a, "/retain editor -default prog",
                         "|  Specify -default option or program, not both -- /retain editor -default prog"),
+                (a) -> assertCommand(a, "/retain editor -default -wait",
+                        "|  -wait applies to external editors, cannot be used with -default"),
                 (a) -> assertCommand(a, "/retain editor prog",
                         "|  Editor set to: prog"),
                 (a) -> assertCommand(a, "/retain editor prog -default",