# Fast commands management function _ff_config_file echo ~/.config/ff/config end function _ff_state_file echo ~/.local/share/ff/state end function _ff_config_value --argument-names key default set -l file (_ff_config_file) if not test -f $file echo $default return end set -l found for line in (cat $file) set line (string trim -- $line) if test -z "$line"; or string match -q '#*' -- $line continue end if string match -q -- "$key=*" $line set found (string split -m 1 -- '=' $line)[2] end end if test -n "$found" echo (string trim -- $found) else echo $default end end function _ff_parse_interval --argument-names raw set -l text (string trim -- (string lower -- $raw)) if test -z "$text" echo 86400 return end if string match -qr '^[0-9]+$' -- $text echo $text return end set -l unit (string sub -s -1 $text) set -l amount (string sub -e -1 $text) if not string match -qr '^[0-9]+$' -- $amount echo 86400 return end switch $unit case s echo $amount case m math $amount '*' 60 case h math $amount '*' 3600 case d math $amount '*' 86400 case '*' echo 86400 end end function _ff_env_no_auto_update if not set -q FF_NO_AUTO_UPDATE return 1 end switch (string lower -- (string trim -- $FF_NO_AUTO_UPDATE)) case 1 true yes on return 0 case '*' return 1 end end function _ff_auto_update_enabled switch (string lower -- (_ff_config_value enabled true)) case 0 false no off return 1 case '*' return 0 end end function _ff_record_auto_update set -l file (_ff_state_file) mkdir -p (dirname $file) or return echo "last_auto_update="(date +%s) >$file end function _ff_last_auto_update set -l file (_ff_state_file) if not test -f $file echo 0 return end set -l val 0 for line in (cat $file) if string match -q 'last_auto_update=*' -- $line set val (string split -m 1 -- '=' $line)[2] end end if string match -qr '^[0-9]+$' -- (string trim -- $val) echo (string trim -- $val) else echo 0 end end function _ff_interval_elapsed --argument-names interval if test "$interval" -le 0 return 0 end set -l last (_ff_last_auto_update) if test "$last" -eq 0 return 0 end set -l now (date +%s) test (math $now - $last) -ge $interval end function _ff_reload for __f in ~/.dotfile/fish/functions/*.fish if test -f $__f functions -e (basename $__f .fish) 2>/dev/null end end source ~/.config/fish/config.fish # fish 对已加载过的 autoload 函数不会在 functions -e 后重新加载, # 必须显式 source 一遍,否则函数(含 ff 自身)会丢失 for __f in ~/.dotfile/fish/functions/*.fish if test -f $__f source $__f end end set -e __f end # 自动 git pull ~/.dotfile。失败/跳过不得打断当前 ff 子命令。 # 返回: 0 有更新, 1 失败或脏树, 2 静默跳过, 3 已是最新 function _ff_auto_update_try_pull set -l dir ~/.dotfile if not test -d $dir return 2 end if not git -C $dir rev-parse --is-inside-work-tree >/dev/null 2>&1 return 2 end set -l porcelain (git -C $dir status --porcelain 2>/dev/null) if test -n "$porcelain" echo "[ff] 自动更新已跳过: 工作区不干净" >&2 return 1 end set -l before (git -C $dir rev-parse HEAD 2>/dev/null) set -lx GIT_TERMINAL_PROMPT 0 set -l st 0 if command -q timeout command timeout 20 git -C $dir pull --ff-only origin main >/dev/null 2>&1 set st $status else git -C $dir pull --ff-only origin main >/dev/null 2>&1 set st $status end if test $st -eq 124 echo "[ff] 自动更新失败,已跳过: git 超时" >&2 return 1 end if test $st -ne 0 echo "[ff] 自动更新失败,已跳过" >&2 return 1 end set -l after (git -C $dir rev-parse HEAD 2>/dev/null) if test -n "$before" -a -n "$after" -a "$before" != "$after" echo "[ff] 已自动更新 dotfile" >&2 return 0 end return 3 end function _ff_maybe_auto_update --argument-names command no_auto_flag if test "$no_auto_flag" = 1 return end if _ff_env_no_auto_update return end # 显式 ff update 自己拉,这里不要再拉一遍 if test "$command" = update return end if not _ff_auto_update_enabled return end set -l interval (_ff_parse_interval (_ff_config_value interval 24h)) if not _ff_interval_elapsed $interval return end _ff_auto_update_try_pull set -l st $status if test $st -eq 2 return end _ff_record_auto_update if test $st -eq 0 _ff_reload end end function ff # 定义命令和描述(格式:命令|描述) set -l commands \ "reload|Reload fish configuration" \ "edit|Edit custom fish configuration" \ "fish|Change to fish config directory" \ "dotfile|Change to dotfile directory" \ "pon|Enable proxy (proxy on)" \ "poff|Disable proxy (proxy off)" \ "pst|Show proxy status" \ "make|Run dotfile Makefile commands" \ "script|Run script from script directory" \ "s|Alias for script" \ "update|Update dotfile now (git pull + reload); also auto every 24h" \ "region|Toggle or set CN_REGION (mirror/proxy region)" \ "ssh|SSH init, copy key + add host config, or copy config to remote" \ "dirsize|Show directory sizes (du wrapper)" \ "pouch|Clone and install pouch to ~/.pouch" \ "help|Show this help message" set -l no_auto 0 set -l args set -l past_command 0 for a in $argv if test $past_command -eq 0; and test "$a" = "--no-auto-update" set no_auto 1 continue end set past_command 1 set -a args $a end set argv $args _ff_maybe_auto_update "$argv[1]" $no_auto # 确保 execute-script 可用(在 fish 自动加载前手动引入一次) if not functions -q execute-script set -l execute_script_path ~/.dotfile/fish/functions/execute-script.fish if test -f $execute_script_path source $execute_script_path end end switch $argv[1] case reload # autoload 会缓存函数定义,先清掉 dotfile 里的函数再重载 _ff_reload echo "Fish configuration reloaded!" case edit vim ~/.config/fish/custom.fish case fish cd ~/.config/fish/ case dotfile cd ~/.dotfile case pon set -gx https_proxy http://127.0.0.1:7890 set -gx http_proxy http://127.0.0.1:7890 set -gx all_proxy socks5://127.0.0.1:7890 echo "✓ Proxy enabled" export | grep -i proxy case poff set -e http_proxy set -e https_proxy set -e ftp_proxy set -e all_proxy set -e HTTP_PROXY set -e HTTPS_PROXY set -e FTP_PROXY set -e ALL_PROXY echo "✓ Proxy disabled" export | grep -i proxy case pst export | grep -i proxy case make # 切换到 dotfile 目录执行脚本管理 makefile set -l dotfile_dir ~/.dotfile if test -d $dotfile_dir make -f $dotfile_dir/Makefile.scripts -C $dotfile_dir $argv[2..-1] else echo "Error: Dotfile directory not found at $dotfile_dir" end case script s if not functions -q execute-script source ~/.dotfile/fish/functions/execute-script.fish end execute-script $argv[2..-1] case help echo "Usage: ff " echo "" echo "Commands:" # 自动生成帮助信息 for cmd in $commands set -l parts (string split '|' $cmd) printf " %-10s %s\n" $parts[1] $parts[2] end echo "" echo "Auto-update: ordinary ff commands git-pull ~/.dotfile every 24h (default on)." echo " Config: ~/.config/ff/config (enabled=true, interval=24h)" echo " Skip: FF_NO_AUTO_UPDATE=1 or ff --no-auto-update " case update set -l dotfile_dir ~/.dotfile if not test -d $dotfile_dir echo "Error: Dotfile directory not found at $dotfile_dir" return 1 end set -l prev_dir (pwd) cd $dotfile_dir if git pull origin main _ff_record_auto_update cd $prev_dir echo "Reloading fish configuration..." ff reload else cd $prev_dir echo "Error: git pull failed, skip reload" return 1 end case ssh # 每次都重新 source,避免 autoload 缓存旧定义 source ~/.dotfile/fish/functions/ff-ssh.fish ff_ssh $argv[2..-1] case dirsize set -l script_path ~/.dotfile/script/dirsize.sh if test -f $script_path bash $script_path $argv[2..-1] else echo "Error: dirsize script not found at $script_path" return 1 end case pouch set -l script_path ~/.dotfile/script/pouch-install.sh if test -f $script_path bash $script_path $argv[2..-1] else echo "Error: pouch-install script not found at $script_path" return 1 end case region if not functions -q region_toggle source ~/.dotfile/fish/functions/region.fish end set -l sub toggle if test (count $argv) -ge 2 set sub $argv[2] end switch $sub case toggle region_toggle case '*' echo "Usage: ff region [toggle]" echo "Unknown subcommand: $sub" end case '*' if test -z "$argv[1]" echo "Error: No command specified" else echo "Error: Unknown command '$argv[1]'" end echo "Run 'ff help' for usage information" end end