| /* |
| * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| package jdk.nashorn.tools.jjs; |
| |
| import java.awt.event.ActionListener; |
| import java.io.File; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.PrintStream; |
| import java.io.Writer; |
| import java.nio.file.Files; |
| import java.util.function.Function; |
| import java.util.stream.Collectors; |
| import jdk.internal.jline.NoInterruptUnixTerminal; |
| import jdk.internal.jline.Terminal; |
| import jdk.internal.jline.TerminalFactory; |
| import jdk.internal.jline.TerminalFactory.Flavor; |
| import jdk.internal.jline.WindowsTerminal; |
| import jdk.internal.jline.console.ConsoleReader; |
| import jdk.internal.jline.console.KeyMap; |
| import jdk.internal.jline.extra.EditingHistory; |
| import jdk.internal.misc.Signal; |
| import jdk.internal.misc.Signal.Handler; |
| |
| class Console implements AutoCloseable { |
| private static final String DOCUMENTATION_SHORTCUT = "\033\133\132"; //Shift-TAB |
| private final ConsoleReader in; |
| private final File historyFile; |
| |
| Console(final InputStream cmdin, final PrintStream cmdout, final File historyFile, |
| final NashornCompleter completer, final Function<String, String> docHelper) throws IOException { |
| this.historyFile = historyFile; |
| |
| TerminalFactory.registerFlavor(Flavor.WINDOWS, isCygwin()? JJSUnixTerminal::new : JJSWindowsTerminal::new); |
| TerminalFactory.registerFlavor(Flavor.UNIX, JJSUnixTerminal::new); |
| in = new ConsoleReader(cmdin, cmdout); |
| in.setExpandEvents(false); |
| in.setHandleUserInterrupt(true); |
| in.setBellEnabled(true); |
| in.setCopyPasteDetection(true); |
| final Iterable<String> existingHistory = historyFile.exists() ? Files.readAllLines(historyFile.toPath()) : null; |
| in.setHistory(new EditingHistory(in, existingHistory) { |
| @Override protected boolean isComplete(CharSequence input) { |
| return completer.isComplete(input.toString()); |
| } |
| }); |
| in.addCompleter(completer); |
| Runtime.getRuntime().addShutdownHook(new Thread((Runnable)this::saveHistory)); |
| bind(DOCUMENTATION_SHORTCUT, (ActionListener)evt -> showDocumentation(docHelper)); |
| try { |
| Signal.handle(new Signal("CONT"), new Handler() { |
| @Override public void handle(Signal sig) { |
| try { |
| in.getTerminal().reset(); |
| in.redrawLine(); |
| in.flush(); |
| } catch (Exception ex) { |
| ex.printStackTrace(); |
| } |
| } |
| }); |
| } catch (IllegalArgumentException ignored) { |
| //the CONT signal does not exist on this platform |
| } |
| } |
| |
| String readLine(final String prompt) throws IOException { |
| return in.readLine(prompt); |
| } |
| |
| @Override |
| public void close() { |
| saveHistory(); |
| } |
| |
| private void saveHistory() { |
| try (Writer out = Files.newBufferedWriter(historyFile.toPath())) { |
| String lineSeparator = System.getProperty("line.separator"); |
| |
| out.write(getHistory().save() |
| .stream() |
| .collect(Collectors.joining(lineSeparator))); |
| } catch (final IOException exp) {} |
| } |
| |
| EditingHistory getHistory() { |
| return (EditingHistory) in.getHistory(); |
| } |
| |
| boolean terminalEditorRunning() { |
| Terminal terminal = in.getTerminal(); |
| if (terminal instanceof JJSUnixTerminal) { |
| return ((JJSUnixTerminal) terminal).isRaw(); |
| } |
| return false; |
| } |
| |
| void suspend() { |
| try { |
| in.getTerminal().restore(); |
| } catch (Exception ex) { |
| throw new IllegalStateException(ex); |
| } |
| } |
| |
| void resume() { |
| try { |
| in.getTerminal().init(); |
| } catch (Exception ex) { |
| throw new IllegalStateException(ex); |
| } |
| } |
| |
| static final class JJSUnixTerminal extends NoInterruptUnixTerminal { |
| JJSUnixTerminal() throws Exception { |
| } |
| |
| boolean isRaw() { |
| try { |
| return getSettings().get("-a").contains("-icanon"); |
| } catch (IOException | InterruptedException ex) { |
| return false; |
| } |
| } |
| |
| @Override |
| public void disableInterruptCharacter() { |
| } |
| |
| @Override |
| public void enableInterruptCharacter() { |
| } |
| } |
| |
| static final class JJSWindowsTerminal extends WindowsTerminal { |
| public JJSWindowsTerminal() throws Exception { |
| } |
| |
| @Override |
| public void init() throws Exception { |
| super.init(); |
| setAnsiSupported(false); |
| } |
| } |
| |
| private static boolean isCygwin() { |
| return System.getenv("SHELL") != null; |
| } |
| |
| private void bind(String shortcut, Object action) { |
| KeyMap km = in.getKeys(); |
| for (int i = 0; i < shortcut.length(); i++) { |
| final Object value = km.getBound(Character.toString(shortcut.charAt(i))); |
| if (value instanceof KeyMap) { |
| km = (KeyMap) value; |
| } else { |
| km.bind(shortcut.substring(i), action); |
| } |
| } |
| } |
| |
| private void showDocumentation(final Function<String, String> docHelper) { |
| final String buffer = in.getCursorBuffer().buffer.toString(); |
| final int cursor = in.getCursorBuffer().cursor; |
| final String doc = docHelper.apply(buffer.substring(0, cursor)); |
| try { |
| if (doc != null) { |
| in.println(); |
| in.println(doc); |
| in.redrawLine(); |
| in.flush(); |
| } else { |
| in.beep(); |
| } |
| } catch (IOException ex) { |
| throw new IllegalStateException(ex); |
| } |
| } |
| } |