Add init and cd shell integration commands
This commit is contained in:
parent
4a465f7787
commit
83787ab868
|
|
@ -11,7 +11,7 @@ function __sandlot_sessions
|
||||||
end
|
end
|
||||||
|
|
||||||
# All top-level subcommands (used for __fish_seen_subcommand_from checks)
|
# 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
|
set -l __sandlot_vm_cmds create start shell status info stop destroy
|
||||||
|
|
||||||
# Disable file completions
|
# 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 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 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 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 cleanup -d 'Remove stale sessions'
|
||||||
complete -c sandlot -n "not __fish_seen_subcommand_from $__sandlot_cmds" -a vm -d 'Manage the sandlot VM'
|
complete -c sandlot -n "not __fish_seen_subcommand_from $__sandlot_cmds" -a vm -d 'Manage the sandlot VM'
|
||||||
|
|
||||||
# ── Session branch completions ───────────────────────────────────────
|
# ── 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)'
|
complete -c sandlot -n "__fish_seen_subcommand_from $cmd; and not __fish_seen_subcommand_from vm" -a '(__sandlot_sessions)'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -71,5 +73,9 @@ complete -c sandlot -n "__fish_seen_subcommand_from vm; and not __fish_seen_subc
|
||||||
|
|
||||||
# ── Global options ───────────────────────────────────────────────────
|
# ── 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 h -l help -d 'Show help'
|
||||||
complete -c sandlot -s V -l version -d 'Show version'
|
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 { action as cleanupAction } from "./commands/cleanup.ts"
|
||||||
import { register as registerVmCommands } from "./commands/vm.ts"
|
import { register as registerVmCommands } from "./commands/vm.ts"
|
||||||
import { action as completionsAction } from "./commands/completions.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()
|
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")
|
.description("Print the worktree path for a session")
|
||||||
.action(dirAction)
|
.action(dirAction)
|
||||||
|
|
||||||
|
program
|
||||||
|
.command("cd")
|
||||||
|
.argument("<branch>", "branch name")
|
||||||
|
.description("Change to a branch's worktree directory")
|
||||||
|
.action(cdAction)
|
||||||
|
|
||||||
// ── Admin ───────────────────────────────────────────────────────────
|
// ── Admin ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
program.commandsGroup("Admin Commands:")
|
program.commandsGroup("Admin Commands:")
|
||||||
|
|
@ -208,6 +216,12 @@ program
|
||||||
.description("Output fish shell completions")
|
.description("Output fish shell completions")
|
||||||
.action((opts: { install?: boolean }) => completionsAction(program, opts))
|
.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` ─────────────────────────────
|
// ── Default: `sandlot` → `sandlot list` ─────────────────────────────
|
||||||
|
|
||||||
if (process.argv.length === 2) {
|
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"
|
import type { Command, Option } from "commander"
|
||||||
|
|
||||||
/** Generate fish completions dynamically from the Commander program tree. */
|
/** Generate fish completion lines from the Commander program tree. */
|
||||||
export function action(program: Command, opts: { install?: boolean } = {}) {
|
export function generateFishCompletions(program: Command): string[] {
|
||||||
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: string[] = [
|
const lines: string[] = [
|
||||||
"# Fish completions for sandlot (auto-generated)",
|
"# Fish completions for sandlot (auto-generated)",
|
||||||
"# Install: sandlot completions > ~/.config/fish/completions/sandlot.fish",
|
|
||||||
"",
|
"",
|
||||||
"complete -c sandlot -f",
|
"complete -c sandlot -f",
|
||||||
"",
|
"",
|
||||||
|
|
@ -65,6 +52,26 @@ export function action(program: Command, opts: { install?: boolean } = {}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
lines.push("")
|
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")
|
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