GetOpt formatting improvements

GetOpt users, I'd like to have Cabal use the standard System.Console.GetOpt but I'd also like the output to look ok. Currently Cabal uses a private modified version of GetOpt with improved formatting and I'd like to get those improvements into the standard one and eliminate the code duplication. Here's what cabal configure looks like with the standard System.Console.GetOpt. This is what it looks like on a standard 80 column console. In particular note that the descriptions wrap around onto the following lines and look ugly. http://hpaste.org/8626 Here's a little snippet: --enable-library-for-ghci Enable compile library for use with GHCi --disable-library-for-ghci Disable compile library for use wit h GHCi --enable-split-objs Enable split library into smaller ob jects to reduce binary sizes (GHC 6.6+) --disable-split-objs Disable split library into smaller o bjects to reduce binary sizes (GHC 6.6+) --enable-executable-stripping Enable strip executables upon instal lation to reduce binary sizes --disable-executable-stripping Disable strip executables upon insta llation to reduce binary sizes --configure-option=OPT Extra option for configure --user Enable doing a per-user installation --global Disable doing a per-user installatio n --package-db=PATH Use a specific package database (to satisfy dependencies and register in) Here's the same but using the internal modified copy of GetOpt: http://hpaste.org/8626#a1 and the same snippet: --enable-library-for-ghci Enable compile library for use with GHCi --disable-library-for-ghci Disable compile library for use with GHCi --enable-split-objs Enable split library into smaller objects to reduce binary sizes (GHC 6.6+) --disable-split-objs Disable split library into smaller objects to reduce binary sizes (GHC 6.6+) --enable-executable-stripping Enable strip executables upon installation to reduce binary sizes --disable-executable-stripping Disable strip executables upon installation to reduce binary sizes --configure-option=OPT Extra option for configure --user Enable doing a per-user installation --global Disable doing a per-user installation --package-db=PATH Use a specific package database (to satisfy dependencies and register in) So there's two things to notice. One is that we wrap the description so it fits in 80 columns. The other is that we use one space rather than two of padding between the short options, the long options and the description. This gives more space for the description. Actually we go one step further and leave off the args on the short options since they're shown on the long options anyway. I'm not sure everyone wants that so I'm not really proposing it. This is what it looks like: -f FLAGS --flags=FLAGS Force values for the given... vs -f --flags=FLAGS Force values for the given flags in... Similarly: -v[n] --verbose[=n] Control verbosity (n is 0--3,... vs -v --verbose[=n] Control verbosity (n is 0--3, default... So if people think this is sensible I'll send three patches: 1. word-wrap the description to fit in 80 columns 2. use less padding between columns 3. omit the args on short options when there is also a corresponding long option though as I say, perhaps not everyone will think the 3rd change is the right thing to do. Duncan

Hi
1. word-wrap the description to fit in 80 columns
(+1). 70 and 80 are defensible, anything else seems a bit of a weird choice for line length...
2. use less padding between columns
(+0.8) - given its in columns the spacing is not that necessary, but still slightly nice. Overall, I'd use less padding but don't think the decision is as clear cut (but I still supported it)
3. omit the args on short options when there is also a corresponding long option
(+1) I recently modified Hoogle to do this, its much more readable, and stops duplicating useless information. The only people who this may confuse are people with less experience, but they should probably use the long options anyway, so it isn't an issue. So agree with all changes. Thanks Neil

On Sun, Jun 29, 2008 at 11:28:54PM +0100, Neil Mitchell wrote:
1. word-wrap the description to fit in 80 columns
(+1). 70 and 80 are defensible, anything else seems a bit of a weird choice for line length...
79 is also defensible, as some terminals wrap if you use the 80th column.

Ross Paterson
On Sun, Jun 29, 2008 at 11:28:54PM +0100, Neil Mitchell wrote:
1. word-wrap the description to fit in 80 columns
(+1). 70 and 80 are defensible, anything else seems a bit of a weird choice for line length...
79 is also defensible, as some terminals wrap if you use the 80th column.
and 72 is also defensible for historical reasons (cobol rather than fortran, I think). And I use 64 for readability reasons at a particular font size. Making it read the terminal width is clearly the best option. -- Jón Fairbairn Jon.Fairbairn@cl.cam.ac.uk http://www.chaos.org.uk/~jf/Stuff-I-dont-want.html (updated 2008-04-26)

On Mon, 30 Jun 2008, Jon Fairbairn wrote:
Making it read the terminal width is clearly the best option.
It should be the maximum of the terminal width and 80 columns.
Tony.
--
f.anthony.n.finch

