Skip to content

Commit 45e9a11

Browse files
Merge pull request #72 from hendriknielaender/auto-completion
feat: zsh & bash auto completion
2 parents e139cb3 + b767352 commit 45e9a11

File tree

3 files changed

+136
-2
lines changed

3 files changed

+136
-2
lines changed

README.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,48 @@ irm https://raw.githubusercontent.com/hendriknielaender/zvm/master/install.ps1 |
4545
powershell -c "irm https://raw.githubusercontent.com/hendriknielaender/zvm/master/install.ps1 | iex"
4646
```
4747

48+
## Shell Completions
49+
50+
`zvm` provides built-in shell completion scripts for both Zsh and Bash. This enhances the command-line experience by allowing tab-completion of subcommands, flags, etc.
51+
52+
### Zsh
53+
54+
1. **Generate** the Zsh completion script:
55+
```bash
56+
zvm completions zsh > _zvm
57+
```
58+
2. **Move** `_zvm` into a directory that Zsh checks for autoloaded completion scripts. For example:
59+
```bash
60+
mkdir -p ~/.zsh/completions
61+
mv _zvm ~/.zsh/completions
62+
```
63+
3. **Add** this to your `~/.zshrc`:
64+
```bash
65+
fpath+=(~/.zsh/completions)
66+
autoload -U compinit && compinit
67+
```
68+
4. **Reload** your shell:
69+
```bash
70+
source ~/.zshrc
71+
```
72+
5. **Test**:
73+
```bash
74+
zvm <TAB>
75+
```
76+
You should see a list of subcommands like `ls`, `install`, `use`, etc.
77+
78+
### Bash
79+
80+
1. **Generate** the Bash completion script:
81+
```bash
82+
zvm completions bash > zvm.bash
83+
```
84+
2. **Source** it in your `~/.bashrc` (or `~/.bash_profile`):
85+
```bash
86+
echo "source $(pwd)/zvm.bash" >> ~/.bashrc
87+
source ~/.bashrc
88+
```
89+
4890
## Usage
4991

5092
**General Syntax:**

build.zig

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ const builtin = @import("builtin");
44
const Build = std.Build;
55

66
const min_zig_string = "0.13.0";
7-
const semver = std.SemanticVersion{ .major = 0, .minor = 7, .patch = 0 };
8-
const semver_string = "0.7.0";
7+
const semver = std.SemanticVersion{ .major = 0, .minor = 8, .patch = 0 };
8+
const semver_string = "0.8.0";
99

1010
const CrossTargetInfo = struct {
1111
crossTarget: std.zig.CrossTarget,

src/command.zig

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ pub const Command = enum {
2323
Clean,
2424
Version,
2525
Help,
26+
Completions,
2627
Unknown,
2728
};
2829

@@ -49,6 +50,7 @@ const command_opts = [_]CommandOption{
4950
.{ .short_handle = null, .handle = "clean", .cmd = Command.Clean },
5051
.{ .short_handle = "-v", .handle = "--version", .cmd = Command.Version },
5152
.{ .short_handle = null, .handle = "--help", .cmd = Command.Help },
53+
.{ .short_handle = null, .handle = "completions", .cmd = .Completions },
5254
};
5355

5456
/// Parse and handle commands
@@ -122,6 +124,7 @@ pub fn handle_command(params: []const []const u8, root_node: std.Progress.Node)
122124
.Clean => try clean_store(),
123125
.Version => try get_version(),
124126
.Help => try display_help(),
127+
.Completions => try handle_completions(params),
125128
.Unknown => try handle_unknown(),
126129
}
127130
}
@@ -526,6 +529,95 @@ fn display_help() !void {
526529
try color.print(help_message);
527530
}
528531

532+
fn handle_completions(params: []const []const u8) !void {
533+
if (params.len < 3) {
534+
std.debug.print("Usage: zvm completions [zsh|bash]\n", .{});
535+
return;
536+
}
537+
538+
const shell = params[2];
539+
540+
if (std.mem.eql(u8, shell, "zsh")) {
541+
try handle_completions_zsh();
542+
} else if (std.mem.eql(u8, shell, "bash")) {
543+
try handle_completions_bash();
544+
} else {
545+
std.debug.print("Unsupported shell: {s}\n", .{shell});
546+
std.debug.print("Usage: zvm completions [zsh|bash]\n", .{});
547+
}
548+
}
549+
550+
fn handle_completions_zsh() !void {
551+
const zsh_script =
552+
\\#compdef zvm
553+
\\
554+
\\# ZVM top-level commands (example)
555+
\\local -a _zvm_commands
556+
\\_zvm_commands=(
557+
\\ 'ls:List local or remote versions'
558+
\\ 'install:Install a version of Zig or zls'
559+
\\ 'use:Switch to a local version of Zig or zls'
560+
\\ 'remove:Remove a local version of Zig or zls'
561+
\\ 'clean:Remove old artifacts'
562+
\\ '--version:Show zvm version'
563+
\\ '--help:Show help message'
564+
\\ 'completions:Generate completion script'
565+
\\)
566+
\\
567+
\\_arguments \
568+
\\ '1: :->cmds' \
569+
\\ '*:: :->args'
570+
\\
571+
\\case $state in
572+
\\ cmds)
573+
\\ _describe -t commands "zvm command" _zvm_commands
574+
\\ ;;
575+
\\ args)
576+
\\ # Subcommand-specific completions if needed
577+
\\ ;;
578+
\\esac
579+
;
580+
581+
const out = std.io.getStdOut().writer();
582+
try out.print("{s}\n", .{zsh_script});
583+
}
584+
585+
fn handle_completions_bash() !void {
586+
const bash_script =
587+
\\#!/usr/bin/env bash
588+
\\# zvm Bash completion
589+
\\
590+
\\_zvm_completions() {
591+
\\ local cur prev words cword
592+
\\ _init_completion || return
593+
\\
594+
\\ local commands="ls install use remove clean --version --help completions"
595+
\\
596+
\\ if [[ $cword -eq 1 ]]; then
597+
\\ COMPREPLY=( $( compgen -W "$commands" -- "$cur" ) )
598+
\\ else
599+
\\ # Add subcommand-specific logic here
600+
\\ case "$prev" in
601+
\\ install)
602+
\\ # e.g. list remote versions
603+
\\ ;;
604+
\\ use)
605+
\\ # e.g. list local versions
606+
\\ ;;
607+
\\ remove)
608+
\\ # e.g. remove local versions
609+
\\ ;;
610+
\\ esac
611+
\\ fi
612+
\\}
613+
\\
614+
\\complete -F _zvm_completions zvm
615+
;
616+
617+
const out = std.io.getStdOut().writer();
618+
try out.print("{s}\n", .{bash_script});
619+
}
620+
529621
fn handle_unknown() !void {
530622
comptime var color = util_color.Color.ComptimeStyle.init();
531623
try color.bold().red().print("Unknown command. Use 'zvm --help' for usage information.\n");

0 commit comments

Comments
 (0)