# ip(8) completion for fish # The difficulty here is that ip allows abbreviating options, so we need to complete "ip a" like "ip address", but not "ip m" like "ip mroute" # Also the manpage and even the grammar it accepts is utter shite (options can only be before commands, some things are only in the BNF, others only in the text) # It also quite likes the word "dev", even though it needs it less than the BNF specifies set -l ip_commands link address addrlabel route rule neigh ntable tunnel tuntap maddr mroute mrule monitor xfrm netns l2tp tcp_metrics set -l ip_addr a ad add addr addre addres address set -l ip_link l li lin link set -l ip_all_commands $ip_commands $ip_addr $ip_link function __fish_ip_commandwords set -l skip 0 set -l cmd (commandline -opc) # HACK: Handle and/or/not specially because they have hardcoded completion behavior # that doesn't remove them from the commandline if contains -- $cmd[1] and or not set -e cmd[1] end # Remove the first word because it's "ip" or an alias for it set -e cmd[1] set -l have_command 0 for word in $cmd switch $word # Normalize the commands to their full form - `ip a` is `ip address` # This can't be just an unambiguity check because there's also `ip addrlabel` case a ad add addr addre addres address # "addr add" is a thing, so we can only echo "address" if it's actually the command if test $have_command = 0 set have_command 1 echo address else echo $word end case l li lin link if test $have_command = 0 set have_command 1 echo link else echo $word end case "addrl*" if test $have_command = 0 set have_command 1 echo addrlabel else echo $word end case r ro rou rout route if test $have_command = 0 set have_command 1 echo route else echo $word end case "ru*" if test $have_command = 0 set have_command 1 echo rule else echo $word end case n ne nei neig neigh if test $have_command = 0 set have_command 1 echo neigh else echo $word end case nt if test $have_command = 0 set have_command 1 echo ntable else echo $word end case t tu tun tunn tunne tunnel if test $have_command = 0 set have_command 1 echo tunnel else echo $word end case "tunt*" if test $have_command = 0 set have_command 1 echo tuntap else echo $word end case m ma mad madd maddr maddre maddres maddress if test $have_command = 0 set have_command 1 echo maddress else echo $word end case mr "mro*" if test $have_command = 0 set have_command 1 echo mroute else echo $word end case "mru*" if test $have_command = 0 set have_command 1 echo mrule else echo $word end case "mo*" if test $have_command = 0 set have_command 1 echo monitor else echo $word end case "x*" if test $have_command = 0 set have_command 1 echo xfrm else echo $word end case "net*" if test $have_command = 0 set have_command 1 echo netns else echo $word end case "l*" if test $have_command = 0 set have_command 1 echo l2tp else echo $word end case "tc*" if test $have_command = 0 set have_command 1 echo tcp_metrics else echo $word end case "to*" if test $have_command = 0 set have_command 1 echo token else echo $word end case -n -netns --netns if test $have_command = 0 set skip 1 else echo $word end case '-*' test $have_command = 0 and continue echo $word case '*' if test $skip = 1 set skip 0 continue end echo $word end end # Print an empty line if the current token is empty, so we know that the one before it is finished # TODO: For some reason it is necessary to always print the current token - why doesn't the above loop catch it? set -l token (commandline -ct) echo $token end function __fish_ip_device ip -o link show | while read -l a b c printf '%s\t%s\n' (string replace ':' '' -- $b) Device end end function __fish_ip_scope if test -r /etc/iproute2/rt_scopes string replace -r '#.*' ''