Fri, 19 Jan 2024 20:48:26 +0100
add helptext descriptions for features
fixes #314 and fixes #315
package de.unixwork.uwproj; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import java.util.List; import static de.unixwork.uwproj.Util.isNotNullOrBlank; import static de.unixwork.uwproj.Util.shId; public class Feature { private String name; private String arg; private boolean auto; private String desc; private TargetData targetData; public static Feature parse(Project project, Element e) throws Exception { Feature feature = new Feature(); String name = e.getAttribute("name"); String arg = e.getAttribute("arg"); if (name.isBlank()) { throw new Exception("feature element requires name attribute"); } feature.setName(name); if (arg.isBlank()) { feature.setArg(name); } else { feature.setArg(arg); } feature.setAuto(Boolean.parseBoolean(e.getAttribute("default"))); feature.setTargetData(TargetData.parse(e)); project.addFeature(feature); // TODO: when Option also receives this feature, we might move this to TargetData.parse() NodeList nodes = e.getChildNodes(); for (int i = 0; i < nodes.getLength(); i++) { Node node = nodes.item(i); if (node.getNodeType() == Node.ELEMENT_NODE) { Element elm = (Element) node; String n = elm.getNodeName(); if (n.equals("desc")) { feature.setDesc(Util.getContent(elm)); } } } return feature; } public String getVarName() { return shId("FEATURE_" + name.toUpperCase()); } public List<String> getDependencies() { return getTargetData().getDependencies(); } public List<Define> getDefines() { return getTargetData().getDefines(); } public String getMake() { return targetData.getMake(); } public boolean hasMake() { return isNotNullOrBlank(targetData.getMake()); } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getArg() { return arg; } public void setArg(String arg) { this.arg = arg; } public String getDesc() { return desc; } public void setDesc(String desc) { this.desc = desc; } public boolean isAuto() { return auto; } public void setAuto(boolean auto) { this.auto = auto; } public TargetData getTargetData() { return targetData; } public void setTargetData(TargetData targetData) { this.targetData = targetData; } /** * Generates help text for the feature option. * <p> * If {@link #isAuto()} returns true, the option name will be --disable-${@link #getArg()}, otherwise * it will be --enable-${@link #getArg()}, preceded by two spaces, respectively. * <p> * If no description is available via {@link #getDesc()}, only the option name is generated. Otherwise, * description is added according to the following rules: * <p> * When the option name does not consume more than 25 characters, the description starts in the same line. * Otherwise, a line break is added, first. The description will be placed in a block starting from column * 27 to 80 and automatically break when necessary. The description must not contain a single word * that is longer than 54 characters, or it will break the layout. * * @return a help text for terminal output */ public String getHelpText() { final var builder = new StringBuilder(); // Compute option name final var opt = (auto ? " --disable-" : " --enable-")+arg; // Add option name builder.append(opt); // Stop, if there is no description if (desc == null || desc.isBlank()) return builder.toString(); // Prepare the description by replacing some unwanted spaces final var hdesc = desc.trim() .replaceAll("\\r", "") .replaceAll("\\n", " ") .replaceAll("\\t", " "); // Declare our frame where the description shall be placed final int startx = 26; final int endx = 80; // Move to startx (break, if we already past that) if (opt.length() >= startx) { builder.append("\n"); builder.append(" ".repeat(startx)); } else { builder.append(" ".repeat(startx-opt.length())); } // Append the description keeping the layout intact int x = startx; for (int i = 0 ; i < hdesc.length() ;) { // get the next word and see where we would end int s = hdesc.indexOf(' ', i); if (s < 0) s = hdesc.length(); int l = s-i; if (x + l > endx) { // does not fit, break into next line builder.append('\n'); builder.append(" ".repeat(startx)); x = startx; } // append the word builder.append(hdesc, i, s); x += l; i += l; // append the next spaces while (i < hdesc.length() && hdesc.charAt(i) == ' ') { i++; x++; // as long as we don't need to break, add the spaces if (x < endx) builder.append(' '); } // break to new line, when spaces moved us outside the frame if (x > endx) { builder.append('\n'); builder.append(" ".repeat(startx)); x = startx; } } return builder.toString(); } }