001 /* 002 * Copyright (C) 2012 eXo Platform SAS. 003 * 004 * This is free software; you can redistribute it and/or modify it 005 * under the terms of the GNU Lesser General Public License as 006 * published by the Free Software Foundation; either version 2.1 of 007 * the License, or (at your option) any later version. 008 * 009 * This software is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 012 * Lesser General Public License for more details. 013 * 014 * You should have received a copy of the GNU Lesser General Public 015 * License along with this software; if not, write to the Free 016 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 017 * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 018 */ 019 020 package org.crsh.processor.jline; 021 022 import jline.console.ConsoleReader; 023 import jline.console.completer.Completer; 024 import org.crsh.cli.impl.completion.CompletionMatch; 025 import org.crsh.cli.impl.Delimiter; 026 import org.crsh.cli.spi.Completion; 027 import org.crsh.shell.Shell; 028 import org.crsh.shell.ShellProcess; 029 import org.crsh.shell.ShellResponse; 030 031 import java.io.IOException; 032 import java.io.PrintWriter; 033 import java.util.List; 034 import java.util.Map; 035 import java.util.concurrent.atomic.AtomicReference; 036 037 public class JLineProcessor implements Runnable, Completer { 038 039 /** . */ 040 private final Shell shell; 041 042 /** . */ 043 final ConsoleReader reader; 044 045 /** . */ 046 final PrintWriter writer; 047 048 /** . */ 049 final AtomicReference<ShellProcess> current; 050 051 /** Whether or not we switched on the alternate screen. */ 052 boolean useAlternate; 053 054 public JLineProcessor(Shell shell, ConsoleReader reader, PrintWriter writer) { 055 this.shell = shell; 056 this.reader = reader; 057 this.writer = writer; 058 this.current = new AtomicReference<ShellProcess>(); 059 this.useAlternate = false; 060 } 061 062 public void run() { 063 String welcome = shell.getWelcome(); 064 writer.println(welcome); 065 writer.flush(); 066 loop(); 067 } 068 069 private String readLine() { 070 StringBuilder buffer = new StringBuilder(); 071 String prompt = getPrompt(); 072 writer.println(); 073 writer.flush(); 074 while (true) { 075 try { 076 String chunk; 077 if ((chunk = reader.readLine(prompt)) == null) { 078 return null; 079 } 080 if (chunk.length() > 0 && chunk.charAt(chunk.length() - 1) == '\\') { 081 prompt = "> "; 082 buffer.append(chunk, 0, chunk.length() - 1); 083 } else { 084 buffer.append(chunk); 085 return buffer.toString(); 086 } 087 } 088 catch (IOException e) { 089 // What should we do other than that ? 090 return null; 091 } 092 } 093 } 094 095 private void loop() { 096 while (true) { 097 String line = readLine(); 098 099 // 100 ShellProcess process = shell.createProcess(line); 101 JLineProcessContext context = new JLineProcessContext(this); 102 current.set(process); 103 try { 104 process.execute(context); 105 try { 106 context.latch.await(); 107 } 108 catch (InterruptedException ignore) { 109 // At the moment 110 } 111 } 112 finally { 113 current.set(null); 114 } 115 116 // 117 ShellResponse response = context.resp.get(); 118 119 // Write message 120 boolean flushed = false; 121 String msg = response.getMessage(); 122 if (msg.length() > 0) { 123 writer.write(msg); 124 writer.flush(); 125 flushed = true; 126 } 127 128 // 129 if (response instanceof ShellResponse.Cancelled) { 130 // Do nothing 131 } else if (response instanceof ShellResponse.Close) { 132 break; 133 } else { 134 if (!flushed) { 135 writer.flush(); 136 } 137 } 138 } 139 } 140 141 public int complete(String buffer, int cursor, List<CharSequence> candidates) { 142 String prefix = buffer.substring(0, cursor); 143 CompletionMatch completion = shell.complete(prefix); 144 Completion vc = completion.getValue(); 145 if (vc.isEmpty()) { 146 return -1; 147 } 148 Delimiter delimiter = completion.getDelimiter(); 149 for (Map.Entry<String, Boolean> entry : vc) { 150 StringBuilder sb = new StringBuilder(); 151 sb.append(vc.getPrefix()); 152 try { 153 delimiter.escape(entry.getKey(), sb); 154 if (entry.getValue()) { 155 sb.append(completion.getDelimiter().getValue()); 156 } 157 candidates.add(sb.toString()); 158 } 159 catch (IOException ignore) { 160 } 161 } 162 return cursor - vc.getPrefix().length(); 163 } 164 165 public void cancel() { 166 ShellProcess process = current.get(); 167 if (process != null) { 168 process.cancel(); 169 } else { 170 // Do nothing 171 } 172 } 173 174 String getPrompt() { 175 String prompt = shell.getPrompt(); 176 return prompt == null ? "% " : prompt; 177 } 178 }