dev: automated commit - 2025-11-11 11:58:51
This commit is contained in:
parent
9181cfeb83
commit
7dbe7f4685
3 changed files with 121 additions and 15 deletions
|
|
@ -5,12 +5,21 @@ set -euo pipefail
|
||||||
CACHE_MAX_AGE=300
|
CACHE_MAX_AGE=300
|
||||||
MRU_SIZE=20
|
MRU_SIZE=20
|
||||||
|
|
||||||
|
# Debug logging (set DEBUG=1 to enable)
|
||||||
|
DEBUG="${DEBUG:-0}"
|
||||||
|
|
||||||
|
debug() {
|
||||||
|
[ "$DEBUG" = "1" ] && echo "[DEBUG] $*" >&2 || true
|
||||||
|
}
|
||||||
|
|
||||||
# Parse arguments - flags first, then DEV_DIR
|
# Parse arguments - flags first, then DEV_DIR
|
||||||
NO_CACHE=false
|
NO_CACHE=false
|
||||||
CLEAR_CACHE=false
|
CLEAR_CACHE=false
|
||||||
EDITOR="${EDITOR:-nvim}"
|
EDITOR="${EDITOR:-nvim}"
|
||||||
DEV_DIR=""
|
DEV_DIR=""
|
||||||
|
|
||||||
|
debug "Starting dev-launcher with args: $*"
|
||||||
|
|
||||||
for arg in "$@"; do
|
for arg in "$@"; do
|
||||||
case "$arg" in
|
case "$arg" in
|
||||||
--help | -h)
|
--help | -h)
|
||||||
|
|
@ -37,11 +46,16 @@ EOF
|
||||||
done
|
done
|
||||||
|
|
||||||
DEV_DIR="${DEV_DIR:-$HOME/Dev}"
|
DEV_DIR="${DEV_DIR:-$HOME/Dev}"
|
||||||
|
debug "DEV_DIR: $DEV_DIR"
|
||||||
|
debug "NO_CACHE: $NO_CACHE"
|
||||||
|
debug "CLEAR_CACHE: $CLEAR_CACHE"
|
||||||
|
|
||||||
# Setup cache files (unique per dev directory)
|
# Setup cache files (unique per dev directory)
|
||||||
DEV_DIR_HASH=$(echo -n "$DEV_DIR" | sha256sum | cut -d' ' -f1 | head -c 16)
|
DEV_DIR_HASH=$(echo -n "$DEV_DIR" | sha256sum | cut -d' ' -f1 | head -c 16)
|
||||||
CACHE_FILE="$HOME/.cache/dev-launcher-cache-${DEV_DIR_HASH}"
|
CACHE_FILE="$HOME/.cache/dev-launcher-cache-${DEV_DIR_HASH}"
|
||||||
MRU_FILE="$HOME/.cache/dev-launcher-mru-${DEV_DIR_HASH}"
|
MRU_FILE="$HOME/.cache/dev-launcher-mru-${DEV_DIR_HASH}"
|
||||||
|
debug "CACHE_FILE: $CACHE_FILE"
|
||||||
|
debug "MRU_FILE: $MRU_FILE"
|
||||||
|
|
||||||
# Handle --clear-cache
|
# Handle --clear-cache
|
||||||
if [ "$CLEAR_CACHE" = true ]; then
|
if [ "$CLEAR_CACHE" = true ]; then
|
||||||
|
|
@ -52,17 +66,29 @@ fi
|
||||||
|
|
||||||
# Find git repositories
|
# Find git repositories
|
||||||
find_git_repos() {
|
find_git_repos() {
|
||||||
|
debug "find_git_repos: checking cache..."
|
||||||
if [ "$NO_CACHE" = false ] && [ -f "$CACHE_FILE" ]; then
|
if [ "$NO_CACHE" = false ] && [ -f "$CACHE_FILE" ]; then
|
||||||
local cache_age=$(($(date +%s) - $(stat -c %Y "$CACHE_FILE" 2>/dev/null || echo 0)))
|
local cache_age=$(($(date +%s) - $(stat -c %Y "$CACHE_FILE" 2>/dev/null || echo 0)))
|
||||||
[ $cache_age -lt $CACHE_MAX_AGE ] && cat "$CACHE_FILE" && return
|
debug "Cache age: ${cache_age}s (max: ${CACHE_MAX_AGE}s)"
|
||||||
|
if [ $cache_age -lt $CACHE_MAX_AGE ]; then
|
||||||
|
debug "Using cached repos"
|
||||||
|
cat "$CACHE_FILE"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
debug "Cache expired, rescanning..."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
debug "Scanning for git repositories in $DEV_DIR..."
|
||||||
if command -v fd >/dev/null 2>&1; then
|
if command -v fd >/dev/null 2>&1; then
|
||||||
|
debug "Using fd for scanning"
|
||||||
fd -H -t d "^\.git$" "$DEV_DIR" -d 3 -0 | xargs -0 -n1 dirname | sort -u >"$CACHE_FILE"
|
fd -H -t d "^\.git$" "$DEV_DIR" -d 3 -0 | xargs -0 -n1 dirname | sort -u >"$CACHE_FILE"
|
||||||
else
|
else
|
||||||
|
debug "Using find for scanning"
|
||||||
find "$DEV_DIR" -maxdepth 3 -type d -name ".git" -print0 |
|
find "$DEV_DIR" -maxdepth 3 -type d -name ".git" -print0 |
|
||||||
xargs -0 -n1 dirname | sort -u >"$CACHE_FILE"
|
xargs -0 -n1 dirname | sort -u >"$CACHE_FILE"
|
||||||
fi
|
fi
|
||||||
|
local repo_count=$(wc -l < "$CACHE_FILE")
|
||||||
|
debug "Found $repo_count repositories"
|
||||||
cat "$CACHE_FILE"
|
cat "$CACHE_FILE"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -106,18 +132,27 @@ sort_by_mru() {
|
||||||
# Main execution
|
# Main execution
|
||||||
REPOS=$(find_git_repos)
|
REPOS=$(find_git_repos)
|
||||||
|
|
||||||
[ -z "$REPOS" ] && notify-send "Dev Launcher" "No git repositories found in $DEV_DIR" 2>/dev/null && exit 1
|
if [ -z "$REPOS" ]; then
|
||||||
|
debug "No repositories found!"
|
||||||
|
notify-send "Dev Launcher" "No git repositories found in $DEV_DIR" 2>/dev/null || true
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
debug "Found repositories, proceeding..."
|
||||||
|
|
||||||
# Build display list (relative paths) and mapping
|
# Build display list (relative paths) and mapping
|
||||||
DISPLAY_LIST=$(echo "$REPOS" | sed "s|^${DEV_DIR}/||")
|
DISPLAY_LIST=$(echo "$REPOS" | sed "s|^${DEV_DIR}/||")
|
||||||
declare -A DISPLAY_TO_PATH
|
declare -A DISPLAY_TO_PATH
|
||||||
|
|
||||||
|
debug "Building display mapping..."
|
||||||
while IFS=$'\t' read -r repo display; do
|
while IFS=$'\t' read -r repo display; do
|
||||||
DISPLAY_TO_PATH["$display"]="$repo"
|
DISPLAY_TO_PATH["$display"]="$repo"
|
||||||
done < <(paste <(echo "$REPOS") <(echo "$DISPLAY_LIST"))
|
done < <(paste <(echo "$REPOS") <(echo "$DISPLAY_LIST"))
|
||||||
|
debug "Mapped ${#DISPLAY_TO_PATH[@]} projects"
|
||||||
|
|
||||||
# Sort by MRU and present in tofi
|
# Sort by MRU and present in tofi
|
||||||
|
debug "Sorting by MRU..."
|
||||||
SORTED_LIST=$(sort_by_mru "$DISPLAY_LIST")
|
SORTED_LIST=$(sort_by_mru "$DISPLAY_LIST")
|
||||||
|
debug "Presenting in tofi..."
|
||||||
SELECTED_DISPLAY=$(echo "$SORTED_LIST" | tofi \
|
SELECTED_DISPLAY=$(echo "$SORTED_LIST" | tofi \
|
||||||
--prompt-text "💀 Poison: " \
|
--prompt-text "💀 Poison: " \
|
||||||
--fuzzy-match true \
|
--fuzzy-match true \
|
||||||
|
|
@ -139,21 +174,31 @@ SELECTED_DISPLAY=$(echo "$SORTED_LIST" | tofi \
|
||||||
--border-color '#31748f' \
|
--border-color '#31748f' \
|
||||||
--prompt-color '#f6c177')
|
--prompt-color '#f6c177')
|
||||||
|
|
||||||
[ -z "$SELECTED_DISPLAY" ] && exit 0
|
debug "Selected display: '$SELECTED_DISPLAY'"
|
||||||
|
[ -z "$SELECTED_DISPLAY" ] && debug "No selection, exiting" && exit 0
|
||||||
|
|
||||||
# Remove star indicator and lookup path
|
# Remove star indicator and lookup path
|
||||||
SELECTED_DISPLAY_CLEAN=$(echo "$SELECTED_DISPLAY" | sed 's/^⭐ //')
|
SELECTED_DISPLAY_CLEAN=$(echo "$SELECTED_DISPLAY" | sed 's/^⭐ //')
|
||||||
SELECTED="${DISPLAY_TO_PATH[$SELECTED_DISPLAY_CLEAN]:-}"
|
SELECTED="${DISPLAY_TO_PATH[$SELECTED_DISPLAY_CLEAN]:-}"
|
||||||
|
|
||||||
[ -z "$SELECTED" ] || [ ! -d "$SELECTED" ] && exit 0
|
debug "Selected display (clean): '$SELECTED_DISPLAY_CLEAN'"
|
||||||
|
debug "Selected path: '$SELECTED'"
|
||||||
|
|
||||||
|
[ -z "$SELECTED" ] || [ ! -d "$SELECTED" ] && debug "Invalid selection, exiting" && exit 0
|
||||||
|
|
||||||
# Update MRU and launch
|
# Update MRU and launch
|
||||||
|
debug "Updating MRU..."
|
||||||
update_mru "$SELECTED_DISPLAY_CLEAN"
|
update_mru "$SELECTED_DISPLAY_CLEAN"
|
||||||
|
|
||||||
PROJECT_NAME=$(get_project_name "$SELECTED")
|
PROJECT_NAME=$(get_project_name "$SELECTED")
|
||||||
SESSION_NAME="dev-${PROJECT_NAME}"
|
SESSION_NAME="dev-${PROJECT_NAME}"
|
||||||
CLASS_NAME="com.mzunino.dev.${PROJECT_NAME}"
|
CLASS_NAME="com.mzunino.dev.${PROJECT_NAME}"
|
||||||
|
|
||||||
|
debug "Project name: $PROJECT_NAME"
|
||||||
|
debug "Session name: $SESSION_NAME"
|
||||||
|
debug "Class name: $CLASS_NAME"
|
||||||
|
debug "Launching ghostty with launch-or-focus..."
|
||||||
|
|
||||||
launch-or-focus ghostty \
|
launch-or-focus ghostty \
|
||||||
--working-directory="$SELECTED" \
|
--working-directory="$SELECTED" \
|
||||||
--title="$PROJECT_NAME" \
|
--title="$PROJECT_NAME" \
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,13 @@ set -euo pipefail
|
||||||
|
|
||||||
# Script to launch an application or focus it if already running in sway
|
# Script to launch an application or focus it if already running in sway
|
||||||
# Usage: launch-or-focus <command> [args...]
|
# Usage: launch-or-focus <command> [args...]
|
||||||
|
# Set DEBUG=1 to enable debug logging
|
||||||
|
|
||||||
|
DEBUG="${DEBUG:-0}"
|
||||||
|
|
||||||
|
debug() {
|
||||||
|
[ "$DEBUG" = "1" ] && echo "[DEBUG] $*" >&2 || true
|
||||||
|
}
|
||||||
|
|
||||||
if [ $# -eq 0 ]; then
|
if [ $# -eq 0 ]; then
|
||||||
echo "Usage: $0 <command> [args...]" >&2
|
echo "Usage: $0 <command> [args...]" >&2
|
||||||
|
|
@ -14,6 +21,9 @@ shift
|
||||||
ARGS=("$@")
|
ARGS=("$@")
|
||||||
|
|
||||||
APP_NAME=$(basename "$COMMAND")
|
APP_NAME=$(basename "$COMMAND")
|
||||||
|
debug "Command: $COMMAND"
|
||||||
|
debug "App name: $APP_NAME"
|
||||||
|
debug "Args: ${ARGS[*]}"
|
||||||
|
|
||||||
# Extract --class and --title values from args if present
|
# Extract --class and --title values from args if present
|
||||||
# Handle both --class VALUE and --class=VALUE formats
|
# Handle both --class VALUE and --class=VALUE formats
|
||||||
|
|
@ -22,25 +32,35 @@ TITLE_NAME=""
|
||||||
for i in "${!ARGS[@]}"; do
|
for i in "${!ARGS[@]}"; do
|
||||||
if [[ "${ARGS[$i]}" =~ ^--class=(.+)$ ]]; then
|
if [[ "${ARGS[$i]}" =~ ^--class=(.+)$ ]]; then
|
||||||
CLASS_NAME="${BASH_REMATCH[1]}"
|
CLASS_NAME="${BASH_REMATCH[1]}"
|
||||||
|
debug "Found class (format --class=VALUE): $CLASS_NAME"
|
||||||
elif [ "${ARGS[$i]}" = "--class" ] && [ $((i+1)) -lt ${#ARGS[@]} ]; then
|
elif [ "${ARGS[$i]}" = "--class" ] && [ $((i+1)) -lt ${#ARGS[@]} ]; then
|
||||||
CLASS_NAME="${ARGS[$((i+1))]}"
|
CLASS_NAME="${ARGS[$((i+1))]}"
|
||||||
|
debug "Found class (format --class VALUE): $CLASS_NAME"
|
||||||
elif [[ "${ARGS[$i]}" =~ ^--title=(.+)$ ]]; then
|
elif [[ "${ARGS[$i]}" =~ ^--title=(.+)$ ]]; then
|
||||||
TITLE_NAME="${BASH_REMATCH[1]}"
|
TITLE_NAME="${BASH_REMATCH[1]}"
|
||||||
|
debug "Found title (format --title=VALUE): $TITLE_NAME"
|
||||||
elif [ "${ARGS[$i]}" = "--title" ] && [ $((i+1)) -lt ${#ARGS[@]} ]; then
|
elif [ "${ARGS[$i]}" = "--title" ] && [ $((i+1)) -lt ${#ARGS[@]} ]; then
|
||||||
TITLE_NAME="${ARGS[$((i+1))]}"
|
TITLE_NAME="${ARGS[$((i+1))]}"
|
||||||
|
debug "Found title (format --title VALUE): $TITLE_NAME"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
debug "Extracted CLASS_NAME: '$CLASS_NAME'"
|
||||||
|
debug "Extracted TITLE_NAME: '$TITLE_NAME'"
|
||||||
|
|
||||||
find_window_by_class() {
|
find_window_by_class() {
|
||||||
local class="$1"
|
local class="$1"
|
||||||
swaymsg -t get_tree | jq -r --arg class "$class" '
|
debug "Searching for window by class: $class"
|
||||||
|
local result
|
||||||
|
result=$(swaymsg -t get_tree | jq -r --arg class "$class" '
|
||||||
recurse(.nodes[]?, .floating_nodes[]?) |
|
recurse(.nodes[]?, .floating_nodes[]?) |
|
||||||
select(
|
select(
|
||||||
((.window_properties.class | type) == "string" and .window_properties.class == $class) or
|
((.window_properties.class | type) == "string" and .window_properties.class == $class) or
|
||||||
((.app_id | type) == "string" and .app_id == $class)
|
((.app_id | type) == "string" and .app_id == $class)
|
||||||
) |
|
) |
|
||||||
.id
|
.id
|
||||||
' | head -n 1
|
' | head -n 1)
|
||||||
|
debug "find_window_by_class result: '$result'"
|
||||||
|
echo "$result"
|
||||||
}
|
}
|
||||||
|
|
||||||
find_window_by_title() {
|
find_window_by_title() {
|
||||||
|
|
@ -48,6 +68,8 @@ find_window_by_title() {
|
||||||
local app="$2"
|
local app="$2"
|
||||||
local result
|
local result
|
||||||
|
|
||||||
|
debug "Searching for window by title: '$title' and app: '$app'"
|
||||||
|
|
||||||
# Search for windows matching both app and title (most specific)
|
# Search for windows matching both app and title (most specific)
|
||||||
result=$(swaymsg -t get_tree | jq -r --arg title "$title" --arg app "$app" '
|
result=$(swaymsg -t get_tree | jq -r --arg title "$title" --arg app "$app" '
|
||||||
recurse(.nodes[]?, .floating_nodes[]?) |
|
recurse(.nodes[]?, .floating_nodes[]?) |
|
||||||
|
|
@ -61,28 +83,36 @@ find_window_by_title() {
|
||||||
.id
|
.id
|
||||||
' | head -n 1)
|
' | head -n 1)
|
||||||
|
|
||||||
|
debug "find_window_by_title (app+title) result: '$result'"
|
||||||
[ -n "$result" ] && [ "$result" != "null" ] && echo "$result" && return
|
[ -n "$result" ] && [ "$result" != "null" ] && echo "$result" && return
|
||||||
|
|
||||||
# Fall back to title-only exact match
|
# Fall back to title-only exact match
|
||||||
|
debug "Falling back to title-only exact match"
|
||||||
result=$(swaymsg -t get_tree | jq -r --arg title "$title" '
|
result=$(swaymsg -t get_tree | jq -r --arg title "$title" '
|
||||||
recurse(.nodes[]?, .floating_nodes[]?) |
|
recurse(.nodes[]?, .floating_nodes[]?) |
|
||||||
select((.name | type) == "string" and .name == $title) |
|
select((.name | type) == "string" and .name == $title) |
|
||||||
.id
|
.id
|
||||||
' | head -n 1)
|
' | head -n 1)
|
||||||
|
|
||||||
|
debug "find_window_by_title (title only) result: '$result'"
|
||||||
[ -n "$result" ] && [ "$result" != "null" ] && echo "$result" && return
|
[ -n "$result" ] && [ "$result" != "null" ] && echo "$result" && return
|
||||||
|
|
||||||
# Final fallback: title-only case-insensitive match
|
# Final fallback: title-only case-insensitive match
|
||||||
swaymsg -t get_tree | jq -r --arg title "$title" '
|
debug "Falling back to title-only case-insensitive match"
|
||||||
|
result=$(swaymsg -t get_tree | jq -r --arg title "$title" '
|
||||||
recurse(.nodes[]?, .floating_nodes[]?) |
|
recurse(.nodes[]?, .floating_nodes[]?) |
|
||||||
select((.name | type) == "string" and (.name | test($title; "i"))) |
|
select((.name | type) == "string" and (.name | test($title; "i"))) |
|
||||||
.id
|
.id
|
||||||
' | head -n 1
|
' | head -n 1)
|
||||||
|
debug "find_window_by_title (case-insensitive) result: '$result'"
|
||||||
|
echo "$result"
|
||||||
}
|
}
|
||||||
|
|
||||||
find_window() {
|
find_window() {
|
||||||
local app_name="$1"
|
local app_name="$1"
|
||||||
swaymsg -t get_tree | jq -r --arg app "$app_name" '
|
debug "Searching for window by app name: $app_name"
|
||||||
|
local result
|
||||||
|
result=$(swaymsg -t get_tree | jq -r --arg app "$app_name" '
|
||||||
recurse(.nodes[]?, .floating_nodes[]?) |
|
recurse(.nodes[]?, .floating_nodes[]?) |
|
||||||
select(
|
select(
|
||||||
((.app_id | type) == "string" and (.app_id == $app or (.app_id | test($app; "i")))) or
|
((.app_id | type) == "string" and (.app_id == $app or (.app_id | test($app; "i")))) or
|
||||||
|
|
@ -90,22 +120,48 @@ find_window() {
|
||||||
((.name | type) == "string" and (.name | test($app; "i")))
|
((.name | type) == "string" and (.name | test($app; "i")))
|
||||||
) |
|
) |
|
||||||
.id
|
.id
|
||||||
' | head -n 1
|
' | head -n 1)
|
||||||
|
debug "find_window result: '$result'"
|
||||||
|
echo "$result"
|
||||||
}
|
}
|
||||||
|
|
||||||
focus_window() {
|
focus_window() {
|
||||||
local window_id="$1"
|
local window_id="$1"
|
||||||
|
debug "Focusing window ID: $window_id"
|
||||||
swaymsg "[con_id=$window_id]" focus
|
swaymsg "[con_id=$window_id]" focus
|
||||||
}
|
}
|
||||||
|
|
||||||
WINDOW_ID=""
|
WINDOW_ID=""
|
||||||
|
|
||||||
[ -n "$CLASS_NAME" ] && WINDOW_ID=$(find_window_by_class "$CLASS_NAME")
|
debug "Starting window search..."
|
||||||
([ -z "$WINDOW_ID" ] || [ "$WINDOW_ID" = "null" ]) && [ -n "$TITLE_NAME" ] && WINDOW_ID=$(find_window_by_title "$TITLE_NAME" "$APP_NAME")
|
# Only search by class/title if provided - don't fall back to generic app search
|
||||||
([ -z "$WINDOW_ID" ] || [ "$WINDOW_ID" = "null" ]) && WINDOW_ID=$(find_window "$APP_NAME")
|
# This prevents matching the wrong window
|
||||||
|
if [ -n "$CLASS_NAME" ]; then
|
||||||
|
WINDOW_ID=$(find_window_by_class "$CLASS_NAME")
|
||||||
|
debug "After class search: WINDOW_ID='$WINDOW_ID'"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ([ -z "$WINDOW_ID" ] || [ "$WINDOW_ID" = "null" ]) && [ -n "$TITLE_NAME" ]; then
|
||||||
|
WINDOW_ID=$(find_window_by_title "$TITLE_NAME" "$APP_NAME")
|
||||||
|
debug "After title search: WINDOW_ID='$WINDOW_ID'"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Only fall back to generic app search if we don't have class or title
|
||||||
|
# This means the user wants to focus any instance of the app
|
||||||
|
if ([ -z "$WINDOW_ID" ] || [ "$WINDOW_ID" = "null" ]) && [ -z "$CLASS_NAME" ] && [ -z "$TITLE_NAME" ]; then
|
||||||
|
WINDOW_ID=$(find_window "$APP_NAME")
|
||||||
|
debug "After app name search (no class/title): WINDOW_ID='$WINDOW_ID'"
|
||||||
|
|
||||||
([ -z "$WINDOW_ID" ] || [ "$WINDOW_ID" = "null" ]) && WINDOW_ID=$(find_window "${APP_NAME,,}")
|
([ -z "$WINDOW_ID" ] || [ "$WINDOW_ID" = "null" ]) && WINDOW_ID=$(find_window "${APP_NAME,,}")
|
||||||
|
debug "After lowercase app name search: WINDOW_ID='$WINDOW_ID'"
|
||||||
|
fi
|
||||||
|
|
||||||
[ -n "$WINDOW_ID" ] && [ "$WINDOW_ID" != "null" ] && [ "$WINDOW_ID" -eq "$WINDOW_ID" ] 2>/dev/null && focus_window "$WINDOW_ID" && exit 0
|
if [ -n "$WINDOW_ID" ] && [ "$WINDOW_ID" != "null" ] && [ "$WINDOW_ID" -eq "$WINDOW_ID" ] 2>/dev/null; then
|
||||||
|
debug "Window found! Focusing window ID: $WINDOW_ID"
|
||||||
|
focus_window "$WINDOW_ID"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
debug "No matching window found, launching new instance"
|
||||||
"$COMMAND" "${ARGS[@]}" >/dev/null 2>&1 &
|
"$COMMAND" "${ARGS[@]}" >/dev/null 2>&1 &
|
||||||
disown
|
disown
|
||||||
|
|
|
||||||
|
|
@ -66,3 +66,8 @@ alias fml='font-manager list'
|
||||||
alias fms='font-manager status'
|
alias fms='font-manager status'
|
||||||
alias fmc='font-manager clean'
|
alias fmc='font-manager clean'
|
||||||
alias fma='font-manager all'
|
alias fma='font-manager all'
|
||||||
|
|
||||||
|
function mcd() {
|
||||||
|
mkdir -p "$1" && cd "$1" || return
|
||||||
|
}
|
||||||
|
compdef _cd mcd
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue