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 void loop() { 070 while (true) { 071 String prompt = getPrompt(); 072 String line; 073 try { 074 writer.println(); 075 writer.flush(); 076 if ((line = reader.readLine(prompt)) == null) { 077 break; 078 } 079 } 080 catch (IOException e) { 081 // What should we do other than that ? 082 break; 083 } 084 085 // 086 ShellProcess process = shell.createProcess(line); 087 JLineProcessContext context = new JLineProcessContext(this); 088 current.set(process); 089 try { 090 process.execute(context); 091 try { 092 context.latch.await(); 093 } 094 catch (InterruptedException ignore) { 095 // At the moment 096 } 097 } 098 finally { 099 current.set(null); 100 } 101 102 // 103 ShellResponse response = context.resp.get(); 104 105 // Write message 106 boolean flushed = false; 107 String msg = response.getMessage(); 108 if (msg.length() > 0) { 109 writer.write(msg); 110 writer.flush(); 111 flushed = true; 112 } 113 114 // 115 if (response instanceof ShellResponse.Cancelled) { 116 // Do nothing 117 } else if (response instanceof ShellResponse.Close) { 118 break; 119 } else { 120 if (!flushed) { 121 writer.flush(); 122 } 123 } 124 } 125 } 126 127 public int complete(String buffer, int cursor, List<CharSequence> candidates) { 128 String prefix = buffer.substring(0, cursor); 129 CompletionMatch completion = shell.complete(prefix); 130 Completion vc = completion.getValue(); 131 if (vc.isEmpty()) { 132 return -1; 133 } 134 Delimiter delimiter = completion.getDelimiter(); 135 for (Map.Entry<String, Boolean> entry : vc) { 136 StringBuilder sb = new StringBuilder(); 137 sb.append(vc.getPrefix()); 138 try { 139 delimiter.escape(entry.getKey(), sb); 140 if (entry.getValue()) { 141 sb.append(completion.getDelimiter().getValue()); 142 } 143 candidates.add(sb.toString()); 144 } 145 catch (IOException ignore) { 146 } 147 } 148 return cursor - vc.getPrefix().length(); 149 } 150 151 public void cancel() { 152 ShellProcess process = current.get(); 153 if (process != null) { 154 process.cancel(); 155 } else { 156 // Do nothing 157 } 158 } 159 160 String getPrompt() { 161 String prompt = shell.getPrompt(); 162 return prompt == null ? "% " : prompt; 163 } 164 }