Skip to content

Commit 0b1e2ec

Browse files
committedJan 20, 2021
Add a getTerminalWidth method, fixes #175
1 parent 8a27841 commit 0b1e2ec

File tree

5 files changed

+70
-7
lines changed

5 files changed

+70
-7
lines changed
 

‎src/main/java/org/fusesource/jansi/AnsiConsole.java

+41-3
Original file line numberDiff line numberDiff line change
@@ -27,17 +27,21 @@
2727
import java.util.Locale;
2828

2929
import org.fusesource.jansi.internal.CLibrary;
30+
import org.fusesource.jansi.internal.CLibrary.WinSize;
3031
import org.fusesource.jansi.io.AnsiOutputStream;
3132
import org.fusesource.jansi.io.AnsiProcessor;
3233
import org.fusesource.jansi.io.FastBufferedOutputStream;
3334
import org.fusesource.jansi.io.WindowsAnsiProcessor;
35+
import org.fusesource.jansi.internal.Kernel32.CONSOLE_SCREEN_BUFFER_INFO;
3436

37+
import static org.fusesource.jansi.internal.CLibrary.ioctl;
3538
import static org.fusesource.jansi.internal.CLibrary.isatty;
3639
import static org.fusesource.jansi.internal.Kernel32.GetConsoleMode;
3740
import static org.fusesource.jansi.internal.Kernel32.GetStdHandle;
3841
import static org.fusesource.jansi.internal.Kernel32.STD_ERROR_HANDLE;
3942
import static org.fusesource.jansi.internal.Kernel32.STD_OUTPUT_HANDLE;
4043
import static org.fusesource.jansi.internal.Kernel32.SetConsoleMode;
44+
import static org.fusesource.jansi.internal.Kernel32.GetConsoleScreenBufferInfo;
4145

4246
/**
4347
* Provides consistent access to an ANSI aware console PrintStream or an ANSI codes stripping PrintStream
@@ -179,6 +183,20 @@ public class AnsiConsole {
179183
@Deprecated
180184
public static PrintStream err;
181185

186+
/**
187+
* Try to find the width of the console for this process.
188+
* Both output and error streams will be checked to determine the width.
189+
* A value of 0 is returned if the width can not be determined.
190+
* @since 2.2
191+
*/
192+
public static int getTerminalWidth() {
193+
int w = out().getTerminalWidth();
194+
if (w <= 0) {
195+
w = err().getTerminalWidth();
196+
}
197+
return w;
198+
}
199+
182200
static final boolean IS_WINDOWS = System.getProperty("os.name").toLowerCase(Locale.ENGLISH).contains("win");
183201

184202
static final boolean IS_CYGWIN = IS_WINDOWS
@@ -210,17 +228,19 @@ private AnsiConsole() {
210228
}
211229

