Inconsistent user intention guesses in error messages between single-dash option name vs. double-dash option name
System.CommandLine version: 2.0.0-beta6.25320.118
Having a CLI with only a single-dash option -s that accepts exactly one value, and providing a commandline -s foo bar with an superfluous value "bar" causes this error message:
Commandline: -s foo bar
'bar' was not matched. Did you mean one of the following?
-h
-s
Unrecognized command or argument 'bar'.
Note that the suggestion/guess about the users intention is -h followed by -s.
Code with single-dash option name:
var singleDashOption = new Option<string>("-s") { Arity = ArgumentArity.ExactlyOne };
var rootCmd = new RootCommand
{
singleDashOption
};
rootCmd.SetAction(parseResult => Console.WriteLine($"Value: {parseResult.GetValue(singleDashOption)}"));
var cmdLine = "-s foo bar";
Console.WriteLine($"Commandline: {cmdLine}");
rootCmd.Parse(cmdLine).Invoke();
However, now change the option name from the single-dash name -s to the double-dash name --str, and the error message is guessing a different user intention:
Commandline: --str foo bar
'bar' was not matched. Did you mean one of the following?
-h
Unrecognized command or argument 'bar'.
Note the absence of --str in the guess of the user intention, which looks inconsistent with the error message given when the option was having a single-dash name.
Same code, but with double-dash option name:
var doubleDashOption = new Option<string>("--str") { Arity = ArgumentArity.ExactlyOne };
var rootCmd = new RootCommand
{
doubleDashOption
};
rootCmd.SetAction(parseResult => Console.WriteLine($"Value: {parseResult.GetValue(doubleDashOption)}"));
var cmdLine = "--str foo bar";
Console.WriteLine($"Commandline: {cmdLine}");
rootCmd.Parse(cmdLine).Invoke();
Update:
For completeness sake, these are the user intention guesses in the error messages for option names --s and -str as well:
Commandline: --s foo bar
'bar' was not matched. Did you mean one of the following?
-h
--s
Commandline: -str foo bar
'bar' was not matched. Did you mean one of the following?
-h
-str
Thus, getting the (slightly) different user intention guess requires the option name to be double-dash and consist of multiple letters.
The guesses are based on the Levenshtein distance between what was typed and the names of the existing identifier symbols that might be valid at that position. For a parser with a small number of commands and options, the top suggestions have the potential to still be very distant from what you typed.
I wonder if it would be reasonable to have the maximum Levenshtein distance vary based on the number of identifiers in the parser. That might make this a little less surprising.
https://github.com/dotnet/command-line-api/blob/b805d096b6caabbabc321ff95425a6bf0de45d90/src/System.CommandLine/Invocation/TypoCorrection.cs#L11
@Keboo
Hmm, using the pure Levenshtein distance compared against a fixed threshold will not provide a useful sense of similarity when rather short strings are measured against. Neither -s, -str, --s, --str nor -h could be considered similar to bar in any meaningful sense.
Modulating or weighing the result of the measurement based on the number of identifiers (i understand this as the number of defined command and option names and aliases) will not really give good results, i am afraid, because the dissimilarity between for example -s and bar is unrelated to and does not change based on the number of possible options and commands.
I guess to make the guesses less sensitive to the low distance values caused by measuring two short strings against each other, the distance and/or the threshold value would have to be normalized or weighted in some way against the length of the larger string measured against, or perhaps against the length difference between the two measured strings. (Just a rough idea based solely on intuition; i have no deeper insights or experimental data to back up this idea.)
It may be worth doing a C# port of this python function as I believe this is what the Azure CLI uses for its suggestions (one of the inspirations for this feature).
UPDATE: Tested the issue against 2.0.0-beta6.25320.118 and updated the report.