Skip to content

Commit

Permalink
Add a getTerminalWidth method, fixes #175
Browse files Browse the repository at this point in the history
  • Loading branch information
gnodet committed Jan 20, 2021
1 parent 8a27841 commit 0b1e2ec
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 7 deletions.
44 changes: 41 additions & 3 deletions src/main/java/org/fusesource/jansi/AnsiConsole.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,17 +27,21 @@
import java.util.Locale;

import org.fusesource.jansi.internal.CLibrary;
import org.fusesource.jansi.internal.CLibrary.WinSize;
import org.fusesource.jansi.io.AnsiOutputStream;
import org.fusesource.jansi.io.AnsiProcessor;
import org.fusesource.jansi.io.FastBufferedOutputStream;
import org.fusesource.jansi.io.WindowsAnsiProcessor;
import org.fusesource.jansi.internal.Kernel32.CONSOLE_SCREEN_BUFFER_INFO;

import static org.fusesource.jansi.internal.CLibrary.ioctl;
import static org.fusesource.jansi.internal.CLibrary.isatty;
import static org.fusesource.jansi.internal.Kernel32.GetConsoleMode;
import static org.fusesource.jansi.internal.Kernel32.GetStdHandle;
import static org.fusesource.jansi.internal.Kernel32.STD_ERROR_HANDLE;
import static org.fusesource.jansi.internal.Kernel32.STD_OUTPUT_HANDLE;
import static org.fusesource.jansi.internal.Kernel32.SetConsoleMode;
import static org.fusesource.jansi.internal.Kernel32.GetConsoleScreenBufferInfo;

/**
* Provides consistent access to an ANSI aware console PrintStream or an ANSI codes stripping PrintStream
Expand Down Expand Up @@ -179,6 +183,20 @@ public class AnsiConsole {
@Deprecated
public static PrintStream err;

/**
* Try to find the width of the console for this process.
* Both output and error streams will be checked to determine the width.
* A value of 0 is returned if the width can not be determined.
* @since 2.2
*/
public static int getTerminalWidth() {
int w = out().getTerminalWidth();
if (w <= 0) {
w = err().getTerminalWidth();
}
return w;
}

static final boolean IS_WINDOWS = System.getProperty("os.name").toLowerCase(Locale.ENGLISH).contains("win");

static final boolean IS_CYGWIN = IS_WINDOWS
Expand Down Expand Up @@ -210,17 +228,19 @@ private AnsiConsole() {
}

