Repository navigation
Read functions and completions directly from the fish binary - #11143
Conversation
|
|
I think the hang might be pre-existing. It hangs doing completions = do_complete(
L!("echo (ls test/complete_test/testfil"),
CompletionRequestOptions::default(),
);And specifically and only that one. Now, Only... without embedding the functions, these aren't ever loaded because $fish_complete_path and $fish_function_path aren't actually defined here, so it will use the default file completions. That's also why, when you comment out that specific one, some other test_complete tests will fail. When the functions are embedded, it hangs in running an external command - the first would be |
|
We did try this in about 2013, IIRC, though the motivation was performance rather than portability. |
|
Yup, it's #15 - which was disabled in fishfish beta 2 (which later became fish 2.0). The big downside there was that "internalized" functions could not be overridden. This solution doesn't have that problem - it does the normal lookup, and only if that fails it'll load the embedded scripts. That means it always runs as if $fish_function_path/$fish_complete_path had the embedded scripts as the last entry (with the one exception of generated_completions, which I'll grant is a hack). I don't believe performance will really be measurably better - we still look at all the paths (including the empty $__fish_data_dir path for now), and we still need to convert the embedded scripts to wstring. The benefit I'm excited by really is that it makes fish easier to install and more resilient - there's no more "what if someone set $fish_function_path wrong or erased /usr/share/fish", there's no more issue with Which would be cool even if you install fish via a package manager, which is why I think this could be a default. |
|
Yeah, that makes senses, and it's a great idea |
4e3958c to
1c86a98
Compare
|
Alright, we have a few places where we kinda wanna get the file from a function. Both So I am tempted to add a This would be weird if it were possible to not have embedded files at all, because either it would print files from the filesystem (in which case we've built a |
9404886 to
51cd09b
Compare
|
Okay, I now made it so it asks to update the extracted assets if they exist. That means we allow an install without (e.g. just copying fish onto a machine that you ssh to), but if they are used we prevent them from going stale. I don't know if that still requires confirmation, we could just write them out when needed. |
51cd09b to
de3efcd
Compare
|
I added two special commands:
These allow us to make a lot of things work without extracting:
That means the only remaining features that need files to be extracted are:
That's because these have multiple files that need to be passed to external tools. webconfig has a bunch of js, the completion generator consists of two python files. We could potentially extract webconfig on-demand, make the man generator a single file and remove
Remaining TODOs:
|
2074977 to
abea863
Compare
|
Alright, made webconfig extract on demand. |
feaddbe to
3e02b41
Compare
|
I think the only good thing to do for the manpage completion generator is to make it a single file. Otherwise we would have to extract it on startup, which is awkward. Also: This PR gets us further away from being able to use gettext. It's currently not functional for installable builds - gettext needs the files in a very specific directory structure. And it needs them as files. Since our translations are currently barely functional at best (for french and german), that's fine. But if we wanted to have translations, this is another reason to switch away. I also noticed that this fixes the issue where packagers place completion scripts in /usr/share/fish/completions. If we no longer install anything there, there's no conflict. |
0b99623 to
8bf8b60
Compare
0df9a7b to
e0a9779
Compare
|
Alright, I've restructured this a bit. It is now optional again, using the same "installable" feature. We could rename that to "embedded", because that's now what it is. The "--install" option is gone because it is no longer necessary (if you really must, you can use The manpage generator won't be run automatically on startup, but if you run To reiterate, the remaining limitations of "embedded" builds:
I've decided to keep $__fish_data_dir set for now, because I'm not sure how many things will break if it's empty. I would like more testing before we make this the default configuration. Other than that, it's pretty much ready now, so I'm marking this as no longer a draft. |
757785b to
cdf257b
Compare
|
I've updated the description to explain what this does now and what I would want to do with it. I believe this is now in a mergeable state and want to do so. If anyone wants to review, now's the time. |
|
|
||
| [features] | ||
| default = ["installable"] | ||
| default = ["embed-data"] |
There was a problem hiding this comment.
My cargo install --path . --debug && ~/.cargo/bin/fish tries to source files from /usr/share/fish/functions
This is because I have this hack in my config:
set -a fish_complete_path /usr/share/fish/vendor_completions.d /usr/share/fish/completions
set -a fish_function_path /usr/share/fish/vendor_functions.d/ /usr/share/fish/functions
of course the added "/usr/share/fish/functions" is wrong, I'm not sure why I added it
I think the vendor_completions.d used to be necessary for running installations to ~/.local,
but they should not be necessary if I set XDG_DATA_DIRS.. probably there's a reason I still have it
There was a problem hiding this comment.
The installable builds never read the vendor stuff, yes.
I'm not sure if it is a good idea to do that or not, because these builds could be much newer than what the vendor stuff is prepared for.
For now, I'm explicitly punting on that for later - if we end up making this style of build the default, we'll have to handle it, but until then it's the status quo.
| default = ["embed-data"] | ||
| benchmark = [] | ||
| installable = ["dep:rust-embed"] | ||
| embed-data = ["dep:rust-embed"] |
There was a problem hiding this comment.
This concern is why I'm not pushing for this to become the default right now - it's still
"experimental" so people have time to adjust their plugins.
Yeah it would be nice if we can directly notify the most important plugins
(or better, give them fixes, even if they're untested).
I haven't checked how many are affected.
Installable builds never read the vendor directories so nothing changes in that respect.
It seems ~/.local/share/fish/vendor_completions.d is still in fish_complete_path when I run an embed-data build with:
env -u XDG_DATA_DIRS HOME=$(mktemp -d) fish
so I assume it's not completely ignored?
There was a problem hiding this comment.
Ah, no, it's $__fish_user_data_dir.
We really added too much guff here.
There was a problem hiding this comment.
Okay, I don't know what to do with that one. I missed it being introduced.
We can either set it to empty too, or keep it (it's in your home dir, so it's more likely that it's up-to-date and usable).
This gets the reader out of asting the source and is needed for autoloader to get it to read a source string directly Also add an "eval_file_wstr" method as a convenience to run a wstr as if it is a *file*, with a block and stuff
This will load the functions and completions from inside of the fish binary. That means its no longer necessary to *install* a self-installable build for basic functionality. The functions/completions will be loaded *last*, so they are still overridable via a file on disk (with the exception of generated_completions, which we only use as a fallback if no completions exist). It still needs to extract files that are to be used by other tools, including the man pages, fish_config.py, the man page completion generator. The remaining issues: - It no longer prompts to install, but if you tried `fish_config` after this it would fail to open the tool, and it would be easy to forget to update those files So: When and how should this remind you that these files need to be extracted? Do we want e.g. a builtin that checks the version file (`status installed`)? This could then be run by `fish_config` and `help` and tell you to run `fish --install`. - `builtin --help` will fail - we could read these things from inside, but we'd need groff for that. Do we want to pre-process these and put them in the builtins themselves? Do we want to print these on demand in `__fish_print_help` to groff? - What directories need to still be defined? Does $__fish_data_dir need to keep existing? Technically this *could* be the main distribution method. Maybe we could let distro packages skip the embedded documentation and external files, but keep the functions/completions in the binary.
This is no longer useful, given that we read files from in the binary. In the upcoming commits, this can be done with status list-files/get-file if you need it
Because these are embedded in the fish binary, we need to rebuild fish once they're changed. This is only for release builds, because rust-embed only really embeds in those (debug builds read from the filesystem).
This allows getting embedded files, so we can use them in functions
This allows us to implement fish_config using embedded files
Technically the fish_update_completions files could also be piped to python, but they'd have to be one file. So for now, if you start a single-file fish, you'll have to run fish_update_completions manually. That fits the idea of having a single file that you move somewhere better, given that it otherwise would run a script in the background that creates a bunch of files
This reflects better what it is - fish doesn't need to "install" itself anymore, it just includes the data in the binary. This also means we could include a separate "embed-man" feature that can be turned off if you want the man pages to be shipped separately. Also explain that in the README.
…d-data fish In case a completion needs a function from another script, run `complete -C"foo "` to load it, so the full autoloading logic is used. Otherwise these things break if the path is off. E.g. cargo's version will fail if you override the cargo completion in ~/.config/fish/completions without also overriding the rustup completions. In other cases, fix for empty $__fish_data_dir, which will be coming in the next commit
These builds do not use it, and setting it makes it possible to use stale files. So instead, we set it to empty. All uses I could find are either fine with that (`set fish_function_path $__fish_data_dir/functions` - which would also just be empty, which means it reads the embedded functions), or would break even if the variable was set but the directory was empty (`source $__fish_data_dir/completions/git.fish`).
Description
This builds on the previous "installable" builds that embedded the assets (functions, completions, man pages etc)
and wrote them out on start or whenever you ran
fish --install, and turns them into "embed-data" builds.These will just read the assets directly from the binary when needed.
When autoloading, these are considered last (except for completions, they're considered before the generated completions), so overriding them via $fish_function_path or $fish_complete_path works like before.
That means, for users, this creates a fully capable single-file fish that does not depend on any other files on the filesystem. Link it statically and you can copy it wherever.
Internals
Some scripts use the new
status list-filesandstatus get-filecommands to get the files.The
fish --installoption is removed - if you need to extract the files you can easily script that with the status commands.pythonfish_update_completions.This is not done automatically at first start to avoid creating directories. Tbh the manpage completions aren't all that useful anymore.
manwrapper andbuiltin --helpwrite out the man pages when neededAs before, the way to do this is to build with the on-by-default "embed-data" feature (which is disabled by cmake, so nothing changes for packagers).
Why?
The idea is that you can copy a single fish binary to a system and run it without having to touch that system's files or care about keeping them up-to-date.
This also makes fish more resilient to misinstallation - there is no more data directory to keep in-sync, uninstallation is just removing the binary. Completions in other packages can no longer conflict with our completions.
It is no longer possible to break a lot of fish by setting $fish_function_path wrong.
Compatibility concerns
$__fish_data_dir is set to empty. This is to avoid embed-data builds reading outdated assets from old installable builds and because it is no longer a useful directory.
That means scripts that go through that directory, e.g. to load another file, are affected will need to be changed to use the status commands.
I've done that for our things (and hopefully got everything), but third-party plugins would also have to be adjusted.
We could also keep $__fish_data_dir set to the old directory,
but that would not help - if anything does
source $__fish_data_dir/completions/git.fishthat breaks if there is no git.fish there,and not having a git.fish there is the point. So there is necessary breakage, and at that point it's easier to make both versions work if the directory is empty.
This concern is why I'm not pushing for this to become the default right now - it's still "experimental" so peope have time to adjust their plugins.
An additional point is that $__fish_data_dir also contains the vendor directories. If we did make this the default also for packaged builds, we would have to do something about that. I'm not entirely sure what, one possibility is to always check /usr/share/fish and /usr/local/share/fish, but that can obviously run into problems if the versions diverge (new fish on an LTS system).
Installable builds never read the vendor directories so nothing changes in that respect.
Future
This could be the default way to build fish, and it could also be used to remove cmake.
The way to build fish for a package could be
As before, embed-data builds do not contain html documentation (because we need fish_indent to build them) or translations (because gettext needs them in a file).
helpwill open the online docs and translations aren't very useful at the moment.TODO
if status test-feature embed-datainstead of tryingstatus list-files?Proposed CHANGELOG wording:
The "self-installable" builds have been changed to read their datafiles directly from the fish binary instead of having to write them out on start. The
--installoption was removed, instead there are more granularstatus list-filesandstatus get-file FILEcommands that can be used to get at these files.That means it is now possible (combined with the changes to make fish_key_reader and fish_indent builtins) to build fish as a single file that you can copy to a compatible system and just run it, including as a different user. As before, this is the default when building fish via cargo (e.g.
cargo install).We're considering making this the default in future because it makes it easier to install fish.
Note to developers of fish integration or plugins: These builds will have $__fish_data_dir set to empty because it would not contain any files. If you want to get a file, consider falling back to
status get-file path/to/file(where "path/to/file" would be the path relative to the datadir, for instance "completions/git.fish") or if you want to load other completions just running them withcomplete -C"foo" >/dev/null.the old description
## DescriptionThis is a rough draft. In particular, for some reason I have not figured out,cargo testgets stuck at completingecho (ls.If anyone sees why that is (it works in actual usage and the scripted tests!), any help would be appreciated.(edit: figured it out, it's a bug that's uncovered by this and now worked around)Anyway, what does it do?
The worst part of self-installable fish currently is that it needs to write out its functions and completions before being in any sensible meaning of the word "functional". Without installing you've got a paper (well, bit-) weight.
So this will instead read these files right from the binary, if necessary.
So if you try to autoload e.g. the fish_prompt function, it will go through the regular $fish_function_path lookup, and if it doesn't find anything, load the version of fish_prompt.fish that was embedded in the binary. Same for completions.
Note that this is not entirely the same thing as currently, when using $__fish_data_dir - it is always last, and cannot be skipped. Not being skippable is probably fine, we might want to add a special "embedded" entry for this that you can add wherever you want (or just interpret the data dir as that). There's a hack to skip the generated completions.
Installing now only writes out the docs and webconfig (with the themes!). It skips functions and completions entirely.
TODOs:
builtin --helpcould be made to work by reading the docs from the binary and piping them togroff