Make sure project descriptions line-wrap at a decent length

See gh-966
This commit is contained in:
HaiTao Zhang
2019-07-29 11:45:18 -07:00
committed by Stephane Nicoll
parent 74e0bcc3a5
commit 4772f4219f
3 changed files with 117 additions and 16 deletions

View File

@@ -114,6 +114,11 @@
<artifactId>jsonassert</artifactId> <artifactId>jsonassert</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-text</artifactId>
<version>1.7</version>
</dependency>
</dependencies> </dependencies>
<build> <build>

View File

@@ -18,8 +18,10 @@ package io.spring.initializr.web.support;
import java.beans.PropertyDescriptor; import java.beans.PropertyDescriptor;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.Comparator; import java.util.Comparator;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@@ -28,6 +30,7 @@ import io.spring.initializr.metadata.Dependency;
import io.spring.initializr.metadata.InitializrMetadata; import io.spring.initializr.metadata.InitializrMetadata;
import io.spring.initializr.metadata.MetadataElement; import io.spring.initializr.metadata.MetadataElement;
import io.spring.initializr.metadata.Type; import io.spring.initializr.metadata.Type;
import org.apache.commons.text.WordUtils;
import org.springframework.beans.BeanWrapperImpl; import org.springframework.beans.BeanWrapperImpl;
@@ -45,6 +48,8 @@ public class CommandLineHelpGenerator {
private final TemplateRenderer template; private final TemplateRenderer template;
private final int desiredWidth = 100;
public CommandLineHelpGenerator(TemplateRenderer template) { public CommandLineHelpGenerator(TemplateRenderer template) {
this.template = template; this.template = template;
} }
@@ -74,6 +79,7 @@ public class CommandLineHelpGenerator {
Map<String, Object> model = initializeCommandLineModel(metadata, serviceUrl); Map<String, Object> model = initializeCommandLineModel(metadata, serviceUrl);
model.put("examples", this.template.render("cli/curl-examples", model)); model.put("examples", this.template.render("cli/curl-examples", model));
model.put("hasExamples", true); model.put("hasExamples", true);
return this.template.render("cli/cli-capabilities", model); return this.template.render("cli/cli-capabilities", model);
} }
@@ -129,7 +135,7 @@ public class CommandLineHelpGenerator {
data[2] = (String) defaults.get(id); data[2] = (String) defaults.get(id);
parameterTable[i++] = data; parameterTable[i++] = data;
} }
model.put("parameters", TableGenerator.generate(parameterTable)); model.put("parameters", TableGenerator.generate(parameterTable, false, this.desiredWidth));
return model; return model;
} }
@@ -153,7 +159,7 @@ public class CommandLineHelpGenerator {
data[2] = (String) defaults.get(id); data[2] = (String) defaults.get(id);
parameterTable[i++] = data; parameterTable[i++] = data;
} }
model.put("parameters", TableGenerator.generate(parameterTable)); model.put("parameters", TableGenerator.generate(parameterTable, false, this.desiredWidth));
return model; return model;
} }
@@ -169,7 +175,7 @@ public class CommandLineHelpGenerator {
data[2] = dep.getVersionRequirement(); data[2] = dep.getVersionRequirement();
dependencyTable[i++] = data; dependencyTable[i++] = data;
} }
return TableGenerator.generate(dependencyTable); return TableGenerator.generate(dependencyTable, true, this.desiredWidth);
} }
protected String generateTypeTable(InitializrMetadata metadata, String linkHeader, boolean addTags) { protected String generateTypeTable(InitializrMetadata metadata, String linkHeader, boolean addTags) {
@@ -191,7 +197,7 @@ public class CommandLineHelpGenerator {
} }
typeTable[i++] = data; typeTable[i++] = data;
} }
return TableGenerator.generate(typeTable); return TableGenerator.generate(typeTable, false, this.desiredWidth);
} }
protected Map<String, Object> buildParametersDescription(InitializrMetadata metadata) { protected Map<String, Object> buildParametersDescription(InitializrMetadata metadata) {
@@ -230,27 +236,44 @@ public class CommandLineHelpGenerator {
* The {@code content} is a two-dimensional array holding the rows of the table. * The {@code content} is a two-dimensional array holding the rows of the table.
* The first entry holds the header of the table. * The first entry holds the header of the table.
* @param content the table content * @param content the table content
* @param emptyRowSeparation add an empty row separator
* @param desiredWidth the width bound for each column
* @return the generated table * @return the generated table
*/ */
static String generate(String[][] content) { static String generate(String[][] content, boolean emptyRowSeparation, int desiredWidth) {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
int[] columnsLength = computeColumnsLength(content); int[] columnsLength = computeColumnsLength(content, desiredWidth);
appendTableSeparation(sb, columnsLength); appendTableSeparation(sb, columnsLength);
appendRow(sb, content, columnsLength, 0); // Headers appendRow(sb, content, columnsLength, 0, desiredWidth); // Headers
appendTableSeparation(sb, columnsLength); appendTableSeparation(sb, columnsLength);
for (int i = 1; i < content.length; i++) { for (int i = 1; i < content.length; i++) {
appendRow(sb, content, columnsLength, i); appendRow(sb, content, columnsLength, i, desiredWidth);
if (emptyRowSeparation && i < content.length - 1) {
appendEmptyRow(sb, columnsLength);
}
} }
appendTableSeparation(sb, columnsLength); appendTableSeparation(sb, columnsLength);
return sb.toString(); return sb.toString();
} }
private static void appendRow(StringBuilder sb, String[][] content, int[] columnsLength, int rowIndex) { private static void appendRow(StringBuilder sb, String[][] content, int[] columnsLength, int rowIndex,
String[] row = content[rowIndex]; int desiredWidth) {
if (row != null) { String[] line = content[rowIndex];
List<String[]> rows = HelpFormatter.format(line, desiredWidth);
if (rows != null) {
for (String[] row : rows) {
for (int i = 0; i < row.length; i++) { for (int i = 0; i < row.length; i++) {
sb.append("| ").append(fill(row[i], columnsLength[i])).append(" "); sb.append("| ").append(fill(row[i], columnsLength[i])).append(" ");
} }
sb.append("|");
sb.append(NEW_LINE);
}
}
}
private static void appendEmptyRow(StringBuilder sb, int[] columnsLength) {
for (int columnLength : columnsLength) {
sb.append("| ").append(fill(null, columnLength)).append(" ");
} }
sb.append("|"); sb.append("|");
sb.append(NEW_LINE); sb.append(NEW_LINE);
@@ -282,16 +305,16 @@ public class CommandLineHelpGenerator {
return s.toString(); return s.toString();
} }
private static int[] computeColumnsLength(String[][] content) { private static int[] computeColumnsLength(String[][] content, int desiredWidth) {
int count = content[0].length; int count = content[0].length;
int[] result = new int[count]; int[] result = new int[count];
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
result[i] = largest(content, i); result[i] = largest(content, i, desiredWidth);
} }
return result; return result;
} }
private static int largest(String[][] content, int column) { private static int largest(String[][] content, int column, int desiredWidth) {
int max = 0; int max = 0;
for (String[] rows : content) { for (String[] rows : content) {
if (rows != null) { if (rows != null) {
@@ -301,9 +324,67 @@ public class CommandLineHelpGenerator {
} }
} }
} }
return (max < desiredWidth) ? max : desiredWidth;
}
}
private static class HelpFormatter {
private static final String NEW_LINE = System.getProperty("line.separator");
/**
* Formats a given content to a desired width.
* @param content the content to format.
* @param desiredWidth the desired width of each column
* @return the formatted rows.
*/
private static List<String[]> format(String[] content, int desiredWidth) {
List<String[]> columns = lineWrap(content, desiredWidth);
List<String[]> rows = new ArrayList<>();
for (int i = 0; i < largest(columns); ++i) {
rows.add(computeRow(columns, i));
}
return rows;
}
private static String[] computeRow(List<String[]> columns, int index) {
String[] line = new String[columns.size()];
int position = 0;
for (String[] column : columns) {
line[position] = itemOrNull(column, index);
position++;
}
return line;
}
private static List<String[]> lineWrap(String[] content, int desiredWidth) {
List<String[]> lineWrapped = new ArrayList<>();
for (String column : content) {
if (column == null) {
lineWrapped.add(new String[0]);
}
else {
lineWrapped.add(WordUtils.wrap(column, desiredWidth).split(NEW_LINE));
}
}
return lineWrapped;
}
private static int largest(List<String[]> columns) {
int max = 0;
for (String[] column : columns) {
if (max < column.length) {
max = column.length;
}
}
return max; return max;
} }
private static String itemOrNull(String[] column, int index) {
return (index >= column.length) ? null : column[index];
}
} }
} }

