Skip to content

Commit

Permalink
✨ single letter options
Browse files Browse the repository at this point in the history
  • Loading branch information
jcaillon committed Jun 26, 2024
1 parent b8d4f75 commit 9b5d4e4
Show file tree
Hide file tree
Showing 14 changed files with 446 additions and 146 deletions.
6 changes: 4 additions & 2 deletions docs/content/docs/800.roadmap/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ url: /docs/roadmap

This page lists the features that I would like to implement in Valet. They come in addition to new features described in the [issues][valet-issues].

- fix option parsing for self build
- Improve the self install script / check for updates by comparing the version number / suggest the user to git pull the repositories existing under .valet.d. Also add snippets and all functions...
- rebuild
- add path to valet on windows as well
- Add full support for interactive mode.
- For dropdown with a set list of options, we can verify that the input value is one of the expected value.
- Generate an autocompletion script for bash and zsh.
Expand All @@ -24,9 +28,7 @@ This page lists the features that I would like to implement in Valet. They come
- A command can declare dependencies to auto check if some tools are installed before running the command.
- Add test:: and also add a snippet to create a new function.
- add valet in brew
- Improve the self install script / check for updates by comparing the version number / suggest the user to git pull the repositories existing under .valet.d. Also add snippets and all functions...
- For argument and option autocompletion, accept any multiline string that will be eval and that should set RETURNED_ARRAY with the list of possible completion.
- Allow to regroup single letter options (e.g. -fsSL).


[valet-issues]: https://github.com/jcaillon/valet/issues
57 changes: 43 additions & 14 deletions tests.d/1001-main-functions/02.arguments-parser.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,57 +2,86 @@

