@@ -90,9 +90,12 @@ public static class Builder implements Supplier<HelpFormatter> {
9090 */
9191 private PrintWriter printStream = createDefaultPrintWriter ();
9292
93+ /** The flag to determine if the since values should be dispalyed */
94+ private boolean showSince ;
95+
9396 @ Override
9497 public HelpFormatter get () {
95- return new HelpFormatter (deprecatedFormatFunc , printStream );
98+ return new HelpFormatter (deprecatedFormatFunc , printStream , showSince );
9699 }
97100
98101 /**
@@ -127,6 +130,17 @@ public Builder setShowDeprecated(final Function<Option, String> showDeprecatedFu
127130 this .deprecatedFormatFunc = showDeprecatedFunc ;
128131 return this ;
129132 }
133+
134+ /**
135+ * Sets whether to show the date the option was first added.
136+ * @param showSince if @{code true} the date the options was first added will be shown.
137+ * @return this builder.
138+ * @since 1.9.0
139+ */
140+ public Builder setShowSince (final boolean showSince ) {
141+ this .showSince = showSince ;
142+ return this ;
143+ }
130144 }
131145
132146 /**
@@ -151,6 +165,15 @@ public int compare(final Option opt1, final Option opt2) {
151165 return opt1 .getKey ().compareToIgnoreCase (opt2 .getKey ());
152166 }
153167 }
168+ /** "Options" text for options header */
169+ private static final String HEADER_OPTIONS = "Options" ;
170+
171+ /** "Since" text for options header */
172+ private static final String HEADER_SINCE = "Since" ;
173+
174+ /** "Description" test for options header */
175+ private static final String HEADER_DESCRIPTION = "Description" ;
176+
154177 /** Default number of characters per line */
155178 public static final int DEFAULT_WIDTH = 74 ;
156179
@@ -285,6 +308,9 @@ public static String getDescription(final Option option) {
285308 */
286309 private final PrintWriter printWriter ;
287310
311+ /** Flag to determine if since field should be displayed */
312+ private final boolean showSince ;
313+
288314 /**
289315 * The separator displayed between the long option and its value.
290316 */
@@ -294,18 +320,19 @@ public static String getDescription(final Option option) {
294320 * Constructs a new instance.
295321 */
296322 public HelpFormatter () {
297- this (null , createDefaultPrintWriter ());
323+ this (null , createDefaultPrintWriter (), false );
298324 }
299325
300326 /**
301327 * Constructs a new instance.
302328 * @param printStream TODO
303329 */
304- private HelpFormatter (final Function <Option , String > deprecatedFormatFunc , final PrintWriter printStream ) {
330+ private HelpFormatter (final Function <Option , String > deprecatedFormatFunc , final PrintWriter printStream , final boolean showSince ) {
305331 // TODO All other instance HelpFormatter instance variables.
306332 // Make HelpFormatter immutable for 2.0
307333 this .deprecatedFormatFunc = deprecatedFormatFunc ;
308334 this .printWriter = printStream ;
335+ this .showSince = showSince ;
309336 }
310337
311338 /**
@@ -748,6 +775,12 @@ public void printWrapped(final PrintWriter pw, final int width, final String tex
748775 printWrapped (pw , width , 0 , text );
749776 }
750777
778+ private int determineMaxSinceLength (final Options options ) {
779+ int minLen = HEADER_SINCE .length ();
780+ int len = options .getOptions ().stream ().map (o -> o .getSince () == null ? minLen : o .getSince ().length ()).max (Integer ::compareTo ).orElse (minLen );
781+ return len < minLen ? minLen : len ;
782+ }
783+
751784 /**
752785 * Renders the specified Options and return the rendered Options in a StringBuffer.
753786 *
@@ -767,6 +800,7 @@ protected StringBuffer renderOptions(final StringBuffer sb, final int width, fin
767800 // the longest opt string this list will be then used to
768801 // sort options ascending
769802 int max = 0 ;
803+ int maxSince = showSince ? determineMaxSinceLength (options ) + leftPad : 0 ;
770804 final List <StringBuffer > prefixList = new ArrayList <>();
771805 final List <Option > optList = options .helpOptions ();
772806 if (getOptionComparator () != null ) {
@@ -792,18 +826,32 @@ protected StringBuffer renderOptions(final StringBuffer sb, final int width, fin
792826 optBuf .append ("<" ).append (argName != null ? option .getArgName () : getArgName ()).append (">" );
793827 }
794828 }
829+
795830 prefixList .add (optBuf );
796- max = Math .max (optBuf .length (), max );
831+ max = Math .max (optBuf .length () + maxSince , max );
797832 }
833+ final int nextLineTabStop = max + descPad ;
834+ if (showSince ) {
835+ StringBuilder optHeader = new StringBuilder (HEADER_OPTIONS ).append (createPadding (max - maxSince - HEADER_OPTIONS .length () + leftPad ))
836+ .append (HEADER_SINCE );
837+ optHeader .append (createPadding (max - optHeader .length ())).append (lpad ).append (HEADER_DESCRIPTION );
838+ renderWrappedText (sb , width , nextLineTabStop , optHeader .toString ());
839+ sb .append (getNewLine ());
840+ }
841+
798842 int x = 0 ;
799843 for (final Iterator <Option > it = optList .iterator (); it .hasNext ();) {
800844 final Option option = it .next ();
801845 final StringBuilder optBuf = new StringBuilder (prefixList .get (x ++).toString ());
802846 if (optBuf .length () < max ) {
847+ optBuf .append (createPadding (max - maxSince - optBuf .length ()));
848+ if (showSince ) {
849+ optBuf .append (lpad ).append (option .getSince () == null ? "-" : option .getSince ());
850+ }
803851 optBuf .append (createPadding (max - optBuf .length ()));
804852 }
805853 optBuf .append (dpad );
806- final int nextLineTabStop = max + descPad ;
854+
807855 if (deprecatedFormatFunc != null && option .isDeprecated ()) {
808856 optBuf .append (deprecatedFormatFunc .apply (option ).trim ());
809857 } else if (option .getDescription () != null ) {
0 commit comments