212230
private static AnsiPrintStream ansiStream(boolean stdout) {
213-
final OutputStream out = new FastBufferedOutputStream(new FileOutputStream(stdout ? FileDescriptor.out : FileDescriptor.err));
231+
FileDescriptor descriptor = stdout ? FileDescriptor.out : FileDescriptor.err;
232+
final OutputStream out = new FastBufferedOutputStream(new FileOutputStream(descriptor));
214233

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

217236
final boolean isatty;
218237
boolean isAtty;
219238
boolean withException;
239+
final int fd = stdout ? CLibrary.STDOUT_FILENO : CLibrary.STDERR_FILENO;
220240
try {
221241
// If we can detect that stdout is not a tty.. then setup
222242
// to strip the ANSI sequences..
223-
isAtty = isatty(stdout ? CLibrary.STDOUT_FILENO : CLibrary.STDERR_FILENO) != 0;
243+
isAtty = isatty(fd) != 0;
224244
withException = false;
225245
} catch (Throwable ignore) {
226246
// These errors happen if the JNI lib is not available for your platform.
@@ -230,6 +250,7 @@ private static AnsiPrintStream ansiStream(boolean stdout) {
230250
}
231251
isatty = isAtty;
232252

253+
final AnsiOutputStream.WidthSupplier width;
233254
final AnsiProcessor processor;
234255
final AnsiType type;
235256
final AnsiOutputStream.IoRunnable installer;
@@ -238,6 +259,7 @@ private static AnsiPrintStream ansiStream(boolean stdout) {
238259
processor = null;
239260
type = withException ? AnsiType.Unsupported : AnsiType.Redirected;
240261
installer = uninstaller = null;
262+
width = new AnsiOutputStream.ZeroWidthSupplier();
241263
}
242264
else if (IS_WINDOWS) {
243265
final long console = GetStdHandle(stdout ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE);
@@ -287,6 +309,14 @@ else if (IS_CONEMU || IS_CYGWIN || IS_MSYSTEM) {
287309
type = ttype;
288310
installer = uninstaller = null;
289311
}
312+
width = new AnsiOutputStream.WidthSupplier() {
313+
@Override
314+
public int getTerminalWidth() {
315+
CONSOLE_SCREEN_BUFFER_INFO info = new CONSOLE_SCREEN_BUFFER_INFO();
316+
GetConsoleScreenBufferInfo(console, info);
317+
return info.windowWidth();
318+
}
319+
};
290320
}
291321

292322
// We must be on some Unix variant...
@@ -295,6 +325,14 @@ else if (IS_CONEMU || IS_CYGWIN || IS_MSYSTEM) {
295325
processor = null;
296326
type = AnsiType.Native;
297327
installer = uninstaller = null;
328+
width = new AnsiOutputStream.WidthSupplier() {
329+
@Override
330+
public int getTerminalWidth() {
331+
WinSize sz = new WinSize();
332+
ioctl(fd, CLibrary.TIOCGWINSZ, sz);
333+
return sz.ws_col;
334+
}
335+
};
298336
}
299337

300338
AnsiMode mode;
@@ -377,7 +415,7 @@ else if (term != null && term.contains("-256color")) {
377415
} catch (UnsupportedCharsetException e) {
378416
}
379417
}
380-
return newPrintStream(new AnsiOutputStream(out, mode, processor, type, colors, cs,
418+
return newPrintStream(new AnsiOutputStream(out, width, mode, processor, type, colors, cs,
381419
installer, uninstaller, resetAtUninstall), cs.name());
382420
}
383421

‎src/main/java/org/fusesource/jansi/AnsiPrintStream.java

+8
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,14 @@ public void setResetAtUninstall(boolean resetAtClose) {
6363
getOut().setResetAtUninstall(resetAtClose);
6464
}
6565

66+
/**
67+
* Returns the width of the terminal associated with this stream or 0.
68+
* @since 2.2
69+
*/
70+
public int getTerminalWidth() {
71+
return getOut().getTerminalWidth();
72+
}
73+
6674
public void install() throws IOException {
6775
getOut().install();
6876
}

‎src/main/java/org/fusesource/jansi/io/AnsiOutputStream.java

+18-1
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,17 @@ public interface IoRunnable {
4545
void run() throws IOException;
4646
}
4747

48+
public interface WidthSupplier {
49+
int getTerminalWidth();
50+
}
51+
52+
public static class ZeroWidthSupplier implements WidthSupplier {
53+
@Override
54+
public int getTerminalWidth() {
55+
return 0;
56+
}
57+
}
58+
4859
private static final int LOOKING_FOR_FIRST_ESC_CHAR = 0;
4960
private static final int LOOKING_FOR_SECOND_ESC_CHAR = 1;
5061
private static final int LOOKING_FOR_NEXT_ARG = 2;
@@ -73,6 +84,7 @@ public interface IoRunnable {
7384
private int state = LOOKING_FOR_FIRST_ESC_CHAR;
7485
private final Charset cs;
7586

87+
private final WidthSupplier width;
7688
private final AnsiProcessor processor;
7789
private final AnsiType type;
7890
private final AnsiColors colors;
@@ -81,10 +93,11 @@ public interface IoRunnable {
8193
private AnsiMode mode;
8294
private boolean resetAtUninstall;
8395

84-
public AnsiOutputStream(OutputStream os, AnsiMode mode,
96+
public AnsiOutputStream(OutputStream os, WidthSupplier width, AnsiMode mode,
8597
AnsiProcessor processor, AnsiType type, AnsiColors colors,
8698
Charset cs, IoRunnable installer, IoRunnable uninstaller, boolean resetAtUninstall) {
8799
super(os);
100+
this.width = width;
88101
this.processor = processor;
89102
this.type = type;
90103
this.colors = colors;
@@ -95,6 +108,10 @@ public AnsiOutputStream(OutputStream os, AnsiMode mode,
95108
setMode(mode);
96109
}
97110

111+
public int getTerminalWidth() {
112+
return width.getTerminalWidth();
113+
}
114+
98115
public AnsiType getType() {
99116
return type;
100117
}

‎src/test/java/org/fusesource/jansi/EncodingTest.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public class EncodingTest {
3535
public void testEncoding8859() throws UnsupportedEncodingException {
3636
ByteArrayOutputStream baos = new ByteArrayOutputStream();
3737
final AtomicReference<String> newLabel = new AtomicReference<String>();
38-
PrintStream ansi = new AnsiPrintStream(new AnsiOutputStream(baos, AnsiMode.Default, new AnsiProcessor(baos) {
38+
PrintStream ansi = new AnsiPrintStream(new AnsiOutputStream(baos, null, AnsiMode.Default, new AnsiProcessor(baos) {
3939
@Override
4040
protected void processChangeWindowTitle(String label) {
4141
newLabel.set(label);
@@ -51,7 +51,7 @@ protected void processChangeWindowTitle(String label) {
5151
public void testEncodingUtf8() throws UnsupportedEncodingException {
5252
ByteArrayOutputStream baos = new ByteArrayOutputStream();
5353
final AtomicReference<String> newLabel = new AtomicReference<String>();
54-
PrintStream ansi = new PrintStream(new AnsiOutputStream(baos, AnsiMode.Default, new AnsiProcessor(baos) {
54+
PrintStream ansi = new PrintStream(new AnsiOutputStream(baos, null, AnsiMode.Default, new AnsiProcessor(baos) {
5555
@Override
5656
protected void processChangeWindowTitle(String label) {
5757
newLabel.set(label);

‎src/test/java/org/fusesource/jansi/io/AnsiOutputStreamTest.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ class AnsiOutputStreamTest {
3131
@Test
3232
void canHandleSgrsWithMultipleOptions() throws IOException {
3333
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
34-
final AnsiOutputStream ansiOutput = new AnsiOutputStream(baos, AnsiMode.Strip, null, AnsiType.Emulation,
34+
final AnsiOutputStream ansiOutput = new AnsiOutputStream(baos, null, AnsiMode.Strip, null, AnsiType.Emulation,
3535
AnsiColors.TrueColor, Charset.forName("UTF-8"), null, null, false);
3636
ansiOutput.write(("\u001B[33mbanana_1 |\u001B[0m 19:59:14.353\u001B[0;38m [debug] A message\u001B[0m\n").getBytes());
3737
assertEquals("banana_1 | 19:59:14.353 [debug] A message\n", baos.toString());

0 commit comments

Comments
 (0)
Please sign in to comment.