On 2008 Jun 30, at 15:17, Henning Thielemann wrote:
On Mon, 30 Jun 2008, Tony Finch wrote:
On Mon, 30 Jun 2008, Jon Fairbairn wrote:
Making it read the terminal width is clearly the best option.
It should be the maximum of the terminal width and 80 columns.
Did you mean the _minimum_ ?
Maximum, I suspect. As in "don't go nuts trying to fit it into someone's 16x22 pssh". -- brandon s. allbery [solaris,freebsd,perl,pugs,haskell] allbery@kf8nh.com system administrator [openafs,heimdal,too many hats] allbery@ece.cmu.edu electrical and computer engineering, carnegie mellon university KF8NH

On Mon, 30 Jun 2008, Henning Thielemann wrote:
On Mon, 30 Jun 2008, Tony Finch wrote:
On Mon, 30 Jun 2008, Jon Fairbairn wrote:
Making it read the terminal width is clearly the best option.
It should be the maximum of the terminal width and 80 columns.
Did you mean the _minimum_ ?
Er yes. Oops! It might make sense to apply a minimum too.
Tony.
--
f.anthony.n.finch

Hello Ross, Monday, June 30, 2008, 12:40:28 PM, you wrote:
79 is also defensible, as some terminals wrap if you use the 80th column.
including windows:
type 80
12345678901234567890123456789012345678901234567890123456789012345678901234567890 12345678901234567890123456789012345678901234567890123456789012345678901234567890 12345678901234567890123456789012345678901234567890123456789012345678901234567890 12345678901234567890123456789012345678901234567890123456789012345678901234567890 -- Best regards, Bulat mailto:Bulat.Ziganshin@gmail.com

So if people think this is sensible I'll send three patches: 1. word-wrap the description to fit in 80 columns 2. use less padding between columns 3. omit the args on short options when there is also a corresponding long option
These changes all look good to me. The first one is the most vital, the other two I wouldn't mind either way, if anyone else objects for good reason. Regards, Malcolm

