Add init and cd shell integration commands
This commit is contained in:
parent
4a465f7787
commit
83787ab868
|
|
@ -11,7 +11,7 @@ function __sandlot_sessions
|
|||
end
|
||||
|
||||
# All top-level subcommands (used for __fish_seen_subcommand_from checks)
|
||||
set -l __sandlot_cmds new list open review shell close rm merge save diff show log dir cleanup vm
|
||||
set -l __sandlot_cmds new list open review shell close rm merge save diff show log dir cd init cleanup vm
|
||||
set -l __sandlot_vm_cmds create start shell status info stop destroy
|
||||
|
||||
# Disable file completions
|
||||
|
|
@ -31,12 +31,14 @@ complete -c sandlot -n "not __fish_seen_subcommand_from $__sandlot_cmds" -a diff
|
|||
complete -c sandlot -n "not __fish_seen_subcommand_from $__sandlot_cmds" -a show -d 'Show prompt and diff for a branch'
|
||||
complete -c sandlot -n "not __fish_seen_subcommand_from $__sandlot_cmds" -a log -d 'Show commits not on main'
|
||||
complete -c sandlot -n "not __fish_seen_subcommand_from $__sandlot_cmds" -a dir -d 'Print the worktree path'
|
||||
complete -c sandlot -n "not __fish_seen_subcommand_from $__sandlot_cmds" -a cd -d 'Change to a branch worktree directory'
|
||||
complete -c sandlot -n "not __fish_seen_subcommand_from $__sandlot_cmds" -a init -d 'Print shell init script'
|
||||
complete -c sandlot -n "not __fish_seen_subcommand_from $__sandlot_cmds" -a cleanup -d 'Remove stale sessions'
|
||||
complete -c sandlot -n "not __fish_seen_subcommand_from $__sandlot_cmds" -a vm -d 'Manage the sandlot VM'
|
||||
|
||||
# ── Session branch completions ───────────────────────────────────────
|
||||
|
||||
for cmd in open review shell close rm merge save diff show log dir
|
||||
for cmd in open review shell close rm merge save diff show log dir cd
|
||||
complete -c sandlot -n "__fish_seen_subcommand_from $cmd; and not __fish_seen_subcommand_from vm" -a '(__sandlot_sessions)'
|
||||
end
|
||||
|
||||
|
|
@ -71,5 +73,9 @@ complete -c sandlot -n "__fish_seen_subcommand_from vm; and not __fish_seen_subc
|
|||
|
||||
# ── Global options ───────────────────────────────────────────────────
|
||||
|
||||
# ── init shell completions ───────────────────────────────────────
|
||||
|
||||
complete -c sandlot -n '__fish_seen_subcommand_from init' -a 'fish bash zsh' -d 'Shell type'
|
||||
|
||||
complete -c sandlot -s h -l help -d 'Show help'
|
||||
complete -c sandlot -s V -l version -d 'Show version'
|
||||
|
|
|
|||
14
src/cli.ts
14
src/cli.ts
|
|
@ -24,6 +24,8 @@ import { action as editAction } from "./commands/edit.ts"
|
|||
import { action as cleanupAction } from "./commands/cleanup.ts"
|
||||
import { register as registerVmCommands } from "./commands/vm.ts"
|
||||
import { action as completionsAction } from "./commands/completions.ts"
|
||||
import { action as initAction } from "./commands/init.ts"
|
||||
import { action as cdAction } from "./commands/cd.ts"
|
||||
|
||||
const pkg = await Bun.file(new URL("../package.json", import.meta.url)).json()
|
||||
|
||||
|
|
@ -172,6 +174,12 @@ program
|
|||
.description("Print the worktree path for a session")
|
||||
.action(dirAction)
|
||||
|
||||
program
|
||||
.command("cd")
|
||||
.argument("<branch>", "branch name")
|
||||
.description("Change to a branch's worktree directory")
|
||||
.action(cdAction)
|
||||
|
||||
// ── Admin ───────────────────────────────────────────────────────────
|
||||
|
||||
program.commandsGroup("Admin Commands:")
|
||||
|
|
@ -208,6 +216,12 @@ program
|
|||
.description("Output fish shell completions")
|
||||
.action((opts: { install?: boolean }) => completionsAction(program, opts))
|
||||
|
||||
program
|
||||
.command("init")
|
||||
.argument("<shell>", "shell type (fish, bash, zsh)")
|
||||
.description("Print shell init script (eval in your shell config)")
|
||||
.action((shell: string) => initAction(program, shell))
|
||||
|
||||
// ── Default: `sandlot` → `sandlot list` ─────────────────────────────
|
||||
|
||||
if (process.argv.length === 2) {
|
||||
|
|
|
|||
7
src/commands/cd.ts
Normal file
7
src/commands/cd.ts
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
import { die } from "../fmt.ts"
|
||||
|
||||
export function action(): never {
|
||||
die(
|
||||
`"sandlot cd" requires shell integration.\n\nAdd one of these to your shell config:\n\n Fish (~/.config/fish/config.fish):\n sandlot init fish | source\n\n Bash (~/.bashrc):\n eval "$(sandlot init bash)"\n\n Zsh (~/.zshrc):\n eval "$(sandlot init zsh)"`
|
||||
)
|
||||
}
|
||||
|
|
@ -1,22 +1,9 @@
|
|||
import type { Command, Option } from "commander"
|
||||
|
||||
/** Generate fish completions dynamically from the Commander program tree. */
|
||||
export function action(program: Command, opts: { install?: boolean } = {}) {
|
||||
if (opts.install) {
|
||||
const dest = "~/.config/fish/completions/sandlot.fish"
|
||||
const lines = [
|
||||
"#!/bin/sh",
|
||||
`mkdir -p ~/.config/fish/completions`,
|
||||
`sandlot completions > ${dest}`,
|
||||
`echo "Installed fish completions to ${dest}"`,
|
||||
]
|
||||
process.stdout.write(lines.join("\n") + "\n")
|
||||
return
|
||||
}
|
||||
|
||||
/** Generate fish completion lines from the Commander program tree. */
|
||||
export function generateFishCompletions(program: Command): string[] {
|
||||
const lines: string[] = [
|
||||
"# Fish completions for sandlot (auto-generated)",
|
||||
"# Install: sandlot completions > ~/.config/fish/completions/sandlot.fish",
|
||||
"",
|
||||
"complete -c sandlot -f",
|
||||
"",
|
||||
|
|
@ -65,6 +52,26 @@ export function action(program: Command, opts: { install?: boolean } = {}) {
|
|||
}
|
||||
|
||||
lines.push("")
|
||||
return lines
|
||||
}
|
||||
|
||||
/** Generate fish completions dynamically from the Commander program tree. */
|
||||
export function action(program: Command, opts: { install?: boolean } = {}) {
|
||||
if (opts.install) {
|
||||
const dest = "~/.config/fish/completions/sandlot.fish"
|
||||
const lines = [
|
||||
"#!/bin/sh",
|
||||
`mkdir -p ~/.config/fish/completions`,
|
||||
`sandlot completions > ${dest}`,
|
||||
`echo "Installed fish completions to ${dest}"`,
|
||||
]
|
||||
process.stdout.write(lines.join("\n") + "\n")
|
||||
return
|
||||
}
|
||||
|
||||
const lines = generateFishCompletions(program)
|
||||
// Add install hint at the top
|
||||
lines.splice(1, 0, "# Install: sandlot completions > ~/.config/fish/completions/sandlot.fish")
|
||||
process.stdout.write(lines.join("\n") + "\n")
|
||||
}
|
||||
|
||||
|
|
|
|||
88
src/commands/init.ts
Normal file
88
src/commands/init.ts
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
import type { Command } from "commander"
|
||||
import { die } from "../fmt.ts"
|
||||
import { generateFishCompletions } from "./completions.ts"
|
||||
|
||||
export function action(program: Command, shell: string) {
|
||||
switch (shell) {
|
||||
case "fish":
|
||||
return emitFish(program)
|
||||
case "bash":
|
||||
return emitBash(program)
|
||||
case "zsh":
|
||||
return emitZsh(program)
|
||||
default:
|
||||
die(`Unsupported shell: ${shell}. Supported shells: fish, bash, zsh`)
|
||||
}
|
||||
}
|
||||
|
||||
function emitFish(program: Command) {
|
||||
const lines: string[] = [
|
||||
"function sandlot --wraps sandlot --description 'Sandlot CLI wrapper'",
|
||||
" if test (count $argv) -ge 1; and test \"$argv[1]\" = cd",
|
||||
" set -l dir (command sandlot dir $argv[2..])",
|
||||
" and cd $dir",
|
||||
" else",
|
||||
" command sandlot $argv",
|
||||
" end",
|
||||
"end",
|
||||
"",
|
||||
...generateFishCompletions(program),
|
||||
]
|
||||
process.stdout.write(lines.join("\n") + "\n")
|
||||
}
|
||||
|
||||
function emitBash(program: Command) {
|
||||
const { subcommands, branchCommands } = collectCommands(program)
|
||||
const lines: string[] = [
|
||||
"sandlot() {",
|
||||
' if [ "$#" -ge 1 ] && [ "$1" = "cd" ]; then',
|
||||
" local dir",
|
||||
' dir="$(command sandlot dir "${@:2}")" && cd "$dir"',
|
||||
" else",
|
||||
' command sandlot "$@"',
|
||||
" fi",
|
||||
"}",
|
||||
"",
|
||||
"_sandlot_completions() {",
|
||||
' local cur prev',
|
||||
' cur="${COMP_WORDS[COMP_CWORD]}"',
|
||||
' prev="${COMP_WORDS[COMP_CWORD-1]}"',
|
||||
"",
|
||||
" if [ \"$COMP_CWORD\" -eq 1 ]; then",
|
||||
` COMPREPLY=( $(compgen -W "${subcommands.join(" ")}" -- "$cur") )`,
|
||||
" return",
|
||||
" fi",
|
||||
"",
|
||||
` case "$prev" in`,
|
||||
` ${branchCommands.join("|")})`,
|
||||
' local branches',
|
||||
' branches="$(command sandlot list --json 2>/dev/null | grep -o \'"branch": *"[^"]*"\' | sed \'s/.*"\\([^"]*\\)"$/\\1/\')"',
|
||||
' COMPREPLY=( $(compgen -W "$branches" -- "$cur") )',
|
||||
" return",
|
||||
" ;;",
|
||||
" esac",
|
||||
"}",
|
||||
"complete -F _sandlot_completions sandlot",
|
||||
]
|
||||
process.stdout.write(lines.join("\n") + "\n")
|
||||
}
|
||||
|
||||
function emitZsh(program: Command) {
|
||||
// Prepend bashcompinit setup, then emit the same bash script
|
||||
process.stdout.write("autoload -Uz bashcompinit && bashcompinit\n")
|
||||
emitBash(program)
|
||||
}
|
||||
|
||||
function collectCommands(program: Command): { subcommands: string[]; branchCommands: string[] } {
|
||||
const subcommands: string[] = []
|
||||
const branchCommands: string[] = []
|
||||
|
||||
for (const cmd of program.commands) {
|
||||
if ((cmd as any)._hidden) continue
|
||||
subcommands.push(cmd.name())
|
||||
const hasBranch = cmd.registeredArguments.some(a => a.name() === "branch")
|
||||
if (hasBranch) branchCommands.push(cmd.name())
|
||||
}
|
||||
|
||||
return { subcommands, branchCommands }
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user