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 }