fix nix bash wrapper detection, add more completion keywords

This commit is contained in:
atagen 2026-03-27 15:28:05 +11:00
parent daa0c24415
commit f9d970f2e5

View file

@ -281,6 +281,34 @@ let nix_wrapper_target path =
end
with _ -> None
(* detect nix bash/sh wrapper scripts that exec a real binary.
* nix sometimes generates small shell scripts (e.g. to set env vars like
* XDG_CONFIG_HOME) that exec the real binary. these look like:
* #!/nix/store/.../bash -e
* export FOO=...
* exec -a "$0" "/nix/store/.../bin/.foo-wrapped" "$@"
* we extract the exec target path and resolve through it. *)
let nix_script_wrapper_target path =
try
let real = Unix.realpath path in
let ic = open_in real in
let size = in_channel_length ic in
if size > 4096 then (close_in ic; None)
else begin
let contents = Bytes.create size in
really_input ic contents 0 size; close_in ic;
let contents = Bytes.to_string contents in
if not (contains_str contents "exec") then None
else
let re = Str.regexp "exec[ \t]+\\(-a[ \t]+\"\\$0\"[ \t]+\\)?\"?\\(/nix/store/[a-z0-9]+-[^\" \t\n]+/bin/[a-zA-Z0-9._-]+\\)\"?" in
try ignore (Str.search_forward re contents 0);
let target = Str.matched_group 2 contents in
let target = Unix.realpath target in
if Sys.file_exists target then Some target else None
with Not_found -> None
end
with _ -> None
(* heuristic filter for binary names that should never be indexed.
* skips: empty names, "-", dotfiles, libraries (lib-prefix), daemon wrappers
* (suffixes -daemon, -wrapped), shared objects (.so suffix), and names with no
@ -299,25 +327,37 @@ let skip_name name =
* Try_native_and_help try native nushell completion first, fall back to --help *)
type bin_class = Skip | Try_help | Try_native_and_help
(* classify an elf binary path for indexing. *)
let classify_elf path =
let scan = elf_scan path ["-h"; "completion"] in
if Hashtbl.mem scan "completion" then Try_native_and_help
else if Hashtbl.mem scan "-h" then Try_help
else Skip
(* classify a binary to decide the indexing strategy.
* decision tree:
* 1. nushell builtin or bad name -> Skip
* 2. not executable -> Skip
* 3. script (has shebang) -> Try_help (scripts can't have native completions)
* 3. script (has shebang) -> resolve through nix script wrapper if possible,
* otherwise Try_help
* 4. elf binary containing "completion" -> Try_native_and_help
* 5. elf binary containing "-h" -> Try_help
* 6. nix wrapper -> Try_help (the wrapper itself is just an exec shim)
* 6. nix c wrapper -> Try_help (the wrapper itself is just an exec shim)
* 7. otherwise -> Skip (binary has no help infrastructure) *)
let classify_binary bindir name =
if is_nushell_builtin name || skip_name name then Skip
else
let path = Filename.concat bindir name in
if not (is_executable path) then Skip
else if is_script path then Try_help
else if is_script path then
match nix_script_wrapper_target path with
| Some target ->
let cls = classify_elf target in
if cls <> Skip then cls else Try_help
| None -> Try_help
else
let scan = elf_scan path ["-h"; "completion"] in
if Hashtbl.mem scan "completion" then Try_native_and_help
else if Hashtbl.mem scan "-h" then Try_help
let cls = classify_elf path in
if cls <> Skip then cls
else if nix_wrapper_target path <> None then Try_help
else Skip
@ -352,6 +392,7 @@ let try_native_completion bin_path =
[bin_path; "--completion"; "nushell"];
[bin_path; "generate-completion"; "nushell"];
[bin_path; "--generate-completion"; "nushell"];
[bin_path; "gen-completions"; "nushell"];
[bin_path; "shell-completions"; "nushell"];
]