From cb66e0584c3694111818099223374fff6b210d1d Mon Sep 17 00:00:00 2001 From: Colin Hicks Date: Tue, 25 Feb 2020 15:34:03 -0500 Subject: [PATCH] feat: support custom column widths in cli (#4616) Allow column width to be explicitly specified in tabular cli output, e.g.: ksql> SET CLI COLUMN-WIDTH 10 Given a customized value, subsequent renderings of output use the setting. The default behavior, in which column width is determined based on the terminal width and column count, can be re-enabled using: ksql> SET CLI COLUMN-WIDTH 0 --- .../confluent/ksql/cli/console/CliConfig.java | 10 +++++ .../confluent/ksql/cli/console/Console.java | 2 +- .../io/confluent/ksql/util/TabularRow.java | 28 ++++++++++---- .../confluent/ksql/util/TabularRowTest.java | 37 ++++++++++++++++--- 4 files changed, 63 insertions(+), 14 deletions(-) diff --git a/ksql-cli/src/main/java/io/confluent/ksql/cli/console/CliConfig.java b/ksql-cli/src/main/java/io/confluent/ksql/cli/console/CliConfig.java index 36c91d01eeac..b26cb998c113 100644 --- a/ksql-cli/src/main/java/io/confluent/ksql/cli/console/CliConfig.java +++ b/ksql-cli/src/main/java/io/confluent/ksql/cli/console/CliConfig.java @@ -27,6 +27,7 @@ public class CliConfig extends AbstractConfig { public static final String WRAP_CONFIG = "WRAP"; + public static final String COLUMN_WIDTH_CONFIG = "COLUMN-WIDTH"; private static final ConfigDef CONFIG_DEF = new ConfigDef() .define( @@ -37,6 +38,15 @@ public class CliConfig extends AbstractConfig { Importance.MEDIUM, "A value of 'OFF' will clip lines to ensure that query results do not exceed the " + "terminal width (i.e. each row will appear on a single line)." + ) + .define( + COLUMN_WIDTH_CONFIG, + Type.INT, + 0, + ConfigValidators.zeroOrPositive(), + Importance.MEDIUM, + "The width in characters of each column in tabular output. A value of '0' indicates " + + "column width should be based on terminal width and number of columns." ); public CliConfig(final Map originals) { diff --git a/ksql-cli/src/main/java/io/confluent/ksql/cli/console/Console.java b/ksql-cli/src/main/java/io/confluent/ksql/cli/console/Console.java index 754f9a9c913f..33deaa48ed1b 100644 --- a/ksql-cli/src/main/java/io/confluent/ksql/cli/console/Console.java +++ b/ksql-cli/src/main/java/io/confluent/ksql/cli/console/Console.java @@ -378,7 +378,7 @@ private void printRowHeader(final LogicalSchema schema) { case JSON: break; case TABULAR: - writer().println(TabularRow.createHeader(getWidth(), schema)); + writer().println(TabularRow.createHeader(getWidth(), schema, config)); break; default: throw new RuntimeException(String.format( diff --git a/ksql-cli/src/main/java/io/confluent/ksql/util/TabularRow.java b/ksql-cli/src/main/java/io/confluent/ksql/util/TabularRow.java index fb6a617d8f7f..436baf7aa1d1 100644 --- a/ksql-cli/src/main/java/io/confluent/ksql/util/TabularRow.java +++ b/ksql-cli/src/main/java/io/confluent/ksql/util/TabularRow.java @@ -34,12 +34,16 @@ public final class TabularRow { private static final String CLIPPED = "..."; private static final int MIN_CELL_WIDTH = 5; - private final int width; + private final int cellWidth; private final List columns; private final boolean isHeader; private final boolean shouldWrap; - public static TabularRow createHeader(final int width, final LogicalSchema schema) { + public static TabularRow createHeader( + final int width, + final LogicalSchema schema, + final CliConfig config + ) { final List headings = schema.columns().stream() .map(Column::name) .map(ColumnName::name) @@ -49,7 +53,7 @@ public static TabularRow createHeader(final int width, final LogicalSchema schem width, headings, true, - true + config ); } @@ -62,7 +66,7 @@ public static TabularRow createRow( width, value.values().stream().map(Objects::toString).collect(Collectors.toList()), false, - config.getString(CliConfig.WRAP_CONFIG).equalsIgnoreCase(OnOff.ON.toString()) + config ); } @@ -70,12 +74,21 @@ private TabularRow( final int width, final List columns, final boolean isHeader, - final boolean shouldWrap + final CliConfig config ) { this.columns = ImmutableList.copyOf(Objects.requireNonNull(columns, "columns")); - this.width = width; this.isHeader = isHeader; - this.shouldWrap = shouldWrap; + this.shouldWrap = isHeader + || config.getString(CliConfig.WRAP_CONFIG).equalsIgnoreCase(OnOff.ON.toString()); + + final int configCellWidth = config.getInt(CliConfig.COLUMN_WIDTH_CONFIG); + if (configCellWidth > 0) { + this.cellWidth = configCellWidth; + } else if (!columns.isEmpty()) { + this.cellWidth = Math.max(width / columns.size() - 2, MIN_CELL_WIDTH); + } else { + cellWidth = MIN_CELL_WIDTH; + } } @Override @@ -84,7 +97,6 @@ public String toString() { return ""; } - final int cellWidth = Math.max(width / columns.size() - 2, MIN_CELL_WIDTH); final StringBuilder builder = new StringBuilder(); if (isHeader) { diff --git a/ksql-cli/src/test/java/io/confluent/ksql/util/TabularRowTest.java b/ksql-cli/src/test/java/io/confluent/ksql/util/TabularRowTest.java index e65014ed04a8..c016c138ee4c 100644 --- a/ksql-cli/src/test/java/io/confluent/ksql/util/TabularRowTest.java +++ b/ksql-cli/src/test/java/io/confluent/ksql/util/TabularRowTest.java @@ -48,7 +48,7 @@ public void shouldFormatHeader() { .build(); // When: - final String formatted = TabularRow.createHeader(20, schema).toString(); + final String formatted = TabularRow.createHeader(20, schema, config).toString(); // Then: assertThat(formatted, is("" @@ -67,7 +67,7 @@ public void shouldMultilineFormatHeader() { .build(); // When: - final String formatted = TabularRow.createHeader(20, schema).toString(); + final String formatted = TabularRow.createHeader(20, schema, config).toString(); // Then: assertThat(formatted, is("" @@ -169,7 +169,7 @@ public void shouldFormatNoColumnsHeader() { .build(); // When: - final String formatted = TabularRow.createHeader(20, schema).toString(); + final String formatted = TabularRow.createHeader(20, schema, config).toString(); // Then: assertThat(formatted, isEmptyString()); @@ -186,7 +186,7 @@ public void shouldFormatMoreColumnsThanWidth() { .build(); // When: - final String formatted = TabularRow.createHeader(3, schema).toString(); + final String formatted = TabularRow.createHeader(3, schema, config).toString(); // Then: assertThat(formatted, @@ -196,6 +196,29 @@ public void shouldFormatMoreColumnsThanWidth() { + "+-----+-----+-----+")); } + @Test + public void shouldFormatCustomColumnWidth() { + // Given: + givenCustomColumnWidth(10); + + final LogicalSchema schema = LogicalSchema.builder() + .noImplicitColumns() + .keyColumn(ColumnName.of("foo"), SqlTypes.BIGINT) + .valueColumn(ColumnName.of("bar"), SqlTypes.STRING) + .valueColumn(ColumnName.of("baz"), SqlTypes.DOUBLE) + .build(); + + // When: + final String formatted = TabularRow.createHeader(999, schema, config).toString(); + + // Then: + assertThat(formatted, + is("" + + "+----------+----------+----------+\n" + + "|foo |bar |baz |\n" + + "+----------+----------+----------+")); + } + private void givenWrappingEnabled() { when(config.getString(CliConfig.WRAP_CONFIG)).thenReturn(OnOff.ON.toString()); } @@ -203,4 +226,8 @@ private void givenWrappingEnabled() { private void givenWrappingDisabled() { when(config.getString(CliConfig.WRAP_CONFIG)).thenReturn("Not ON"); } -} \ No newline at end of file + + private void givenCustomColumnWidth(int width) { + when(config.getInt(CliConfig.COLUMN_WIDTH_CONFIG)).thenReturn(width); + } +}