diff options
author | 2021-11-01 03:43:27 -0700 | |
---|---|---|
committer | 2021-11-01 03:43:27 -0700 | |
commit | 173724b4020cae1f0caacb5116a31aec86f2c2db (patch) | |
tree | 72bb511823910a8d5fe0697e33b7648d3827ecef | |
parent | 64d3927879e9af94eaf1aa7f304ab28f03e7a6bb (diff) | |
download | bun-173724b4020cae1f0caacb5116a31aec86f2c2db.tar.gz bun-173724b4020cae1f0caacb5116a31aec86f2c2db.tar.zst bun-173724b4020cae1f0caacb5116a31aec86f2c2db.zip |
[CLI] Add zsh completions and auto-install completions
Diffstat (limited to '')
-rw-r--r-- | Makefile | 3 | ||||
-rw-r--r-- | completions/bun.bash | 8 | ||||
-rw-r--r-- | completions/bun.fish | 80 | ||||
-rw-r--r-- | completions/bun.zsh | 63 | ||||
-rw-r--r-- | src/bun_js.zig | 6 | ||||
-rw-r--r-- | src/cli.zig | 247 | ||||
-rw-r--r-- | src/cli/install.sh | 97 | ||||
-rw-r--r-- | src/cli/run_command.zig | 21 | ||||
-rw-r--r-- | src/cli/shell_completions.zig | 16 | ||||
-rw-r--r-- | src/exact_size_matcher.zig | 9 |
10 files changed, 473 insertions, 77 deletions
@@ -153,7 +153,7 @@ s2n-ubuntu-deps: unzip \ valgrind \ zlib1g-dev \ - zlibc \ + zlib \ cmake \ tox \ libtool \ @@ -626,6 +626,7 @@ test-dev-bun-run: cd integration/apps && BUN_BIN=$(DEBUG_BUN) bash bun-run-check.sh test-dev-all: test-dev-with-hmr test-dev-no-hmr test-dev-create-next test-dev-create-react test-dev-bun-run +test-dev-bunjs: test-dev: test-dev-with-hmr diff --git a/completions/bun.bash b/completions/bun.bash new file mode 100644 index 000000000..fa9830a15 --- /dev/null +++ b/completions/bun.bash @@ -0,0 +1,8 @@ +#/usr/bin/env bash + +# This is not implemented yet. +# But a PR implementing it would be very welcome! +_bun_completions() { +} + +complete -F _bun_completions bun diff --git a/completions/bun.fish b/completions/bun.fish index d3484a78c..176e662ac 100644 --- a/completions/bun.fish +++ b/completions/bun.fish @@ -6,44 +6,54 @@ function __fish__get_bun_scripts string split ' ' (bun getcompletes s) end +function __fish__get_bun_bun_js_files + string split ' ' (bun getcompletes j) +end + set -l bun_builtin_cmds dev create help bun upgrade discord run set -l bun_builtin_cmds_without_run dev create help bun upgrade discord set -l bun_builtin_cmds_without_create dev help bun upgrade discord run -complete -c bun -n "not __fish_seen_subcommand_from $bun_builtin_cmds_without_run; and not __fish_seen_subcommand_from (__fish__get_bun_bins); and not __fish_seen_subcommand_from (__fish__get_bun_scripts); and __fish_use_subcommand" -a '(__fish__get_bun_bins)' -d 'package bin' - -complete -c bun -n "not __fish_seen_subcommand_from $bun_builtin_cmds_without_run; and not __fish_seen_subcommand_from (__fish__get_bun_bins); and not __fish_seen_subcommand_from (__fish__get_bun_scripts); and __fish_use_subcommand" -a '(__fish__get_bun_scripts)' -d 'script' - -complete -c bun -n "not __fish_seen_subcommand_from $bun_builtin_cmds_without_run; and not __fish_seen_subcommand_from (__fish__get_bun_bins); and not __fish_seen_subcommand_from (__fish__get_bun_scripts); and __fish_seen_subcommand_from run" -a '(__fish__get_bun_bins)' -d 'package bin' - -complete -c bun -n "not __fish_seen_subcommand_from $bun_builtin_cmds_without_run; and not __fish_seen_subcommand_from (__fish__get_bun_bins); and not __fish_seen_subcommand_from (__fish__get_bun_scripts); and __fish_seen_subcommand_from run" -a '(__fish__get_bun_scripts)' -d 'script' - -complete -c bun -n "not __fish_seen_subcommand_from $bun_builtin_cmds; and not __fish_seen_subcommand_from (__fish__get_bun_bins); and not __fish_seen_subcommand_from (__fish__get_bun_scripts) and __fish_use_subcommand" -a 'run' -d 'Run a script or bin' - -complete -c bun -n "not __fish_seen_subcommand_from (__fish__get_bun_bins); and not __fish_seen_subcommand_from (__fish__get_bun_scripts);" -F -s 'u' -l 'origin' -r -d 'Server URL. Rewrites import paths' - -complete -c bun -n "not __fish_seen_subcommand_from (__fish__get_bun_bins); and not __fish_seen_subcommand_from (__fish__get_bun_scripts);" -F -s 'p' -l 'port' -r -d 'Port number to start server from' - -complete -c bun -n "not __fish_seen_subcommand_from (__fish__get_bun_bins); and not __fish_seen_subcommand_from (__fish__get_bun_scripts);" -F -s 'd' -l 'define' -r -d 'Substitute K:V while parsing, e.g. --define process.env.NODE_ENV:\"development\"' - -complete -c bun -n "not __fish_seen_subcommand_from (__fish__get_bun_bins); and not __fish_seen_subcommand_from (__fish__get_bun_scripts);" -F -s 'e' -l 'external' -r -d 'Exclude module from transpilation (can use * wildcards). ex: -e react' - -complete -c bun -n "not __fish_seen_subcommand_from (__fish__get_bun_bins); and not __fish_seen_subcommand_from (__fish__get_bun_scripts);" -F -l 'use' -r -d 'Use a framework (ex: next)' - -complete -c bun -n "not __fish_seen_subcommand_from $bun_builtin_cmds; and not __fish_seen_subcommand_from (__fish__get_bun_bins); and not __fish_seen_subcommand_from (__fish__get_bun_scripts) and __fish_use_subcommand" -a 'dev' -d 'Start dev server' - -complete -c bun -n "not __fish_seen_subcommand_from $bun_builtin_cmds; and not __fish_seen_subcommand_from (__fish__get_bun_bins); and not __fish_seen_subcommand_from (__fish__get_bun_scripts); and __fish_use_subcommand" -F -a 'create' -d 'Create a new project from a template' - -complete -c bun -n "not __fish_seen_subcommand_from $bun_builtin_cmds_without_create next react; and not __fish_seen_subcommand_from (__fish__get_bun_bins); and not __fish_seen_subcommand_from (__fish__get_bun_scripts); and __fish_seen_subcommand_from create" -f -a 'next' -d 'Create a new Next.js project' - -complete -c bun -n "not __fish_seen_subcommand_from $bun_builtin_cmds_without_create next react; and not __fish_seen_subcommand_from (__fish__get_bun_bins); and not __fish_seen_subcommand_from (__fish__get_bun_scripts); and __fish_seen_subcommand_from create" -f -a 'react' -d 'Create a new React project' - -complete -c bun -n "not __fish_seen_subcommand_from $bun_builtin_cmds_without_create; and not __fish_seen_subcommand_from (__fish__get_bun_bins); and not __fish_seen_subcommand_from (__fish__get_bun_scripts); and __fish_seen_subcommand_from create next" -F - -complete -c bun -n "not __fish_seen_subcommand_from $bun_builtin_cmds; and not __fish_seen_subcommand_from (__fish__get_bun_bins); and not __fish_seen_subcommand_from (__fish__get_bun_scripts); and __fish_use_subcommand" -x -a 'upgrade' -d 'Upgrade Bun to the latest version' - -complete -c bun -n "not __fish_seen_subcommand_from $bun_builtin_cmds; and not __fish_seen_subcommand_from (__fish__get_bun_bins); and not __fish_seen_subcommand_from (__fish__get_bun_scripts); and __fish_use_subcommand" -x -a '--help' -d 'See all commands and flags' - -complete -c bun -n "not __fish_seen_subcommand_from $bun_builtin_cmds; and not __fish_seen_subcommand_from (__fish__get_bun_bins); and not __fish_seen_subcommand_from (__fish__get_bun_scripts); and __fish_use_subcommand" -x -a 'discord' -d 'Open Bun\'s Discord server' +complete -c bun \ + -n "not __fish_seen_subcommand_from $bun_builtin_cmds_without_run; and not __fish_seen_subcommand_from (__fish__get_bun_bins); and not __fish_seen_subcommand_from (__fish__get_bun_scripts); and __fish_use_subcommand" -a '(__fish__get_bun_bins)' -d 'package bin' +complete -c bun \ + -n "not __fish_seen_subcommand_from $bun_builtin_cmds_without_run; and not __fish_seen_subcommand_from (__fish__get_bun_bins); and not __fish_seen_subcommand_from (__fish__get_bun_scripts); and __fish_use_subcommand" -a '(__fish__get_bun_scripts)' -d 'script' +complete -c bun \ + -n "not __fish_seen_subcommand_from $bun_builtin_cmds_without_run; and not __fish_seen_subcommand_from (__fish__get_bun_bins); and not __fish_seen_subcommand_from (__fish__get_bun_scripts); and __fish_seen_subcommand_from run" -a '(__fish__get_bun_bins)' -d 'package bin' +complete -c bun \ + -n "not __fish_seen_subcommand_from $bun_builtin_cmds_without_run; and not __fish_seen_subcommand_from (__fish__get_bun_bins); and not __fish_seen_subcommand_from (__fish__get_bun_scripts); and __fish_seen_subcommand_from run" -a '(__fish__get_bun_scripts)' -d 'script' +complete -c bun \ + -n "not __fish_seen_subcommand_from $bun_builtin_cmds_without_run; and not __fish_seen_subcommand_from (__fish__get_bun_bins); and not __fish_seen_subcommand_from (__fish__get_bun_scripts); and __fish_seen_subcommand_from run" -a '(__fish__get_bun_bun_js_files)' -d 'Bun.js' +complete -c bun \ + -n "not __fish_seen_subcommand_from $bun_builtin_cmds; and not __fish_seen_subcommand_from (__fish__get_bun_bins); and not __fish_seen_subcommand_from (__fish__get_bun_scripts) and __fish_use_subcommand" -a 'run' -f -d 'Run a script or bin' +complete -c bun \ + -n "not __fish_seen_subcommand_from (__fish__get_bun_bins); and not __fish_seen_subcommand_from (__fish__get_bun_scripts);" -F -s 'u' -l 'origin' -r -d 'Server URL. Rewrites import paths' +complete -c bun \ + -n "not __fish_seen_subcommand_from (__fish__get_bun_bins); and not __fish_seen_subcommand_from (__fish__get_bun_scripts);" -F -s 'p' -l 'port' -r -d 'Port number to start server from' +complete -c bun \ + -n "not __fish_seen_subcommand_from (__fish__get_bun_bins); and not __fish_seen_subcommand_from (__fish__get_bun_scripts);" -F -s 'd' -l 'define' -r -d 'Substitute K:V while parsing, e.g. --define process.env.NODE_ENV:\"development\"' +complete -c bun \ + -n "not __fish_seen_subcommand_from (__fish__get_bun_bins); and not __fish_seen_subcommand_from (__fish__get_bun_scripts);" -F -s 'e' -l 'external' -r -d 'Exclude module from transpilation (can use * wildcards). ex: -e react' +complete -c bun \ + -n "not __fish_seen_subcommand_from (__fish__get_bun_bins); and not __fish_seen_subcommand_from (__fish__get_bun_scripts);" -F -l 'use' -r -d 'Use a framework (ex: next)' +complete -c bun \ + -n "not __fish_seen_subcommand_from $bun_builtin_cmds; and not __fish_seen_subcommand_from (__fish__get_bun_bins); and not __fish_seen_subcommand_from (__fish__get_bun_scripts) and __fish_use_subcommand" -a 'dev' -d 'Start dev server' +complete -c bun \ + -n "not __fish_seen_subcommand_from $bun_builtin_cmds; and not __fish_seen_subcommand_from (__fish__get_bun_bins); and not __fish_seen_subcommand_from (__fish__get_bun_scripts); and __fish_use_subcommand" -F -a 'create' -d 'Create a new project from a template' +complete -c bun \ + -n "not __fish_seen_subcommand_from $bun_builtin_cmds_without_create next react; and not __fish_seen_subcommand_from (__fish__get_bun_bins); and not __fish_seen_subcommand_from (__fish__get_bun_scripts); and __fish_seen_subcommand_from create" -f -a 'next' -d 'Create a new Next.js project' +complete -c bun \ + -n "not __fish_seen_subcommand_from $bun_builtin_cmds_without_create next react; and not __fish_seen_subcommand_from (__fish__get_bun_bins); and not __fish_seen_subcommand_from (__fish__get_bun_scripts); and __fish_seen_subcommand_from create" -f -a 'react' -d 'Create a new React project' +complete -c bun \ + -n "not __fish_seen_subcommand_from $bun_builtin_cmds_without_create; and not __fish_seen_subcommand_from (__fish__get_bun_bins); and not __fish_seen_subcommand_from (__fish__get_bun_scripts); and __fish_seen_subcommand_from create next" -F +complete -c bun \ + -n "not __fish_seen_subcommand_from $bun_builtin_cmds; and not __fish_seen_subcommand_from (__fish__get_bun_bins); and not __fish_seen_subcommand_from (__fish__get_bun_scripts); and __fish_use_subcommand" -a 'upgrade' -d 'Upgrade Bun to the latest version' -x +complete -c bun \ + -n "not __fish_seen_subcommand_from $bun_builtin_cmds; and not __fish_seen_subcommand_from (__fish__get_bun_bins); and not __fish_seen_subcommand_from (__fish__get_bun_scripts); and __fish_use_subcommand" -a '--help' -d 'See all commands and flags' -x + +complete -c bun \ + -n "not __fish_seen_subcommand_from $bun_builtin_cmds; and not __fish_seen_subcommand_from (__fish__get_bun_bins); and not __fish_seen_subcommand_from (__fish__get_bun_scripts); and __fish_use_subcommand" -l "version" -s "v" -a '--version' -d 'Bun\'s version' -x +complete -c bun \ + -n "not __fish_seen_subcommand_from $bun_builtin_cmds; and not __fish_seen_subcommand_from (__fish__get_bun_bins); and not __fish_seen_subcommand_from (__fish__get_bun_scripts); and __fish_use_subcommand" -a 'discord' -d 'Open Bun\'s Discord server' -x diff --git a/completions/bun.zsh b/completions/bun.zsh new file mode 100644 index 000000000..f63861e0b --- /dev/null +++ b/completions/bun.zsh @@ -0,0 +1,63 @@ +__bun_first_cmd() { + echo "${words[2]}" +} + +__bun_first_cmd_arg() { + echo "${words[3]}" +} + +__bun_arg_count() { + echo "$#words" +} + +_bun_run() { + if [[ ("$(__bun_arg_count)" = "2") ]]; then + local -a options + options=(${(f)"$(SHELL=zsh bun getcompletes)"}) + + _describe 'values' options + elif [[ ("$(__bun_arg_count)" = "3") ]]; then + local -a run + run=("${(f)"$(SHELL=zsh bun getcompletes g)"}") + compadd $run + else + _files + return + fi + + # Make sure we don't run default completion + custom_completion=true +} + +_bun() { + + # Store custom completion status + local custom_completion=false + + # Load custom completion commands + case "$(__bun_first_cmd)" in + create) + return; + ;; + dev) + return; + ;; + bun) + return; + ;; + upgrade) + return; + ;; + discord) + return; + ;; + run) + _bun_run + ;; + esac + + # Fall back to default completion if we haven't done a custom one + [[ $custom_completion = false ]] && _bun_run +} + +compdef _bun bun diff --git a/src/bun_js.zig b/src/bun_js.zig index 681aa08ef..e5fb2bcd7 100644 --- a/src/bun_js.zig +++ b/src/bun_js.zig @@ -48,7 +48,8 @@ pub const Run = struct { } else { run.vm.log.printForLogLevelWithEnableAnsiColors(Output.errorWriter(), false) catch {}; } - + Output.prettyErrorln("\n", .{}); + Output.flush(); std.os.exit(1); }; run.vm.bundler.configureDefines() catch |err| { @@ -57,7 +58,8 @@ pub const Run = struct { } else { run.vm.log.printForLogLevelWithEnableAnsiColors(Output.errorWriter(), false) catch {}; } - + Output.prettyErrorln("\n", .{}); + Output.flush(); std.os.exit(1); }; diff --git a/src/cli.zig b/src/cli.zig index 5274deafc..f26bd4b6f 100644 --- a/src/cli.zig +++ b/src/cli.zig @@ -613,7 +613,244 @@ pub const Command = struct { } pub const InstallCompletionsCommand = struct { - pub fn exec(ctx: *std.mem.Allocator) !void {} + pub fn testPath(completions_dir: string) !std.fs.Dir {} + pub fn exec(allocator: *std.mem.Allocator) !void { + var shell = ShellCompletions.Shell.unknown; + if (std.os.getenv("SHELL")) |shell_name| { + shell = ShellCompletions.Shell.fromEnv(@TypeOf(shell_name), shell_name); + } + + switch (shell) { + .bash => { + Output.prettyErrorln("<r><red>error:<r> Bash completions aren't implemented yet, just zsh & fish. A PR is welcome!", .{}); + std.os.exit(1); + }, + .unknown => { + Output.prettyErrorln("<r><red>error:<r> Unknown or unsupported shell. Please set $SHELL to one of zsh, fish, or bash. To manually output completions, run this:\n bun getcompletes", .{}); + std.os.exit(1); + }, + else => {}, + } + + var stdout = std.io.getStdOut(); + + if (std.os.getenvZ("IS_BUN_AUTO_UPDATE") == null) { + if (!stdout.isTty()) { + try stdout.writeAll(shell.completions()); + std.os.exit(0); + } + } + + var completions_dir: string = ""; + found: { + var cwd_buf: [std.fs.MAX_PATH_BYTES]u8 = undefined; + var cwd = std.os.getcwd(&cwd_buf) catch { + Output.prettyErrorln("<r><red>error<r>: Could not get current working directory", .{}); + Output.flush(); + std.os.exit(1); + }; + + for (std.os.argv) |arg, i| { + if (strings.eqlComptime(std.mem.span(arg), "completions")) { + if (std.os.argv.len > i + 1) { + const input = std.mem.span(std.os.argv[i + 1]); + + if (!std.fs.path.isAbsolute(input)) { + completions_dir = resolve_path.joinAbs( + cwd, + .auto, + input, + ); + } + + if (!std.fs.path.isAbsolute(completions_dir)) { + Output.prettyErrorln("<r><red>error:<r> Please pass an absolute path. {s} is invalid", .{completions_dir}); + Output.flush(); + std.os.exit(1); + } + + std.os.access(completions_dir, std.os.O_DIRECTORY | std.os.O_WRONLY) catch |err| { + Output.prettyErrorln("<r><red>error:<r> accessing {s} errored {s}", .{ completions_dir, @errorName(err) }); + Output.flush(); + std.os.exit(1); + }; + + break :found; + } + + break; + } + } + + switch (shell) { + .fish => { + if (std.os.getenvZ("XDG_CONFIG_HOME")) |config_dir| { + outer: { + var paths = [_]string{ std.mem.span(config_dir), "./fish/completions" }; + completions_dir = resolve_path.joinAbsString(cwd, &paths, .auto); + std.os.access(completions_dir, std.os.O_DIRECTORY | std.os.O_WRONLY) catch |err| { + Output.prettyErrorln("<r><red>{s}:<r> accessing {s}, trying next one.", .{ + @errorName(err), + completions_dir, + }); + Output.flush(); + break :outer; + }; + break :found; + } + } + + if (std.os.getenvZ("XDG_DATA_HOME")) |data_dir| { + outer: { + var paths = [_]string{ std.mem.span(data_dir), "./fish/completions" }; + completions_dir = resolve_path.joinAbsString(cwd, &paths, .auto); + + std.os.access(completions_dir, std.os.O_DIRECTORY | std.os.O_WRONLY) catch |err| { + Output.prettyErrorln("<r><red>{s}:<r> accessing {s}, trying next one.", .{ + @errorName(err), + completions_dir, + }); + Output.flush(); + break :outer; + }; + + break :found; + } + } + + if (std.os.getenvZ("HOME")) |home_dir| { + outer: { + var paths = [_]string{ std.mem.span(home_dir), "./.config/fish/completions" }; + completions_dir = resolve_path.joinAbsString(cwd, &paths, .auto); + std.os.access(completions_dir, std.os.O_DIRECTORY | std.os.O_WRONLY) catch |err| { + Output.prettyErrorln("<r><red>{s}:<r> accessing {s}, trying next one.", .{ + @errorName(err), + completions_dir, + }); + Output.flush(); + break :outer; + }; + break :found; + } + } + + outer: { + if (Environment.isMac) { + if (!Environment.isAarch64) { + // homebrew fish + completions_dir = "/usr/local/share/fish/completions"; + std.os.access(completions_dir, std.os.O_WRONLY | std.os.O_DIRECTORY) catch |err| { + Output.prettyErrorln("<r><red>{s}:<r> accessing {s}, trying next one.", .{ + @errorName(err), + completions_dir, + }); + Output.flush(); + break :outer; + }; + break :found; + } else { + // homebrew fish + completions_dir = "/opt/homebrew/share/fish/completions"; + std.os.access(completions_dir, std.os.O_WRONLY | std.os.O_DIRECTORY) catch |err| { + Output.prettyErrorln("<r><red>{s}:<r> accessing {s}, trying next one.", .{ + @errorName(err), + completions_dir, + }); + Output.flush(); + break :outer; + }; + break :found; + } + } + } + + { + completions_dir = "/etc/fish/completions"; + std.os.access(completions_dir, std.os.O_WRONLY | std.os.O_DIRECTORY) catch |err| { + Output.prettyErrorln( + "<r><red>error:<r> Could not find a directory to install completions in. Please either pipe \"bun completions > /to/a/file\" or pass a directory in like this:\n bun completions /my/completions/dir", + .{}, + ); + Output.flush(); + std.os.exit(1); + }; + break :found; + } + }, + .zsh => { + if (std.os.getenvZ("fpath")) |fpath| { + var splitter = std.mem.split(u8, std.mem.span(fpath), " "); + + while (splitter.next()) |dir| { + std.os.access(dir, std.os.O_DIRECTORY | std.os.O_WRONLY) catch continue; + completions_dir = dir; + break :found; + } + } + }, + .bash => { + Output.prettyErrorln("<r><red>error:<r> Please pass a directory or pipe to a file. Not sure where bash completions go.", .{}); + Output.flush(); + std.os.exit(1); + }, + else => unreachable, + } + + Output.prettyErrorln( + "<r><red>error:<r> Could not find a directory to install completions in. Please either pipe \"bun completions > /to/a/file\" or pass a directory in like this:\n bun completions /my/completions/dir", + .{}, + ); + Output.flush(); + std.os.exit(1); + } + + const filename = switch (shell) { + .fish => "bun.fish", + .zsh => "_bun", + .bash => "_bun.bash", + else => unreachable, + }; + + std.debug.assert(completions_dir.len > 0); + + var output_dir = std.fs.openDirAbsolute(completions_dir, .{ .iterate = true }) catch |err| { + Output.prettyErrorln("<r><red>error:<r> Could not open {s} for writing: {s}", .{ + completions_dir, + @errorName(err), + }); + Output.flush(); + std.os.exit(1); + }; + + var output_file = output_dir.createFileZ(filename, .{ + .truncate = true, + }) catch |err| { + Output.prettyErrorln("<r><red>error:<r> Could not open {s} for writing: {s}", .{ + filename, + @errorName(err), + }); + Output.flush(); + std.os.exit(1); + }; + + output_file.writeAll(shell.completions()) catch |err| { + Output.prettyErrorln("<r><red>error:<r> Could not write to {s}: {s}", .{ + filename, + @errorName(err), + }); + Output.flush(); + std.os.exit(1); + }; + + output_file.close(); + output_dir.close(); + + Output.prettyErrorln("<r><d>Installed completions to {s}/{s}<r>", .{ + completions_dir, + filename, + }); + Output.flush(); + } }; const default_completions_list = [_]string{ @@ -651,6 +888,10 @@ pub const Command = struct { try BuildCommand.exec(ctx); }, + .InstallCompletionsCommand => { + try InstallCompletionsCommand.exec(allocator); + return; + }, .GetCompletionsCommand => { const ctx = try Command.Context.create(allocator, log, .GetCompletionsCommand); var filter = ctx.positionals; @@ -677,6 +918,10 @@ pub const Command = struct { completions = try RunCommand.completions(ctx, null, .bin); } else if (strings.eqlComptime(filter[0], "r")) { completions = try RunCommand.completions(ctx, null, .all); + } else if (strings.eqlComptime(filter[0], "g")) { + completions = try RunCommand.completions(ctx, null, .all_plus_bun_js); + } else if (strings.eqlComptime(filter[0], "j")) { + completions = try RunCommand.completions(ctx, null, .bun_js); } completions.print(); diff --git a/src/cli/install.sh b/src/cli/install.sh index 3e487f448..e49d69f64 100644 --- a/src/cli/install.sh +++ b/src/cli/install.sh @@ -13,41 +13,40 @@ BGreen='' if test -t 1; then # Reset - Color_Off='\033[0m' # Text Reset + Color_Off='\033[0m' # Text Reset # Regular Colors - Red='\033[0;31m' # Red - Green='\033[0;32m' # Green - White='\033[0;37m' # White + Red='\033[0;31m' # Red + Green='\033[0;32m' # Green + White='\033[0;37m' # White # Bold - BGreen='\033[1;32m' # Green - BWhite='\033[1;37m' # White + BGreen='\033[1;32m' # Green + BWhite='\033[1;37m' # White fi - if ! command -v unzip >/dev/null; then - echo -e "${Red}error${Color_Off}: unzip is required to install Bun (see: https://github.com/Jarred-Sumner/bun#unzip-is-required)." 1>&2 - exit 1 + echo -e "${Red}error${Color_Off}: unzip is required to install Bun (see: https://github.com/Jarred-Sumner/bun#unzip-is-required)." 1>&2 + exit 1 fi if [ "$OS" = "Windows_NT" ]; then - echo "error: Please install Bun using Windows Subsystem for Linux." + echo "error: Please install Bun using Windows Subsystem for Linux." exit 1 else - case $(uname -sm) in - "Darwin x86_64") target="darwin-x64" ;; - "Darwin arm64") target="darwin-aarch64" ;; - *) target="linux-x64" ;; - esac + case $(uname -sm) in + "Darwin x86_64") target="darwin-x64" ;; + "Darwin arm64") target="darwin-aarch64" ;; + *) target="linux-x64" ;; + esac fi github_repo="https://github.com/Jarred-Sumner/bun-releases-for-updater" if [ $# -eq 0 ]; then - bun_uri="$github_repo/releases/latest/download/bun-${target}.zip" + bun_uri="$github_repo/releases/latest/download/bun-${target}.zip" else - bun_uri="$github_repo/releases/download/${1}/bun-${target}.zip" + bun_uri="$github_repo/releases/download/${1}/bun-${target}.zip" fi bun_install="${BUN_INSTALL:-$HOME/.bun}" @@ -55,27 +54,32 @@ bin_dir="$bun_install/bin" exe="$bin_dir/bun" if [ ! -d "$bin_dir" ]; then - mkdir -p "$bin_dir" + mkdir -p "$bin_dir" + + if (($?)); then + echo -e "${Red}error${Color_Off}: Failed to create install directory $bin_dir" 1>&2 + exit 1 + fi fi curl --fail --location --progress-bar --output "$exe.zip" "$bun_uri" -if (( $? )); then +if (($?)); then echo -e "${Red}error${Color_Off}: Failed to download Bun from $bun_uri" 1>&2 exit 1 fi unzip -d "$bin_dir" -q -o "$exe.zip" -if (( $? )); then +if (($?)); then echo -e "${Red}error${Color_Off}: Failed to extract Bun" 1>&2 exit 1 fi mv "$bin_dir/bun-${target}/bun" "$exe" -if (( $? )); then +if (($?)); then echo -e "${Red}error${Color_Off}: Failed to extract Bun" 1>&2 exit 1 fi chmod +x "$exe" -if (( $? )); then +if (($?)); then echo -e "${Red}error${Color_Off}: Failed to set permissions on bun executable." 1>&2 exit 1 fi @@ -85,22 +89,47 @@ rm "$exe.zip" echo -e "${Green}Bun was installed successfully to ${BGreen}$exe$Color_Off" if command -v bun --version >/dev/null; then + # Install completions, but we don't care if it fails + IS_BUN_AUTO_UPDATE="true" $exe completions >/dev/null 2>&1 + echo "Run 'bun --help' to get started" exit 0 fi if test $(basename $SHELL) == "fish"; then - echo "" - echo "Manually add the directory to your \$HOME/.config/fish" - echo "" - echo -e " $BWhite set -Ux BUN_INSTALL \"$bun_install\"$Color_Off" - echo -e " $BWhite set -px --path PATH \"$bin_dir\"$Color_Off" -elif test $(basename $SHELL) == "zsh"; then - echo "" - echo "Manually add the directory to your \$HOME/.zshrc (or similar)" - echo "" - echo -e " $BWhite export BUN_INSTALL=\"$bun_install\"$Color_Off" - echo -e " $BWhite export PATH=\"\$BUN_INSTALL/bin:\$PATH\"$Color_Off" + # Install completions, but we don't care if it fails + IS_BUN_AUTO_UPDATE="true" SHELL="fish" $exe completions >/dev/null 2>&1 + if test -f $HOME/.config/fish; then + echo -e "\nset -Ux BUN_INSTALL \"$bun_install\"\n" >>"$HOME/.config/fish" + echo -e "\nset -px --path PATH \"$bin_dir\"\n" >>"$HOME/.config/fish" + echo "" + echo -e "$BWhite Added \"$bin_dir\" to PATH in \"$HOME/.config/fish\"$Color_Off" + else + echo "" + echo "Manually add the directory to your \$HOME/.config/fish" + echo "" + echo -e " $BWhite set -Ux BUN_INSTALL \"$bun_install\"$Color_Off" + echo -e " $BWhite set -px --path PATH \"$bin_dir\"$Color_Off" + fi +elif + test $(basename $SHELL) == "zsh" +then + # Install completions, but we don't care if it fails + IS_BUN_AUTO_UPDATE="true" SHELL="zsh" $exe completions >/dev/null 2>&1 + + if test -f $HOME/.zshrc; then + echo -e "export BUN_INSTALL=\"$bun_install\"" >>"$HOME/.zshrc" + echo -e "export PATH=\"\$BUN_INSTALL/bin:\$PATH\"" >>"$HOME/.zshrc" + echo "" + echo -e "$BWhite Added \"$bin_dir\" to PATH in \"$HOME/.zshrc\"$Color_Off" + else + echo "" + echo "Manually add the directory to your \$HOME/.zshrc (or similar)" + echo "" + echo -e " $BWhite export BUN_INSTALL=\"$bun_install\"$Color_Off" + echo -e " $BWhite export PATH=\"\$BUN_INSTALL/bin:\$PATH\"$Color_Off" + fi + else echo "" echo "Manually add the directory to your \$HOME/.bashrc (or similar)" @@ -111,4 +140,4 @@ fi echo "" echo -e "To get started, run" echo -e "$BWhite" -echo -e " bun --help$Color_Off"
\ No newline at end of file +echo -e " bun --help$Color_Off" diff --git a/src/cli/run_command.zig b/src/cli/run_command.zig index 60688e5a9..4593f5f6a 100644 --- a/src/cli/run_command.zig +++ b/src/cli/run_command.zig @@ -273,6 +273,8 @@ pub const RunCommand = struct { script, bin, all, + bun_js, + all_plus_bun_js, }; pub fn completions(ctx: Command.Context, default_completions: ?[]const string, comptime filter: Filter) !ShellCompletions { @@ -327,7 +329,7 @@ pub const RunCommand = struct { } } - if (filter == Filter.bin or filter == Filter.all) { + if (filter == Filter.bin or filter == Filter.all or filter == Filter.all_plus_bun_js) { for (this_bundler.resolver.binDirs()) |bin_path| { if (this_bundler.resolver.readDirInfo(bin_path) catch null) |bin_dir| { if (bin_dir.getEntriesConst()) |entries| { @@ -360,7 +362,22 @@ pub const RunCommand = struct { } } - if (filter == Filter.script or filter == Filter.all) { + if (filter == Filter.all_plus_bun_js or filter == Filter.bun_js) { + if (this_bundler.resolver.readDirInfo(this_bundler.fs.top_level_dir) catch null) |dir_info| { + if (dir_info.getEntriesConst()) |entries| { + var iter = entries.data.iterator(); + + while (iter.next()) |entry| { + const name = entry.value.base(); + if (this_bundler.options.loader(std.fs.path.extension(name)).isJavaScriptLike() and !strings.contains(name, ".d.ts") and entry.value.kind(&this_bundler.fs.fs) == .file) { + _ = try results.getOrPut(this_bundler.fs.filename_store.append(@TypeOf(name), name) catch continue); + } + } + } + } + } + + if (filter == Filter.script or filter == Filter.all or filter == Filter.all_plus_bun_js) { if (root_dir_info.enclosing_package_json) |package_json| { if (package_json.scripts) |scripts| { try results.ensureUnusedCapacity(scripts.count()); diff --git a/src/cli/shell_completions.zig b/src/cli/shell_completions.zig index f64e7befb..295593d49 100644 --- a/src/cli/shell_completions.zig +++ b/src/cli/shell_completions.zig @@ -7,6 +7,19 @@ pub const Shell = enum { zsh, fish, + const bash_completions = @embedFile("../../completions/bun.bash"); + const zsh_completions = @embedFile("../../completions/bun.zsh"); + const fish_completions = @embedFile("../../completions/bun.fish"); + + pub fn completions(this: Shell) []const u8 { + return switch (this) { + .bash => std.mem.span(bash_completions), + .zsh => std.mem.span(zsh_completions), + .fish => std.mem.span(fish_completions), + else => "", + }; + } + pub fn fromEnv(comptime Type: type, SHELL: Type) Shell { const basename = std.fs.path.basename(SHELL); if (strings.eqlComptime(basename, "bash")) { @@ -30,12 +43,13 @@ pub fn print(this: @This()) void { var writer = Output.writer(); if (this.commands.len == 0) return; + const delimiter = if (this.shell == Shell.fish) " " else "\n"; writer.writeAll(this.commands[0]) catch return; if (this.commands.len > 1) { for (this.commands[1..]) |cmd, i| { - writer.writeAll(" ") catch return; + writer.writeAll(delimiter) catch return; writer.writeAll(cmd) catch return; } diff --git a/src/exact_size_matcher.zig b/src/exact_size_matcher.zig index 3fd6c2056..3c122e9d9 100644 --- a/src/exact_size_matcher.zig +++ b/src/exact_size_matcher.zig @@ -4,7 +4,7 @@ pub fn ExactSizeMatcher(comptime max_bytes: usize) type { const a: u32 = 1000; switch (max_bytes) { - 1, 2, 4, 8, 12 => {}, + 1, 2, 4, 8, 12, 16 => {}, else => { @compileError("max_bytes must be 1, 2, 4, 8, or 12."); }, @@ -64,3 +64,10 @@ test "ExactSizeMatcher 4 letter" { try expect(Four.match(word) == Four.case("from")); try expect(Four.match(word) != Four.case("fro")); } + +test "ExactSizeMatcher 12 letter" { + const Four = ExactSizeMatcher(12); + const word = "from"; + try expect(Four.match(word) == Four.case("from")); + try expect(Four.match(word) != Four.case("fro")); +} |