1 /* 2 * Copyright (c) 2015, 2018, 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 26 package jdk.nashorn.tools.jjs; 27 28 import java.io.File; 29 import java.io.IOException; 30 import java.io.InputStream; 31 import java.io.PrintStream; 32 import java.io.Writer; 33 import java.nio.file.Files; 34 import java.util.ArrayList; 35 import java.util.List; 36 import java.util.function.Function; 37 import java.util.stream.Collectors; 38 import java.util.stream.StreamSupport; 39 40 import jdk.internal.org.jline.reader.Candidate; 41 import jdk.internal.org.jline.reader.CompletingParsedLine; 42 import jdk.internal.org.jline.reader.EOFError; 43 import jdk.internal.org.jline.reader.History; 44 import jdk.internal.org.jline.reader.LineReader; 45 import jdk.internal.org.jline.reader.LineReader.Option; 46 import jdk.internal.org.jline.reader.LineReaderBuilder; 47 import jdk.internal.org.jline.reader.Parser; 48 import jdk.internal.org.jline.reader.Parser.ParseContext; 49 import jdk.internal.org.jline.reader.Widget; 50 import jdk.internal.org.jline.reader.impl.LineReaderImpl; 51 import jdk.internal.org.jline.reader.impl.completer.ArgumentCompleter.ArgumentLine; 52 import jdk.internal.org.jline.terminal.Attributes.LocalFlag; 53 import jdk.internal.org.jline.terminal.Terminal; 54 55 class Console implements AutoCloseable { 56 private static final String DOCUMENTATION_SHORTCUT = "\033\133\132"; //Shift-TAB 57 private final LineReader in; 58 private final File historyFile; 59 60 Console(final InputStream cmdin, final PrintStream cmdout, final File historyFile, 61 final NashornCompleter completer, final Function<String, String> docHelper) throws IOException { 62 this.historyFile = historyFile; 63 64 Parser parser = (line, cursor, context) -> { 65 if (context == ParseContext.COMPLETE) { 66 List<Candidate> candidates = new ArrayList<>(); 67 int anchor = completer.complete(line, cursor, candidates); 68 anchor = Math.min(anchor, line.length()); 69 return new CompletionLine(line.substring(anchor), cursor, candidates); 70 } else if (!completer.isComplete(line)) { 71 throw new EOFError(cursor, cursor, line); 72 } 73 return new ArgumentLine(line, cursor); 74 }; 75 in = LineReaderBuilder.builder() 76 .option(Option.DISABLE_EVENT_EXPANSION, true) 77 .completer((in, line, candidates) -> candidates.addAll(((CompletionLine) line).candidates)) 78 .parser(parser) 79 .build(); 80 if (historyFile.exists()) { 81 StringBuilder line = new StringBuilder(); 82 for (String h : Files.readAllLines(historyFile.toPath())) { 83 if (line.length() > 0) { 84 line.append("\n"); 85 } 86 line.append(h); 87 try { 88 parser.parse(line.toString(), line.length()); 89 in.getHistory().add(line.toString()); 90 line.delete(0, line.length()); 91 } catch (EOFError e) { 92 //continue; 93 } 94 } 95 if (line.length() > 0) { 96 in.getHistory().add(line.toString()); 97 } 98 } 99 Runtime.getRuntime().addShutdownHook(new Thread((Runnable)this::saveHistory)); 100 bind(DOCUMENTATION_SHORTCUT, ()->showDocumentation(docHelper)); 101 } 102 103 String readLine(final String prompt, final String continuationPrompt) throws IOException { 104 in.setVariable(LineReader.SECONDARY_PROMPT_PATTERN, continuationPrompt); 105 return in.readLine(prompt); 106 } 107 108 String readUserLine(final String prompt) throws IOException { 109 Parser prevParser = in.getParser(); 110 111 try { 112 ((LineReaderImpl) in).setParser((line, cursor, context) -> new ArgumentLine(line, cursor)); 113 return in.readLine(prompt); 114 } finally { 115 ((LineReaderImpl) in).setParser(prevParser); 116 } 117 } 118 119 @Override 120 public void close() { 121 saveHistory(); 122 } 123 124 private void saveHistory() { 125 try (Writer out = Files.newBufferedWriter(historyFile.toPath())) { 126 String lineSeparator = System.getProperty("line.separator"); 127 128 out.write(StreamSupport.stream(getHistory().spliterator(), false) 129 .map(e -> e.line()) 130 .collect(Collectors.joining(lineSeparator))); 131 } catch (final IOException exp) {} 132 } 133 134 History getHistory() { 135 return in.getHistory(); 136 } 137 138 boolean terminalEditorRunning() { 139 Terminal terminal = in.getTerminal(); 140 return !terminal.getAttributes().getLocalFlag(LocalFlag.ICANON); 141 } 142 143 void suspend() { 144 } 145 146 void resume() { 147 } 148 149 private void bind(String shortcut, Widget action) { 150 in.getKeyMaps().get(LineReader.MAIN).bind(action, shortcut); 151 } 152 153 private boolean showDocumentation(final Function<String, String> docHelper) { 154 final String buffer = in.getBuffer().toString(); 155 final int cursor = in.getBuffer().cursor(); 156 final String doc = docHelper.apply(buffer.substring(0, cursor)); 157 if (doc != null) { 158 in.getTerminal().writer().println(); 159 in.printAbove(doc); 160 return true; 161 } else { 162 return false; 163 } 164 } 165 166 private static final class CompletionLine extends ArgumentLine implements CompletingParsedLine { 167 public final List<Candidate> candidates; 168 169 public CompletionLine(String word, int cursor, List<Candidate> candidates) { 170 super(word, cursor); 171 this.candidates = candidates; 172 } 173 174 public CharSequence escape(CharSequence candidate, boolean complete) { 175 return candidate; 176 } 177 178 public int rawWordCursor() { 179 return word().length(); 180 } 181 182 public int rawWordLength() { 183 return word().length(); 184 } 185 } 186 }