package org.rascalmpl.library.util; import java.io.IOException; import java.io.PrintWriter; import java.io.Writer; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.List; import org.rascalmpl.interpreter.Evaluator; import org.rascalmpl.interpreter.IEvaluatorContext; import org.rascalmpl.interpreter.StackTrace; import org.rascalmpl.interpreter.result.ICallableValue; import org.rascalmpl.library.experiments.Compiler.RVM.Interpreter.ideservices.IDEServices; import org.rascalmpl.repl.BaseREPL; import org.rascalmpl.repl.CompletionResult; import org.rascalmpl.value.IConstructor; import org.rascalmpl.value.IInteger; import org.rascalmpl.value.IList; import org.rascalmpl.value.ISourceLocation; import org.rascalmpl.value.IString; import org.rascalmpl.value.ITuple; import org.rascalmpl.value.IValue; import org.rascalmpl.value.IValueFactory; import org.rascalmpl.value.type.Type; import org.rascalmpl.value.type.TypeFactory; import jline.TerminalFactory; public class TermREPL { private final IValueFactory vf; public TermREPL(IValueFactory vf) { this.vf = vf; } public void startREPL(IConstructor repl, IEvaluatorContext ctx) { try { new TheREPL(repl, ctx, null).run(); } catch (IOException | URISyntaxException e) { e.printStackTrace(ctx.getStdErr()); } } class TheREPL extends BaseREPL { private final TypeFactory tf = TypeFactory.getInstance(); private PrintWriter stdout; private PrintWriter stderr; private String currentPrompt; private final ICallableValue handler; private final IEvaluatorContext ctx; private final ICallableValue completor; public TheREPL(IConstructor repl, IEvaluatorContext ctx, PathConfig pcfg) throws IOException, URISyntaxException { super(pcfg, ctx.getREPL() == null ? System.in : ctx.getREPL().getInput(), ctx.getREPL() == null ? System.out : ctx.getREPL().getOutput(), true, true, ((ISourceLocation)repl.get("history")), ctx.getREPL() == null ? TerminalFactory.get() : ctx.getREPL().getTerminal(), null); this.ctx = ctx; this.handler = (ICallableValue)repl.get("handler"); this.completor = (ICallableValue)repl.get("completor"); this.currentPrompt = ((IString)repl.get("prompt")).getValue(); assert stdout != null; stdout.println(((IString)repl.get("welcome")).getValue()); } @Override protected void cancelRunningCommandRequested() { ctx.interrupt(); } @Override protected void terminateRequested() { ctx.interrupt(); } @Override public void stop() { ctx.interrupt(); super.stop(); } @Override protected void stackTraceRequested() { StackTrace trace = ctx.getStackTrace(); Writer err = ctx.getStdErr(); try { err.write("Current stack trace:\n"); err.write(trace.toLinkedString()); err.flush(); } catch (IOException e) { } } @Override protected void initialize(PathConfig pcfg, Writer stdout, Writer stderr, IDEServices ideServices) { this.stdout = new PrintWriter(stdout); this.stderr = new PrintWriter(stderr); } @Override protected String getPrompt() { return currentPrompt; } @Override protected void handleInput(String line) throws InterruptedException { ITuple result = (ITuple)call(handler, new Type[] { tf.stringType() }, new IValue[] { vf.string(line) }); String str = ((IString)result.get(0)).getValue(); if (!str.isEmpty()) { stdout.write(str + "\n"); } IList errors = (IList)result.get(1); for (IValue v: errors) { IConstructor msg = (IConstructor)v; stderr.write(msg.toString() + "\n"); } currentPrompt = ((IString)result.get(2)).getValue(); } @Override protected boolean supportsCompletion() { return true; } @Override protected boolean printSpaceAfterFullCompletion() { return false; } private IValue call(ICallableValue f, Type[] types, IValue[] args) { synchronized (ctx) { Evaluator eval = (Evaluator)ctx; PrintWriter prevErr = eval.getStdErr(); PrintWriter prevOut = eval.getStdOut(); try { eval.overrideDefaultWriters(stdout, stderr); return f.call(types, args, null).getValue(); } finally { stdout.flush(); stderr.flush(); eval.overrideDefaultWriters(prevOut, prevErr); } } } @Override protected CompletionResult completeFragment(String line, int cursor) { ITuple result = (ITuple)call(completor, new Type[] { tf.stringType(), tf.integerType() }, new IValue[] { vf.string(line), vf.integer(cursor) }); List<String> suggestions = new ArrayList<>(); for (IValue v: (IList)result.get(1)) { suggestions.add(((IString)v).getValue()); } if (suggestions.isEmpty()) { return null; } int offset = ((IInteger)result.get(0)).intValue(); return new CompletionResult(offset, suggestions); } @Override protected void handleReset() throws InterruptedException { // TODO: add a rascal callback for this? handleInput(""); } } }