Compare commits

..
Author SHA1 Message Date
Robby RussellandClaude Fable 5.1 de76990950 fix(cli): address review feedback on omz generate plugin
- Turn %placeholders% into private markers when a template is read and
  strip the marker from every value, so text inserted from user input
  (e.g. -d '%name%') is never rescanned as a placeholder.
- When --enable is used for a name already in $plugins (a custom
  override of an enabled built-in), report it and reload instead of
  failing in _omz::plugin::enable.
- Make the completion note accurate whether or not a _<cmd> file was
  generated.
- Test the write-phase cleanup with a template set missing a later
  template, and test that placeholder-looking values come out literally.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-05 14:48:54 -07:00
Robby RussellandClaude Fable 5.1 46aee25f27 feat(cli): add omz generate plugin to scaffold custom plugins
Adds a rails-style generator that creates a custom plugin in
$ZSH_CUSTOM/plugins/<name> from commented templates: the plugin file,
a README in the usual shape and, for plugins that wrap a command-line
tool, a completion skeleton (or the cached-completion block when the
tool generates its own). Anything not passed as an option is asked
for interactively; non-interactive shells use defaults.

`omz generate` is a new top-level command so that other generators
(e.g. `omz generate theme`) can follow the same shape.

Templates live in templates/generators/plugin and are syntax-checked
by CI. lib/tests/generate-plugin.test.zsh covers the non-interactive
path, name validation and the prompt helper.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-05 14:28:32 -07:00
41 changed files with 820 additions and 327 deletions
+1 -1
View File
@@ -12,7 +12,7 @@ dependencies:
plugins/gradle:
repo: gradle/gradle-completion
branch: master
version: e7c881dbef7462bc35ad05328397e1582fe25cb8
version: 63578c9bc56134b80766e88957ba0239e6be2343
precopy: |
set -e
find . ! -name _gradle ! -name LICENSE -delete
+1 -1
View File
@@ -16,7 +16,7 @@ jobs:
contents: write # this is needed to push commits and branches
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1
uses: step-security/harden-runner@05e31511f85b41b11d1cf0ef85d0992719546e2c # v2.21.0
with:
egress-policy: audit
+2 -2
View File
@@ -26,7 +26,7 @@ jobs:
- macos-latest
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1
uses: step-security/harden-runner@05e31511f85b41b11d1cf0ef85d0992719546e2c # v2.21.0
with:
egress-policy: audit
@@ -47,7 +47,7 @@ jobs:
- test
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1
uses: step-security/harden-runner@05e31511f85b41b11d1cf0ef85d0992719546e2c # v2.21.0
with:
egress-policy: audit
+2 -1
View File
@@ -24,7 +24,7 @@ jobs:
if: github.repository == 'ohmyzsh/ohmyzsh'
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1
uses: step-security/harden-runner@05e31511f85b41b11d1cf0ef85d0992719546e2c # v2.21.0
with:
egress-policy: audit
@@ -38,6 +38,7 @@ jobs:
./lib/*.zsh \
./plugins/*/*.plugin.zsh \
./plugins/*/_* \
./templates/generators/*/*.zsh-template \
./themes/*.zsh-theme; do
zsh -n "$file" || return 1
done
+1 -1
View File
@@ -17,7 +17,7 @@ jobs:
if: github.repository == 'ohmyzsh/ohmyzsh'
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1
uses: step-security/harden-runner@05e31511f85b41b11d1cf0ef85d0992719546e2c # v2.21.0
with:
egress-policy: audit
- name: Authenticate as @ohmyzsh
+1 -1
View File
@@ -36,7 +36,7 @@ jobs:
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1
uses: step-security/harden-runner@05e31511f85b41b11d1cf0ef85d0992719546e2c # v2.21.0
with:
egress-policy: audit
+4 -8
View File
@@ -23,14 +23,6 @@ Twitter), and join us on [Discord](https://discord.gg/ohmyzsh).
[![Mastodon Follow](https://img.shields.io/mastodon/follow/111169632522566717?label=%40ohmyzsh&domain=https%3A%2F%2Fmstdn.social&logo=mastodon&style=flat)](https://mstdn.social/@ohmyzsh)
[![Discord server](https://img.shields.io/discord/642496866407284746)](https://discord.gg/ohmyzsh)
<a href="https://commitgoods.com/collections/oh-my-zsh?utm_source=github">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://cdn.shopify.com/s/files/1/0101/8752/files/commitgoods-badge-stacked-navy-dark.svg">
<img src="https://cdn.shopify.com/s/files/1/0101/8752/files/commitgoods-badge-stacked-navy.svg" height="28" alt="Official merch on commitgoods.com">
</picture>
</a>
<details>
<summary>Table of Contents</summary>
@@ -354,6 +346,10 @@ directory.
If you have many functions that go well together, you can put them as a `XYZ.plugin.zsh` file in the
`custom/plugins/` directory and then enable this plugin.
The quickest way to start one is `omz generate plugin <name>`. It creates the plugin directory with a
commented `<name>.plugin.zsh`, a `README.md`, and (for plugins that wrap a command-line tool) a completion
skeleton, then tells you how to try it out.
If you would like to override the functionality of a plugin distributed with Oh My Zsh, create a plugin of the
same name in the `custom/plugins/` directory and it will be loaded instead of the one in `plugins/`.
+382 -9
View File
@@ -24,6 +24,7 @@ function _omz {
local -a cmds subcmds
cmds=(
'changelog:Print the changelog'
'generate:Run a generator, e.g. to create a custom plugin'
'help:Usage information'
'plugin:Manage plugins'
'pr:Manage Oh My Zsh Pull Requests'
@@ -41,6 +42,8 @@ function _omz {
changelog) local -a refs
refs=("${(@f)$(builtin cd -q "$ZSH"; command git for-each-ref --format="%(refname:short):%(subject)" refs/heads refs/tags)}")
_describe 'command' refs ;;
generate) subcmds=('plugin:Create a custom plugin')
_describe 'generator' subcmds ;;
plugin) subcmds=(
'disable:Disable plugin(s)'
'enable:Enable plugin(s)'
@@ -56,6 +59,8 @@ function _omz {
esac
elif (( CURRENT == 4 )); then
case "${words[2]}::${words[3]}" in
generate::plugin)
_omz::generate::plugin::complete_options ;;
plugin::(disable|enable|load))
local -aU valid_plugins
@@ -84,6 +89,8 @@ function _omz {
esac
elif (( CURRENT > 4 )); then
case "${words[2]}::${words[3]}" in
generate::plugin)
_omz::generate::plugin::complete_options ;;
plugin::(enable|disable|load))
local -aU valid_plugins
@@ -169,15 +176,16 @@ Usage: omz <command> [options]
Available commands:
help Print this help message
changelog Print the changelog
plugin <command> Manage plugins
pr <command> Manage Oh My Zsh Pull Requests
reload Reload the current zsh session
shop Open the Oh My Zsh shop
theme <command> Manage themes
update Update Oh My Zsh
version Show the version
help Print this help message
changelog Print the changelog
generate <generator> Run a generator, e.g. to create a custom plugin
plugin <command> Manage plugins
pr <command> Manage Oh My Zsh Pull Requests
reload Reload the current zsh session
shop Open the Oh My Zsh shop
theme <command> Manage themes
update Update Oh My Zsh
version Show the version
EOF
}
@@ -202,6 +210,371 @@ EOF
ZSH="$ZSH" command zsh -f "$ZSH/tools/changelog.sh" "$version" "${2:-}" "$format"
}
function _omz::generate {
(( $# > 0 && $+functions[$0::$1] )) || {
cat >&2 <<EOF
Usage: ${(j: :)${(s.::.)0#_}} <generator> [options]
Available generators:
plugin [<name>] Create a custom plugin in \$ZSH_CUSTOM/plugins
EOF
return 1
}
local command="$1"
shift
$0::$command "$@"
}
function _omz::generate::plugin {
setopt localoptions extendedglob
local -A opts
zparseopts -D -E -A opts -- d: -description: c: -command: -no-completion -enable y -yes h -help || return 1
if (( ${+opts[-h]} || ${+opts[--help]} )); then
_omz::generate::plugin::usage
return 0
fi
# zparseopts -E leaves anything it doesn't recognise in $@
local arg
for arg in "$@"; do
if [[ "$arg" == -* ]]; then
_omz::log error "unknown option '$arg'."
_omz::generate::plugin::usage
return 1
fi
done
if (( $# > 1 )); then
_omz::generate::plugin::usage
return 1
fi
# Only ask questions when there is someone there to answer them
local interactive=0
if [[ -o interactive && -t 0 ]] && (( ! ${+opts[-y]} && ! ${+opts[--yes]} )); then
interactive=1
fi
local custom="${ZSH_CUSTOM:-${ZSH:+$ZSH/custom}}"
if [[ -z "$custom" ]]; then
_omz::log error "\$ZSH is not set. Is Oh My Zsh loaded?"
return 1
fi
local templates="$ZSH/templates/generators/plugin"
if [[ ! -d "$templates" ]]; then
_omz::log error "templates not found at '${templates/#$HOME/\~}'. Try running 'omz update'."
return 1
fi
# Check we can write to $ZSH_CUSTOM/plugins, or the closest directory that exists
local probe="$custom/plugins"
while [[ ! -e "$probe" && "$probe" != "${probe:h}" ]]; do
probe="${probe:h}"
done
if [[ ! -w "$probe" ]]; then
_omz::log error "cannot write to '${probe/#$HOME/\~}'. Set \$ZSH_CUSTOM to a directory you own."
return 1
fi
## Gather answers. Nothing is written until all of them are in.
local name="$1" attempts=0
if [[ -n "$name" ]]; then
_omz::generate::plugin::validate_name "$name" || return 1
elif (( interactive )); then
while true; do
_omz::generate::plugin::ask "Plugin name" || return 1
name="$REPLY"
_omz::generate::plugin::validate_name "$name" && break
if (( ++attempts >= 3 )); then
_omz::log error "giving up."
return 1
fi
done
else
_omz::generate::plugin::usage
return 1
fi
local dir="$custom/plugins/$name"
if [[ -e "$dir" ]]; then
_omz::log error "'$name' already exists at '${dir/#$HOME/\~}'. Remove it or pick another name."
return 1
fi
if [[ -d "$ZSH/plugins/$name" ]]; then
_omz::log warn "'$name' is also a built-in plugin. Your custom plugin will override it."
if (( interactive )); then
_omz::confirm "Continue? [y/N] "
if [[ "$REPLY" != [yY] ]]; then
_omz::log info "aborted."
return 1
fi
fi
fi
local description="${opts[-d]:-${opts[--description]:-}}"
if [[ -z "$description" ]]; then
description="$name plugin for Oh My Zsh"
if (( interactive )); then
_omz::generate::plugin::ask "Short description" "$description" || return 1
description="$REPLY"
fi
fi
# The description goes into a comment and a README line: keep it on one line
description="${description//[[:cntrl:]]/ }"
local cmd="${opts[-c]:-${opts[--command]:-}}"
if [[ -n "$cmd" ]]; then
_omz::generate::plugin::validate_command "$cmd" || return 1
elif (( interactive )); then
local default_cmd=""
(( $+commands[$name] )) && default_cmd="$name"
attempts=0
while true; do
if [[ -n "$default_cmd" ]]; then
_omz::generate::plugin::ask "Command-line tool this plugin wraps (or 'none')" "$default_cmd" || return 1
else
_omz::generate::plugin::ask "Command-line tool this plugin wraps (blank for none)" || return 1
fi
cmd="$REPLY"
[[ "$cmd" != (none|-) ]] || cmd=""
[[ -n "$cmd" ]] || break
_omz::generate::plugin::validate_command "$cmd" && break
if (( ++attempts >= 3 )); then
_omz::log error "giving up."
return 1
fi
done
fi
# completion: "" (none), "file" (a _<cmd> skeleton) or "cached" (the tool generates it)
local completion="" compgen=""
if [[ -n "$cmd" ]] && (( ! ${+opts[--no-completion]} )); then
completion=file
if (( interactive )); then
_omz::confirm "Add a completion function for $cmd? [Y/n] "
if [[ "$REPLY" == [nN] ]]; then
completion=""
else
_omz::confirm "Does $cmd generate its own zsh completion (e.g. '$cmd completion zsh')? [y/N] "
if [[ "$REPLY" == [yY] ]]; then
completion=cached
_omz::generate::plugin::ask "Command that prints the completion script" "$cmd completion zsh" || return 1
compgen="$REPLY"
fi
fi
fi
fi
local enable=${+opts[--enable]}
if (( interactive && ! enable )); then
_omz::confirm "Add $name to plugins=() in your .zshrc now? [y/N] "
[[ "$REPLY" != [yY] ]] || enable=1
fi
## Write the files. If anything fails, remove only what we created.
local -a created
local block="" cache="" ok=0
{
if [[ ! -d "$custom/plugins" ]]; then
command mkdir -p "$custom/plugins" || return 1
_omz::generate::plugin::created "$custom/plugins"
fi
command mkdir "$dir" || return 1
_omz::generate::plugin::created "$dir"
local plugin_tpl=plugin.zsh-template readme_tpl=README.md-template
if [[ -z "$cmd" ]]; then
plugin_tpl=plugin-generic.zsh-template
readme_tpl=README-generic.md-template
elif [[ "$completion" == cached ]]; then
block="$(_omz::generate::plugin::template completion-cached.zsh-template)" || return 1
cache="$(_omz::generate::plugin::template README-cache.md-template)" || return 1
else
block="$(_omz::generate::plugin::template completion-note.zsh-template)" || return 1
fi
_omz::generate::plugin::render "$plugin_tpl" "$dir/$name.plugin.zsh" || return 1
_omz::generate::plugin::render "$readme_tpl" "$dir/README.md" || return 1
if [[ "$completion" == file ]]; then
_omz::generate::plugin::render completion.zsh-template "$dir/_$cmd" || return 1
fi
ok=1
} always {
if (( ! ok )); then
_omz::log error "could not generate the plugin. Cleaning up..."
(( ${#created} )) && command rm -f -- "${created[@]}"
[[ ! -d "$dir" ]] || command rmdir -- "$dir" 2>/dev/null
fi
}
print
_omz::log info "plugin '$name' generated."
print
print -r -- "Next steps:"
print
print -r -- " 1. Edit it: ${EDITOR:-vim} ${dir/#$HOME/\~}/$name.plugin.zsh"
print -r -- " 2. Try it now: omz plugin load $name"
(( enable )) || print -r -- " 3. Keep it: omz plugin enable $name"
print
print -r -- "Docs: https://github.com/ohmyzsh/ohmyzsh/wiki/Customization#overriding-and-adding-plugins"
# Last thing we do: in an interactive shell this restarts zsh
if (( enable )); then
print
if (( ${plugins[(Ie)$name]} )); then
# Already enabled (e.g. a custom override of a built-in): just reload
_omz::log info "'$name' is already in your plugins list."
[[ ! -o interactive ]] || _omz::reload
else
_omz::plugin::enable "$name"
fi
fi
}
function _omz::generate::plugin::usage {
cat >&2 <<EOF
Usage: omz generate plugin [<name>] [options]
Creates a custom plugin in \$ZSH_CUSTOM/plugins/<name>. Anything not given as an
option is asked for interactively. In a non-interactive shell (or with --yes)
defaults are used instead.
Options:
-d, --description <text> One-line description for the README and file header
-c, --command <cmd> Command-line tool this plugin wraps. Adds a check
that it is installed and scaffolds its completion
--no-completion Don't scaffold a completion function
--enable Add the plugin to plugins=() in .zshrc when done
(restarts your shell)
-y, --yes Never prompt; use defaults for anything not given
-h, --help Show this help
EOF
}
function _omz::generate::plugin::complete_options {
local -a opts
opts=(
'--description:One-line description for the README'
'--command:Command-line tool this plugin wraps'
'--no-completion:Skip the completion scaffold'
'--enable:Enable the plugin when done'
'--yes:Never prompt'
)
_describe -o 'options' opts
}
# The name ends up in a mkdir path and, via --enable, inside the awk script that
# rewrites .zshrc. This is the one place it gets checked.
function _omz::generate::plugin::validate_name {
setopt localoptions extendedglob
local name="$1"
if [[ -z "$name" ]]; then
_omz::log error "plugin name cannot be empty."
elif (( ${#name} > 64 )); then
_omz::log error "plugin name is too long (64 characters max)."
elif [[ "$name" != "${name:l}" ]]; then
_omz::log error "plugin names must be lowercase. Did you mean '${name:l}'?"
elif [[ "$name" != [a-z0-9][a-z0-9_-]# ]]; then
_omz::log error "invalid plugin name: use only lowercase letters, digits, '-' and '_', starting with a letter or digit."
else
return 0
fi
return 1
}
# The command name ends up in $+commands[...], _comps[...] and a filename
function _omz::generate::plugin::validate_command {
setopt localoptions extendedglob
if (( ${#1} > 64 )) || [[ "$1" != [[:alnum:]_][[:alnum:]_.+-]# ]]; then
_omz::log error "invalid command name: use only letters, digits, '.', '_', '+' and '-'."
return 1
fi
}
# Ask a question and leave the answer in $REPLY. An empty answer takes the
# default. Returns 1 when there is no more input (Ctrl-D).
function _omz::generate::plugin::ask {
setopt localoptions extendedglob
local question="$1" default="$2"
[[ -z "$default" ]] || question+=" [$default]"
_omz::log prompt "$question: " "omz::generate::plugin"
if ! builtin read -r; then
print >&2
_omz::log info "aborted." "omz::generate::plugin"
return 1
fi
[[ -n "$REPLY" ]] || REPLY="$default"
# Answers are single-line values: flatten control characters and trim
REPLY="${REPLY//[[:cntrl:]]/ }"
REPLY="${${REPLY##[[:space:]]#}%%[[:space:]]#}"
}
# Print a template file with its %placeholders% turned into private markers,
# or explain which one is missing
function _omz::generate::plugin::template {
if [[ ! -f "$templates/$1" ]]; then
_omz::log error "missing template '${templates/#$HOME/\~}/$1'." "omz::generate::plugin"
return 1
fi
local content="$(<"$templates/$1")" token m=$'\x1f'
for token in completion cache name command description compgen; do
content="${content//\%${token}\%/${m}${token}${m}}"
done
print -r -- "$content"
}
# Fill in the placeholders of a template and write it to a file. Values come
# from the caller: $name, $cmd, $description, $compgen, $block and $cache.
#
# Placeholders are matched as the markers that ::template produced, and the
# marker character is stripped from every value, so a value that happens to
# look like a placeholder (e.g. -d '%name%') is written out literally.
function _omz::generate::plugin::render {
setopt localoptions extendedglob
local content m=$'\x1f'
content="$(_omz::generate::plugin::template "$1")" || return 1
# Blocks go first: they are templates too and carry markers of their own
content="${content//${m}completion${m}/$block}"
content="${content//${m}cache${m}/$cache}"
content="${content//${m}name${m}/${name//$m/}}"
content="${content//${m}command${m}/${cmd//$m/}}"
content="${content//${m}description${m}/${description//$m/}}"
content="${content//${m}compgen${m}/${compgen//$m/}}"
# Drop blank lines left behind by an empty placeholder at the end
content="${content%%$'\n'##}"
created+=("$2")
print -r -- "$content" > "$2" || return 1
_omz::generate::plugin::created "$2"
}
function _omz::generate::plugin::created {
# Colour the verb with print -P, but print the path with print -r so that a
# '%' somewhere in the path isn't treated as a prompt escape.
# Keep the output plain when it is being piped.
if [[ -t 1 ]]; then
print -Pn " %F{green}create%f "
else
print -n " create "
fi
print -r -- "${1/#$HOME/\~}"
}
function _omz::plugin {
(( $# > 0 && $+functions[$0::$1] )) || {
cat >&2 <<EOF
-1
View File
@@ -21,7 +21,6 @@ function chruby_prompt_info \
azure_prompt_info \
tf_prompt_info \
conda_prompt_info \
go_prompt_info \
{
return 1
}
+198
View File
@@ -0,0 +1,198 @@
#!/usr/bin/zsh -df
# Tests for `omz generate plugin`. Run with: zsh lib/tests/generate-plugin.test.zsh
ZSH="${0:A:h:h:h}"
ZSH_CUSTOM="$(mktemp -d)"
trap 'command rm -rf "$ZSH_CUSTOM"' EXIT
source "$ZSH/lib/cli.zsh"
failures=0
pass() { print -u2 "\e[32mSuccess\e[0m: $1" }
fail() { print -u2 "\e[31mError\e[0m: $1"; (( failures++ )) }
# assert_files <plugin> <expected files, space separated>
assert_files() {
local actual="$(print -l "$ZSH_CUSTOM"/plugins/$1/*(N:t) "$ZSH_CUSTOM"/plugins/$1/_*(N:t) | sort -u | tr '\n' ' ')"
local expected="$(print -l ${=2} | sort -u | tr '\n' ' ')"
if [[ "$actual" == "$expected" ]]; then
pass "$1 has files: $expected"
else
fail "$1 has files '$actual', expected '$expected'"
fi
}
# assert_syntax <plugin>: every generated file must pass zsh -n, like CI does
assert_syntax() {
local file
for file in "$ZSH_CUSTOM"/plugins/$1/*.plugin.zsh(N) "$ZSH_CUSTOM"/plugins/$1/_*(N); do
if zsh -n "$file"; then
pass "${file:t} passes zsh -n"
else
fail "${file:t} fails zsh -n"
fi
done
}
# assert_contains <file> <literal string>
assert_contains() {
if grep -qF -- "$2" "$1"; then
pass "${1:t} contains '$2'"
else
fail "${1:t} does not contain '$2'"
fi
}
## Templates must be valid zsh even before substitution (CI checks this too)
for file in "$ZSH"/templates/generators/plugin/*.zsh-template; do
if zsh -n "$file"; then
pass "template ${file:t} passes zsh -n"
else
fail "template ${file:t} fails zsh -n"
fi
done
## A plugin that wraps a command
description='Shortcuts & stuff $(id) `id` 100%'
if omz generate plugin foo -d "$description" -c foo --yes >/dev/null 2>&1; then
pass "generates a command-wrapping plugin"
else
fail "generating a command-wrapping plugin failed"
fi
assert_files foo "README.md _foo foo.plugin.zsh"
assert_syntax foo
assert_contains "$ZSH_CUSTOM/plugins/foo/README.md" 'plugins=(... foo)'
assert_contains "$ZSH_CUSTOM/plugins/foo/README.md" "$description"
assert_contains "$ZSH_CUSTOM/plugins/foo/foo.plugin.zsh" "$description"
assert_contains "$ZSH_CUSTOM/plugins/foo/foo.plugin.zsh" '$+commands[foo]'
assert_contains "$ZSH_CUSTOM/plugins/foo/_foo" '#compdef foo'
if grep -q '%[a-z]*%' "$ZSH_CUSTOM"/plugins/foo/*; then
fail "foo still has unfilled placeholders"
else
pass "foo has no unfilled placeholders"
fi
## Values that look like placeholders are written literally
omz generate plugin lit -d '%compgen% and %name% stay' -c lit --yes >/dev/null 2>&1
assert_contains "$ZSH_CUSTOM/plugins/lit/README.md" '%compgen% and %name% stay'
assert_contains "$ZSH_CUSTOM/plugins/lit/lit.plugin.zsh" '%compgen% and %name% stay'
## A plugin with no command
if omz generate plugin bar --yes >/dev/null 2>&1; then
pass "generates a generic plugin"
else
fail "generating a generic plugin failed"
fi
assert_files bar "README.md bar.plugin.zsh"
assert_syntax bar
assert_contains "$ZSH_CUSTOM/plugins/bar/README.md" 'This plugin does not add any aliases.'
assert_contains "$ZSH_CUSTOM/plugins/bar/bar.plugin.zsh" 'bar plugin for Oh My Zsh'
## --no-completion, and the rails-style output
output="$(omz generate plugin baz -c baz --no-completion --yes 2>/dev/null)"
assert_files baz "README.md baz.plugin.zsh"
create_lines=$(print -r -- "$output" | grep -c '^ create ')
if (( create_lines == 3 )); then
pass "prints one create line per path"
else
fail "expected 3 create lines, got $create_lines"
fi
if [[ "$output" == *"omz plugin load baz"* ]]; then
pass "prints next steps"
else
fail "next steps missing from output"
fi
## Bad names are rejected without touching the filesystem
before="$(print -l "$ZSH_CUSTOM"/plugins/*(N:t))"
for bad in '' 'Foo' 'a b' '../evil' 'a/b' '.hidden' '.' '..' '-x' 'foo`id`' 'foo"$(id)"' $'a\nb' "$(printf 'x%.0s' {1..65})"; do
if omz generate plugin "$bad" --yes >/dev/null 2>&1; then
fail "accepted bad name ${(qq)bad}"
else
pass "rejected bad name ${(qq)bad}"
fi
done
after="$(print -l "$ZSH_CUSTOM"/plugins/*(N:t))"
if [[ "$before" == "$after" ]]; then
pass "bad names created nothing"
else
fail "bad names changed \$ZSH_CUSTOM/plugins"
fi
## Bad command names are rejected
for bad in 'a b' '../x' 'foo;id' 'foo$(id)'; do
if omz generate plugin qux -c "$bad" --yes >/dev/null 2>&1; then
fail "accepted bad command ${(qq)bad}"
else
pass "rejected bad command ${(qq)bad}"
fi
done
## Existing plugin, unknown option, missing templates
if omz generate plugin foo --yes >/dev/null 2>&1; then
fail "overwrote an existing plugin"
else
pass "refuses to overwrite an existing plugin"
fi
if omz generate plugin quux --bogus --yes >/dev/null 2>&1; then
fail "accepted an unknown option"
else
pass "rejects an unknown option"
fi
if ( ZSH=/nonexistent; omz generate plugin quux --yes >/dev/null 2>&1 ); then
fail "ran without templates"
else
pass "fails cleanly when templates are missing"
fi
[[ -e "$ZSH_CUSTOM/plugins/quux" ]] && fail "quux was created by a failed run"
## A failure after the first file is written removes everything it created
broken="$(mktemp -d)"
mkdir -p "$broken/templates/generators/plugin" "$broken/plugins"
cp "$ZSH/templates/generators/plugin/plugin.zsh-template" \
"$ZSH/templates/generators/plugin/completion-note.zsh-template" "$broken/templates/generators/plugin/"
# README.md-template is missing, so the plugin file is written and then the README fails
if ( ZSH="$broken"; omz generate plugin partial -c partial --yes >/dev/null 2>&1 ); then
fail "succeeded with a missing README template"
else
pass "fails when a later template is missing"
fi
if [[ -e "$ZSH_CUSTOM/plugins/partial" ]]; then
fail "partial plugin directory was left behind: $(ls "$ZSH_CUSTOM/plugins/partial")"
else
pass "cleans up the partial plugin directory"
fi
command rm -rf "$broken"
## The prompt helper
_omz::generate::plugin::ask "Q" "dflt" 2>/dev/null < <(print '')
[[ "$REPLY" == "dflt" ]] && pass "ask: empty answer takes the default" || fail "ask: got '$REPLY', expected 'dflt'"
_omz::generate::plugin::ask "Q" "dflt" 2>/dev/null < <(print ' hi there ')
[[ "$REPLY" == "hi there" ]] && pass "ask: trims whitespace" || fail "ask: got '$REPLY', expected 'hi there'"
if _omz::generate::plugin::ask "Q" 2>/dev/null < /dev/null; then
fail "ask: did not fail on EOF"
else
pass "ask: fails on EOF"
fi
print -u2
if (( failures )); then
print -u2 "\e[31m$failures test(s) failed\e[0m"
exit 1
fi
print -u2 "\e[32mAll tests passed\e[0m"
+7 -17
View File
@@ -64,7 +64,7 @@ if [[ ! -w "$ZSH_CACHE_DIR" ]]; then
fi
# Create cache and completions dir and add to $fpath
[[ -d "$ZSH_CACHE_DIR/completions" ]] || mkdir -p "$ZSH_CACHE_DIR/completions"
mkdir -p "$ZSH_CACHE_DIR/completions"
(( ${fpath[(Ie)$ZSH_CACHE_DIR/completions]} )) || fpath=("$ZSH_CACHE_DIR/completions" $fpath)
# Check for updates on initial load...
@@ -114,17 +114,9 @@ fi
zcompdump_revision="#omz revision: $(builtin cd -q "$ZSH"; git rev-parse HEAD 2>/dev/null)"
zcompdump_fpath="#omz fpath: $fpath"
# Check whether the zcompdump file carries the current OMZ metadata lines
_omz_compdump_has_metadata() {
local -a lines
[[ -r "$ZSH_COMPDUMP" ]] || return 1
lines=("${(@f)$(<"$ZSH_COMPDUMP")}")
(( ${lines[(Ie)$zcompdump_revision]} && ${lines[(Ie)$zcompdump_fpath]} ))
}
# Delete the zcompdump file if OMZ zcompdump metadata changed
zcompdump_refresh=0
if ! _omz_compdump_has_metadata; then
if ! command grep -q -Fx "$zcompdump_revision" "$ZSH_COMPDUMP" 2>/dev/null \
|| ! command grep -q -Fx "$zcompdump_fpath" "$ZSH_COMPDUMP" 2>/dev/null; then
command rm -f "$ZSH_COMPDUMP"
zcompdump_refresh=1
fi
@@ -142,8 +134,9 @@ else
compinit -u -d "$ZSH_COMPDUMP"
fi
# Append zcompdump metadata if missing (compinit may have regenerated the file)
if (( zcompdump_refresh )) || ! _omz_compdump_has_metadata; then
# Append zcompdump metadata if missing
if (( $zcompdump_refresh )) \
|| ! command grep -q -Fx "$zcompdump_revision" "$ZSH_COMPDUMP" 2>/dev/null; then
# Use `tee` in case the $ZSH_COMPDUMP filename is invalid, to silence the error
# See https://github.com/ohmyzsh/ohmyzsh/commit/dd1a7269#commitcomment-39003489
tee -a "$ZSH_COMPDUMP" &>/dev/null <<EOF
@@ -153,12 +146,9 @@ $zcompdump_fpath
EOF
fi
unset zcompdump_revision zcompdump_fpath zcompdump_refresh
unset -f _omz_compdump_has_metadata
# zcompile the completion dump file if the .zwc is older or missing.
# Test that first so the lock directory and zrecompile are skipped when it's fresh.
if [[ ! "${ZSH_COMPDUMP}.zwc" -nt "$ZSH_COMPDUMP" ]] \
&& command mkdir "${ZSH_COMPDUMP}.lock" 2>/dev/null; then
if command mkdir "${ZSH_COMPDUMP}.lock" 2>/dev/null; then
zrecompile -q -p "$ZSH_COMPDUMP"
command rm -rf "$ZSH_COMPDUMP.zwc.old" "${ZSH_COMPDUMP}.lock"
fi
-15
View File
@@ -39,18 +39,3 @@ plugins=(... golang)
| gov | `go vet` | Vet examines Go source code and reports suspicious constructs |
| gove | `go version` | Prints Go version |
| gow | `go work` | Work provides access to operations on workspaces |
## Prompt
This plugin provides the `go_prompt_info` function to add the installed Go
version to your prompt.
For example:
```
PROMPT="%~$ "
RPROMPT='$(go_prompt_info)'
```
changes your prompt to:
```
~/go/project$ ▋ 1.22.3
```
-9
View File
@@ -36,12 +36,3 @@ alias gotofx='go tool fix'
alias gov='go vet'
alias gove='go version'
alias gow='go work'
## prompt
function go_prompt_info() {
(( $+commands[go] )) || return
local go_prompt
go_prompt=$(go version | awk '{ print substr($3, 3) }')
[[ -z "${go_prompt}" ]] && return
echo "${ZSH_THEME_GO_PROMPT_PREFIX}${go_prompt:gs/%/%%}${ZSH_THEME_GO_PROMPT_SUFFIX}"
}
+4 -10
View File
@@ -284,9 +284,6 @@ __gradle_subcommand() {
'-Dorg.gradle.debug.suspend=[]' \
'-Dorg.gradle.debug=[]:org.gradle.debug:(true false)' \
'-Dorg.gradle.dependency.verification=[Configures the dependency verification mode. Supported values are 'strict', 'lenient', or 'off'.]:org.gradle.dependency.verification:(strict lenient off)' \
'-Dorg.gradle.isolated-projects.dangerously-ignore-problems=[]' \
'-Dorg.gradle.isolated-projects.diagnostics=[]' \
'-Dorg.gradle.isolated-projects=[Enables Isolated Projects. Projects are configured in parallel. Implies `--configuration-cache`.]' \
'-Dorg.gradle.java.home=[]:org.gradle.java.home:_directories' \
'-Dorg.gradle.java.installations.auto-detect=[]' \
'-Dorg.gradle.java.installations.auto-download=[]' \
@@ -302,6 +299,8 @@ __gradle_subcommand() {
'-Dorg.gradle.problems.report=[Enables the HTML problems report.]' \
'-Dorg.gradle.projectcachedir=[Specifies the project-specific cache directory. Default is .gradle in the root project directory.]:org.gradle.projectcachedir:_directories' \
'-Dorg.gradle.tooling.parallel=[]' \
'-Dorg.gradle.unsafe.isolated-projects.diagnostics=[]' \
'-Dorg.gradle.unsafe.isolated-projects=[]' \
'-Dorg.gradle.vfs.verbose=[]' \
'-Dorg.gradle.vfs.watch=[Enables file system watching. Reuses file system data for subsequent builds.]:org.gradle.vfs.watch:(true false)' \
'-Dorg.gradle.warning.mode=[Specifies which mode of warnings to generate. Supported values are 'all', 'fail', 'summary' (default), or 'none'.]' \
@@ -329,14 +328,12 @@ __gradle_subcommand() {
\*'--include-build[Includes the specified build in the composite.]:include build:_directories' \
(--quiet,-q,--warn,-w,--debug,-d){-i,--info}'[Sets the log level to info.]' \
\*{-I,--init-script}'[Specifies an initialization script.]:init script:_files -g \*.gradle(|.kts)' \
(--no-isolated-projects)'--isolated-projects[Enables Isolated Projects. Projects are configured in parallel. Implies `--configuration-cache`. (incubating)]' \
'--max-workers[Configures the maximum number of concurrent workers Gradle is allowed to use.]' \
(--build-cache)'--no-build-cache[Disables the Gradle build cache.]' \
(--configuration-cache)'--no-configuration-cache[Disables the configuration cache.]' \
(--configure-on-demand)'--no-configure-on-demand[Disables the use of configuration on demand. (incubating)]' \
(--continue)'--no-continue[Stops task execution after a task failure.]' \
(--daemon)'--no-daemon[Runs the build without the Gradle daemon. Useful occasionally if you have configured Gradle to always run with the daemon by default.]' \
(--isolated-projects)'--no-isolated-projects[Disables Isolated Projects. (incubating)]' \
(--parallel)'--no-parallel[Disables parallel project execution.]' \
(--problems-report)'--no-problems-report[Disables the HTML problems report. (incubating)]' \
{-a,--no-rebuild}'[Disables rebuilding of project dependencies.]' \
@@ -424,9 +421,6 @@ _gradle() {
'-Dorg.gradle.debug.suspend=[]:->argument-expected' \
'-Dorg.gradle.debug=[]:org.gradle.debug:(true false):->argument-expected' \
'-Dorg.gradle.dependency.verification=[Configures the dependency verification mode. Supported values are 'strict', 'lenient', or 'off'.]:org.gradle.dependency.verification:(strict lenient off):->argument-expected' \
'-Dorg.gradle.isolated-projects.dangerously-ignore-problems=[]:->argument-expected' \
'-Dorg.gradle.isolated-projects.diagnostics=[]:->argument-expected' \
'-Dorg.gradle.isolated-projects=[Enables Isolated Projects. Projects are configured in parallel. Implies `--configuration-cache`.]:->argument-expected' \
'-Dorg.gradle.java.home=[]:org.gradle.java.home:_directories:->argument-expected' \
'-Dorg.gradle.java.installations.auto-detect=[]:->argument-expected' \
'-Dorg.gradle.java.installations.auto-download=[]:->argument-expected' \
@@ -442,6 +436,8 @@ _gradle() {
'-Dorg.gradle.problems.report=[Enables the HTML problems report.]:->argument-expected' \
'-Dorg.gradle.projectcachedir=[Specifies the project-specific cache directory. Default is .gradle in the root project directory.]:org.gradle.projectcachedir:_directories:->argument-expected' \
'-Dorg.gradle.tooling.parallel=[]:->argument-expected' \
'-Dorg.gradle.unsafe.isolated-projects.diagnostics=[]:->argument-expected' \
'-Dorg.gradle.unsafe.isolated-projects=[]:->argument-expected' \
'-Dorg.gradle.vfs.verbose=[]:->argument-expected' \
'-Dorg.gradle.vfs.watch=[Enables file system watching. Reuses file system data for subsequent builds.]:org.gradle.vfs.watch:(true false):->argument-expected' \
'-Dorg.gradle.warning.mode=[Specifies which mode of warnings to generate. Supported values are 'all', 'fail', 'summary' (default), or 'none'.]:->argument-expected' \
@@ -470,14 +466,12 @@ _gradle() {
\*'--include-build[Includes the specified build in the composite.]:include build:_directories:->argument-expected' \
(--quiet,-q,--warn,-w,--debug,-d){-i,--info}'[Sets the log level to info.]' \
\*{-I,--init-script}'[Specifies an initialization script.]:init script:_files -g \*.gradle(|.kts):->argument-expected' \
(--no-isolated-projects)'--isolated-projects[Enables Isolated Projects. Projects are configured in parallel. Implies `--configuration-cache`. (incubating)]' \
'--max-workers[Configures the maximum number of concurrent workers Gradle is allowed to use.]:->argument-expected' \
(--build-cache)'--no-build-cache[Disables the Gradle build cache.]' \
(--configuration-cache)'--no-configuration-cache[Disables the configuration cache.]' \
(--configure-on-demand)'--no-configure-on-demand[Disables the use of configuration on demand. (incubating)]' \
(--continue)'--no-continue[Stops task execution after a task failure.]' \
(--daemon)'--no-daemon[Runs the build without the Gradle daemon. Useful occasionally if you have configured Gradle to always run with the daemon by default.]' \
(--isolated-projects)'--no-isolated-projects[Disables Isolated Projects. (incubating)]' \
(--parallel)'--no-parallel[Disables parallel project execution.]' \
(--problems-report)'--no-problems-report[Disables the HTML problems report. (incubating)]' \
{-a,--no-rebuild}'[Disables rebuilding of project dependencies.]' \
-19
View File
@@ -19,54 +19,35 @@ plugins=(... jj)
| jjbd | `jj bookmark delete` |
| jjbf | `jj bookmark forget` |
| jjbl | `jj bookmark list` |
| jjblt | `jj bookmark list --tracked` |
| jjbm | `jj bookmark move` |
| jjbr | `jj bookmark rename` |
| jjbs | `jj bookmark set` |
| jjbt | `jj bookmark track` |
| jjbu | `jj bookmark untrack` |
| jjc | `jj commit` |
| jjcfg | `jj config` |
| jjcfgl | `jj config list` |
| jjcmsg | `jj commit --message` |
| jjd | `jj diff` |
| jjdmsg | `jj desc --message` |
| jjds | `jj desc` |
| jjdst | `jj diff --stat` |
| jje | `jj edit` |
| jjev | `jj evolog` |
| jjf | `jj file` |
| jjfl | `jj file list` |
| jjg | `jj git` |
| jjgcl | `jj git clone` |
| jjgf | `jj git fetch` |
| jjgfa | `jj git fetch --all-remotes` |
| jjgi | `jj git init` |
| jjgp | `jj git push` |
| jjgpa | `jj git push --all` |
| jjgpd | `jj git push --deleted` |
| jjgpt | `jj git push --tracked` |
| jjgrl | `jj git remote list` |
| jjl | `jj log` |
| jjla | `jj log -r "all()"` |
| jjn | `jj new` |
| jjnt | `jj new "trunk()"` |
| jjop | `jj op` |
| jjopl | `jj op log` |
| jjor | `jj op restore` |
| jjrb | `jj rebase` |
| jjrbm | `jj rebase -d "trunk()"` |
| jjrs | `jj restore` |
| jjrt | `cd "$(jj root \|\| echo .)"` |
| jjs | `jj show` |
| jjsp | `jj split` |
| jjsq | `jj squash` |
| jjst | `jj status` |
| jju | `jj undo` |
| jjw | `jj workspace` |
| jjwa | `jj workspace add` |
| jjwf | `jj workspace forget` |
| jjwl | `jj workspace list` |
## Prompt usage
-17
View File
@@ -45,51 +45,34 @@ alias jjbc='jj bookmark create'
alias jjbd='jj bookmark delete'
alias jjbf='jj bookmark forget'
alias jjbl='jj bookmark list'
alias jjblt='jj bookmark list --tracked'
alias jjbm='jj bookmark move'
alias jjbr='jj bookmark rename'
alias jjbs='jj bookmark set'
alias jjbt='jj bookmark track'
alias jjbu='jj bookmark untrack'
alias jjc='jj commit'
alias jjcfg='jj config'
alias jjcfgl='jj config list'
alias jjcmsg='jj commit --message'
alias jjd='jj diff'
alias jjdmsg='jj desc --message'
alias jjds='jj desc'
alias jjdst='jj diff --stat'
alias jje='jj edit'
alias jjev='jj evolog'
alias jjf='jj file'
alias jjfl='jj file list'
alias jjg='jj git'
alias jjgcl='jj git clone'
alias jjgf='jj git fetch'
alias jjgfa='jj git fetch --all-remotes'
alias jjgi='jj git init'
alias jjgp='jj git push'
alias jjgpa='jj git push --all'
alias jjgpd='jj git push --deleted'
alias jjgpt='jj git push --tracked'
alias jjgrl='jj git remote list'
alias jjl='jj log'
alias jjla='jj log -r "all()"'
alias jjn='jj new'
alias jjnt='jj new "trunk()"'
alias jjop='jj op'
alias jjopl='jj op log'
alias jjor='jj op restore'
alias jjrb='jj rebase'
alias jjrbm='jj rebase -d "trunk()"'
alias jjrs='jj restore'
alias jjrt='cd "$(jj root || echo .)"'
alias jjs='jj show'
alias jjsp='jj split'
alias jjsq='jj squash'
alias jjst='jj status'
alias jju='jj undo'
alias jjw='jj workspace'
alias jjwa='jj workspace add'
alias jjwf='jj workspace forget'
alias jjwl='jj workspace list'
-1
View File
@@ -35,7 +35,6 @@ alias jdlr='juju debug-log --ms --replay'
alias jcon='juju consume'
alias jeb='juju export-bundle'
alias jex='juju expose'
alias junex='juju unexpose'
alias jh='juju help'
alias jkc='juju kill-controller -y -t 0'
alias jm='juju models'
-1
View File
@@ -71,7 +71,6 @@ plugins=(... kubectl)
| | | **Secret management** |
| kgsec | `kubectl get secret` | Get secret for decoding |
| kgseca | `kubectl get secret --all-namespaces` | List secrets across all namespaces |
| kesec | `kubectl edit secret` | Edit secret resource |
| kdsec | `kubectl describe secret` | Describe secret resource in detail |
| kdelsec | `kubectl delete secret` | Delete the secret |
| | | **Deployment management** |
-1
View File
@@ -93,7 +93,6 @@ alias kdelcm='kubectl delete configmap'
# Secret management
alias kgsec='kubectl get secret'
alias kgseca='kubectl get secret --all-namespaces'
alias kesec='kubectl edit secret'
alias kdsec='kubectl describe secret'
alias kdelsec='kubectl delete secret'
-1
View File
@@ -43,7 +43,6 @@ plugins=(... laravel)
| `pami` | `php artisan make:interface` |
| `pamtr` | `php artisan make:trait` |
| `pamv` | `php artisan make:view` |
| `pammig` | `php artisan make:migration` |
## Clears
-1
View File
@@ -32,7 +32,6 @@ alias pamen='php artisan make:enum'
alias pami='php artisan make:interface'
alias pamtr='php artisan make:trait'
alias pamv='php artisan make:view'
alias pammig='php artisan make:migration'
# Clears
-111
View File
@@ -1,111 +0,0 @@
# opencode plugin
This plugin adds completion and aliases for the [OpenCode](https://opencode.ai) CLI.
Install OpenCode using its [installation instructions](https://opencode.ai/docs/#install),
and make sure `opencode` is available on your `$PATH`.
To enable it, add `opencode` to the plugins array in your `.zshrc`:
```zsh
plugins=(... opencode)
```
## Learning OpenCode from the terminal
Use tab completion to explore commands and their descriptions without leaving the
shell. For example, these are some of the candidates shown by the CLI:
```text
$ opencode <TAB>
agent -- manage agents
models -- list all available models
run -- run opencode with a message
session -- manage sessions
stats -- show token usage and cost statistics
...
$ opencode session <TAB>
delete -- delete a session
list -- list sessions
$ opencode run --<TAB>
--agent -- agent to use
--continue -- continue the last session
--file -- file(s) to attach to message
--model -- model to use in the format of provider/model
...
```
Completion works through the aliases too. Type a partial command or option and
press Tab to finish it:
```text
$ opc session li<TAB>
$ opc session list
$ opcr --att<TAB>
$ opcr --attach
$ opcc --log-l<TAB>
$ opcc --log-level
```
Candidates depend on your installed OpenCode version. In OpenCode 1.18.29, the
CLI's completion output omits top-level TUI flags such as `--model` and `--fork`,
even though those flags work when typed. Use `opencode --help` or
`opencode run --help` for the full options, and see the
[CLI reference](https://opencode.ai/docs/cli/) for more workflows.
## Aliases
| Alias | Command | Description |
| ------ | --------------------- | ----------------------------------- |
| `opc` | `opencode` | Launch the terminal UI |
| `opcc` | `opencode --continue` | Continue the last session |
| `opcr` | `opencode run` | Run a prompt without opening the UI |
To keep completion but skip the aliases, set this in your `.zshrc`, above the line
that sources Oh My Zsh:
```zsh
# ~/.zshrc
zstyle ':omz:plugins:opencode' aliases no
plugins=(... opencode)
source $ZSH/oh-my-zsh.sh
```
## Common workflows
All aliases accept additional arguments:
```zsh
# Open a project
opc ~/projects/my-app
# Explore a different approach by forking the last session
opcc --fork
# Ask a question without entering the terminal UI
opcr "Explain the test setup in this project"
# Reuse an already running backend to avoid MCP server cold starts
opcr --attach http://localhost:4096 "Explain this project's architecture"
```
## Completion
Completion queries the installed OpenCode CLI on demand for commands and options,
including through the aliases above. It falls back to Zsh's default completion when
the CLI returns no candidates. No OpenCode process is started at shell startup,
and no generated completion cache is needed.
## Credits
Based on the OpenCode plugin proposals by
[pranavavva](https://github.com/pranavavva) in
[#13545](https://github.com/ohmyzsh/ohmyzsh/pull/13545) and
[mskadu](https://github.com/mskadu) in
[#13794](https://github.com/ohmyzsh/ohmyzsh/pull/13794).
The completion adapter is derived from OpenCode's `opencode completion` output.
-12
View File
@@ -1,12 +0,0 @@
#compdef opencode
# Adapted from `opencode completion`, using the CLI's live Yargs completions.
local -a reply
reply=("${(@f)$(COMP_CWORD="$((CURRENT-1))" COMP_LINE="$BUFFER" COMP_POINT="$CURSOR" \
command opencode --get-yargs-completions "${words[@]}" < /dev/null 2> /dev/null)}")
if [[ -n "${reply[1]}" ]]; then
_describe 'values' reply
else
_default
fi
-7
View File
@@ -1,7 +0,0 @@
if (( ! $+commands[opencode] )); then
return
fi
alias opc='opencode'
alias opcc='opencode --continue'
alias opcr='opencode run'
+1 -1
View File
@@ -23,7 +23,7 @@ _togglePoetryShell() {
# Activate the environment if in a Poetry directory and no environment is currently active
if [[ $in_poetry_dir -eq 1 ]] && [[ $poetry_active -ne 1 ]]; then
local venv_dir=$(poetry env info --path 2>/dev/null)
venv_dir=$(poetry env info --path 2>/dev/null)
# Handle case where poetry returns "." for in-project virtual environments
if [[ "$venv_dir" == "." ]]; then
venv_dir="$PWD/.venv"
+13 -21
View File
@@ -15,8 +15,8 @@
# - https://github.com/symfony/symfony/blob/5.4/src/Symfony/Component/Console/Resources/completion.bash
#
_sf_console() {
local lastParam out comp sf_cmd
local -a completions flagPrefix requestComp inputs
local lastParam flagPrefix requestComp out comp
local -a completions
# The user could have moved the cursor backwards on the command-line.
# We need to trigger completion from the $CURRENT location, so we need
@@ -29,20 +29,11 @@ _sf_console() {
setopt local_options BASH_REMATCH
if [[ "${lastParam}" =~ '-.*=' ]]; then
# We are dealing with a flag with an =
flagPrefix=(-P "${BASH_REMATCH}")
flagPrefix="-P ${BASH_REMATCH}"
fi
# Prepare the command to obtain completions. An alias is resolved here,
# because the request is no longer read again by the shell.
sf_cmd="${words[1]}"
if [[ -n "${aliases[$sf_cmd]}" ]]; then
requestComp=(${(z)aliases[$sf_cmd]})
else
requestComp=(${~sf_cmd})
fi
requestComp+=(_complete --no-interaction -szsh -a1 "-c$((CURRENT-1))")
# Prepare the command to obtain completions
requestComp="${words[0]} ${words[1]} _complete --no-interaction -szsh -a1 -c$((CURRENT-1))" i=""
for w in ${words[@]}; do
w=$(printf -- '%b' "$w")
# remove quotes from typed values
@@ -56,18 +47,19 @@ _sf_console() {
fi
# empty values are ignored
if [ ! -z "$w" ]; then
inputs+=("-i$w")
i="${i}-i${w} "
fi
done
# Ensure at least 1 input
if (( ! $#inputs )); then
inputs=(-i' ')
if [ "${i}" = "" ]; then
requestComp="${requestComp} -i\" \""
else
requestComp="${requestComp} ${i}"
fi
# The request is run without being read again by the shell, so that a
# "$(...)" or a backtick typed on the command line is not executed
out=$(SHELL_VERBOSITY=0 "${requestComp[@]}" "${inputs[@]}" 2>/dev/null)
# Use eval to handle any environment variables and such
out=$(eval ${requestComp} 2>/dev/null)
while IFS='\n' read -r comp; do
if [ -n "$comp" ]; then
@@ -83,7 +75,7 @@ _sf_console() {
done < <(printf "%s\n" "${out[@]}")
# Let inbuilt _describe handle completions
_describe "completions" completions "${flagPrefix[@]}"
eval _describe "completions" completions $flagPrefix
return $?
}
+1 -1
View File
@@ -127,7 +127,7 @@ case $state in
(box)
__vagrant-box
;;
(up|provision|port|package|destroy|reload|ssh|ssh-config|halt|resume|status|suspend|rdp)
(up|provision|port|package|destroy|reload|ssh|ssh-config|halt|resume|status)
_arguments ':feature:__vm_list'
esac
;;
-5
View File
@@ -51,11 +51,6 @@ Available search contexts are:
| `npmpkg` | `https://www.npmjs.com/search?q=` |
| `packagist` | `https://packagist.org/?query=` |
| `gopkg` | `https://pkg.go.dev/search?m=package&q=` |
| `maven` | `https://central.sonatype.com/search?q=` |
| `pypi` | `https://pypi.org/search/?q=` |
| `aur` | `https://aur.archlinux.org/packages?O=0&K=` |
| `archwiki` | `https://wiki.archlinux.org/index.php?search=` |
| `wikipedia` | `https://en.wikipedia.org/w/index.php?search=` |
| `chatgpt` | `https://chatgpt.com/?q=` |
| `claudeai` | `https://claude.ai/new?q=` |
| `grokcom` | `https://grok.com/?q=` |
+1 -10
View File
@@ -32,11 +32,6 @@ function web_search() {
npmpkg "https://www.npmjs.com/search?q="
packagist "https://packagist.org/?query="
gopkg "https://pkg.go.dev/search?m=package&q="
maven "https://central.sonatype.com/search?q="
pypi "https://pypi.org/search/?q="
aur "https://aur.archlinux.org/packages?O=0&K="
archwiki "https://wiki.archlinux.org/index.php?search="
wikipedia "https://en.wikipedia.org/w/index.php?search="
chatgpt "https://chatgpt.com/?q="
grok "https://grok.com/?q="
claudeai "https://claude.ai/new?q="
@@ -97,11 +92,6 @@ alias gems='web_search gems'
alias npmpkg='web_search npmpkg'
alias packagist='web_search packagist'
alias gopkg='web_search gopkg'
alias maven='web_search maven'
alias pypi='web_search pypi'
alias aur='web_search aur'
alias archwiki='web_search archwiki'
alias wikipedia='web_search wikipedia'
alias chatgpt='web_search chatgpt'
alias grokcom='web_search grok'
alias claudeai='web_search claudeai'
@@ -126,3 +116,4 @@ if [[ ${#ZSH_WEB_SEARCH_ENGINES} -gt 0 ]]; then
done
unset engines key
fi
@@ -0,0 +1,7 @@
## Cache
This plugin caches the completion script and automatically updates it when the
plugin is loaded, which is usually when you start a new terminal emulator.
The cache is stored at `$ZSH_CACHE_DIR/completions/_%command%`.
@@ -0,0 +1,11 @@
# %name% plugin
%description%
To use it, add `%name%` to the plugins array in your zshrc file:
```zsh
plugins=(... %name%)
```
This plugin does not add any aliases.
@@ -0,0 +1,20 @@
# %name% plugin
%description%
To use it, add `%name%` to the plugins array in your zshrc file:
```zsh
plugins=(... %name%)
```
## Aliases
| Alias | Command | Description |
| :----------- | ------------------ | :-------------------- |
| `%command%s` | `%command% status` | Show %command% status |
## Requirements
This plugin requires [%command%](https://example.com) to be installed.
%cache%
@@ -0,0 +1,22 @@
#
# Completion
#
# %command% generates its own zsh completion, so we cache it instead of
# maintaining a `_%command%` file by hand.
#
# On the first shell after installing this plugin the cache file doesn't exist
# yet, so compinit hasn't bound it. Do that manually here.
if [[ ! -f "$ZSH_CACHE_DIR/completions/_%command%" ]]; then
typeset -g -A _comps
autoload -Uz _%command%
_comps[%command%]=_%command%
fi
# Regenerate the cache in the background so it never blocks shell startup.
# TMPPREFIX puts the temp file next to the destination, which keeps the move
# atomic.
zmodload -F zsh/files b:zf_mv
() {
local TMPPREFIX="$ZSH_CACHE_DIR/completions/_%command%"
zf_mv -f -- =( %compgen% ) "$TMPPREFIX"
} &|
@@ -0,0 +1,6 @@
#
# Completion
#
# Oh My Zsh adds this directory to $fpath, so a file named `_%command%` next to
# this one is picked up automatically as the completion for %command%.
#
@@ -0,0 +1,32 @@
#compdef %command%
#
# Completion for %command%.
#
# This is a starting point. Delete what you don't need. Two references:
# https://zsh.sourceforge.io/Doc/Release/Completion-System.html
# https://github.com/zsh-users/zsh/blob/master/Etc/completion-style-guide
local -a subcommands
subcommands=(
'status:Show the current status'
'run:Run something'
'help:Show help for a command'
)
_arguments -C \
'(-h --help)'{-h,--help}'[show help]' \
'(-v --version)'{-v,--version}'[show the version]' \
'1: :->subcommand' \
'*:: :->args'
case $state in
subcommand)
_describe -t commands '%command% subcommand' subcommands
;;
args)
case $words[1] in
status) _arguments '--short[one-line output]' ;;
run) _files ;;
esac
;;
esac
@@ -0,0 +1,48 @@
# %name% plugin
#
# %description%
#
# This file is sourced by Oh My Zsh in every new shell, so keep it fast.
# Avoid running external commands at the top level unless you really need to.
#
# Wrapping a command-line tool? Guard everything so people who don't have it
# installed don't end up with broken aliases:
#
# if (( ! $+commands[sometool] )); then
# return
# fi
#
# Aliases
#
# Document every alias in README.md. Short, obvious, and few beats many.
#
# alias %name%='echo "hello from %name%"'
#
# Functions
#
# Anything that needs arguments in the middle, or more than one command,
# belongs here rather than in an alias.
#
# function %name%_hello() {
# echo "hello, ${1:-world}"
# }
#
# Completion
#
# Oh My Zsh adds this directory to $fpath, so a file named `_sometool` next to
# this one is picked up automatically as the completion for `sometool`.
#
#
# Need the path to this plugin's own directory (for data files, a lib/ dir...)?
# This is the standard way to get it, since $0 is not reliable on its own:
# https://zdharma-continuum.github.io/Zsh-100-Commits-Club/Zsh-Plugin-Standard.html
#
# 0="${ZERO:-${${0:#$ZSH_ARGZERO}:-${(%):-%N}}}"
# 0="${${(M)0:#/*}:-$PWD/$0}"
# source "${0:A:h}/lib/helpers.zsh"
@@ -0,0 +1,43 @@
# %name% plugin
#
# %description%
#
# This file is sourced by Oh My Zsh in every new shell, so keep it fast.
# Avoid running external commands at the top level unless you really need to.
# Only define anything if the tool is actually installed. Without this guard,
# people who don't have %command% get aliases that fail with "command not found".
if (( ! $+commands[%command%] )); then
return
fi
#
# Aliases
#
# Document every alias in README.md. Short, obvious, and few beats many.
# The one below is just an example: replace it with something %command% can do.
#
alias %command%s='%command% status'
#
# Functions
#
# Anything that needs arguments in the middle, or more than one command,
# belongs here rather than in an alias.
#
# function %name%_current() {
# %command% status --short "$@"
# }
%completion%
#
# Need the path to this plugin's own directory (for data files, a lib/ dir...)?
# This is the standard way to get it, since $0 is not reliable on its own:
# https://zdharma-continuum.github.io/Zsh-100-Commits-Club/Zsh-Plugin-Standard.html
#
# 0="${ZERO:-${${0:#$ZSH_ARGZERO}:-${(%):-%N}}}"
# 0="${${(M)0:#/*}:-$PWD/$0}"
# source "${0:A:h}/lib/helpers.zsh"
+2 -2
View File
@@ -4,8 +4,8 @@ export LSCOLORS=dxFxCxDxBxegedabagacad
local git_branch='$(git_prompt_info)%{$reset_color%}$(git_remote_status)'
PROMPT="%{%(#~$fg[red]~$fg[green])%}╭─%n@%m %{$reset_color%}%{$fg[yellow]%}in %~ %{$reset_color%}${git_branch}
%{%(#~$fg[red]~$fg[green])%}╰%(#~#~\$) %{$reset_color%}"
PROMPT="%{$fg[green]%}╭─%n@%m %{$reset_color%}%{$fg[yellow]%}in %~ %{$reset_color%}${git_branch}
%{$fg[green]%}╰\$ %{$reset_color%}"
ZSH_THEME_GIT_PROMPT_PREFIX="%{$fg_bold[yellow]%}on "
ZSH_THEME_GIT_PROMPT_SUFFIX="%{$reset_color%}"
+4 -24
View File
@@ -23,32 +23,12 @@ zstyle -s ':omz:update' mode update_mode || {
# - the automatic update is disabled
# - the current user doesn't have write permissions nor owns the $ZSH directory
# - is not run from a tty
if [[ "$update_mode" = disabled ]] \
|| [[ ! -w "$ZSH" || ! -O "$ZSH" ]] \
|| [[ ! -t 1 && ${POWERLEVEL9K_INSTANT_PROMPT:-off} == off ]]; then
unset update_mode
return
fi
# Cancel update if it's not time to check yet. This runs before the git checks
# below because those fork on every startup. Skipped when a background update
# left a result that still needs to be reported (EXIT_STATUS is set).
() {
local LAST_EPOCH EXIT_STATUS ERROR epoch_target
zmodload zsh/datetime
source "${ZSH_CACHE_DIR}/.zsh-update" 2>/dev/null || return 1
[[ -n "$LAST_EPOCH" && -z "$EXIT_STATUS" ]] || return 1
zstyle -s ':omz:update' frequency epoch_target || epoch_target=${UPDATE_ZSH_DAYS:-13}
(( ( EPOCHSECONDS / 60 / 60 / 24 - LAST_EPOCH ) < epoch_target ))
} && {
unset update_mode
return
}
# Cancel update if:
# - git is unavailable on the system
# - $ZSH is not a git repository
if ! command git --version >/dev/null 2>&1 \
if [[ "$update_mode" = disabled ]] \
|| [[ ! -w "$ZSH" || ! -O "$ZSH" ]] \
|| [[ ! -t 1 && ${POWERLEVEL9K_INSTANT_PROMPT:-off} == off ]] \
|| ! command git --version >/dev/null 2>&1 \
|| (builtin cd -q "$ZSH"; ! command git rev-parse --is-inside-work-tree &>/dev/null); then
unset update_mode
return
+3 -4
View File
@@ -528,10 +528,9 @@ print_success() {
"$(fmt_code "$(fmt_link ".zshrc" "file://$zdot/.zshrc" --text)")" \
"file to select plugins, themes, and options."
printf '\n'
printf '%s\n' "• Follow along on X ($(fmt_link @ohmyzsh https://x.com/ohmyzsh)) or Bluesky ($(fmt_link @ohmyz.sh https://bsky.app/profile/ohmyz.sh))"
printf '%s\n' "Questions or ideas? Join the community on $(fmt_link Discord https://discord.gg/ohmyzsh)"
printf '%s\n' "Help support the project: sponsor us on $(fmt_link "Open Collective" https://opencollective.com/ohmyzsh),"
printf '%s\n' " or grab stickers, shirts, and swag from $(fmt_link CommitGoods https://commitgoods.com/ohmyzsh)"
printf '%s\n' "• Follow us on X: $(fmt_link @ohmyzsh https://x.com/ohmyzsh)"
printf '%s\n' "Join our Discord community: $(fmt_link "Discord server" https://discord.gg/ohmyzsh)"
printf '%s\n' "Get stickers, t-shirts, coffee mugs and more: $(fmt_link "CommitGoods Shop" https://commitgoods.com/collections/oh-my-zsh)"
printf '%s\n' $FMT_RESET
}
+3 -12
View File
@@ -271,18 +271,9 @@ if LANG= git pull --quiet --rebase $remote $branch; then
printf '%s %s %s %s /____/ %s %s %s %s\n' $RAINBOW $RESET
printf '\n'
printf "${BLUE}%s${RESET}\n\n" "$message"
printf "${BLUE}${BOLD}%s %s %s %s${RESET}\n" \
"Follow along on X" "($(fmt_link @ohmyzsh https://x.com/ohmyzsh))" \
"or Bluesky" "($(fmt_link @ohmyz.sh https://bsky.app/profile/ohmyz.sh))"
printf "${BLUE}${BOLD}%s %s${RESET}\n" \
"Questions or ideas? Join the community on" \
"$(fmt_link Discord https://discord.gg/ohmyzsh)"
printf "${BLUE}${BOLD}%s %s,${RESET}\n" \
"Help support the project: sponsor us on" \
"$(fmt_link "Open Collective" https://opencollective.com/ohmyzsh)"
printf "${BLUE}${BOLD}%s %s${RESET}\n" \
"or grab stickers, shirts, and swag from" \
"$(fmt_link CommitGoods https://commitgoods.com/ohmyzsh)"
printf "${BLUE}${BOLD}%s %s${RESET}\n" "To keep up with the latest news and updates, follow us on X:" "$(fmt_link @ohmyzsh https://x.com/ohmyzsh)"
printf "${BLUE}${BOLD}%s %s${RESET}\n" "Want to get involved in the community? Join our Discord:" "$(fmt_link "Discord server" https://discord.gg/ohmyzsh)"
printf "${BLUE}${BOLD}%s %s${RESET}\n" "Get your Oh My Zsh swag at:" "$(fmt_link "CommitGoods Shop" https://commitgoods.com/collections/oh-my-zsh)"
elif [[ $verbose_mode == minimal ]]; then
printf "${BLUE}%s${RESET}\n" "$message"
fi