org.jboss.galleon.cli.terminal.CliShell Maven / Gradle / Ivy
/*
* Copyright 2016-2019 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jboss.galleon.cli.terminal;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.function.Consumer;
import org.aesh.command.shell.Shell;
import org.aesh.io.Encoder;
import org.aesh.readline.Prompt;
import org.aesh.readline.Readline;
import org.aesh.readline.action.ActionDecoder;
import org.aesh.readline.editing.EditModeBuilder;
import org.aesh.readline.terminal.Key;
import org.aesh.readline.tty.terminal.TerminalConnection;
import org.aesh.terminal.Attributes;
import org.aesh.terminal.Terminal;
import org.aesh.terminal.tty.Capability;
import org.aesh.terminal.tty.Size;
import org.aesh.utils.Config;
import org.jboss.galleon.cli.CliLogging;
/**
* A shell to handle ANSI and reading input from terminal. This shell is
* attached to a not started terminal connection that is started stopped when
* input reading is required.
*
* @author [email protected]
*/
class CliShell implements Shell {
private final Terminal terminal;
private final Consumer stdOut;
private final TerminalConnection connection;
private final Readline readline;
CliShell(TerminalConnection connection) throws IOException {
this.connection = connection;
this.terminal = connection.getTerminal();
if (terminal.getCodePointConsumer() == null) {
stdOut = new Encoder(Charset.defaultCharset(), this::write);
} else {
stdOut = terminal.getCodePointConsumer();
}
readline = new Readline(EditModeBuilder.builder().create());
}
public void close() throws IOException {
terminal.close();
}
private void write(byte[] data) {
try {
terminal.output().write(data);
} catch (IOException ex) {
CliLogging.exception(ex);
}
}
@Override
public void write(String msg, boolean paging) {
stdOut.accept(msg.codePoints().toArray());
}
@Override
public void writeln(String msg, boolean paging) {
stdOut.accept((msg + Config.getLineSeparator()).codePoints().toArray());
}
@Override
public void write(int[] out) {
stdOut.accept(out);
}
@Override
public void write(char out) {
stdOut.accept(new int[]{out});
}
@Override
public String readLine() throws InterruptedException {
return readLine(new Prompt(""));
}
@Override
public String readLine(Prompt prompt) throws InterruptedException {
final String[] out = {null};
readline.readline(connection, prompt, event -> {
out[0] = event;
connection.stopReading();
});
try {
connection.openBlocking();
} finally {
connection.setStdinHandler(null);
}
return out[0];
}
@Override
public Key read() throws InterruptedException {
ActionDecoder decoder = new ActionDecoder();
final Key[] key = {null};
Attributes attributes = connection.enterRawMode();
try {
connection.setStdinHandler(keys -> {
decoder.add(keys);
if (decoder.hasNext()) {
key[0] = Key.findStartKey(decoder.next().buffer().array());
connection.stopReading();
}
});
try {
connection.openBlocking();
} finally {
connection.setStdinHandler(null);
}
} finally {
connection.setAttributes(attributes);
}
return key[0];
}
@Override
public Key read(Prompt prompt) throws InterruptedException {
return null;
}
@Override
public boolean enableAlternateBuffer() {
return connection.put(Capability.enter_ca_mode);
}
@Override
public boolean enableMainBuffer() {
return connection.put(Capability.exit_ca_mode);
}
@Override
public Size size() {
return connection.size();
}
@Override
public void clear() {
connection.put(Capability.clear_screen);
}
}