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 }