function testMain::parseFunctionArguments() {

# missing argument
echo "# missing argument"
echo "→ main::parseFunctionArguments selfMock2"
main::parseFunctionArguments selfMock2 && echo "${RETURNED_VALUE}"
echo

# ok
echo "# ok"
echo "→ main::parseFunctionArguments selfMock2 -o -2 optionValue2 arg1 more1 more2"
main::parseFunctionArguments selfMock2 -o -2 optionValue2 arg1 more1 more2 && echo "${RETURNED_VALUE}"
echo

# missing argument
echo "# missing argument"
echo "→ main::parseFunctionArguments selfMock2 -o -2 optionValue2 arg1"
main::parseFunctionArguments selfMock2 -o -2 optionValue2 arg1 && echo "${RETURNED_VALUE}"
echo

# unknown options
echo "→ main::parseFunctionArguments selfMock2 -unknown -what optionValue2 arg"
main::parseFunctionArguments selfMock2 -unknown -what optionValue2 arg && echo "${RETURNED_VALUE}"
echo "# unknown options"
echo "→ main::parseFunctionArguments selfMock2 --unknown --what optionValue2 arg"
main::parseFunctionArguments selfMock2 --unknown --what optionValue2 arg && echo "${RETURNED_VALUE}"
echo

# ok with the option at the end
echo "# ok with the option at the end"
echo "→ main::parseFunctionArguments selfMock2 arg more1 more2 -o"
main::parseFunctionArguments selfMock2 arg more1 more2 -o && echo "${RETURNED_VALUE}"
echo

# fuzzy match the option -this
echo "→ main::parseFunctionArguments selfMock2 -this arg more1"
main::parseFunctionArguments selfMock2 -this arg more1 && echo "${RETURNED_VALUE}"
echo "# fuzzy match the option --this"
echo "→ main::parseFunctionArguments selfMock2 --this arg more1"
main::parseFunctionArguments selfMock2 --this arg more1 && echo "${RETURNED_VALUE}"
echo

# ok, --option1 is interpreted as the value for --this-is-option2
echo "# ok, --option1 is interpreted as the value for --this-is-option2"
echo "→ main::parseFunctionArguments selfMock2 --this-is-option2 --option1 arg more1"
main::parseFunctionArguments selfMock2 --this-is-option2 --option1 arg more1 && echo "${RETURNED_VALUE}"
echo

# ok only args
echo "# ok only args"
echo "→ main::parseFunctionArguments selfMock4 arg1 arg2"
main::parseFunctionArguments selfMock4 arg1 arg2 && echo "${RETURNED_VALUE}"
echo

# ok with -- to separate options from args
echo "# ok with -- to separate options from args"
echo "→ main::parseFunctionArguments selfMock2 -- --arg1-- --arg2--"
main::parseFunctionArguments selfMock2 -- --arg1-- --arg2-- && echo "${RETURNED_VALUE}"
echo

echo "# missing a value for the option 2"
echo "→ main::parseFunctionArguments selfMock2 arg1 arg2 --this-is-option2"
main::parseFunctionArguments selfMock2 arg1 arg2 --this-is-option2 && echo "${RETURNED_VALUE}"
echo

# ambiguous fuzzy match
echo "# ambiguous fuzzy match"
echo "→ main::parseFunctionArguments selfMock2 arg1 arg2 --th"
main::parseFunctionArguments selfMock2 arg1 arg2 --th && echo "${RETURNED_VALUE}"
echo

echo "# ok single letter options grouped together"
echo "→ main::parseFunctionArguments selfMock2 -o3 allo1 allo2 allo3 allo4"
main::parseFunctionArguments selfMock2 -o3 allo1 allo2 allo3 allo4 && echo "${RETURNED_VALUE}"
echo

echo "# ok single letter options, consume argument as option values"
echo "→ main::parseFunctionArguments selfMock2 -o243 allo1 allo2 allo3 allo4"
main::parseFunctionArguments selfMock2 -o243 allo1 allo2 allo3 allo4 && echo "${RETURNED_VALUE}"
echo

echo "# ko, single letter options, invalid one"
echo "→ main::parseFunctionArguments selfMock2 -3ao allo1 allo2"
main::parseFunctionArguments selfMock2 -3ao allo1 allo2 && echo "${RETURNED_VALUE}"
echo

echo "# ko, missing a value for the option 4"
echo "→ main::parseFunctionArguments selfMock2 arg1 arg2 -4"
main::parseFunctionArguments selfMock2 arg1 arg2 -4 && echo "${RETURNED_VALUE}"
echo

echo "# ko, missing multiple values in a group"
echo "→ main::parseFunctionArguments selfMock2 arg1 arg2 -4444"
main::parseFunctionArguments selfMock2 arg1 arg2 -4444 && echo "${RETURNED_VALUE}"
echo

test::endTest "Testing main::parseFunctionArguments" 0
}

Expand Down
8 changes: 8 additions & 0 deletions tests.d/1001-main-functions/99.tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -80,11 +80,19 @@ function testFuzzyFindOption() {
test::endTest "Testing main::fuzzyFindOption" 0
}

function testMainGetSingleLetterOptions() {
echo "→ main::getSingleLetterOptions -a --opt1 --derp2 -b --allo3 -c"
main::getSingleLetterOptions -a --opt1 --derp2 -b --allo3 -c && echo "${RETURNED_VALUE}"

test::endTest "Testing main::getSingleLetterOptions" 0
}

function main() {
testGetFunctionNameFromCommand
testFuzzyMatchCommandtoFunctionName
testGetMaxPossibleCommandLevel
testFuzzyFindOption
testMainGetSingleLetterOptions
}

main
141 changes: 126 additions & 15 deletions tests.d/1001-main-functions/results.approved.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ Exit code: `0`
**Standard** output:

```plaintext
# missing argument
→ main::parseFunctionArguments selfMock2
local parsingErrors option1 thisIsOption2 flag3 withDefault help firstArg
local -a more
Expand All @@ -121,6 +122,7 @@ valet [global options] self mock2 [options] [--] <firstArg> <more...>"
more=(
)
# ok
→ main::parseFunctionArguments selfMock2 -o -2 optionValue2 arg1 more1 more2
local parsingErrors option1 thisIsOption2 flag3 withDefault help firstArg
local -a more
Expand All @@ -136,6 +138,7 @@ more=(
"more2"
)
# missing argument
→ main::parseFunctionArguments selfMock2 -o -2 optionValue2 arg1
local parsingErrors option1 thisIsOption2 flag3 withDefault help firstArg
local -a more
Expand All @@ -153,24 +156,20 @@ firstArg="arg1"
more=(
)
→ main::parseFunctionArguments selfMock2 -unknown -what optionValue2 arg
# unknown options
→ main::parseFunctionArguments selfMock2 --unknown --what optionValue2 arg
local parsingErrors option1 thisIsOption2 flag3 withDefault help firstArg
local -a more
option1=""
thisIsOption2="${VALET_THIS_IS_OPTION2:-}"
flag3="${VALET_FLAG3:-}"
help=""
parsingErrors="Unknown option ⌜-unknown⌝, valid options are:
-o
--option1
-2
--this-is-option2
-3
--flag3
-4
--with-default
-h
--help
parsingErrors="Unknown option ⌜--unknown⌝, valid options are:
-o --option1
-2 --this-is-option2
-3 --flag3
-4 --with-default
-h --help
Expecting ⌜2⌝ argument(s) but got ⌜1⌝.
Use ⌜valet self mock2 --help⌝ to get help.
Expand All @@ -181,6 +180,7 @@ firstArg="arg"
more=(
)
# ok with the option at the end
→ main::parseFunctionArguments selfMock2 arg more1 more2 -o
local parsingErrors option1 thisIsOption2 flag3 withDefault help firstArg
local -a more
Expand All @@ -196,7 +196,8 @@ more=(
"more2"
)
→ main::parseFunctionArguments selfMock2 -this arg more1
# fuzzy match the option --this
→ main::parseFunctionArguments selfMock2 --this arg more1
local parsingErrors option1 thisIsOption2 flag3 withDefault help firstArg
local -a more
option1=""
Expand All @@ -213,6 +214,7 @@ firstArg="more1"
more=(
)
# ok, --option1 is interpreted as the value for --this-is-option2
→ main::parseFunctionArguments selfMock2 --this-is-option2 --option1 arg more1
local parsingErrors option1 thisIsOption2 flag3 withDefault help firstArg
local -a more
Expand All @@ -227,6 +229,7 @@ more=(
"more1"
)
# ok only args
→ main::parseFunctionArguments selfMock4 arg1 arg2
local parsingErrors help firstArg secondArg
help=""
Expand All @@ -235,6 +238,7 @@ firstArg="arg1"
secondArg="arg2"
# ok with -- to separate options from args
→ main::parseFunctionArguments selfMock2 -- --arg1-- --arg2--
local parsingErrors option1 thisIsOption2 flag3 withDefault help firstArg
local -a more
Expand All @@ -249,7 +253,22 @@ more=(
"--arg2--"
)
# missing a value for the option 2
→ main::parseFunctionArguments selfMock2 arg1 arg2 --this-is-option2
local parsingErrors option1 thisIsOption2 flag3 withDefault help firstArg
local -a more
option1=""
flag3="${VALET_FLAG3:-}"
withDefault="${VALET_WITH_DEFAULT:-"cool"}"
help=""
parsingErrors="Missing value for option ⌜thisIsOption2⌝.
Use ⌜valet self mock2 --help⌝ to get help."
firstArg="arg1"
more=(
"arg2"
)
# ambiguous fuzzy match
→ main::parseFunctionArguments selfMock2 arg1 arg2 --th
local parsingErrors option1 thisIsOption2 flag3 withDefault help firstArg
local -a more
Expand All @@ -267,13 +286,94 @@ more=(
"arg2"
)
# ok single letter options grouped together
→ main::parseFunctionArguments selfMock2 -o3 allo1 allo2 allo3 allo4
local parsingErrors option1 thisIsOption2 flag3 withDefault help firstArg
local -a more
thisIsOption2="${VALET_THIS_IS_OPTION2:-}"
withDefault="${VALET_WITH_DEFAULT:-"cool"}"
help=""
parsingErrors=""
option1="true"
flag3="true"
firstArg="allo1"
more=(
"allo2"
"allo3"
"allo4"
)
# ok single letter options, consume argument as option values
→ main::parseFunctionArguments selfMock2 -o243 allo1 allo2 allo3 allo4
local parsingErrors option1 thisIsOption2 flag3 withDefault help firstArg
local -a more
help=""
parsingErrors=""
option1="true"
thisIsOption2="allo1"
withDefault="allo2"
flag3="true"
firstArg="allo3"
more=(
"allo4"
)
# ko, single letter options, invalid one
→ main::parseFunctionArguments selfMock2 -3ao allo1 allo2
local parsingErrors option1 thisIsOption2 flag3 withDefault help firstArg
local -a more
thisIsOption2="${VALET_THIS_IS_OPTION2:-}"
withDefault="${VALET_WITH_DEFAULT:-"cool"}"
help=""
parsingErrors="Unknown option letter ⌜a⌝ in group ⌜-3ao⌝. Valid single letter options are: ⌜o⌝, ⌜2⌝, ⌜3⌝, ⌜4⌝, ⌜h⌝.
Use ⌜valet self mock2 --help⌝ to get help."
flag3="true"
option1="true"
firstArg="allo1"
more=(
"allo2"
)
# ko, missing a value for the option 4
→ main::parseFunctionArguments selfMock2 arg1 arg2 -4
local parsingErrors option1 thisIsOption2 flag3 withDefault help firstArg
local -a more
option1=""
thisIsOption2="${VALET_THIS_IS_OPTION2:-}"
flag3="${VALET_FLAG3:-}"
help=""
parsingErrors="Missing value for option ⌜withDefault⌝.
Use ⌜valet self mock2 --help⌝ to get help."
firstArg="arg1"
more=(
"arg2"
)
# ko, missing multiple values in a group
→ main::parseFunctionArguments selfMock2 arg1 arg2 -4444
local parsingErrors option1 thisIsOption2 flag3 withDefault help firstArg
local -a more
option1=""
thisIsOption2="${VALET_THIS_IS_OPTION2:-}"
flag3="${VALET_FLAG3:-}"
help=""
parsingErrors="Missing value for option ⌜withDefault⌝.
Missing value for option ⌜withDefault⌝.
Missing value for option ⌜withDefault⌝.
Missing value for option ⌜withDefault⌝.
Use ⌜valet self mock2 --help⌝ to get help."
firstArg="arg1"
more=(
"arg2"
)
```

**Error** output:

```log
INFO Fuzzy matching the option ⌜-what⌝ to ⌜--with-default⌝.
INFO Fuzzy matching the option ⌜-this⌝ to ⌜--this-is-option2⌝.
INFO Fuzzy matching the option ⌜--what⌝ to ⌜--with-default⌝.
INFO Fuzzy matching the option ⌜--this⌝ to ⌜--this-is-option2⌝.
```

## Test script 99.tests
Expand Down Expand Up @@ -388,3 +488,14 @@ Unknown option ⌜thing⌝, valid options are:
INFO Fuzzy matching the option ⌜de⌝ to ⌜--derp2⌝.
```

### Testing main::getSingleLetterOptions

Exit code: `0`

**Standard** output:

```plaintext
→ main::getSingleLetterOptions -a --opt1 --derp2 -b --allo3 -c
Valid single letter options are: ⌜a⌝, ⌜b⌝, ⌜c⌝.
```

2 changes: 1 addition & 1 deletion tests.d/1300-valet-cli/01.command-help.sh
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ function testHelp() {

# test that we catch option errors
echo "→ valet help --unknown -colo"
("${GLOBAL_VALET_HOME}/valet" help --unknown -colo) || echo "Failed as expected."
("${GLOBAL_VALET_HOME}/valet" help --unknown --colo) || echo "Failed as expected."
test::endTest "Testing that we catch option errors in help" 1

# test that no arguments show the valet help
Expand Down
Loading

0 comments on commit 9b5d4e4

Please sign in to comment.