From 281d468285f3d6f0a9c22afb315bd80bacdc4afd Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Fri, 8 Sep 2017 18:11:43 +0200 Subject: [PATCH] Rewrite systemctl completion with argparse This - Offers more candidates - Is more reactive (it'll always incorporate "--state=" and "--type=" - Is faster (about 800ms to about 120ms) - Needs fewer function files All __fish_systemctl_* functions except __fish_systemctl_services have been removed. --- share/completions/systemctl.fish | 80 ++++-------------- .../__fish_systemctl_automounts.fish | 9 -- share/functions/__fish_systemctl_devices.fish | 11 --- share/functions/__fish_systemctl_mounts.fish | 9 -- share/functions/__fish_systemctl_scopes.fish | 11 --- .../__fish_systemctl_service_paths.fish | 9 -- share/functions/__fish_systemctl_slices.fish | 11 --- .../functions/__fish_systemctl_snapshots.fish | 12 --- share/functions/__fish_systemctl_sockets.fish | 9 -- share/functions/__fish_systemctl_swaps.fish | 9 -- share/functions/__fish_systemctl_targets.fish | 9 -- share/functions/__fish_systemctl_timers.fish | 9 -- share/functions/_fish_systemctl.fish | 84 +++++++++++++++++++ 13 files changed, 101 insertions(+), 171 deletions(-) delete mode 100644 share/functions/__fish_systemctl_automounts.fish delete mode 100644 share/functions/__fish_systemctl_devices.fish delete mode 100644 share/functions/__fish_systemctl_mounts.fish delete mode 100644 share/functions/__fish_systemctl_scopes.fish delete mode 100644 share/functions/__fish_systemctl_service_paths.fish delete mode 100644 share/functions/__fish_systemctl_slices.fish delete mode 100644 share/functions/__fish_systemctl_snapshots.fish delete mode 100644 share/functions/__fish_systemctl_sockets.fish delete mode 100644 share/functions/__fish_systemctl_swaps.fish delete mode 100644 share/functions/__fish_systemctl_targets.fish delete mode 100644 share/functions/__fish_systemctl_timers.fish create mode 100644 share/functions/_fish_systemctl.fish diff --git a/share/completions/systemctl.fish b/share/completions/systemctl.fish index 1a949088b..42e204b34 100644 --- a/share/completions/systemctl.fish +++ b/share/completions/systemctl.fish @@ -1,5 +1,9 @@ set -l systemd_version (systemctl --version | string match "systemd*" | string replace -r "\D*(\d+)" '$1') -set -l commands list-units list-sockets start stop reload restart try-restart reload-or-restart reload-or-try-restart isolate kill is-active is-failed status show get-cgroup-attr set-cgroup-attr unset-cgroup-attr set-cgroup help reset-failed list-unit-files enable disable is-enabled reenable preset mask unmask link load list-jobs cancel dump list-dependencies snapshot delete daemon-reload daemon-reexec show-environment set-environment unset-environment default rescue emergency halt poweroff reboot kexec exit suspend hibernate hybrid-sleep switch-root +set -l commands list-units list-sockets start stop reload restart try-restart reload-or-restart reload-or-try-restart \ +isolate kill is-active is-failed status show get-cgroup-attr set-cgroup-attr unset-cgroup-attr set-cgroup help \ +reset-failed list-unit-files enable disable is-enabled reenable preset mask unmask link load list-jobs cancel dump \ +list-dependencies snapshot delete daemon-reload daemon-reexec show-environment set-environment unset-environment \ +default rescue emergency halt poweroff reboot kexec exit suspend hibernate hybrid-sleep switch-root if test $systemd_version -gt 208 set commands $commands cat if test $systemd_version -gt 217 @@ -9,29 +13,15 @@ end set -l types services sockets mounts service_paths targets automounts timers function __fish_systemd_properties - if type -q /usr/lib/systemd/systemd - /usr/lib/systemd/systemd --dump-configuration-items | string split -m1 = | while read key value - test -n "$value" - and echo $key - end - else if type -q /lib/systemd/systemd # Debian has not merged /lib and /usr/lib - /lib/systemd/systemd --dump-configuration-items | string split -m1 = | while read key value - test -n "$value" - and echo $key - end + # We need to call the main systemd binary (the thing that is run as PID1). + # Unfortunately, it's usually not in $PATH. + if test -f /usr/lib/systemd/systemd + /usr/lib/systemd/systemd --dump-configuration-items | string replace -rf '(.+)=(.+)$' '$1\t$2' + else if test -f /lib/systemd/systemd # Debian has not merged /lib and /usr/lib + /lib/systemd/systemd --dump-configuration-items | string replace -rf '(.+)=(.+)$' '$1\t$2' end end -function __fish_systemctl_failed - if __fish_contains_opt user - # Without arguments, no "--type=" will be passed - systemctl --user list-units --state=failed --no-legend --type=$argv ^/dev/null | cut -f 1 -d ' ' - else - systemctl list-units --state=failed --no-legend --type=$argv ^/dev/null | cut -f 1 -d ' ' - end -end - -complete -f -e -c systemctl # All systemctl commands complete -f -c systemctl -n "not __fish_seen_subcommand_from $commands" -a "$commands" @@ -45,49 +35,13 @@ complete -f -c systemctl -n "not __fish_seen_subcommand_from $commands" -a disab complete -f -c systemctl -n "not __fish_seen_subcommand_from $commands" -a isolate -d 'Start a unit and dependencies and disable all others' complete -f -c systemctl -n "not __fish_seen_subcommand_from $commands" -a set-default -d 'Set the default target to boot into' -set -l commands_types start stop restart try-restart reload-or-restart reload-or-try-restart is-active is-failed is-enabled reenable mask loaded link list-dependencies show status +# Command completion done via argparse. +complete -c systemctl -a '(_fish_systemctl)' -f -if test $systemd_version -gt 208 - complete -f -c systemctl -n "not __fish_seen_subcommand_from $commands" -a cat -d 'Show a unit' - set commands_types $commands_types cat - if test $systemd_version -gt 217 - complete -f -c systemctl -n "not __fish_seen_subcommand_from $commands" -a edit -d 'Edit a unit' - set commands_types $commands_types edit - end -end - -for command in $commands_types - for t in $types - complete -f -c systemctl -n "__fish_seen_subcommand_from $command" -a "(eval __fish_systemctl_$t)" - end -end - -# Handle reset-failed specially because it doesn't apply to unit-files (only units that have been tried can have failed) and a second "--state=" argument doesn't override the earlier one. -complete -f -c systemctl -n "__fish_seen_subcommand_from reset-failed" -a "(__fish_systemctl_failed)" - -# Enable/Disable: Only show units with matching state -for t in services sockets timers service_paths - complete -f -c systemctl -n "__fish_seen_subcommand_from enable" -a "(eval __fish_systemctl_$t --state=disabled)" - complete -f -c systemctl -n "__fish_seen_subcommand_from disable" -a "(eval __fish_systemctl_$t --state=enabled)" -end - -# These are useless for the other commands -# .device in particular creates too much noise -for t in devices slices scopes swaps - for command in status show list-dependencies - complete -f -c systemctl -n "__fish_seen_subcommand_from $command" -a "(eval __fish_systemctl_$t)" - end -end - -complete -f -c systemctl -n "__fish_seen_subcommand_from isolate" -a '(__fish_systemctl_targets)' -d 'Target' -complete -f -c systemctl -n "__fish_seen_subcommand_from isolate" -a '(__fish_systemctl_snapshots)' -d 'Snapshot' - -complete -f -c systemctl -n "__fish_seen_subcommand_from set-default" -a '(__fish_systemctl_targets)' -d 'Target' -complete -f -c systemctl -n "__fish_seen_subcommand_from set-default" -a '(__fish_systemctl_services)' -d 'Service' - -complete -f -c systemctl -s t -l type -d 'List of unit types' -xa 'service mount socket target slice scope swap snapshot automount timer path' -complete -f -c systemctl -l state -d 'List of unit states' -xa 'LOAD, SUB, ACTIVE,' -complete -f -c systemctl -s p -l property -d 'Properties displayed in the "show" command' -a '(__fish_systemd_properties)' +# These "--x=help" outputs always have lines like "Available unit types:". We use the fact that they end in a ":" to filter them out. +complete -f -c systemctl -s t -l type -d 'List of unit types' -xa '(systemctl --type=help --no-legend --no-pager | string match -v "*:")' +complete -f -c systemctl -l state -d 'List of unit states' -xa '(systemctl --state=help --no-legend --no-pager | string match -v "*:")' +complete -f -c systemctl -s p -l property -a '(__fish_systemd_properties)' complete -f -c systemctl -s a -l all -d 'Show all units or properties' complete -f -c systemctl -s r -l recursive -d 'Show also units of local containers' complete -f -c systemctl -l reverse -d 'Show reverse dependencies between units' diff --git a/share/functions/__fish_systemctl_automounts.fish b/share/functions/__fish_systemctl_automounts.fish deleted file mode 100644 index e62b8afc5..000000000 --- a/share/functions/__fish_systemctl_automounts.fish +++ /dev/null @@ -1,9 +0,0 @@ -function __fish_systemctl_automounts - if type -q systemctl - if __fish_contains_opt user - systemctl --user list-unit-files --no-legend --type=automount ^/dev/null | cut -f 1 -d ' ' - else - systemctl list-unit-files --no-legend --type=automount ^/dev/null | cut -f 1 -d ' ' - end - end -end diff --git a/share/functions/__fish_systemctl_devices.fish b/share/functions/__fish_systemctl_devices.fish deleted file mode 100644 index 082e65d7d..000000000 --- a/share/functions/__fish_systemctl_devices.fish +++ /dev/null @@ -1,11 +0,0 @@ -function __fish_systemctl_devices - if type -q systemctl - if __fish_contains_opt user - # Devices are usually generated at runtime - # Therefore show known _units_, not unit-files - systemctl --user list-units --no-legend --type=device ^/dev/null | cut -f 1 -d ' ' - else - systemctl list-units --no-legend --type=device ^/dev/null | cut -f 1 -d ' ' - end - end -end diff --git a/share/functions/__fish_systemctl_mounts.fish b/share/functions/__fish_systemctl_mounts.fish deleted file mode 100644 index feac7b1a6..000000000 --- a/share/functions/__fish_systemctl_mounts.fish +++ /dev/null @@ -1,9 +0,0 @@ -function __fish_systemctl_mounts - if type -q systemctl - if __fish_contains_opt user - systemctl --user list-unit-files --no-legend --type=mount ^/dev/null | cut -f 1 -d ' ' - else - systemctl list-unit-files --no-legend --type=mount ^/dev/null | cut -f 1 -d ' ' - end - end -end diff --git a/share/functions/__fish_systemctl_scopes.fish b/share/functions/__fish_systemctl_scopes.fish deleted file mode 100644 index 3b705718b..000000000 --- a/share/functions/__fish_systemctl_scopes.fish +++ /dev/null @@ -1,11 +0,0 @@ -function __fish_systemctl_scopes - if type -q systemctl - if __fish_contains_opt user - # Scopes are usually generated at runtime - # Therefore show known _units_, not unit-files - systemctl --user list-units --no-legend --type=scope ^/dev/null | cut -f 1 -d ' ' - else - systemctl list-units --no-legend --type=scope ^/dev/null | cut -f 1 -d ' ' - end - end -end diff --git a/share/functions/__fish_systemctl_service_paths.fish b/share/functions/__fish_systemctl_service_paths.fish deleted file mode 100644 index 5208bf8ba..000000000 --- a/share/functions/__fish_systemctl_service_paths.fish +++ /dev/null @@ -1,9 +0,0 @@ -function __fish_systemctl_service_paths - if type -q systemctl - if __fish_contains_opt user - systemctl --user list-unit-files --no-legend --type=path ^/dev/null $argv | cut -f 1 -d ' ' - else - systemctl list-unit-files --no-legend --type=path ^/dev/null $argv | cut -f 1 -d ' ' - end - end -end diff --git a/share/functions/__fish_systemctl_slices.fish b/share/functions/__fish_systemctl_slices.fish deleted file mode 100644 index 4d8ddf6dc..000000000 --- a/share/functions/__fish_systemctl_slices.fish +++ /dev/null @@ -1,11 +0,0 @@ -function __fish_systemctl_slices - if type -q systemctl - if __fish_contains_opt user - # Slices are usually generated at runtime - # Therefore show known _units_, not unit-files - systemctl --user list-units --no-legend --type=slice ^/dev/null | cut -f 1 -d ' ' - else - systemctl list-units --no-legend --type=slice ^/dev/null | cut -f 1 -d ' ' - end - end -end diff --git a/share/functions/__fish_systemctl_snapshots.fish b/share/functions/__fish_systemctl_snapshots.fish deleted file mode 100644 index 7017d8075..000000000 --- a/share/functions/__fish_systemctl_snapshots.fish +++ /dev/null @@ -1,12 +0,0 @@ -function __fish_systemctl_snapshots - if type -q systemctl - if __fish_contains_opt user - # Snapshots are usually generated at runtime - # Therefore show known _units_, not unit-files - # They are also often not loaded, so add "--all" - systemctl --user list-units --all --no-legend --type=snapshot ^/dev/null | cut -f 1 -d ' ' - else - systemctl list-units --all --no-legend --type=snapshot ^/dev/null | cut -f 1 -d ' ' - end - end -end diff --git a/share/functions/__fish_systemctl_sockets.fish b/share/functions/__fish_systemctl_sockets.fish deleted file mode 100644 index 5358eaf4c..000000000 --- a/share/functions/__fish_systemctl_sockets.fish +++ /dev/null @@ -1,9 +0,0 @@ -function __fish_systemctl_sockets - if type -q systemctl - if __fish_contains_opt user - systemctl --user list-unit-files --no-legend --type=socket ^/dev/null $argv | cut -f 1 -d ' ' - else - systemctl list-unit-files --no-legend --type=socket ^/dev/null $argv | cut -f 1 -d ' ' - end - end -end diff --git a/share/functions/__fish_systemctl_swaps.fish b/share/functions/__fish_systemctl_swaps.fish deleted file mode 100644 index 91547f752..000000000 --- a/share/functions/__fish_systemctl_swaps.fish +++ /dev/null @@ -1,9 +0,0 @@ -function __fish_systemctl_swaps - if type -q systemctl - if __fish_contains_opt user - systemctl --user list-unit-files --no-legend --type=swap ^/dev/null | cut -f 1 -d ' ' - else - systemctl list-unit-files --no-legend --type=swap ^/dev/null | cut -f 1 -d ' ' - end - end -end diff --git a/share/functions/__fish_systemctl_targets.fish b/share/functions/__fish_systemctl_targets.fish deleted file mode 100644 index d444d0cb6..000000000 --- a/share/functions/__fish_systemctl_targets.fish +++ /dev/null @@ -1,9 +0,0 @@ -function __fish_systemctl_targets - if type -q systemctl - if __fish_contains_opt user - systemctl --user list-unit-files --no-legend --type=target ^/dev/null | cut -f 1 -d ' ' - else - systemctl list-unit-files --no-legend --type=target ^/dev/null | cut -f 1 -d ' ' - end - end -end diff --git a/share/functions/__fish_systemctl_timers.fish b/share/functions/__fish_systemctl_timers.fish deleted file mode 100644 index 726a7100a..000000000 --- a/share/functions/__fish_systemctl_timers.fish +++ /dev/null @@ -1,9 +0,0 @@ -function __fish_systemctl_timers - if type -q systemctl - if __fish_contains_opt user - systemctl --user list-unit-files --no-legend --type=timer ^/dev/null $argv | cut -f 1 -d ' ' - else - systemctl list-unit-files --no-legend --type=timer ^/dev/null $argv | cut -f 1 -d ' ' - end - end -end diff --git a/share/functions/_fish_systemctl.fish b/share/functions/_fish_systemctl.fish new file mode 100644 index 000000000..0baee21ca --- /dev/null +++ b/share/functions/_fish_systemctl.fish @@ -0,0 +1,84 @@ +function _fish_systemctl --description 'Call systemctl with some options from the current commandline' + # These options are all global - before or after subcommand/arguments. + # There's a bunch of long-only options in here, so we need to be creative with the mandatory short version. + set -l opts t/type= s-state= p/property= a/all + set opts $opts r/recursive R-reverse A-after B-before + set opts $opts l/full V-value S-show-types J-job-mode= + set opts $opts F-fail i/ignore-inhibitors q/quiet N-no-block + set opts $opts W-wait U-user Y-system I-failed O-no-wall + set opts $opts G-global E-reload K-no-ask-password 4-kill-who + set opts $opts ü-signal f-force 5-message ö-now ä-root + set opts $opts Ü-runtime Ö-preset-mode= n/lines= o/output= + set opts $opts Ä-firmware-setup ß-plain H/host= M/machine= + set opts $opts 1-no-pager 2-no-legend h/help 3-version + + set -l args $argv + set -l cmdline (commandline -opc) (commandline -ct) + set -e cmdline[1] + argparse $opts -- $cmdline ^/dev/null + or return + + # If no subcommand has been given, return so this can be used as a condition. + test -n "$argv[1]" + or return + set -l cmd $argv[1] + set -e argv[1] + + # Flags we want to pass on. + set -l passflags $_flag_user $_flag_system $_flag_failed + switch "$cmd" + # These are the normal commands, so just complete all units. + # For "restart" et al, also complete non-running ones, since it can be used regardless of state. + case reenable status reload {try-,}{reload-or-,}restart is-{active,enabled,failed} show cat \ + help reset-failed list-dependencies list-units revert add-{wants,requires} edit + case enable + # This will only work for "list-unit-files", but won't print an error for "list-units". + set -q _flag_state; or set _flag_state disabled + case disable + set -q _flag_state; or set _flag_state enabled + case start + # Running `start` on an already started unit isn't an _error_, but useless. + set -q _flag_state; or set _flag_state dead,failed + case mask + set -q _flag_state; or set _flag_state loaded + case unmask + set -q _flag_state; or set _flag_state masked + case stop kill + # TODO: Is "kill" useful on other unit types? + # Running as the catch-all, "mounted" for .mount units, "active" for .target. + set -q _flag_state; or set _flag_state running,mounted,active + case isolate set-default + # These only take one unit. + set -q argv[1]; and return + case list-sockets + set _flag_type socket + case list-timers + set _flag_type timer + case get-default show-environment daemon-{reload,reexec} is-system-running default rescue emergency halt poweroff kexec \ + suspend hibernate hybrid-sleep + # Accept no arguments. + return + case '*' + # Unknown subcommand. Since we don't want to execute just anything, return. + # Note that this could also be a partial token, which is completed elsewhere. + return + end + + # Add the flags back so we can pass them to our systemctl invocations. + set -q _flag_type; and set passflags $passflags --type=$_flag_type + set -q _flag_state; and set passflags $passflags --state=$_flag_state + set -q _flag_property; and set passflags $passflags --property=$_flag_property + set -q _flag_machine; and set passflags $passflags --machine=$_flag_machine + set -q _flag_host; and set passflags $passflags --host=$_flag_host + + # Output looks like + # systemd-tmpfiles-clean.timer [more whitespace] loaded active waiting Daily Cleanup[...] + # Use the last part as the description. + systemctl --no-legend --no-pager --all list-units $passflags | string replace -r "(?: +(\S+)){4}" \t'$1' + # We need this for disabled/static units. Also instance units without an active instance. + # Output looks like + # systemd-tmpfiles-clean.timer static + # Just use the state as the description, since we won't get it here. + # This is an issue for units that appear in both. + systemctl --no-legend --no-pager --all list-unit-files $passflags | string replace -r "(?: +(\S+)){1}" \t'$1' +end