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 
  26 package jdk.nashorn.tools.jjs;
  27 
  28 import java.awt.event.ActionListener;
  29 import java.io.File;
  30 import java.io.IOException;
  31 import java.io.InputStream;
  32 import java.io.PrintStream;
  33 import java.util.ArrayList;
  34 import java.util.Arrays;
  35 import java.util.Collections;
  36 import java.util.Iterator;
  37 import java.util.List;
  38 import java.util.function.Function;
  39 import jdk.internal.jline.NoInterruptUnixTerminal;
  40 import jdk.internal.jline.Terminal;
  41 import jdk.internal.jline.TerminalFactory;
  42 import jdk.internal.jline.TerminalFactory.Flavor;
  43 import jdk.internal.jline.WindowsTerminal;
  44 import jdk.internal.jline.console.ConsoleReader;
  45 import jdk.internal.jline.console.KeyMap;
  46 import jdk.internal.jline.console.completer.Completer;
  47 import jdk.internal.jline.console.history.FileHistory;
  48 
  49 class Console implements AutoCloseable {
  50     private static final String DOCUMENTATION_SHORTCUT = "\033\133\132"; //Shift-TAB
  51     private final ConsoleReader in;
  52     private final FileHistory history;
  53 
  54     Console(final InputStream cmdin, final PrintStream cmdout, final File historyFile,
  55             final Completer completer, final Function<String, String> docHelper) throws IOException {
  56         TerminalFactory.registerFlavor(Flavor.WINDOWS, isCygwin()? JJSUnixTerminal::new : JJSWindowsTerminal::new);
  57         TerminalFactory.registerFlavor(Flavor.UNIX, JJSUnixTerminal::new);
  58         in = new ConsoleReader(cmdin, cmdout);
  59         in.setExpandEvents(false);
  60         in.setHandleUserInterrupt(true);
  61         in.setBellEnabled(true);
  62         in.setHistory(history = new FileHistory(historyFile));
  63         in.addCompleter(completer);
  64         Runtime.getRuntime().addShutdownHook(new Thread((Runnable)this::saveHistory));
  65         bind(DOCUMENTATION_SHORTCUT, (ActionListener)evt -> showDocumentation(docHelper));
  66     }
  67 
  68     String readLine(final String prompt) throws IOException {
  69         return in.readLine(prompt);
  70     }
  71 
  72     @Override
  73     public void close() {
  74         saveHistory();
  75     }
  76 
  77     private void saveHistory() {
  78         try {
  79             getHistory().flush();
  80         } catch (final IOException exp) {}
  81     }
  82 
  83     FileHistory getHistory() {
  84         return (FileHistory) in.getHistory();
  85     }
  86 
  87     boolean terminalEditorRunning() {
  88         Terminal terminal = in.getTerminal();
  89         if (terminal instanceof JJSUnixTerminal) {
  90             return ((JJSUnixTerminal) terminal).isRaw();
  91         }
  92         return false;
  93     }
  94 
  95     void suspend() {
  96         try {
  97             in.getTerminal().restore();
  98         } catch (Exception ex) {
  99             throw new IllegalStateException(ex);
 100         }
 101     }
 102 
 103     void resume() {
 104         try {
 105             in.getTerminal().init();
 106         } catch (Exception ex) {
 107             throw new IllegalStateException(ex);
 108         }
 109     }
 110 
 111     static final class JJSUnixTerminal extends NoInterruptUnixTerminal {
 112         JJSUnixTerminal() throws Exception {
 113         }
 114 
 115         boolean isRaw() {
 116             try {
 117                 return getSettings().get("-a").contains("-icanon");
 118             } catch (IOException | InterruptedException ex) {
 119                 return false;
 120             }
 121         }
 122 
 123         @Override
 124         public void disableInterruptCharacter() {
 125         }
 126 
 127         @Override
 128         public void enableInterruptCharacter() {
 129         }
 130     }
 131 
 132     static final class JJSWindowsTerminal extends WindowsTerminal {
 133         public JJSWindowsTerminal() throws Exception {
 134         }
 135 
 136         @Override
 137         public void init() throws Exception {
 138             super.init();
 139             setAnsiSupported(false);
 140         }
 141     }
 142 
 143     private static boolean isCygwin() {
 144         return System.getenv("SHELL") != null;
 145     }
 146 
 147     private void bind(String shortcut, Object action) {
 148         KeyMap km = in.getKeys();
 149         for (int i = 0; i < shortcut.length(); i++) {
 150             final Object value = km.getBound(Character.toString(shortcut.charAt(i)));
 151             if (value instanceof KeyMap) {
 152                 km = (KeyMap) value;
 153             } else {
 154                 km.bind(shortcut.substring(i), action);
 155             }
 156         }
 157     }
 158 
 159     private void showDocumentation(final Function<String, String> docHelper) {
 160         final String buffer = in.getCursorBuffer().buffer.toString();
 161         final int cursor = in.getCursorBuffer().cursor;
 162         final String doc = docHelper.apply(buffer.substring(0, cursor));
 163         try {
 164             if (doc != null) {
 165                 in.println();
 166                 in.println(doc);
 167                 in.redrawLine();
 168                 in.flush();
 169             } else {
 170                 in.beep();
 171             }
 172         } catch (IOException ex) {
 173             throw new IllegalStateException(ex);
 174         }
 175     }
 176 }
--- EOF ---