View File

@@ -94,6 +94,21 @@ class CommandLineHelpGeneratorTests {
assertThat(content).contains("curl https://fake-service"); assertThat(content).contains("curl https://fake-service");
} }
@Test
void generateGeneralCapabilitiesWithLineWrap() throws IOException {
InitializrMetadata metadata = InitializrMetadataTestBuilder.withDefaults()
.addDependencyGroup("test", createDependency("id-b",
"Ratpack is a set of Java libraries that facilitate fast, efficient, evolvable and well tested HTTP applications. Built on Netty the event-driven networking engine."))
.build();
String content = this.generator.generateGenericCapabilities(metadata, "https://fake-service");
assertCommandLineCapabilities(content);
assertThat(content).contains(
"id-b | Ratpack is a set of Java libraries that facilitate fast, efficient, evolvable and well tested HTTP |");
assertThat(content).contains(
" | applications. Built on Netty the event-driven networking engine. |");
assertThat(content).contains("https://fake-service");
}
@Test @Test
void generateHttpCapabilities() throws IOException { void generateHttpCapabilities() throws IOException {
InitializrMetadata metadata = InitializrMetadataTestBuilder.withDefaults().addDependencyGroup("test", InitializrMetadata metadata = InitializrMetadataTestBuilder.withDefaults().addDependencyGroup("test",