Console.java revision 1695:be28ce2f1054
1/* 2 * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26package jdk.nashorn.tools.jjs; 27 28import java.awt.event.ActionListener; 29import java.io.File; 30import java.io.IOException; 31import java.io.InputStream; 32import java.io.PrintStream; 33import java.io.Writer; 34import java.nio.file.Files; 35import java.util.function.Function; 36import java.util.stream.Collectors; 37import jdk.internal.jline.NoInterruptUnixTerminal; 38import jdk.internal.jline.Terminal; 39import jdk.internal.jline.TerminalFactory; 40import jdk.internal.jline.TerminalFactory.Flavor; 41import jdk.internal.jline.WindowsTerminal; 42import jdk.internal.jline.console.ConsoleReader; 43import jdk.internal.jline.console.KeyMap; 44import jdk.internal.jline.extra.EditingHistory; 45 46class Console implements AutoCloseable { 47 private static final String DOCUMENTATION_SHORTCUT = "\033\133\132"; //Shift-TAB 48 private final ConsoleReader in; 49 private final File historyFile; 50 51 Console(final InputStream cmdin, final PrintStream cmdout, final File historyFile, 52 final NashornCompleter completer, final Function<String, String> docHelper) throws IOException { 53 this.historyFile = historyFile; 54 55 TerminalFactory.registerFlavor(Flavor.WINDOWS, isCygwin()? JJSUnixTerminal::new : JJSWindowsTerminal::new); 56 TerminalFactory.registerFlavor(Flavor.UNIX, JJSUnixTerminal::new); 57 in = new ConsoleReader(cmdin, cmdout); 58 in.setExpandEvents(false); 59 in.setHandleUserInterrupt(true); 60 in.setBellEnabled(true); 61 in.setHistory(new EditingHistory(in, Files.readAllLines(historyFile.toPath())) { 62 @Override protected boolean isComplete(CharSequence input) { 63 return completer.isComplete(input.toString()); 64 } 65 }); 66 in.addCompleter(completer); 67 Runtime.getRuntime().addShutdownHook(new Thread((Runnable)this::saveHistory)); 68 bind(DOCUMENTATION_SHORTCUT, (ActionListener)evt -> showDocumentation(docHelper)); 69 } 70 71 String readLine(final String prompt) throws IOException { 72 return in.readLine(prompt); 73 } 74 75 @Override 76 public void close() { 77 saveHistory(); 78 } 79 80 private void saveHistory() { 81 try (Writer out = Files.newBufferedWriter(historyFile.toPath())) { 82 String lineSeparator = System.getProperty("line.separator"); 83 84 out.write(getHistory().save() 85 .stream() 86 .collect(Collectors.joining(lineSeparator))); 87 } catch (final IOException exp) {} 88 } 89 90 EditingHistory getHistory() { 91 return (EditingHistory) in.getHistory(); 92 } 93 94 boolean terminalEditorRunning() { 95 Terminal terminal = in.getTerminal(); 96 if (terminal instanceof JJSUnixTerminal) { 97 return ((JJSUnixTerminal) terminal).isRaw(); 98 } 99 return false; 100 } 101 102 void suspend() { 103 try { 104 in.getTerminal().restore(); 105 } catch (Exception ex) { 106 throw new IllegalStateException(ex); 107 } 108 } 109 110 void resume() { 111 try { 112 in.getTerminal().init(); 113 } catch (Exception ex) { 114 throw new IllegalStateException(ex); 115 } 116 } 117 118 static final class JJSUnixTerminal extends NoInterruptUnixTerminal { 119 JJSUnixTerminal() throws Exception { 120 } 121 122 boolean isRaw() { 123 try { 124 return getSettings().get("-a").contains("-icanon"); 125 } catch (IOException | InterruptedException ex) { 126 return false; 127 } 128 } 129 130 @Override 131 public void disableInterruptCharacter() { 132 } 133 134 @Override 135 public void enableInterruptCharacter() { 136 } 137 } 138 139 static final class JJSWindowsTerminal extends WindowsTerminal { 140 public JJSWindowsTerminal() throws Exception { 141 } 142 143 @Override 144 public void init() throws Exception { 145 super.init(); 146 setAnsiSupported(false); 147 } 148 } 149 150 private static boolean isCygwin() { 151 return System.getenv("SHELL") != null; 152 } 153 154 private void bind(String shortcut, Object action) { 155 KeyMap km = in.getKeys(); 156 for (int i = 0; i < shortcut.length(); i++) { 157 final Object value = km.getBound(Character.toString(shortcut.charAt(i))); 158 if (value instanceof KeyMap) { 159 km = (KeyMap) value; 160 } else { 161 km.bind(shortcut.substring(i), action); 162 } 163 } 164 } 165 166 private void showDocumentation(final Function<String, String> docHelper) { 167 final String buffer = in.getCursorBuffer().buffer.toString(); 168 final int cursor = in.getCursorBuffer().cursor; 169 final String doc = docHelper.apply(buffer.substring(0, cursor)); 170 try { 171 if (doc != null) { 172 in.println(); 173 in.println(doc); 174 in.redrawLine(); 175 in.flush(); 176 } else { 177 in.beep(); 178 } 179 } catch (IOException ex) { 180 throw new IllegalStateException(ex); 181 } 182 } 183} 184