io.vertx.ext.shell.term.impl.TermImpl Maven / Gradle / Ivy
/*
* Copyright 2015 Red Hat, Inc.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*
*
* Copyright (c) 2015 The original author or authors
* ------------------------------------------------------
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*
*/
package io.vertx.ext.shell.term.impl;
import io.termd.core.readline.Keymap;
import io.termd.core.readline.Readline;
import io.termd.core.tty.TtyConnection;
import io.termd.core.util.Helper;
import io.vertx.core.Handler;
import io.vertx.core.Vertx;
import io.vertx.ext.shell.cli.CliToken;
import io.vertx.ext.shell.cli.Completion;
import io.vertx.ext.shell.session.Session;
import io.vertx.ext.shell.term.SignalHandler;
import io.vertx.ext.shell.term.Term;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
import java.util.stream.Collectors;
/**
* @author Julien Viet
*/
public class TermImpl implements Term {
private static final List readlineFunctions = Helper.loadServices(Thread.currentThread().getContextClassLoader(), io.termd.core.readline.Function.class);
private final Vertx vertx;
private final Readline readline;
private final Consumer echoHandler;
final TtyConnection conn;
volatile Handler stdinHandler;
private SignalHandler interruptHandler;
private SignalHandler suspendHandler;
private Session session;
private boolean inReadline;
public TermImpl(Vertx vertx, TtyConnection conn) {
this(vertx, io.vertx.ext.shell.term.impl.Helper.defaultKeymap(), conn);
}
public TermImpl(Vertx vertx, Keymap keymap, TtyConnection conn) {
this.vertx = vertx;
this.conn = conn;
readline = new Readline(keymap);
readlineFunctions.forEach(readline::addFunction);
echoHandler = codePoints -> {
// Echo
echo(codePoints);
readline.queueEvent(codePoints);
};
conn.setStdinHandler(echoHandler);
conn.setEventHandler((event, key) -> {
switch (event) {
case INTR:
if (interruptHandler == null || !interruptHandler.deliver(key)) {
echo(key, '\n');
}
break;
case EOF:
// Pseudo signal
if (stdinHandler != null) {
stdinHandler.handle(Helper.fromCodePoints(new int[]{key}));
} else {
echo(key);
readline.queueEvent(new int[]{key});
}
break;
case SUSP:
if (suspendHandler == null || !suspendHandler.deliver(key)) {
echo(key, 'Z' - 64);
}
break;
}
});
}
@Override
public Term setSession(Session session) {
this.session = session;
return this;
}
@Override
public void readline(String prompt, Handler lineHandler) {
if (conn.getStdinHandler() != echoHandler) {
throw new IllegalStateException();
}
if (inReadline) {
throw new IllegalStateException();
}
inReadline = true;
readline.readline(conn, prompt, line -> {
inReadline = false;
lineHandler.handle(line);
});
}
public void readline(String prompt, Handler lineHandler, Handler completionHandler) {
if (conn.getStdinHandler() != echoHandler) {
throw new IllegalStateException();
}
if (inReadline) {
throw new IllegalStateException();
}
inReadline = true;
readline.readline(conn, prompt, line -> {
inReadline = false;
lineHandler.handle(line);
}, abc -> {
String line = Helper.fromCodePoints(abc.line());
List tokens = Collections.unmodifiableList(CliToken.tokenize(line));
Completion comp = new Completion() {
@Override
public Vertx vertx() {
return vertx;
}
@Override
public Session session() {
return session;
}
@Override
public String rawLine() {
return line;
}
@Override
public List lineTokens() {
return tokens;
}
@Override
public void complete(List candidates) {
if (candidates.size() > 0) {
abc.suggest(candidates.stream().
map(Helper::toCodePoints).
collect(Collectors.toList()));
} else {
abc.end();
}
}
@Override
public void complete(String value, boolean terminal) {
abc.complete(Helper.toCodePoints(value), terminal);
}
};
completionHandler.handle(comp);
});
}
@Override
public Term closeHandler(Handler handler) {
if (handler != null) {
conn.setCloseHandler(handler::handle);
} else {
conn.setCloseHandler(null);
}
return this;
}
public long lastAccessedTime() {
return conn.lastAccessedTime();
}
@Override
public String type() {
return conn.terminalType();
}
@Override
public int width() {
return conn.size() != null ? conn.size().x() : -1;
}
@Override
public int height() {
return conn.size() != null ? conn.size().y() : -1;
}
void checkPending() {
if (stdinHandler != null && readline.hasEvent()) {
stdinHandler.handle(Helper.fromCodePoints(readline.nextEvent().buffer().array()));
vertx.runOnContext(v -> checkPending());
}
}
@Override
public TermImpl resizehandler(Handler handler) {
if (inReadline) {
throw new IllegalStateException();
}
if (handler != null) {
conn.setSizeHandler(resize -> handler.handle(null));
} else {
conn.setSizeHandler(null);
}
return this;
}
@Override
public Term stdinHandler(Handler handler) {
if (inReadline) {
throw new IllegalStateException();
}
stdinHandler = handler;
if (handler != null) {
conn.setStdinHandler(codePoints -> handler.handle(Helper.fromCodePoints(codePoints)));
checkPending();
} else {
conn.setStdinHandler(echoHandler);
}
return this;
}
@Override
public Term write(String data) {
conn.write(data);
return this;
}
public TermImpl interruptHandler(SignalHandler handler) {
interruptHandler = handler;
return this;
}
public TermImpl suspendHandler(SignalHandler handler) {
suspendHandler = handler;
return this;
}
public void close() {
conn.close();
}
public TermImpl echo(String text) {
echo(Helper.toCodePoints(text));
return this;
}
public void echo(int... codePoints) {
Consumer out = conn.stdoutHandler();
for (int codePoint : codePoints) {
if (codePoint < 32) {
if (codePoint == '\t') {
out.accept(new int[]{'\t'});
} else if (codePoint == '\b') {
out.accept(new int[]{'\b', ' ', '\b'});
} else if (codePoint == '\r' || codePoint == '\n') {
out.accept(new int[]{'\n'});
} else {
out.accept(new int[]{'^', codePoint + 64});
}
} else {
if (codePoint == 127) {
out.accept(new int[]{'\b', ' ', '\b'});
} else {
out.accept(new int[]{codePoint});
}
}
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy