dotcli: Support nested folder commands.
This commit is contained in:
@@ -17,8 +17,15 @@ This is pure prefactoring: no KDE-specific behavior is introduced here.
|
|||||||
|
|
||||||
## Acceptance criteria
|
## Acceptance criteria
|
||||||
|
|
||||||
- [ ] `dot help` lists a subcommand that lives at `commands/<name>/<name>.fish`
|
- [x] `dot help` lists a subcommand that lives at `commands/<name>/<name>.fish`
|
||||||
- [ ] `dot <name>` sources and dispatches to `commands/<name>/<name>.fish`'s `_dot_<name>` function
|
- [x] `dot <name>` sources and dispatches to `commands/<name>/<name>.fish`'s `_dot_<name>` function
|
||||||
- [ ] Tab-completion (`__dot_custom_subcommands`) lists a nested-directory subcommand
|
- [x] Tab-completion (`__dot_custom_subcommands`) lists a nested-directory subcommand
|
||||||
- [ ] Existing flat-file subcommands (`dot install`) are still discovered and dispatched correctly
|
- [x] Existing flat-file subcommands (`dot install`) are still discovered and dispatched correctly
|
||||||
- [ ] `tests/dot.fish` covers a nested-directory dummy command dispatching correctly, alongside the existing flat-file dispatch case
|
- [x] `tests/dot.fish` covers a nested-directory dummy command dispatching correctly, alongside the existing flat-file dispatch case
|
||||||
|
|
||||||
|
## Implementation Notes
|
||||||
|
|
||||||
|
- The dispatch check in `dot.fish` tries the flat file first, then falls back to `commands/<name>/<name>.fish` — a flat file always wins if both somehow exist for the same name.
|
||||||
|
- The nested-directory scan requires the file basename to match its containing directory's name (`commands/foo/foo.fish`), not just any `.fish` file one level deep — this matches the acceptance criteria's exact convention and avoids misclassifying a stray companion file (e.g. a `.py` helper) as its own subcommand.
|
||||||
|
- Tab-completion's nested-directory listing was verified manually (sourcing `completions/dot.fish` and calling `__dot_custom_subcommands` directly) rather than via an automated test — `tests/dot.fish` has no existing infrastructure for testing completions at all, even for pre-existing flat commands, so adding one here would be out of scope for this prefactoring task.
|
||||||
|
- Updated `CLAUDE.md`'s "Architecture" and "Adding a subcommand" sections to document the new nested-directory convention, since it previously only described the flat-file dispatch contract.
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ shared: fish only autoloads a function from a file named after that function,
|
|||||||
so a helper defined inside `dot.fish` would be undefined if tab-completion
|
so a helper defined inside `dot.fish` would be undefined if tab-completion
|
||||||
ran before `dot` had ever been sourced in the session. Keep both copies in
|
ran before `dot` had ever been sourced in the session. Keep both copies in
|
||||||
sync when the listing logic changes.
|
sync when the listing logic changes.
|
||||||
|
Both copies also glob one directory level deeper, matching `~/.config/dot/commands/<name>/<name>.fish`, so a subcommand's companion file (e.g. a Python helper) can live alongside it in its own directory.
|
||||||
|
|
||||||
`dot init`:
|
`dot init`:
|
||||||
|
|
||||||
@@ -76,7 +77,9 @@ sync when the listing logic changes.
|
|||||||
### Adding a subcommand
|
### Adding a subcommand
|
||||||
|
|
||||||
Beyond `init`, `dot` looks for `~/.config/dot/commands/<name>.fish`, sources
|
Beyond `init`, `dot` looks for `~/.config/dot/commands/<name>.fish`, sources
|
||||||
it, and calls `_dot_<name>`. These files are deliberately kept out of
|
it, and calls `_dot_<name>`.
|
||||||
|
A subcommand needing a companion file can instead live nested one level deeper, as `~/.config/dot/commands/<name>/<name>.fish` — both layouts dispatch identically.
|
||||||
|
These files are deliberately kept out of
|
||||||
`~/.config/fish/functions/` (fish's autoload path) so they never become
|
`~/.config/fish/functions/` (fish's autoload path) so they never become
|
||||||
independently invokable top-level commands or clutter tab-completion outside
|
independently invokable top-level commands or clutter tab-completion outside
|
||||||
of `dot` itself.
|
of `dot` itself.
|
||||||
|
|||||||
@@ -73,6 +73,19 @@ end" >$HOME/.config/dot/commands/mark.fish
|
|||||||
dot mark >/dev/null 2>&1
|
dot mark >/dev/null 2>&1
|
||||||
@test "dispatches to a command file under ~/.config/dot/commands/" (cat $marker) = marked
|
@test "dispatches to a command file under ~/.config/dot/commands/" (cat $marker) = marked
|
||||||
|
|
||||||
|
# --- dispatches to a nested commands/<name>/<name>.fish, same as a flat file
|
||||||
|
set -gx HOME (mktemp -d)
|
||||||
|
dot init --url $remote >/dev/null 2>&1
|
||||||
|
|
||||||
|
mkdir -p $HOME/.config/dot/commands/nested
|
||||||
|
set -l nested_marker (mktemp)
|
||||||
|
echo "function _dot_nested
|
||||||
|
echo nested-marked >$nested_marker
|
||||||
|
end" >$HOME/.config/dot/commands/nested/nested.fish
|
||||||
|
|
||||||
|
dot nested >/dev/null 2>&1
|
||||||
|
@test "dispatches to a nested commands/<name>/<name>.fish" (cat $nested_marker) = nested-marked
|
||||||
|
|
||||||
# --- dot help ---
|
# --- dot help ---
|
||||||
set -gx HOME (mktemp -d)
|
set -gx HOME (mktemp -d)
|
||||||
dot init --url $remote >/dev/null 2>&1
|
dot init --url $remote >/dev/null 2>&1
|
||||||
@@ -93,6 +106,14 @@ end" >$HOME/.config/dot/commands/mark.fish
|
|||||||
set -l help_with_custom (dot help)
|
set -l help_with_custom (dot help)
|
||||||
@test "dot help lists custom commands found under ~/.config/dot/commands/" (string match -q '*mark*' -- $help_with_custom; echo $status) -eq 0
|
@test "dot help lists custom commands found under ~/.config/dot/commands/" (string match -q '*mark*' -- $help_with_custom; echo $status) -eq 0
|
||||||
|
|
||||||
|
mkdir -p $HOME/.config/dot/commands/nested
|
||||||
|
echo "function _dot_nested
|
||||||
|
echo nested
|
||||||
|
end" >$HOME/.config/dot/commands/nested/nested.fish
|
||||||
|
|
||||||
|
set -l help_with_nested (dot help)
|
||||||
|
@test "dot help lists a nested-directory subcommand" (string match -q '*nested*' -- $help_with_nested; echo $status) -eq 0
|
||||||
|
|
||||||
# --- dot install ---
|
# --- dot install ---
|
||||||
# pacman and sudo are faked out via a bin dir prepended to PATH: sudo just
|
# pacman and sudo are faked out via a bin dir prepended to PATH: sudo just
|
||||||
# execs its arguments, and pacman logs each invocation to $PACMAN_LOG (one
|
# execs its arguments, and pacman logs each invocation to $PACMAN_LOG (one
|
||||||
|
|||||||
@@ -2,6 +2,13 @@ function __dot_custom_subcommands
|
|||||||
echo init
|
echo init
|
||||||
echo help
|
echo help
|
||||||
path basename $HOME/.config/dot/commands/*.fish 2>/dev/null | path change-extension ''
|
path basename $HOME/.config/dot/commands/*.fish 2>/dev/null | path change-extension ''
|
||||||
|
|
||||||
|
for d in $HOME/.config/dot/commands/*/
|
||||||
|
test -d $d; or continue
|
||||||
|
set -l name (path basename $d)
|
||||||
|
test -f $d$name.fish; or continue
|
||||||
|
echo $name
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
complete -c dot -n __fish_use_subcommand -a "(__dot_custom_subcommands)"
|
complete -c dot -n __fish_use_subcommand -a "(__dot_custom_subcommands)"
|
||||||
|
|||||||
@@ -14,11 +14,18 @@ function dot --wraps=git --description 'Manage dotfiles via a bare repo checked
|
|||||||
|
|
||||||
set -l commands_dir $HOME/.config/dot/commands
|
set -l commands_dir $HOME/.config/dot/commands
|
||||||
set -l command_file $commands_dir/$argv[1].fish
|
set -l command_file $commands_dir/$argv[1].fish
|
||||||
|
set -l nested_command_file $commands_dir/$argv[1]/$argv[1].fish
|
||||||
|
|
||||||
if test -n "$argv[1]" -a -f "$command_file"
|
if test -n "$argv[1]"
|
||||||
|
if test -f "$command_file"
|
||||||
source $command_file
|
source $command_file
|
||||||
_dot_$argv[1] $argv[2..-1]
|
_dot_$argv[1] $argv[2..-1]
|
||||||
return $status
|
return $status
|
||||||
|
else if test -f "$nested_command_file"
|
||||||
|
source $nested_command_file
|
||||||
|
_dot_$argv[1] $argv[2..-1]
|
||||||
|
return $status
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
git --git-dir=$dotfiles_dir --work-tree=$HOME $argv
|
git --git-dir=$dotfiles_dir --work-tree=$HOME $argv
|
||||||
@@ -109,6 +116,13 @@ Commands:
|
|||||||
echo " "(path basename $f | path change-extension '')
|
echo " "(path basename $f | path change-extension '')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
for d in $HOME/.config/dot/commands/*/
|
||||||
|
test -d $d; or continue
|
||||||
|
set -l name (path basename $d)
|
||||||
|
test -f $d$name.fish; or continue
|
||||||
|
echo " $name"
|
||||||
|
end
|
||||||
|
|
||||||
echo "
|
echo "
|
||||||
Run 'dot <command> help' for flags on a specific command.
|
Run 'dot <command> help' for flags on a specific command.
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user