Readit News logoReadit News
homebrewer · 18 days ago
With fish, if the program you're interested in hasn't betrayed the decades-old tradition of shipping man pages, it's often as simple as running `fish_update_completions`.

It parses all man pages on your system and generates completion files for you. By default, they go into ~/.cache/fish/generated_completions/*

If the man page was written poorly/is missing, you can always write your own completion (and hopefully send it upstream). fish uses such a simple format that I don't think there's any need for tutorials save the official doc:

https://fishshell.com/docs/current/completions.html

For example, here's an excerpt from curl

  complete --command curl --short-option 'L' --long-option 'location' --description 'Follow redirects' 
  complete --command curl --short-option 'O' --long-option 'remote-name' --description 'Write output to file named as remote file'

btreecat · 18 days ago
When I screen share, people don't realize I'm not using zsh and dozen plugins. It's just fish and it's beautiful out of the box.
yencabulator · 18 days ago
I'll switch to fish after it stops expanding `car TAB` to `blkdiscard` when I don't have `cargo` in path. Non-prefix completion for commands is plain evil.
wpm · 18 days ago
I’ll switch to fish when it comes preinstalled on all of the computers I use so I can write scripts in it.
dingnuts · 18 days ago
I bet this is configurable but I wanted to say that this is totally personal preference; I have the exact opposite opinion. Prefix only matching requires much more tab slapping in my experience.
kekebo · 18 days ago
Thank you for the comment. https://github.com/umlx5h/zsh-manpage-completion-generator appears to adapt this to ZSH. Have yet to try through
cb321 · 18 days ago
For those programs that have betrayed shipping man pages, instead say relying only on a --help system, do you happen to know if the fish shell has an analogue to Zsh `_gnu_generic` and Bash `complete -F _longopt`? If not, do you have any insight into why not/what it would take to make that happen?
mr_mitm · 18 days ago
At least for a subset of Python CLI programs, I wrote this: https://github.com/AdrianVollmer/pycompgen

Still in an early stage, but it should work.

nikita2206 · 18 days ago
The OP mentions them in the last part of their comment, there is `complete …` commands for registering completions
sanewombat · 18 days ago
It's surprising that on OpenSUSE `zypper search fish-completion` returns more than 200 packages. Something is fishy here.
OptionOfT · 18 days ago
That is to get live data in your completions.

Say you have a widget that has 3 commands: list, start and stop.

With one of those completion files widget stop <tab> will show you a tab-able list of widgets which are running.

IgorPartola · 18 days ago
man pages are so underrated. I mean every project nowadays has README.md so I don’t see why we can’t just auto generate them with or without an LLM helping. Also I wish programs would use standardized generic arguments for help, config file, version, background the task, PID file, log file, and log level.
jnpnj · 18 days ago
oh wow, it's parsing all 9461 man pages on my arch install, for a cute total of 13MB

thanks a lot

hnlmorg · 17 days ago
Fish isn’t the only shell that does this. Murex has supported man page completions for years too. And it’s fully automatic in Murex.
fishywang · 17 days ago
I tried to use fish on some of my debian servers that i only rarely update packages/kernel, so I don't have to carry my bashrc there, but found their completion for apt is pretty naive. for example after updated kernel i would want to clean up the old ones, but `apt purge linux-image-<TAB>` would list all available kernel versions, not just the ones currently installed.

in the end i switched back to bash.

derriz · 18 days ago
I feel that the ergonomics of bash completion took a hit as the configurations got “smarter” and “helpfully” started blocking file or directory name completion if it thinks it wouldn’t be appropriate to have a file name at the current cursor position. Instead of blocking, the default should always be to fall back to filename completion.

Sometimes I’m close to disabling/uninstalling all completion scripts out of irritation as decades of muscle memory are frustrated by this behavior.

It’s like that bad/annoying UX with text fields where the UI is constantly fighting against you in order prevent you from producing “illegal” intermediate input - e.g. let me paste the clipboard here goddammit - I know what I’m doing - I’ll correct it.

JNRowe · 18 days ago
There is the complete-filename function that only completes filenames in bash, bound to M-/ by default. You can use that in any place you want a filename where "complete"(the function normally bound to tab) would do something you don't desire.

There are a collection of other non-context aware completion functions that are bound by default too, useful for example when you when you wish to complete hostnames in a for-loop.

zle has what is largely a significant superset of this, the documentation is spread about between the zshzle and zshcomp* manpages.

loeg · 18 days ago
There is one particular command I occasionally use that has totally broken completion for files, so I've taken to just using 'ls X Y Z' to get the right completion behavior and then changing 'ls' to the right command as the last step.
jmholla · 17 days ago
Hopefully you saw one of the sibling comments: https://news.ycombinator.com/item?id=44855551

TLDR: M-/ (Alt /) will do file auto-completions regardless of the commands auto-comoletion. It is a different method, but maybe could help.

IshKebab · 18 days ago
I agree that is annoying. It's waaay less confusing to complete a filename and then get an error from the actual program than it is for just ...nothing to happen so you get confused and have to `ls` to check if the file actually exists and it does and so you think tab completion is broken for some reason and you copy & paste the filename and then finally you get the error that explains what is going on.

It should at least print a message like "file foo.exe exists but it isn't executable".

kevindamm · 18 days ago
If you get into this position what you can do is `ls <tab-completed-path>` or other command to put the filename in the previous command's argument, then you can access it via !$ or !^ (or use !!:1 or your shell's notation for indexing an argument that was already in the previous command).

It's not a fix but it'll save a little time sometimes.

cryptoz · 18 days ago
I have come to absolutely despise web form inputs with front end email validators that are broken. Input field hints to type your email, so you start typing. As soon as you type the first letter it goes red and says “error!!! Invalid email!”

Unbelievably frustrating.

pastage · 18 days ago
As a guy who is vicously guarding our company email valdation I can tell you that it is a rite of passage for new frontend hires to mess that up.
compressedgas · 18 days ago
As it works as desired after running: complete -r; there is something broken about the bash-completion script.
lihaoyi · 18 days ago
I wrote this, hope everyone finds it as interesting reading this as I did figuring this out for the first time!
bbkane · 18 days ago
Thanks for sharing! I hope to incorporate your bash completion ideas into my CLIs (I've already got zsh completions).

Instead of sourcing the zsh completion script on every startup, you can install it into somewhere on $fpath and zsh will "compile" and cache the completions. This can really speed up shell startup time, but of course is harder to set up. Users have to understand $fpath to put the completions there.

I distribute my CLIs via Homebrew which can install completions automatically.

tetha · 18 days ago
It's a good first dive into zsh completion. The whole thing is quite the large system to wrap ones head around it and I'm still somewhat struggling.

But at work, I've been slowly adding auto completion to our ansible wrapper scripts, like explanations which playbooks to use when, smart `-l` completion based off a possibly selected playbook (so, if the playbook is postgres.yml, it doesn't suggest mariadb groups), tag autocompletion (with a few, admittedly, hardcoded explanations how these tags should be used) and such.

It's somewhat of a friday-afternoon struggle project, but it's making the big ansible project pretty approachable to use.

imcritic · 18 days ago
Could you share how you do it? Ansible playbooks are ran via command `ansible-playbook` command and it surely has its own tab auto completion script.
oezi · 18 days ago
Thanks, for the interesting read.
paradox460 · 18 days ago
I've started using jdx's usage[1] for my clis. It integrates neatly into clap, and can be used stand alone in scripts. It can generate completions, argparse, manpages, and more

I'm still on the fence if replacing the argparse blocks in my fish scripts is worth the hassle, but against things like old school optparse, it's far better

[1]: https://usage.jdx.dev/

FujiApple · 18 days ago
I’ve recently been building a similar tool [1] which defines a specification for CLIs, though the goals are slightly different to the tool you mention I think. I just added support for fish as it happens.

[1] https://github.com/fujiapple852/claptrap

paradox460 · 17 days ago
Nice. I'll have to add that to my tool belt, although I think usage will still sit front and center haha
medv · 18 days ago
JSON fields autocomplete right in bash/zsh: https://fx.wtf/install#autocomplete
imcritic · 18 days ago
Thanks for linking this! This is a lightweight solution, compared to ijq (interactive jq), but it still may come in handy.

https://github.com/gpanders/ijq

vcdimension · 18 days ago
Here's another tutorial for creating zsh completers using the built-in functions: https://github.com/vapniks/zsh-completions/blob/master/zsh-c...
oezi · 18 days ago
Isn't there a standard flag which programs can implement to avoid writing this bash script?

Ideally this could all be part of a library such as argparse for typical cases, right?

duckerude · 18 days ago
Rust has the clap_complete package for its most popular arg parsing library: https://crates.io/crates/clap_complete

ripgrep exposes its (bespoke) shell completion and man page generation through a --generate option: rg --generate=man, rg --generate=complete-bash, etcetera. In xh (clap-based) we provide the same but AFAIK we're the only one to copy that interface.

Symfony (for PHP) provides some kind of runtime completion generation but I don't know the details.

vcdimension · 18 days ago
In zsh you can use the _gnu_generic function for simple completion of commands with a --help flag. Just put a line like this somewhere in your startup file: compdef _gnu_generic <CMD>
cb321 · 18 days ago
_gnu_generic is fantastic. I use it all the time. If your CLI toolkit emits colorized help, but skips said colorization if NO_COLOR[1] is set then you can prefix _gnu_generic with NO_COLOR=1 as in https://github.com/c-blake/cligen/wiki/Zsh-completion-for-cl... for the cligen Nim CLI toolkit.

A similar thing in Bash is `complete -F _longopt YourCmd`, but these will not work with "multi-commands" that have "sub-commands" as the article of this thread covers. Truth is, as non-standard as GNU long opts already are, how subcommands work is even more non-standard (are there even global options? Or between each subcommand? Is it how Python does it? Or Go? Or some one specific library or ..?)

[^1]: https://no-color.org/

mnahkies · 18 days ago
I've wondered this as well - it would sure be nice if there was a standard --completion or something that common argument parsing libraries could automatically implement for us (much like they often implement automatic help text)
gosub100 · 18 days ago
I did something similar to this for tab-completing server names for use with ssh. I went a step further and allowed pattern matching based on the server being qa/prod and on locale. so for instance you could type `ssh co prod <tab>` and it would tab-complete / suggest any servers that were production and located in the Denver datacenter (co is the state abbrev for Colorado, for non-US readers).

Unfortunately my work doesn't allow me to share code, but essentially I remapped ssh to a bash script that maintains an environment variable containing the args (you must do this because each <tab> press is an independent invocation of the script. Then you run into persistence problems, so I added a call to compute elapsed seconds so that it flushes the state variable after a 10s timeout).

The bash script then forwards the args to a python script that reads a JSON file and figures out which params (such as 'co' or 'qa') map to which hostnames. It also matches against partial hostnames, so when you see this after tab

qa-server-co1 qa-server-co2 pr-server-co3

you only need to add '3' to the list of args to narrow it down to 1 match, then hit <enter> to ssh to that host.