private static AnsiPrintStream ansiStream(boolean stdout) {
final OutputStream out = new FastBufferedOutputStream(new FileOutputStream(stdout ? FileDescriptor.out : FileDescriptor.err));
FileDescriptor descriptor = stdout ? FileDescriptor.out : FileDescriptor.err;
final OutputStream out = new FastBufferedOutputStream(new FileOutputStream(descriptor));

String enc = System.getProperty(stdout ? "sun.stdout.encoding" : "sun.stderr.encoding");

final boolean isatty;
boolean isAtty;
boolean withException;
final int fd = stdout ? CLibrary.STDOUT_FILENO : CLibrary.STDERR_FILENO;
try {
// If we can detect that stdout is not a tty.. then setup
// to strip the ANSI sequences..
isAtty = isatty(stdout ? CLibrary.STDOUT_FILENO : CLibrary.STDERR_FILENO) != 0;
isAtty = isatty(fd) != 0;
withException = false;
} catch (Throwable ignore) {
// These errors happen if the JNI lib is not available for your platform.
Expand All @@ -230,6 +250,7 @@ private static AnsiPrintStream ansiStream(boolean stdout) {
}
isatty = isAtty;

final AnsiOutputStream.WidthSupplier width;
final AnsiProcessor processor;
final AnsiType type;
final AnsiOutputStream.IoRunnable installer;
Expand All @@ -238,6 +259,7 @@ private static AnsiPrintStream ansiStream(boolean stdout) {
processor = null;
type = withException ? AnsiType.Unsupported : AnsiType.Redirected;
installer = uninstaller = null;
width = new AnsiOutputStream.ZeroWidthSupplier();
}
else if (IS_WINDOWS) {
final long console = GetStdHandle(stdout ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE);
Expand Down Expand Up @@ -287,6 +309,14 @@ else if (IS_CONEMU || IS_CYGWIN || IS_MSYSTEM) {
type = ttype;
installer = uninstaller = null;
}
width = new AnsiOutputStream.WidthSupplier() {
@Override
public int getTerminalWidth() {
CONSOLE_SCREEN_BUFFER_INFO info = new CONSOLE_SCREEN_BUFFER_INFO();
GetConsoleScreenBufferInfo(console, info);
return info.windowWidth();
}
};
}

// We must be on some Unix variant...
Expand All @@ -295,6 +325,14 @@ else if (IS_CONEMU || IS_CYGWIN || IS_MSYSTEM) {
processor = null;
type = AnsiType.Native;
installer = uninstaller = null;
width = new AnsiOutputStream.WidthSupplier() {
@Override
public int getTerminalWidth() {
WinSize sz = new WinSize();
ioctl(fd, CLibrary.TIOCGWINSZ, sz);
return sz.ws_col;
}
};
}

AnsiMode mode;
Expand Down Expand Up @@ -377,7 +415,7 @@ else if (term != null && term.contains("-256color")) {
} catch (UnsupportedCharsetException e) {
}
}
return newPrintStream(new AnsiOutputStream(out, mode, processor, type, colors, cs,
return newPrintStream(new AnsiOutputStream(out, width, mode, processor, type, colors, cs,
installer, uninstaller, resetAtUninstall), cs.name());
}

Expand Down
8 changes: 8 additions & 0 deletions src/main/java/org/fusesource/jansi/AnsiPrintStream.java
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,14 @@ public void setResetAtUninstall(boolean resetAtClose) {
getOut().setResetAtUninstall(resetAtClose);
}

/**
* Returns the width of the terminal associated with this stream or 0.
* @since 2.2
*/
public int getTerminalWidth() {
return getOut().getTerminalWidth();
}

public void install() throws IOException {
getOut().install();
}
Expand Down
19 changes: 18 additions & 1 deletion src/main/java/org/fusesource/jansi/io/AnsiOutputStream.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,17 @@ public interface IoRunnable {
void run() throws IOException;
}

public interface WidthSupplier {
int getTerminalWidth();
}

public static class ZeroWidthSupplier implements WidthSupplier {
@Override
public int getTerminalWidth() {
return 0;
}
}

private static final int LOOKING_FOR_FIRST_ESC_CHAR = 0;
private static final int LOOKING_FOR_SECOND_ESC_CHAR = 1;
private static final int LOOKING_FOR_NEXT_ARG = 2;
Expand Down Expand Up @@ -73,6 +84,7 @@ public interface IoRunnable {
private int state = LOOKING_FOR_FIRST_ESC_CHAR;
private final Charset cs;

private final WidthSupplier width;
private final AnsiProcessor processor;
private final AnsiType type;
private final AnsiColors colors;
Expand All @@ -81,10 +93,11 @@ public interface IoRunnable {
private AnsiMode mode;
private boolean resetAtUninstall;

public AnsiOutputStream(OutputStream os, AnsiMode mode,
public AnsiOutputStream(OutputStream os, WidthSupplier width, AnsiMode mode,
AnsiProcessor processor, AnsiType type, AnsiColors colors,
Charset cs, IoRunnable installer, IoRunnable uninstaller, boolean resetAtUninstall) {
super(os);
this.width = width;
this.processor = processor;
this.type = type;
this.colors = colors;
Expand All @@ -95,6 +108,10 @@ public AnsiOutputStream(OutputStream os, AnsiMode mode,
setMode(mode);
}

public int getTerminalWidth() {
return width.getTerminalWidth();
}

public AnsiType getType() {
return type;
}
Expand Down
4 changes: 2 additions & 2 deletions src/test/java/org/fusesource/jansi/EncodingTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public class EncodingTest {
public void testEncoding8859() throws UnsupportedEncodingException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
final AtomicReference<String> newLabel = new AtomicReference<String>();
PrintStream ansi = new AnsiPrintStream(new AnsiOutputStream(baos, AnsiMode.Default, new AnsiProcessor(baos) {
PrintStream ansi = new AnsiPrintStream(new AnsiOutputStream(baos, null, AnsiMode.Default, new AnsiProcessor(baos) {
@Override
protected void processChangeWindowTitle(String label) {
newLabel.set(label);
Expand All @@ -51,7 +51,7 @@ protected void processChangeWindowTitle(String label) {
public void testEncodingUtf8() throws UnsupportedEncodingException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
final AtomicReference<String> newLabel = new AtomicReference<String>();
PrintStream ansi = new PrintStream(new AnsiOutputStream(baos, AnsiMode.Default, new AnsiProcessor(baos) {
PrintStream ansi = new PrintStream(new AnsiOutputStream(baos, null, AnsiMode.Default, new AnsiProcessor(baos) {
@Override
protected void processChangeWindowTitle(String label) {
newLabel.set(label);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ class AnsiOutputStreamTest {
@Test
void canHandleSgrsWithMultipleOptions() throws IOException {
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
final AnsiOutputStream ansiOutput = new AnsiOutputStream(baos, AnsiMode.Strip, null, AnsiType.Emulation,
final AnsiOutputStream ansiOutput = new AnsiOutputStream(baos, null, AnsiMode.Strip, null, AnsiType.Emulation,
AnsiColors.TrueColor, Charset.forName("UTF-8"), null, null, false);
ansiOutput.write(("\u001B[33mbanana_1 |\u001B[0m 19:59:14.353\u001B[0;38m [debug] A message\u001B[0m\n").getBytes());
assertEquals("banana_1 | 19:59:14.353 [debug] A message\n", baos.toString());
Expand Down

0 comments on commit 0b1e2ec

Please sign in to comment.