8134562: jjs history object should have methods to save/load history to/from given file and also allow reexecution of commands by a call
Reviewed-by: hannesw, attila
--- a/nashorn/src/jdk.scripting.nashorn.shell/share/classes/jdk/nashorn/tools/jjs/EditObject.java Wed Aug 26 20:30:34 2015 +0200
+++ b/nashorn/src/jdk.scripting.nashorn.shell/share/classes/jdk/nashorn/tools/jjs/EditObject.java Thu Aug 27 14:35:06 2015 +0530
@@ -45,16 +45,16 @@
props = Collections.unmodifiableSet(s);
}
+ private final Console console;
private final Consumer<String> errorHandler;
private final Consumer<String> evaluator;
- private final Console console;
private String editor;
- EditObject(final Consumer<String> errorHandler, final Consumer<String> evaluator,
- final Console console) {
+ EditObject(final Console console, final Consumer<String> errorHandler,
+ final Consumer<String> evaluator) {
+ this.console = console;
this.errorHandler = errorHandler;
this.evaluator = evaluator;
- this.console = console;
}
@Override
--- a/nashorn/src/jdk.scripting.nashorn.shell/share/classes/jdk/nashorn/tools/jjs/HistoryObject.java Wed Aug 26 20:30:34 2015 +0200
+++ b/nashorn/src/jdk.scripting.nashorn.shell/share/classes/jdk/nashorn/tools/jjs/HistoryObject.java Thu Aug 27 14:35:06 2015 +0530
@@ -25,10 +25,15 @@
package jdk.nashorn.tools.jjs;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
import java.io.IOException;
+import java.io.PrintWriter;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
+import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import jdk.internal.jline.console.history.FileHistory;
@@ -36,6 +41,7 @@
import jdk.nashorn.api.scripting.AbstractJSObject;
import jdk.nashorn.api.scripting.JSObject;
import jdk.nashorn.internal.runtime.JSType;
+import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
/*
@@ -47,16 +53,51 @@
final HashSet<String> s = new HashSet<>();
s.add("clear");
s.add("forEach");
+ s.add("load");
s.add("print");
+ s.add("save");
s.add("size");
s.add("toString");
props = Collections.unmodifiableSet(s);
}
private final FileHistory hist;
+ private final PrintWriter err;
+ private final Consumer<String> evaluator;
- HistoryObject(final FileHistory hist) {
+ HistoryObject(final FileHistory hist, final PrintWriter err,
+ final Consumer<String> evaluator) {
this.hist = hist;
+ this.err = err;
+ this.evaluator = evaluator;
+ }
+
+ @Override
+ public boolean isFunction() {
+ return true;
+ }
+
+ @Override
+ public Object call(final Object thiz, final Object... args) {
+ if (args.length > 0) {
+ int index = JSType.toInteger(args[0]);
+ if (index < 0) {
+ index += (hist.size() - 1);
+ } else {
+ index--;
+ }
+
+ if (index >= 0 && index < (hist.size() - 1)) {
+ final CharSequence src = hist.get(index);
+ hist.replace(src);
+ err.println(src);
+ evaluator.accept(src.toString());
+ } else {
+ hist.removeLast();
+ err.println("no history entry @ " + (index + 1));
+ }
+ }
+ return UNDEFINED;
}
@Override
@@ -66,8 +107,12 @@
return (Runnable)hist::clear;
case "forEach":
return (Function<JSObject, Object>)this::iterate;
+ case "load":
+ return (Consumer<Object>)this::load;
case "print":
return (Runnable)this::print;
+ case "save":
+ return (Consumer<Object>)this::save;
case "size":
return hist.size();
case "toString":
@@ -98,9 +143,32 @@
return props;
}
+ private void save(final Object obj) {
+ final File file = getFile(obj);
+ try (final PrintWriter pw = new PrintWriter(file)) {
+ for (History.Entry e : hist) {
+ pw.println(e.value());
+ }
+ } catch (final IOException exp) {
+ throw new RuntimeException(exp);
+ }
+ }
+
+ private void load(final Object obj) {
+ final File file = getFile(obj);
+ String item = null;
+ try (final BufferedReader r = new BufferedReader(new FileReader(file))) {
+ while ((item = r.readLine()) != null) {
+ hist.add(item);
+ }
+ } catch (final IOException exp) {
+ throw new RuntimeException(exp);
+ }
+ }
+
private void print() {
for (History.Entry e : hist) {
- System.out.println(e.value());
+ System.out.printf("%3d %s\n", e.index() + 1, e.value());
}
}
@@ -112,4 +180,17 @@
}
return UNDEFINED;
}
+
+ private static File getFile(final Object obj) {
+ File file = null;
+ if (obj instanceof String) {
+ file = new File((String)obj);
+ } else if (obj instanceof File) {
+ file = (File)obj;
+ } else {
+ throw typeError("not.a.file", JSType.toString(obj));
+ }
+
+ return file;
+ }
}
--- a/nashorn/src/jdk.scripting.nashorn.shell/share/classes/jdk/nashorn/tools/jjs/Main.java Wed Aug 26 20:30:34 2015 +0200
+++ b/nashorn/src/jdk.scripting.nashorn.shell/share/classes/jdk/nashorn/tools/jjs/Main.java Thu Aug 27 14:35:06 2015 +0530
@@ -33,6 +33,7 @@
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
+import java.util.function.Consumer;
import jdk.internal.jline.console.completer.Completer;
import jdk.internal.jline.console.UserInterruptException;
import jdk.nashorn.api.scripting.NashornException;
@@ -116,26 +117,27 @@
global.addShellBuiltins();
if (System.getSecurityManager() == null) {
+ final Consumer<String> evaluator = str -> {
+ // could be called from different thread (GUI), we need to handle Context set/reset
+ final Global _oldGlobal = Context.getGlobal();
+ final boolean _globalChanged = (oldGlobal != global);
+ if (_globalChanged) {
+ Context.setGlobal(global);
+ }
+ try {
+ evalImpl(context, global, str, err, env._dump_on_error);
+ } finally {
+ if (_globalChanged) {
+ Context.setGlobal(_oldGlobal);
+ }
+ }
+ };
+
// expose history object for reflecting on command line history
- global.addOwnProperty("history", Property.NOT_ENUMERABLE, new HistoryObject(in.getHistory()));
+ global.addOwnProperty("history", Property.NOT_ENUMERABLE, new HistoryObject(in.getHistory(), err, evaluator));
// 'edit' command
- global.addOwnProperty("edit", Property.NOT_ENUMERABLE, new EditObject(err::println,
- str -> {
- // could be called from different thread (GUI), we need to handle Context set/reset
- final Global _oldGlobal = Context.getGlobal();
- final boolean _globalChanged = (oldGlobal != global);
- if (_globalChanged) {
- Context.setGlobal(global);
- }
- try {
- evalImpl(context, global, str, err, env._dump_on_error);
- } finally {
- if (_globalChanged) {
- Context.setGlobal(_oldGlobal);
- }
- }
- }, in));
+ global.addOwnProperty("edit", Property.NOT_ENUMERABLE, new EditObject(in, err::println, evaluator));
}
while (true) {