Duncan Coutts wrote:
Actually we go one step further and leave off the args on the short options since they're shown on the long options anyway. I'm not sure everyone wants that so I'm not really proposing it. This is what it looks like:
-f FLAGS --flags=FLAGS Force values for the given... vs -f --flags=FLAGS Force values for the given flags in...
okay..
Similarly:
-v[n] --verbose[=n] Control verbosity (n is 0--3,... vs -v --verbose[=n] Control verbosity (n is 0--3, default...
This confuses me... perhaps because the argument is optional, I don't as easily understand that it's allowed on the short form. What about short-form spacing? is "-v 2" allowed, or only "-v2"? Is this contradictory to the above example where "-f FLAGS" is expected (though probably "-fFLAGS" works too)? maybe these can be resolved somehow without keeping really verbose documentation: what do other people think? Are these cases obscure but nevertheless standard cases of getopt since Unix/GNU history? (e.g. it seems that compilers behave differently from "normal programs", behave differently from everyone else's slightly different implementations of arguments) -Isaac

On 2008 Jun 29, at 19:40, Isaac Dupree wrote:
maybe these can be resolved somehow without keeping really verbose documentation: what do other people think? Are these cases obscure but nevertheless standard cases of getopt since Unix/GNU history? (e.g. it seems that compilers behave differently from "normal programs", behave differently from everyone else's slightly different implementations of arguments)
On of the points of getopt is that -fFLAGS and -f FLAGS both work without any extra code. Standard getopt doesn't do optional arguments at all; GNU getopt handles -vn, -v n, and -v -- or -v -x (for random options -v, -x, the former taking an optional argument; the latter two cases are recognized as argument omitted). (That compilers behave differently is not, IMO, a good thing --- but - l is problematic at best.) -- brandon s. allbery [solaris,freebsd,perl,pugs,haskell] allbery@kf8nh.com system administrator [openafs,heimdal,too many hats] allbery@ece.cmu.edu electrical and computer engineering, carnegie mellon university KF8NH

Brandon S. Allbery KF8NH wrote:
On 2008 Jun 29, at 19:40, Isaac Dupree wrote:
maybe these can be resolved somehow without keeping really verbose documentation: what do other people think? Are these cases obscure but nevertheless standard cases of getopt since Unix/GNU history? (e.g. it seems that compilers behave differently from "normal programs", behave differently from everyone else's slightly different implementations of arguments)
On of the points of getopt is that -fFLAGS and -f FLAGS both work without any extra code. Standard getopt doesn't do optional arguments at all; GNU getopt handles -vn, -v n, and -v -- or -v -x (for random options -v, -x, the former taking an optional argument; the latter two cases are recognized as argument omitted).
ah-ha, so it is an unpleasant behaviour of ambiguity resolution. Oh, well, I don't know what I want. But then saying "-v[n]" to me seems a little more misleading to say than "-v [n]" although they really mean the same thing (what if "-v[n]" makes me think "-v foobar.txt" would pass foobar.txt as a positional argument rather than a verbosity level?). Proposal #3 is to just say "-v". It took me a while to figure out how passing arguments to short and long options worked at all (one requires the presence of "=", the other requires its absence... again, except for exceptions such as ghc), so I might err on the side of being more verbose, less obscure. -Isaac

Brandon S. Allbery KF8NH wrote:
On of the points of getopt is that -fFLAGS and -f FLAGS both work without any extra code. Standard getopt doesn't do optional arguments at all; GNU getopt handles -vn, -v n, and -v -- or -v -x (for random options -v, -x, the former taking an optional argument; the latter two cases are recognized as argument omitted).
and we say cabal uses a getopt equivalent to this? Yet it doesn't work that way for me, in cabal-install 0.5.1:
cabal fetch -v 3 random cabal: Failed to parse package dependency: "3" cabal fetch -v3 random Reading installed packages... ("/usr/bin/ghc-pkg",["list"]) Reading available packages...
(which, incidentally, I was doing to try to find out why 'cabal fetch' doesn't seem to do anything. And failed miserably. No messages, no new directories in ~/.cabal/packages/hackage.haskell.org/ or in ./ ... Oh wait, fetching uvector works. Maybe it's because GHC comes with random-1.0.0.0 so cabal erroneously thinks it already has the source code that I want to look at, i.e. "for later installation *or study*" as `cabal --help` says?) -Isaac

On Wed, 2008-07-02 at 06:40 -0400, Isaac Dupree wrote:
(which, incidentally, I was doing to try to find out why 'cabal fetch' doesn't seem to do anything. And failed miserably. No messages, no new directories in ~/.cabal/packages/hackage.haskell.org/ or in ./ ... Oh wait, fetching uvector works. Maybe it's because GHC comes with random-1.0.0.0 so cabal erroneously thinks it already has the source code that I want to look at, i.e. "for later installation *or study*" as `cabal --help` says?)
http://hackage.haskell.org/trac/hackage/ticket/297 "cabal fetch command don't fetch packages those have already been installed" It shouldn't be too hard to fix, so you're most welcome to send in a patch :-) Duncan

On 2008 Jul 2, at 6:40, Isaac Dupree wrote:
Brandon S. Allbery KF8NH wrote:
On of the points of getopt is that -fFLAGS and -f FLAGS both work without any extra code. Standard getopt doesn't do optional arguments at all; GNU getopt handles -vn, -v n, and -v -- or -v -x (for random options -v, -x, the former taking an optional argument; the latter two cases are recognized as argument omitted).
and we say cabal uses a getopt equivalent to this? Yet it doesn't work that way for me, in
We've apparently reinvented the situation that led to AT&T freeing the original getopt() code, I see :) People who roll their own getopt() clone usually miss the corner cases. -- brandon s. allbery [solaris,freebsd,perl,pugs,haskell] allbery@kf8nh.com system administrator [openafs,heimdal,too many hats] allbery@ece.cmu.edu electrical and computer engineering, carnegie mellon university KF8NH

On 2008 Jul 2, at 6:40, Isaac Dupree wrote:
Brandon S. Allbery KF8NH wrote:
On of the points of getopt is that -fFLAGS and -f FLAGS both work without any extra code. Standard getopt doesn't do optional arguments at all; GNU getopt handles -vn, -v n, and -v -- or -v -x (for random options -v, -x, the former taking an optional argument; the latter two cases are recognized as argument omitted).
and we say cabal uses a getopt equivalent to this? Yet it doesn't work that way for me, in
We've apparently reinvented the situation that led to AT&T freeing the original getopt() code, I see :) People who roll their own getopt() clone usually miss the corner cases.
cabal-install 0.5.1:
cabal fetch -v 3 random cabal: Failed to parse package dependency: "3" cabal fetch -v3 random Reading installed packages... ("/usr/bin/ghc-pkg",["list"]) Reading available packages...
(which, incidentally, I was doing to try to find out why 'cabal fetch' doesn't seem to do anything. And failed miserably. No messages, no new directories in ~/.cabal/packages/ hackage.haskell.org/ or in ./ ... Oh wait, fetching uvector works. Maybe it's because GHC comes with random-1.0.0.0 so cabal erroneously thinks it already has the source code that I want to look at, i.e. "for later installation *or study*" as `cabal --help` says?)
-Isaac
-- brandon s. allbery [solaris,freebsd,perl,pugs,haskell] allbery@kf8nh.com system administrator [openafs,heimdal,too many hats] allbery@ece.cmu.edu electrical and computer engineering, carnegie mellon university KF8NH

On Sun, Jun 29, 2008 at 11:46 PM, Duncan Coutts
So if people think this is sensible I'll send three patches: 1. word-wrap the description to fit in 80 columns 2. use less padding between columns 3. omit the args on short options when there is also a corresponding long option
What does GNU getopt do in all these cases? I'd prefer if the programs I use on the command line are consistent in their `--help' display and GNU getopt seems to be the standard here. Trying to follow what other people do also makes shell scripting easier. -- Johan

On 2008 Jun 30, at 2:00, Johan Tibell wrote:
On Sun, Jun 29, 2008 at 11:46 PM, Duncan Coutts
wrote: So if people think this is sensible I'll send three patches: 1. word-wrap the description to fit in 80 columns 2. use less padding between columns 3. omit the args on short options when there is also a corresponding long option
What does GNU getopt do in all these cases? I'd prefer if the programs I use on the command line are consistent in their `--help' display and GNU getopt seems to be the standard here. Trying to follow what other people do also makes shell scripting easier.
Options are indented by two spaces. Short options are separated from long by ", ", and that column is left empty if there is no corresponding short option. Option descriptions begin one space after the longest option that doesn't extend past column 30, and word wrap with wrapped lines indented by two additional spaces. If any options go past column 30, their descriptions start one space after the options without attempting to align with the other descriptions, except that wrapped lines are aligned. Practical example: GNU sort
Ordering options:
Mandatory arguments to long options are mandatory for short options too. -b, --ignore-leading-blanks ignore leading blanks -d, --dictionary-order consider only blanks and alphanumeric characters -f, --ignore-case fold lower case to upper case characters -g, --general-numeric-sort compare according to general numerical value -i, --ignore-nonprinting consider only printable characters -M, --month-sort compare (unknown) < `JAN' < ... < `DEC' -n, --numeric-sort compare according to string numerical value -r, --reverse reverse the result of comparisons
Other options:
-c, --check check whether input is sorted; do not sort -k, --key=POS1[,POS2] start a key at POS1, end it at POS 2 (origin 1) -m, --merge merge already sorted files; do not sort -o, --output=FILE write result to FILE instead of standard output -s, --stable stabilize sort by disabling last-resort comparison -S, --buffer-size=SIZE use SIZE for main memory buffer -t, --field-separator=SEP use SEP instead of non-blank to blank transition -T, --temporary-directory=DIR use DIR for temporaries, not $TMPDIR or /tmp; multiple options specify multiple directories -u, --unique with -c, check for strict ordering; without -c, output only the first of an equal run -z, --zero-terminated end lines with 0 byte, not newline --help display this help and exit --version output version information and exit
Note --help and --version (no short options; also, they're boilerplate and apparently GNU getopt makes no attempt to match their descriptions' indentations with the other options, instead using a tab) and --temporary-directory (ends at column 31; word wrap matches the following option); also note the option groups are aligned independently. Also note the explanatory text about option arguments before the first option group. -- brandon s. allbery [solaris,freebsd,perl,pugs,haskell] allbery@kf8nh.com system administrator [openafs,heimdal,too many hats] allbery@ece.cmu.edu electrical and computer engineering, carnegie mellon university KF8NH

Brandon S. Allbery KF8NH wrote:
On 2008 Jun 30, at 2:00, Johan Tibell wrote:
On Sun, Jun 29, 2008 at 11:46 PM, Duncan Coutts
wrote: So if people think this is sensible I'll send three patches: 1. word-wrap the description to fit in 80 columns
I agree with others that 79 is better default. (Btw, finding out the terminal width is a IO action, so we can't do that. I like it very much that the API for GetOpt is pure.) Anyway, the maximum output width, as well as a maximum indentation for the option descriptions should be arguments to (new, additional) functions getOptExt and usageInfoExt, with the old getOpt and usageInfo being defined in terms of the new variants.
2. use less padding between columns
Yes, but add a comma, see below.
3. omit the args on short options when there is also a corresponding long option
I think this is an excellent idea.
What does GNU getopt do in all these cases? I'd prefer if the programs I use on the command line are consistent in their `--help' display and GNU getopt seems to be the standard here. Trying to follow what other people do also makes shell scripting easier.
Options are indented by two spaces.
Whitespace shouldn't matter for parsing usage output, so we can deviate from GNU here. I think one space is enough.
Short options are separated from long by ", ",
Here we should follow GNU, I think.
and that column is left empty if there is no corresponding short option.
This looks nice IMO (and we agree with GNU already).
Option descriptions begin one space after the longest option that doesn't extend past column 30, and word wrap with wrapped lines indented by two additional spaces. If any options go past column 30, their descriptions start one space after the options without attempting to align with the other descriptions, except that wrapped lines are aligned.
It makes sense to have a _maximum_ indentation for the description, otherwise one very long option name completely ruins the layout. Following GNU (de facto) standard makes sense here.
Note --help and --version (no short options; also, they're boilerplate and apparently GNU getopt makes no attempt to match their descriptions' indentations with the other options, instead using a tab)
Which is a bug, IMO, we should not copy that. Cheers Ben

Hi
Anyway, the maximum output width, as well as a maximum indentation for the option descriptions should be arguments to (new, additional) functions getOptExt and usageInfoExt, with the old getOpt and usageInfo being defined in terms of the new variants.
I think thats a really bad idea. We want options that work and do the sensible thing. When would a user want to set these options to different values? Without a use case, and a potential user, these extra variants are a waste of hard-drive space. Thanks Neil

Neil Mitchell wrote:
Anyway, the maximum output width, as well as a maximum indentation for the option descriptions should be arguments to (new, additional) functions getOptExt and usageInfoExt, with the old getOpt and usageInfo being defined in terms of the new variants.
I think thats a really bad idea. We want options that work and do the sensible thing. When would a user want to set these options to different values? Without a use case, and a potential user, these extra variants are a waste of hard-drive space.
I thought the use case I had in mind was apparent from the context: finding out the terminal width and then setting the wrap-width accordingly. And the max. description indentation could be set as a percentage of the terminal width. This is all quite similar to what pretty printing libs allow. Generally I think that hard-coding numerical constants in code is nearly always a very bad idea. Much worse than adding a few 100 bytes to the libraries. Cheers Ben

Hi
I think thats a really bad idea. We want options that work and do the sensible thing. When would a user want to set these options to different values? Without a use case, and a potential user, these extra variants are a waste of hard-drive space.
I thought the use case I had in mind was apparent from the context: finding out the terminal width and then setting the wrap-width accordingly. And the max. description indentation could be set as a percentage of the terminal width. This is all quite similar to what pretty printing libs allow.
I'd suggest a much better idea would be to have one function which did all this, in the IO Monad. Some sort of showTheGetOptHelpWithTheRightWidth :: IO (). Before adding a function I think its important to ask "what will this function be used for" - if there are no particularly good answers, then its best not to add it. If it happens to be a general case of something where everyone will want a specific case then the specific case seems more appealing.
Generally I think that hard-coding numerical constants in code is nearly always a very bad idea.
Agreed :-) Thanks Neil

On 2008-07-02, Neil Mitchell
Hi
I think thats a really bad idea. We want options that work and do the sensible thing. When would a user want to set these options to different values? Without a use case, and a potential user, these extra variants are a waste of hard-drive space.
I thought the use case I had in mind was apparent from the context: finding out the terminal width and then setting the wrap-width accordingly. And the max. description indentation could be set as a percentage of the terminal width. This is all quite similar to what pretty printing libs allow.
I'd suggest a much better idea would be to have one function which did all this, in the IO Monad. Some sort of showTheGetOptHelpWithTheRightWidth :: IO (). Before adding a function I think its important to ask "what will this function be used for" - if there are no particularly good answers, then its best not to add it. If it happens to be a general case of something where everyone will want a specific case then the specific case seems more appealing.
Everyone? No one will want to ever put it into a gui widget with a a width that's not 80? Composability and flexibility are cheap in this case. -- Aaron Denney -><-
participants (14)
-
Aaron Denney
-
Benjamin Franksen
-
Brandon S. Allbery KF8NH
-
Brandon S.Allbery KF8NH
-
Bulat Ziganshin
-
Duncan Coutts
-
Henning Thielemann
-
Isaac Dupree
-
Johan Tibell
-
Jon Fairbairn
-
Malcolm Wallace
-
Neil Mitchell
-
Ross Paterson
-
Tony Finch