|
@@ -1,218 +1,244 @@
|
|
|
-#!/usr/bin/env bash
|
|
|
|
|
-set -euo pipefail
|
|
|
|
|
-
|
|
|
|
|
-# Configuration
|
|
|
|
|
-CACHE_MAX_AGE=300
|
|
|
|
|
-MRU_SIZE=5
|
|
|
|
|
-
|
|
|
|
|
-# Debug logging (set DEBUG=1 to enable via environment variable)
|
|
|
|
|
-DEBUG="${DEBUG:-0}"
|
|
|
|
|
-
|
|
|
|
|
-debug() {
|
|
|
|
|
- [ "$DEBUG" = "1" ] && echo "[DEBUG] $*" >&2 || true
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-# Parse arguments - flags first, then DEV_DIR
|
|
|
|
|
-NO_CACHE=false
|
|
|
|
|
-CLEAR_CACHE=false
|
|
|
|
|
-EDITOR="${EDITOR:-nvim}"
|
|
|
|
|
-DEV_DIR=""
|
|
|
|
|
-
|
|
|
|
|
-debug "Starting dev-launcher with args: $*"
|
|
|
|
|
-
|
|
|
|
|
-for arg in "$@"; do
|
|
|
|
|
- case "$arg" in
|
|
|
|
|
- --help | -h)
|
|
|
|
|
- cat <<EOF
|
|
|
|
|
-Usage: $0 [DEV_DIR] [OPTIONS]
|
|
|
|
|
-
|
|
|
|
|
-Options:
|
|
|
|
|
- --no-cache Bypass cache and rescan repositories
|
|
|
|
|
- --clear-cache Clear cache and MRU files
|
|
|
|
|
- --help, -h Show this help message
|
|
|
|
|
-EOF
|
|
|
|
|
- exit 0
|
|
|
|
|
- ;;
|
|
|
|
|
- --no-cache)
|
|
|
|
|
- NO_CACHE=true
|
|
|
|
|
- ;;
|
|
|
|
|
- --clear-cache)
|
|
|
|
|
- CLEAR_CACHE=true
|
|
|
|
|
- ;;
|
|
|
|
|
- *)
|
|
|
|
|
- [ -z "$DEV_DIR" ] && DEV_DIR="$arg"
|
|
|
|
|
- ;;
|
|
|
|
|
- esac
|
|
|
|
|
-done
|
|
|
|
|
-
|
|
|
|
|
-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)
|
|
|
|
|
-DEV_DIR_HASH=$(echo -n "$DEV_DIR" | sha256sum | cut -d' ' -f1 | head -c 16)
|
|
|
|
|
-CACHE_FILE="$HOME/.cache/dev-launcher-cache-${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
|
|
|
|
|
-if [ "$CLEAR_CACHE" = true ]; then
|
|
|
|
|
- rm -f "$CACHE_FILE" "$MRU_FILE"
|
|
|
|
|
- notify-send "Dev Launcher" "Cache cleared" 2>/dev/null || true
|
|
|
|
|
- exit 0
|
|
|
|
|
-fi
|
|
|
|
|
-
|
|
|
|
|
-# Find git repositories
|
|
|
|
|
-find_git_repos() {
|
|
|
|
|
- debug "find_git_repos: checking cache..."
|
|
|
|
|
- if [ "$NO_CACHE" = false ] && [ -f "$CACHE_FILE" ]; then
|
|
|
|
|
- local cache_age=$(($(date +%s) - $(stat -c %Y "$CACHE_FILE" 2>/dev/null || echo 0)))
|
|
|
|
|
- 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
|
|
|
|
|
-
|
|
|
|
|
- debug "Scanning for git repositories in $DEV_DIR..."
|
|
|
|
|
- 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"
|
|
|
|
|
- else
|
|
|
|
|
- debug "Using find for scanning"
|
|
|
|
|
- find "$DEV_DIR" -maxdepth 3 -type d -name ".git" -print0 |
|
|
|
|
|
- xargs -0 -n1 dirname | sort -u >"$CACHE_FILE"
|
|
|
|
|
- fi
|
|
|
|
|
- local repo_count=$(wc -l <"$CACHE_FILE")
|
|
|
|
|
- debug "Found $repo_count repositories"
|
|
|
|
|
- cat "$CACHE_FILE"
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-# Get sanitized project name for tmux session
|
|
|
|
|
-get_project_name() {
|
|
|
|
|
- basename "$1" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g' | sed 's/--*/-/g' | sed 's/^-\|-$//g'
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-# Update MRU list
|
|
|
|
|
-update_mru() {
|
|
|
|
|
- local selected="$1"
|
|
|
|
|
- local temp_file
|
|
|
|
|
- temp_file=$(mktemp)
|
|
|
|
|
-
|
|
|
|
|
- echo "$selected" >"$temp_file"
|
|
|
|
|
- [ -f "$MRU_FILE" ] && grep -vFx "$selected" "$MRU_FILE" >>"$temp_file" || true
|
|
|
|
|
- head -n "$MRU_SIZE" "$temp_file" >"$MRU_FILE"
|
|
|
|
|
- rm -f "$temp_file"
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-# Sort by MRU (MRU items first with ⭐, then others)
|
|
|
|
|
-sort_by_mru() {
|
|
|
|
|
- local all_repos="$1"
|
|
|
|
|
- local mru_list
|
|
|
|
|
- local temp_file
|
|
|
|
|
-
|
|
|
|
|
- [ -f "$MRU_FILE" ] && mru_list=$(grep -v '^$' "$MRU_FILE") || mru_list=""
|
|
|
|
|
-
|
|
|
|
|
- # If MRU list is empty, just return sorted repos
|
|
|
|
|
- if [ -z "$mru_list" ]; then
|
|
|
|
|
- echo "$all_repos" | sort
|
|
|
|
|
|
|
+#!/usr/bin/env python3
|
|
|
|
|
+"""i3 Dev Launcher - Launch development environments from git repositories"""
|
|
|
|
|
+
|
|
|
|
|
+import argparse
|
|
|
|
|
+import hashlib
|
|
|
|
|
+import json
|
|
|
|
|
+import os
|
|
|
|
|
+import re
|
|
|
|
|
+import shutil
|
|
|
|
|
+import subprocess
|
|
|
|
|
+import sys
|
|
|
|
|
+import time
|
|
|
|
|
+from pathlib import Path
|
|
|
|
|
+
|
|
|
|
|
+CACHE_MAX_AGE = 300
|
|
|
|
|
+MRU_SIZE = 5
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def debug(msg):
|
|
|
|
|
+ if os.getenv("DEBUG") == "1":
|
|
|
|
|
+ print(f"[DEBUG] {msg}", file=sys.stderr)
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def get_cache_files(dev_dir):
|
|
|
|
|
+ dev_dir_hash = hashlib.sha256(dev_dir.encode()).hexdigest()[:16]
|
|
|
|
|
+ cache_dir = Path.home() / ".cache"
|
|
|
|
|
+ return (
|
|
|
|
|
+ cache_dir / f"i3-dev-launcher-cache-{dev_dir_hash}",
|
|
|
|
|
+ cache_dir / f"i3-dev-launcher-mru-{dev_dir_hash}",
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+_HAS_FD = None
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def _has_fd():
|
|
|
|
|
+ global _HAS_FD
|
|
|
|
|
+ if _HAS_FD is None:
|
|
|
|
|
+ _HAS_FD = shutil.which("fd") is not None
|
|
|
|
|
+ return _HAS_FD
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def find_git_repos(dev_dir, no_cache=False):
|
|
|
|
|
+ cache_file, _ = get_cache_files(dev_dir)
|
|
|
|
|
+
|
|
|
|
|
+ if not no_cache and cache_file.exists():
|
|
|
|
|
+ if time.time() - os.path.getmtime(cache_file) < CACHE_MAX_AGE:
|
|
|
|
|
+ return [r for r in cache_file.read_text().splitlines() if r]
|
|
|
|
|
+
|
|
|
|
|
+ if not Path(dev_dir).exists():
|
|
|
|
|
+ return []
|
|
|
|
|
+
|
|
|
|
|
+ if _has_fd():
|
|
|
|
|
+ result = subprocess.run(
|
|
|
|
|
+ ["fd", "-H", "-t", "d", "^\\.git$", dev_dir, "-d", "3", "-0"],
|
|
|
|
|
+ capture_output=True,
|
|
|
|
|
+ text=True,
|
|
|
|
|
+ )
|
|
|
|
|
+ else:
|
|
|
|
|
+ result = subprocess.run(
|
|
|
|
|
+ ["find", dev_dir, "-maxdepth", "3", "-type", "d", "-name", ".git", "-print0"],
|
|
|
|
|
+ capture_output=True,
|
|
|
|
|
+ text=True,
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ repos = []
|
|
|
|
|
+ if result.returncode == 0:
|
|
|
|
|
+ repos = [str(Path(p).parent) for p in result.stdout.strip("\0").split("\0") if p]
|
|
|
|
|
+
|
|
|
|
|
+ repos = sorted(set(repos))
|
|
|
|
|
+ cache_file.parent.mkdir(parents=True, exist_ok=True)
|
|
|
|
|
+ cache_file.write_text("\n".join(repos))
|
|
|
|
|
+ return repos
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def get_project_name(repo_path):
|
|
|
|
|
+ name = Path(repo_path).name.lower()
|
|
|
|
|
+ name = re.sub(r"[^a-z0-9]", "-", name)
|
|
|
|
|
+ name = re.sub(r"-+", "-", name)
|
|
|
|
|
+ return name.strip("-")
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def update_mru(selected_display, dev_dir):
|
|
|
|
|
+ _, mru_file = get_cache_files(dev_dir)
|
|
|
|
|
+ mru = mru_file.read_text().splitlines() if mru_file.exists() else []
|
|
|
|
|
+ mru = [x for x in mru if x != selected_display]
|
|
|
|
|
+ mru.insert(0, selected_display)
|
|
|
|
|
+ mru_file.parent.mkdir(parents=True, exist_ok=True)
|
|
|
|
|
+ mru_file.write_text("\n".join(mru[:MRU_SIZE]))
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def sort_by_mru(display_list, dev_dir):
|
|
|
|
|
+ _, mru_file = get_cache_files(dev_dir)
|
|
|
|
|
+ if not mru_file.exists():
|
|
|
|
|
+ return sorted(display_list)
|
|
|
|
|
+
|
|
|
|
|
+ mru = [x for x in mru_file.read_text().splitlines() if x]
|
|
|
|
|
+ mru_set = set(mru)
|
|
|
|
|
+
|
|
|
|
|
+ starred = [f"⭐ {d}" for d in display_list if d in mru_set]
|
|
|
|
|
+ rest = [d for d in display_list if d not in mru_set]
|
|
|
|
|
+ return sorted(starred) + sorted(rest)
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def find_window_by_class(class_name):
|
|
|
|
|
+ try:
|
|
|
|
|
+ result = subprocess.run(
|
|
|
|
|
+ ["i3-msg", "-t", "get_tree"],
|
|
|
|
|
+ capture_output=True,
|
|
|
|
|
+ text=True,
|
|
|
|
|
+ check=False,
|
|
|
|
|
+ )
|
|
|
|
|
+ tree = json.loads(result.stdout)
|
|
|
|
|
+
|
|
|
|
|
+ def walk(node):
|
|
|
|
|
+ wp = node.get("window_properties") or {}
|
|
|
|
|
+ if wp.get("class", "").lower() == class_name.lower():
|
|
|
|
|
+ return node.get("id")
|
|
|
|
|
+ for child in node.get("nodes", []) + node.get("floating_nodes", []):
|
|
|
|
|
+ found = walk(child)
|
|
|
|
|
+ if found:
|
|
|
|
|
+ return found
|
|
|
|
|
+ return None
|
|
|
|
|
+
|
|
|
|
|
+ return walk(tree)
|
|
|
|
|
+ except Exception:
|
|
|
|
|
+ return None
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def focus_or_launch_alacritty(class_name, title, working_dir, session_name):
|
|
|
|
|
+ window_id = find_window_by_class(class_name)
|
|
|
|
|
+
|
|
|
|
|
+ if window_id:
|
|
|
|
|
+ subprocess.run(
|
|
|
|
|
+ ["i3-msg", f"[con_id={window_id}] focus"],
|
|
|
|
|
+ stdout=subprocess.DEVNULL,
|
|
|
|
|
+ stderr=subprocess.DEVNULL,
|
|
|
|
|
+ )
|
|
|
return
|
|
return
|
|
|
- fi
|
|
|
|
|
-
|
|
|
|
|
- # Output MRU items first with indicator
|
|
|
|
|
- echo "$mru_list" | sed 's/^/⭐ /'
|
|
|
|
|
-
|
|
|
|
|
- # Output non-MRU items using comm (fast set difference)
|
|
|
|
|
- temp_file=$(mktemp)
|
|
|
|
|
- echo "$all_repos" | sort >"$temp_file.all"
|
|
|
|
|
- echo "$mru_list" | sort >"$temp_file.mru"
|
|
|
|
|
- comm -23 "$temp_file.all" "$temp_file.mru" 2>/dev/null || cat "$temp_file.all"
|
|
|
|
|
- rm -f "$temp_file.all" "$temp_file.mru"
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-# Main execution
|
|
|
|
|
-REPOS=$(find_git_repos)
|
|
|
|
|
-
|
|
|
|
|
-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
|
|
|
|
|
-DISPLAY_LIST=$(echo "$REPOS" | sed "s|^${DEV_DIR}/||")
|
|
|
|
|
-declare -A DISPLAY_TO_PATH
|
|
|
|
|
-
|
|
|
|
|
-debug "Building display mapping..."
|
|
|
|
|
-while IFS=$'\t' read -r repo display; do
|
|
|
|
|
- DISPLAY_TO_PATH["$display"]="$repo"
|
|
|
|
|
-done < <(paste <(echo "$REPOS") <(echo "$DISPLAY_LIST"))
|
|
|
|
|
-debug "Mapped ${#DISPLAY_TO_PATH[@]} projects"
|
|
|
|
|
-
|
|
|
|
|
-# Sort by MRU and present in tofi
|
|
|
|
|
-debug "Sorting by MRU..."
|
|
|
|
|
-SORTED_LIST=$(sort_by_mru "$DISPLAY_LIST")
|
|
|
|
|
-debug "Presenting in tofi..."
|
|
|
|
|
-SELECTED_DISPLAY=$(echo "$SORTED_LIST" | tofi \
|
|
|
|
|
- --prompt-text "💀 Poison: " \
|
|
|
|
|
- --fuzzy-match true \
|
|
|
|
|
- --width 30% \
|
|
|
|
|
- --height 50% \
|
|
|
|
|
- --anchor center \
|
|
|
|
|
- --padding-left 20 \
|
|
|
|
|
- --padding-right 20 \
|
|
|
|
|
- --padding-top 15 \
|
|
|
|
|
- --padding-bottom 15 \
|
|
|
|
|
- --border-width 2 \
|
|
|
|
|
- --outline-width 0 \
|
|
|
|
|
- --font "$(fc-match -f '%{family}' 2>/dev/null || echo 'sans')" \
|
|
|
|
|
- --font-size 16 \
|
|
|
|
|
- --background-color '#191724' \
|
|
|
|
|
- --text-color '#e0def4' \
|
|
|
|
|
- --selection-color '#31748f' \
|
|
|
|
|
- --selection-background '#1f1d2e' \
|
|
|
|
|
- --border-color '#31748f' \
|
|
|
|
|
- --prompt-color '#f6c177')
|
|
|
|
|
-
|
|
|
|
|
-debug "Selected display: '$SELECTED_DISPLAY'"
|
|
|
|
|
-[ -z "$SELECTED_DISPLAY" ] && debug "No selection, exiting" && exit 0
|
|
|
|
|
-
|
|
|
|
|
-# Remove star indicator and lookup path
|
|
|
|
|
-SELECTED_DISPLAY_CLEAN=$(echo "$SELECTED_DISPLAY" | sed 's/^⭐ //')
|
|
|
|
|
-SELECTED="${DISPLAY_TO_PATH[$SELECTED_DISPLAY_CLEAN]:-}"
|
|
|
|
|
-
|
|
|
|
|
-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
|
|
|
|
|
-debug "Updating MRU..."
|
|
|
|
|
-update_mru "$SELECTED_DISPLAY_CLEAN"
|
|
|
|
|
-
|
|
|
|
|
-PROJECT_NAME=$(get_project_name "$SELECTED")
|
|
|
|
|
-SESSION_NAME="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 \
|
|
|
|
|
- --class "$CLASS_NAME" \
|
|
|
|
|
- --title "$PROJECT_NAME" \
|
|
|
|
|
- --working-directory="$SELECTED" \
|
|
|
|
|
- -e bash -c "if tmux has-session -t '$SESSION_NAME' 2>/dev/null; then \
|
|
|
|
|
- tmux attach -t '$SESSION_NAME'; \
|
|
|
|
|
- else \
|
|
|
|
|
- tmux new-session -c '$SELECTED' -s '$SESSION_NAME' -d '$EDITOR'; \
|
|
|
|
|
- tmux split-window -h -c '$SELECTED' -t '$SESSION_NAME'; \
|
|
|
|
|
- tmux select-pane -t '$SESSION_NAME:0.0'; \
|
|
|
|
|
- tmux attach -t '$SESSION_NAME'; \
|
|
|
|
|
- fi"
|
|
|
|
|
|
|
+
|
|
|
|
|
+ editor = "nvim"
|
|
|
|
|
+ tmux_cmd = f"""if tmux has-session -t '{session_name}' 2>/dev/null; then
|
|
|
|
|
+ tmux attach -t '{session_name}';
|
|
|
|
|
+else
|
|
|
|
|
+ tmux new-session -c '{working_dir}' -s '{session_name}' -d '{editor}';
|
|
|
|
|
+ tmux split-window -h -c '{working_dir}' -t '{session_name}';
|
|
|
|
|
+ tmux select-pane -t '{session_name}:0.0';
|
|
|
|
|
+ tmux attach -t '{session_name}';
|
|
|
|
|
+fi"""
|
|
|
|
|
+
|
|
|
|
|
+ subprocess.Popen(
|
|
|
|
|
+ [
|
|
|
|
|
+ "alacritty",
|
|
|
|
|
+ "--class",
|
|
|
|
|
+ f"{class_name},{class_name}",
|
|
|
|
|
+ "--title",
|
|
|
|
|
+ title,
|
|
|
|
|
+ "--working-directory",
|
|
|
|
|
+ working_dir,
|
|
|
|
|
+ "-e",
|
|
|
|
|
+ "bash",
|
|
|
|
|
+ "-c",
|
|
|
|
|
+ tmux_cmd,
|
|
|
|
|
+ ],
|
|
|
|
|
+ stdout=subprocess.DEVNULL,
|
|
|
|
|
+ stderr=subprocess.DEVNULL,
|
|
|
|
|
+ start_new_session=True,
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def run_menu(options):
|
|
|
|
|
+ if shutil.which("rofi"):
|
|
|
|
|
+ proc = subprocess.Popen(
|
|
|
|
|
+ ["rofi", "-dmenu", "-i", "-p", "dev", "-dpi", "1"],
|
|
|
|
|
+ stdin=subprocess.PIPE,
|
|
|
|
|
+ stdout=subprocess.PIPE,
|
|
|
|
|
+ text=True,
|
|
|
|
|
+ )
|
|
|
|
|
+ else:
|
|
|
|
|
+ proc = subprocess.Popen(
|
|
|
|
|
+ ["dmenu", "-i", "-l", "20", "-p", "dev"],
|
|
|
|
|
+ stdin=subprocess.PIPE,
|
|
|
|
|
+ stdout=subprocess.PIPE,
|
|
|
|
|
+ text=True,
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ out, _ = proc.communicate("\n".join(options))
|
|
|
|
|
+ return out.strip()
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def main():
|
|
|
|
|
+ parser = argparse.ArgumentParser(description="i3 Dev Launcher")
|
|
|
|
|
+ parser.add_argument("dev_dir", nargs="?", default=os.path.expanduser("~/Dev"))
|
|
|
|
|
+ parser.add_argument("--no-cache", action="store_true")
|
|
|
|
|
+ parser.add_argument("--clear-cache", action="store_true")
|
|
|
|
|
+ args = parser.parse_args()
|
|
|
|
|
+
|
|
|
|
|
+ cache_file, mru_file = get_cache_files(args.dev_dir)
|
|
|
|
|
+
|
|
|
|
|
+ if args.clear_cache:
|
|
|
|
|
+ if cache_file.exists():
|
|
|
|
|
+ cache_file.unlink()
|
|
|
|
|
+ if mru_file.exists():
|
|
|
|
|
+ mru_file.unlink()
|
|
|
|
|
+ sys.exit(0)
|
|
|
|
|
+
|
|
|
|
|
+ repos = find_git_repos(args.dev_dir, args.no_cache)
|
|
|
|
|
+ if not repos:
|
|
|
|
|
+ sys.exit(1)
|
|
|
|
|
+
|
|
|
|
|
+ display_to_path = {}
|
|
|
|
|
+ display_list = []
|
|
|
|
|
+
|
|
|
|
|
+ for repo in repos:
|
|
|
|
|
+ display = (
|
|
|
|
|
+ str(Path(repo).relative_to(args.dev_dir))
|
|
|
|
|
+ if repo.startswith(args.dev_dir)
|
|
|
|
|
+ else repo
|
|
|
|
|
+ )
|
|
|
|
|
+ display_list.append(display)
|
|
|
|
|
+ display_to_path[display] = repo
|
|
|
|
|
+
|
|
|
|
|
+ sorted_list = sort_by_mru(display_list, args.dev_dir)
|
|
|
|
|
+ selected_display = run_menu(sorted_list)
|
|
|
|
|
+
|
|
|
|
|
+ if not selected_display:
|
|
|
|
|
+ sys.exit(0)
|
|
|
|
|
+
|
|
|
|
|
+ clean = selected_display.replace("⭐ ", "", 1)
|
|
|
|
|
+ selected = display_to_path.get(clean)
|
|
|
|
|
+ if not selected or not Path(selected).is_dir():
|
|
|
|
|
+ sys.exit(0)
|
|
|
|
|
+
|
|
|
|
|
+ update_mru(clean, args.dev_dir)
|
|
|
|
|
+
|
|
|
|
|
+ project_name = get_project_name(selected)
|
|
|
|
|
+ session_name = f"dev-{project_name}"
|
|
|
|
|
+ class_name = f"com.mzunino.dev.{project_name}"
|
|
|
|
|
+
|
|
|
|
|
+ focus_or_launch_alacritty(class_name, project_name, selected, session_name)
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+if __name__ == "__main__":
|
|
|
|
|
+ main()
|