Console.java revision 1701:7ab7fc00b147
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.setCopyPasteDetection(true); 62 in.setHistory(new EditingHistory(in, Files.readAllLines(historyFile.toPath())) { 63 @Override protected boolean isComplete(CharSequence input) { 64 return completer.isComplete(input.toString()); 65 } 66 }); 67 in.addCompleter(completer); 68 Runtime.getRuntime().addShutdownHook(new Thread((Runnable)this::saveHistory)); 69 bind(DOCUMENTATION_SHORTCUT, (ActionListener)evt -> showDocumentation(docHelper)); 70 } 71 72 String readLine(final String prompt) throws IOException { 73 return in.readLine(prompt); 74 } 75 76 @Override 77 public void close() { 78 saveHistory(); 79 } 80 81 private void saveHistory() { 82 try (Writer out = Files.newBufferedWriter(historyFile.toPath())) { 83 String lineSeparator = System.getProperty("line.separator"); 84 85 out.write(getHistory().save() 86 .stream() 87 .collect(Collectors.joining(lineSeparator))); 88 } catch (final IOException exp) {} 89 } 90 91 EditingHistory getHistory() { 92 return (EditingHistory) in.getHistory(); 93 } 94 95 boolean terminalEditorRunning() { 96 Terminal terminal = in.getTerminal(); 97 if (terminal instanceof JJSUnixTerminal) { 98 return ((JJSUnixTerminal) terminal).isRaw(); 99 } 100 return false; 101 } 102 103 void suspend() { 104 try { 105 in.getTerminal().restore(); 106 } catch (Exception ex) { 107 throw new IllegalStateException(ex); 108 } 109 } 110 111 void resume() { 112 try { 113 in.getTerminal().init(); 114 } catch (Exception ex) { 115 throw new IllegalStateException(ex); 116 } 117 } 118 119 static final class JJSUnixTerminal extends NoInterruptUnixTerminal { 120 JJSUnixTerminal() throws Exception { 121 } 122 123 boolean isRaw() { 124 try { 125 return getSettings().get("-a").contains("-icanon"); 126 } catch (IOException | InterruptedException ex) { 127 return false; 128 } 129 } 130 131 @Override 132 public void disableInterruptCharacter() { 133 } 134 135 @Override 136 public void enableInterruptCharacter() { 137 } 138 } 139 140 static final class JJSWindowsTerminal extends WindowsTerminal { 141 public JJSWindowsTerminal() throws Exception { 142 } 143 144 @Override 145 public void init() throws Exception { 146 super.init(); 147 setAnsiSupported(false); 148 } 149 } 150 151 private static boolean isCygwin() { 152 return System.getenv("SHELL") != null; 153 } 154 155 private void bind(String shortcut, Object action) { 156 KeyMap km = in.getKeys(); 157 for (int i = 0; i < shortcut.length(); i++) { 158 final Object value = km.getBound(Character.toString(shortcut.charAt(i))); 159 if (value instanceof KeyMap) { 160 km = (KeyMap) value; 161 } else { 162 km.bind(shortcut.substring(i), action); 163 } 164 } 165 } 166 167 private void showDocumentation(final Function<String, String> docHelper) { 168 final String buffer = in.getCursorBuffer().buffer.toString(); 169 final int cursor = in.getCursorBuffer().cursor; 170 final String doc = docHelper.apply(buffer.substring(0, cursor)); 171 try { 172 if (doc != null) { 173 in.println(); 174 in.println(doc); 175 in.redrawLine(); 176 in.flush(); 177 } else { 178 in.beep(); 179 } 180 } catch (IOException ex) { 181 throw new IllegalStateException(ex); 182 } 183 } 184} 185