Compare changes

Choose any two refs to compare.

+1
git/ignore
··· 1 .DS_Store 2 note 3 neorg.log 4 5 # vim:ft=gitignore
··· 1 .DS_Store 2 note 3 neorg.log 4 + /.opencode 5 6 # vim:ft=gitignore
+26 -2
nix/darwin.nix
··· 3 # $ nix-env -qaP | grep wget 4 environment.systemPackages = 5 [ 6 - pkgs.brave 7 pkgs.fzf 8 pkgs.tmux 9 pkgs.tree 10 pkgs.vim 11 # I don't want to touch this due to current neovim settings. 12 # remove it when lux.nvim is out 13 (pkgs-unstable.lua5_1.withPackages (ps: with ps; [luarocks])) 14 ]; 15 16 # Necessary for using flakes on this system. 17 nix.settings.experimental-features = "nix-command flakes"; ··· 173 "sanesidebuttons" 174 ]; 175 masApps = { 176 Things3 = 904280696; 177 - Pages = 409201541; 178 }; 179 }; 180 }
··· 3 # $ nix-env -qaP | grep wget 4 environment.systemPackages = 5 [ 6 pkgs.fzf 7 pkgs.tmux 8 pkgs.tree 9 pkgs.vim 10 + pkgs.git 11 + pkgs.ollama 12 # I don't want to touch this due to current neovim settings. 13 # remove it when lux.nvim is out 14 (pkgs-unstable.lua5_1.withPackages (ps: with ps; [luarocks])) 15 ]; 16 + 17 + launchd.user.agents = { 18 + # reference: 19 + # - https://www.danielcorin.com/til/nix-darwin/launch-agents/ 20 + # - https://medium.com/@anand34577/setting-up-ollama-as-a-background-service-on-macos-66f7492b5cc8 21 + ollama-serve = { 22 + environment = { 23 + OLLAMA_HOST = "0.0.0.0"; 24 + }; 25 + serviceConfig = { 26 + Label = "com.ollama.serve"; 27 + ProgramArguments = [ "${pkgs.ollama}/bin/ollama" "serve" ]; 28 + KeepAlive = true; 29 + RunAtLoad = true; 30 + # TODO: ensure /var/log/ollama exist 31 + # StandardOutPath = "/var/log/ollama/stdout.log"; 32 + # StandardErrorPath = "/var/log/ollama/stderr.log"; 33 + StandardOutPath = "/tmp/ollama_boltless.log"; 34 + StandardErrorPath = "/tmp/ollama_boltless.err"; 35 + }; 36 + }; 37 + }; 38 39 # Necessary for using flakes on this system. 40 nix.settings.experimental-features = "nix-command flakes"; ··· 196 "sanesidebuttons" 197 ]; 198 masApps = { 199 + Numbers = 409203825; 200 + Pages = 409201541; 201 Things3 = 904280696; 202 }; 203 }; 204 }
+43 -3
nix/flake.nix
··· 35 inherit self pkgs-unstable; 36 }; 37 }; 38 # based on https://zaynetro.com/post/2024-you-dont-need-home-manager-nix 39 # $ nix profile install . 40 # $ nix profile upgrade nix 41 packages.${system}.default = pkgs.buildEnv { 42 name = "my-env"; 43 paths = [ 44 - pkgs.anki-bin 45 pkgs.bacon 46 pkgs.btop 47 pkgs.dust ··· 49 pkgs.fd 50 pkgs.gh 51 pkgs.hello 52 - pkgs.iina 53 pkgs.janet 54 pkgs.jpm 55 pkgs.meld 56 pkgs.nodejs_24 57 pkgs.ripgrep 58 pkgs.sumneko-lua-language-server 59 pkgs.unixtools.watch 60 - pkgs.qmk 61 pkgs-unstable.jujutsu 62 pkgs-unstable.neovim 63 # TODO: automate this with nix: 64 # $ ln -sfn ~/.nix-profile/Applications ~/Applications/Nix\ User\ Apps 65 ]; 66 }; 67 };
··· 35 inherit self pkgs-unstable; 36 }; 37 }; 38 + darwinConfigurations."boltless-Studio" = nix-darwin.lib.darwinSystem { 39 + modules = [ 40 + ./darwin.nix 41 + ]; 42 + specialArgs = { 43 + inherit self pkgs-unstable; 44 + }; 45 + }; 46 # based on https://zaynetro.com/post/2024-you-dont-need-home-manager-nix 47 # $ nix profile install . 48 # $ nix profile upgrade nix 49 packages.${system}.default = pkgs.buildEnv { 50 name = "my-env"; 51 paths = [ 52 pkgs.bacon 53 pkgs.btop 54 pkgs.dust ··· 56 pkgs.fd 57 pkgs.gh 58 pkgs.hello 59 pkgs.janet 60 pkgs.jpm 61 pkgs.meld 62 pkgs.nodejs_24 63 + pkgs.qmk 64 pkgs.ripgrep 65 pkgs.sumneko-lua-language-server 66 pkgs.unixtools.watch 67 pkgs-unstable.jujutsu 68 pkgs-unstable.neovim 69 + ] ++ (if pkgs.stdenv.isDarwin then [ 70 # TODO: automate this with nix: 71 # $ ln -sfn ~/.nix-profile/Applications ~/Applications/Nix\ User\ Apps 72 + pkgs.anki-bin 73 + pkgs.brave 74 + pkgs.iina 75 + pkgs.macmon 76 + ] else []); 77 + }; 78 + packages.aarch64-linux.default = let 79 + system = "aarch64-linux"; 80 + pkgs = import nixpkgs { 81 + inherit system; 82 + overlays = [ (import ./overlays/jpm.nix) ]; 83 + }; 84 + pkgs-unstable = nixpkgs-unstable.legacyPackages.${system}; 85 + in pkgs.buildEnv { 86 + name = "my-env"; 87 + paths = [ 88 + pkgs.bacon 89 + pkgs.btop 90 + pkgs.dust 91 + pkgs.eza 92 + pkgs.fd 93 + pkgs.gh 94 + pkgs.hello 95 + pkgs.janet 96 + pkgs.jpm 97 + pkgs.meld 98 + pkgs.nodejs_24 99 + pkgs.qmk 100 + pkgs.ripgrep 101 + pkgs.sumneko-lua-language-server 102 + pkgs.unixtools.watch 103 + pkgs-unstable.jujutsu 104 + pkgs-unstable.neovim 105 ]; 106 }; 107 };
+5 -2
tmux/.tmux.conf
··· 30 # bind ctrl-ed version of other mappings 31 # e.g. `n` and `p` for navigating windows 32 33 - bind-key r source-file ~/.tmux.conf 34 35 set-window-option -g mode-keys vi 36 bind-key -T copy-mode-vi v send-keys -X begin-selection 37 bind-key -T copy-mode-vi V send-keys -X select-line 38 - bind-key -T copy-mode-vi y send-keys -X copy-selection 39 bind-key -T copy-mode-vi r send-keys -X rectangle-toggle 40 bind-key -T copy-mode-vi i send-keys -X cancel 41 bind-key -T copy-mode-vi C-c send-keys -X cancel 42 43 bind-key C-e run-shell "tmux neww -n 'sessionizer' tmux-sessionizer" 44 bind-key e run-shell "tmux neww -n 'sessionizer' tmux-sessionizer"
··· 30 # bind ctrl-ed version of other mappings 31 # e.g. `n` and `p` for navigating windows 32 33 + bind-key r source-file ~/.tmux.conf \; display-message "sourced" 34 35 set-window-option -g mode-keys vi 36 bind-key -T copy-mode-vi v send-keys -X begin-selection 37 bind-key -T copy-mode-vi V send-keys -X select-line 38 + # bind-key -T copy-mode-vi y send-keys -X copy-selection 39 + bind-key -T copy-mode-vi y send-keys -X copy-pipe "pbcopy" 40 bind-key -T copy-mode-vi r send-keys -X rectangle-toggle 41 bind-key -T copy-mode-vi i send-keys -X cancel 42 bind-key -T copy-mode-vi C-c send-keys -X cancel 43 44 bind-key C-e run-shell "tmux neww -n 'sessionizer' tmux-sessionizer" 45 bind-key e run-shell "tmux neww -n 'sessionizer' tmux-sessionizer" 46 + bind-key C-i switch-client -l 47 + bind-key i switch-client -l
+5 -1
zsh/.zprofile
··· 4 eval "$(/opt/homebrew/bin/brew shellenv)" 5 6 # Rust 7 - . "$HOME/.cargo/env" 8 9 # bob-nvim 10 export PATH=$HOME/.local/share/bob/nvim-bin:$PATH ··· 24 25 # HACK: python binary path. honestly I don't like this hard-coded method 26 export PATH="$HOME/Library/Python/3.9/bin:$PATH" 27 28 29
··· 4 eval "$(/opt/homebrew/bin/brew shellenv)" 5 6 # Rust 7 + if [ -f "$HOME/.cargo/env" ]; then 8 + . "$HOME/.cargo/env" 9 + fi 10 11 # bob-nvim 12 export PATH=$HOME/.local/share/bob/nvim-bin:$PATH ··· 26 27 # HACK: python binary path. honestly I don't like this hard-coded method 28 export PATH="$HOME/Library/Python/3.9/bin:$PATH" 29 + 30 + export PATH="$HOME/dot/bin:$PATH" 31 32 33
+11 -4
zsh/.zshrc
··· 7 alias gs="git status" 8 alias ta="tmux attach" 9 alias nn="NVIM_APPNAME=nativevim nvim" 10 11 ## Custom prompt 12 - export PROMPT='%F{green}%n@%m %F{blue}%~%f %# ' 13 - 14 - ## Custom prompt (with asychronous rich data rendering) 15 - source "$HOME/projects/dotfiles/zsh/prompt.zsh" 16 17 ## Editor 18 export EDITOR="$(command -v nvim || command -v vim)" ··· 24 zle -N edit-command-line 25 bindkey '^Xe' edit-command-line 26 bindkey '^X^E' edit-command-line
··· 7 alias gs="git status" 8 alias ta="tmux attach" 9 alias nn="NVIM_APPNAME=nativevim nvim" 10 + alias nd="nix develop -c zsh -l" 11 12 ## Custom prompt 13 + setopt prompt_subst 14 + export PS1='%F{green}%n@%m %F{blue}%~%f${IN_NIX_SHELL:+ nix:($IN_NIX_SHELL)} %# ' 15 16 ## Editor 17 export EDITOR="$(command -v nvim || command -v vim)" ··· 23 zle -N edit-command-line 24 bindkey '^Xe' edit-command-line 25 bindkey '^X^E' edit-command-line 26 + 27 + if [[ -n "$IN_NIX_SHELL" ]]; then 28 + export SHELL=$(which zsh) 29 + fi 30 + 31 + # Added by LM Studio CLI (lms) 32 + export PATH="$PATH:$HOME/.lmstudio/bin" 33 + # End of LM Studio CLI section
-672
zsh/async.zsh
··· 1 - #!/usr/bin/env zsh 2 - 3 - # 4 - # zsh-async 5 - # 6 - # version: v1.8.6 7 - # author: Mathias Fredriksson 8 - # url: https://github.com/mafredri/zsh-async 9 - # 10 - 11 - typeset -g ASYNC_VERSION=1.8.6 12 - # Produce debug output from zsh-async when set to 1. 13 - typeset -g ASYNC_DEBUG=${ASYNC_DEBUG:-0} 14 - 15 - # Execute commands that can manipulate the environment inside the async worker. Return output via callback. 16 - _async_eval() { 17 - local ASYNC_JOB_NAME 18 - # Rename job to _async_eval and redirect all eval output to cat running 19 - # in _async_job. Here, stdout and stderr are not separated for 20 - # simplicity, this could be improved in the future. 21 - { 22 - eval "$@" 23 - } &> >(ASYNC_JOB_NAME=[async/eval] _async_job 'command -p cat') 24 - } 25 - 26 - # Wrapper for jobs executed by the async worker, gives output in parseable format with execution time 27 - _async_job() { 28 - # Disable xtrace as it would mangle the output. 29 - setopt localoptions noxtrace 30 - 31 - # Store start time for job. 32 - float -F duration=$EPOCHREALTIME 33 - 34 - # Run the command and capture both stdout (`eval`) and stderr (`cat`) in 35 - # separate subshells. When the command is complete, we grab write lock 36 - # (mutex token) and output everything except stderr inside the command 37 - # block, after the command block has completed, the stdin for `cat` is 38 - # closed, causing stderr to be appended with a $'\0' at the end to mark the 39 - # end of output from this job. 40 - local jobname=${ASYNC_JOB_NAME:-$1} out 41 - out="$( 42 - local stdout stderr ret tok 43 - { 44 - stdout=$(eval "$@") 45 - ret=$? 46 - duration=$(( EPOCHREALTIME - duration )) # Calculate duration. 47 - 48 - print -r -n - $'\0'${(q)jobname} $ret ${(q)stdout} $duration 49 - } 2> >(stderr=$(command -p cat) && print -r -n - " "${(q)stderr}$'\0') 50 - )" 51 - if [[ $out != $'\0'*$'\0' ]]; then 52 - # Corrupted output (aborted job?), skipping. 53 - return 54 - fi 55 - 56 - # Grab mutex lock, stalls until token is available. 57 - read -r -k 1 -p tok || return 1 58 - 59 - # Return output (<job_name> <return_code> <stdout> <duration> <stderr>). 60 - print -r -n - "$out" 61 - 62 - # Unlock mutex by inserting a token. 63 - print -n -p $tok 64 - } 65 - 66 - # The background worker manages all tasks and runs them without interfering with other processes 67 - _async_worker() { 68 - # Reset all options to defaults inside async worker. 69 - emulate -R zsh 70 - 71 - # Make sure monitor is unset to avoid printing the 72 - # pids of child processes. 73 - unsetopt monitor 74 - 75 - # Redirect stderr to `/dev/null` in case unforseen errors produced by the 76 - # worker. For example: `fork failed: resource temporarily unavailable`. 77 - # Some older versions of zsh might also print malloc errors (know to happen 78 - # on at least zsh 5.0.2 and 5.0.8) likely due to kill signals. 79 - exec 2>/dev/null 80 - 81 - # When a zpty is deleted (using -d) all the zpty instances created before 82 - # the one being deleted receive a SIGHUP, unless we catch it, the async 83 - # worker would simply exit (stop working) even though visible in the list 84 - # of zpty's (zpty -L). This has been fixed around the time of Zsh 5.4 85 - # (not released). 86 - if ! is-at-least 5.4.1; then 87 - TRAPHUP() { 88 - return 0 # Return 0, indicating signal was handled. 89 - } 90 - fi 91 - 92 - local -A storage 93 - local unique=0 94 - local notify_parent=0 95 - local parent_pid=0 96 - local coproc_pid=0 97 - local processing=0 98 - 99 - local -a zsh_hooks zsh_hook_functions 100 - zsh_hooks=(chpwd periodic precmd preexec zshexit zshaddhistory) 101 - zsh_hook_functions=(${^zsh_hooks}_functions) 102 - unfunction $zsh_hooks &>/dev/null # Deactivate all zsh hooks inside the worker. 103 - unset $zsh_hook_functions # And hooks with registered functions. 104 - unset zsh_hooks zsh_hook_functions # Cleanup. 105 - 106 - close_idle_coproc() { 107 - local -a pids 108 - pids=(${${(v)jobstates##*:*:}%\=*}) 109 - 110 - # If coproc (cat) is the only child running, we close it to avoid 111 - # leaving it running indefinitely and cluttering the process tree. 112 - if (( ! processing )) && [[ $#pids = 1 ]] && [[ $coproc_pid = $pids[1] ]]; then 113 - coproc : 114 - coproc_pid=0 115 - fi 116 - } 117 - 118 - child_exit() { 119 - close_idle_coproc 120 - 121 - # On older version of zsh (pre 5.2) we notify the parent through a 122 - # SIGWINCH signal because `zpty` did not return a file descriptor (fd) 123 - # prior to that. 124 - if (( notify_parent )); then 125 - # We use SIGWINCH for compatibility with older versions of zsh 126 - # (pre 5.1.1) where other signals (INFO, ALRM, USR1, etc.) could 127 - # cause a deadlock in the shell under certain circumstances. 128 - kill -WINCH $parent_pid 129 - fi 130 - } 131 - 132 - # Register a SIGCHLD trap to handle the completion of child processes. 133 - trap child_exit CHLD 134 - 135 - # Process option parameters passed to worker. 136 - while getopts "np:uz" opt; do 137 - case $opt in 138 - n) notify_parent=1;; 139 - p) parent_pid=$OPTARG;; 140 - u) unique=1;; 141 - z) notify_parent=0;; # Uses ZLE watcher instead. 142 - esac 143 - done 144 - 145 - # Terminate all running jobs, note that this function does not 146 - # reinstall the child trap. 147 - terminate_jobs() { 148 - trap - CHLD # Ignore child exits during kill. 149 - coproc : # Quit coproc. 150 - coproc_pid=0 # Reset pid. 151 - 152 - if is-at-least 5.4.1; then 153 - trap '' HUP # Catch the HUP sent to this process. 154 - kill -HUP -$$ # Send to entire process group. 155 - trap - HUP # Disable HUP trap. 156 - else 157 - # We already handle HUP for Zsh < 5.4.1. 158 - kill -HUP -$$ # Send to entire process group. 159 - fi 160 - } 161 - 162 - killjobs() { 163 - local tok 164 - local -a pids 165 - pids=(${${(v)jobstates##*:*:}%\=*}) 166 - 167 - # No need to send SIGHUP if no jobs are running. 168 - (( $#pids == 0 )) && continue 169 - (( $#pids == 1 )) && [[ $coproc_pid = $pids[1] ]] && continue 170 - 171 - # Grab lock to prevent half-written output in case a child 172 - # process is in the middle of writing to stdin during kill. 173 - (( coproc_pid )) && read -r -k 1 -p tok 174 - 175 - terminate_jobs 176 - trap child_exit CHLD # Reinstall child trap. 177 - } 178 - 179 - local request do_eval=0 180 - local -a cmd 181 - while :; do 182 - # Wait for jobs sent by async_job. 183 - read -r -d $'\0' request || { 184 - # Unknown error occurred while reading from stdin, the zpty 185 - # worker is likely in a broken state, so we shut down. 186 - terminate_jobs 187 - 188 - # Stdin is broken and in case this was an unintended 189 - # crash, we try to report it as a last hurrah. 190 - print -r -n $'\0'"'[async]'" $(( 127 + 3 )) "''" 0 "'$0:$LINENO: zpty fd died, exiting'"$'\0' 191 - 192 - # We use `return` to abort here because using `exit` may 193 - # result in an infinite loop that never exits and, as a 194 - # result, high CPU utilization. 195 - return $(( 127 + 1 )) 196 - } 197 - 198 - # We need to clean the input here because sometimes when a zpty 199 - # has died and been respawned, messages will be prefixed with a 200 - # carraige return (\r, or \C-M). 201 - request=${request#$'\C-M'} 202 - 203 - # Check for non-job commands sent to worker 204 - case $request in 205 - _killjobs) killjobs; continue;; 206 - _async_eval*) do_eval=1;; 207 - esac 208 - 209 - # Parse the request using shell parsing (z) to allow commands 210 - # to be parsed from single strings and multi-args alike. 211 - cmd=("${(z)request}") 212 - 213 - # Name of the job (first argument). 214 - local job=$cmd[1] 215 - 216 - # Check if a worker should perform unique jobs, unless 217 - # this is an eval since they run synchronously. 218 - if (( !do_eval )) && (( unique )); then 219 - # Check if a previous job is still running, if yes, 220 - # skip this job and let the previous one finish. 221 - for pid in ${${(v)jobstates##*:*:}%\=*}; do 222 - if [[ ${storage[$job]} == $pid ]]; then 223 - continue 2 224 - fi 225 - done 226 - fi 227 - 228 - # Guard against closing coproc from trap before command has started. 229 - processing=1 230 - 231 - # Because we close the coproc after the last job has completed, we must 232 - # recreate it when there are no other jobs running. 233 - if (( ! coproc_pid )); then 234 - # Use coproc as a mutex for synchronized output between children. 235 - coproc command -p cat 236 - coproc_pid="$!" 237 - # Insert token into coproc 238 - print -n -p "t" 239 - fi 240 - 241 - if (( do_eval )); then 242 - shift cmd # Strip _async_eval from cmd. 243 - _async_eval $cmd 244 - else 245 - # Run job in background, completed jobs are printed to stdout. 246 - _async_job $cmd & 247 - # Store pid because zsh job manager is extremely unflexible (show jobname as non-unique '$job')... 248 - storage[$job]="$!" 249 - fi 250 - 251 - processing=0 # Disable guard. 252 - 253 - if (( do_eval )); then 254 - do_eval=0 255 - 256 - # When there are no active jobs we can't rely on the CHLD trap to 257 - # manage the coproc lifetime. 258 - close_idle_coproc 259 - fi 260 - done 261 - } 262 - 263 - # 264 - # Get results from finished jobs and pass it to the to callback function. This is the only way to reliably return the 265 - # job name, return code, output and execution time and with minimal effort. 266 - # 267 - # If the async process buffer becomes corrupt, the callback will be invoked with the first argument being `[async]` (job 268 - # name), non-zero return code and fifth argument describing the error (stderr). 269 - # 270 - # usage: 271 - # async_process_results <worker_name> <callback_function> 272 - # 273 - # callback_function is called with the following parameters: 274 - # $1 = job name, e.g. the function passed to async_job 275 - # $2 = return code 276 - # $3 = resulting stdout from execution 277 - # $4 = execution time, floating point e.g. 2.05 seconds 278 - # $5 = resulting stderr from execution 279 - # $6 = has next result in buffer (0 = buffer empty, 1 = yes) 280 - # 281 - async_process_results() { 282 - setopt localoptions unset noshwordsplit noksharrays noposixidentifiers noposixstrings 283 - 284 - local worker=$1 285 - local callback=$2 286 - local caller=$3 287 - local -a items 288 - local null=$'\0' data 289 - integer -l len pos num_processed has_next 290 - 291 - typeset -gA ASYNC_PROCESS_BUFFER 292 - 293 - # Read output from zpty and parse it if available. 294 - while zpty -r -t $worker data 2>/dev/null; do 295 - ASYNC_PROCESS_BUFFER[$worker]+=$data 296 - len=${#ASYNC_PROCESS_BUFFER[$worker]} 297 - pos=${ASYNC_PROCESS_BUFFER[$worker][(i)$null]} # Get index of NULL-character (delimiter). 298 - 299 - # Keep going until we find a NULL-character. 300 - if (( ! len )) || (( pos > len )); then 301 - continue 302 - fi 303 - 304 - while (( pos <= len )); do 305 - # Take the content from the beginning, until the NULL-character and 306 - # perform shell parsing (z) and unquoting (Q) as an array (@). 307 - items=("${(@Q)${(z)ASYNC_PROCESS_BUFFER[$worker][1,$pos-1]}}") 308 - 309 - # Remove the extracted items from the buffer. 310 - ASYNC_PROCESS_BUFFER[$worker]=${ASYNC_PROCESS_BUFFER[$worker][$pos+1,$len]} 311 - 312 - len=${#ASYNC_PROCESS_BUFFER[$worker]} 313 - if (( len > 1 )); then 314 - pos=${ASYNC_PROCESS_BUFFER[$worker][(i)$null]} # Get index of NULL-character (delimiter). 315 - fi 316 - 317 - has_next=$(( len != 0 )) 318 - if (( $#items == 5 )); then 319 - items+=($has_next) 320 - $callback "${(@)items}" # Send all parsed items to the callback. 321 - (( num_processed++ )) 322 - elif [[ -z $items ]]; then 323 - # Empty items occur between results due to double-null ($'\0\0') 324 - # caused by commands being both pre and suffixed with null. 325 - else 326 - # In case of corrupt data, invoke callback with *async* as job 327 - # name, non-zero exit status and an error message on stderr. 328 - $callback "[async]" 1 "" 0 "$0:$LINENO: error: bad format, got ${#items} items (${(q)items})" $has_next 329 - fi 330 - done 331 - done 332 - 333 - (( num_processed )) && return 0 334 - 335 - # Avoid printing exit value when `setopt printexitvalue` is active.` 336 - [[ $caller = trap || $caller = watcher ]] && return 0 337 - 338 - # No results were processed 339 - return 1 340 - } 341 - 342 - # Watch worker for output 343 - _async_zle_watcher() { 344 - setopt localoptions noshwordsplit 345 - typeset -gA ASYNC_PTYS ASYNC_CALLBACKS 346 - local worker=$ASYNC_PTYS[$1] 347 - local callback=$ASYNC_CALLBACKS[$worker] 348 - 349 - if [[ -n $2 ]]; then 350 - # from man zshzle(1): 351 - # `hup' for a disconnect, `nval' for a closed or otherwise 352 - # invalid descriptor, or `err' for any other condition. 353 - # Systems that support only the `select' system call always use 354 - # `err'. 355 - 356 - # this has the side effect to unregister the broken file descriptor 357 - async_stop_worker $worker 358 - 359 - if [[ -n $callback ]]; then 360 - $callback '[async]' 2 "" 0 "$0:$LINENO: error: fd for $worker failed: zle -F $1 returned error $2" 0 361 - fi 362 - return 363 - fi; 364 - 365 - if [[ -n $callback ]]; then 366 - async_process_results $worker $callback watcher 367 - fi 368 - } 369 - 370 - _async_send_job() { 371 - setopt localoptions noshwordsplit noksharrays noposixidentifiers noposixstrings 372 - 373 - local caller=$1 374 - local worker=$2 375 - shift 2 376 - 377 - zpty -t $worker &>/dev/null || { 378 - typeset -gA ASYNC_CALLBACKS 379 - local callback=$ASYNC_CALLBACKS[$worker] 380 - 381 - if [[ -n $callback ]]; then 382 - $callback '[async]' 3 "" 0 "$0:$LINENO: error: no such worker: $worker" 0 383 - else 384 - print -u2 "$caller: no such async worker: $worker" 385 - fi 386 - return 1 387 - } 388 - 389 - zpty -w $worker "$@"$'\0' 390 - } 391 - 392 - # 393 - # Start a new asynchronous job on specified worker, assumes the worker is running. 394 - # 395 - # Note if you are using a function for the job, it must have been defined before the worker was 396 - # started or you will get a `command not found` error. 397 - # 398 - # usage: 399 - # async_job <worker_name> <my_function> [<function_params>] 400 - # 401 - async_job() { 402 - setopt localoptions noshwordsplit noksharrays noposixidentifiers noposixstrings 403 - 404 - local worker=$1; shift 405 - 406 - local -a cmd 407 - cmd=("$@") 408 - if (( $#cmd > 1 )); then 409 - cmd=(${(q)cmd}) # Quote special characters in multi argument commands. 410 - fi 411 - 412 - _async_send_job $0 $worker "$cmd" 413 - } 414 - 415 - # 416 - # Evaluate a command (like async_job) inside the async worker, then worker environment can be manipulated. For example, 417 - # issuing a cd command will change the PWD of the worker which will then be inherited by all future async jobs. 418 - # 419 - # Output will be returned via callback, job name will be [async/eval]. 420 - # 421 - # usage: 422 - # async_worker_eval <worker_name> <my_function> [<function_params>] 423 - # 424 - async_worker_eval() { 425 - setopt localoptions noshwordsplit noksharrays noposixidentifiers noposixstrings 426 - 427 - local worker=$1; shift 428 - 429 - local -a cmd 430 - cmd=("$@") 431 - if (( $#cmd > 1 )); then 432 - cmd=(${(q)cmd}) # Quote special characters in multi argument commands. 433 - fi 434 - 435 - # Quote the cmd in case RC_EXPAND_PARAM is set. 436 - _async_send_job $0 $worker "_async_eval $cmd" 437 - } 438 - 439 - # This function traps notification signals and calls all registered callbacks 440 - _async_notify_trap() { 441 - setopt localoptions noshwordsplit 442 - 443 - local k 444 - for k in ${(k)ASYNC_CALLBACKS}; do 445 - async_process_results $k ${ASYNC_CALLBACKS[$k]} trap 446 - done 447 - } 448 - 449 - # 450 - # Register a callback for completed jobs. As soon as a job is finnished, async_process_results will be called with the 451 - # specified callback function. This requires that a worker is initialized with the -n (notify) option. 452 - # 453 - # usage: 454 - # async_register_callback <worker_name> <callback_function> 455 - # 456 - async_register_callback() { 457 - setopt localoptions noshwordsplit nolocaltraps 458 - 459 - typeset -gA ASYNC_PTYS ASYNC_CALLBACKS 460 - local worker=$1; shift 461 - 462 - ASYNC_CALLBACKS[$worker]="$*" 463 - 464 - # Enable trap when the ZLE watcher is unavailable, allows 465 - # workers to notify (via -n) when a job is done. 466 - if [[ ! -o interactive ]] || [[ ! -o zle ]]; then 467 - trap '_async_notify_trap' WINCH 468 - elif [[ -o interactive ]] && [[ -o zle ]]; then 469 - local fd w 470 - for fd w in ${(@kv)ASYNC_PTYS}; do 471 - if [[ $w == $worker ]]; then 472 - zle -F $fd _async_zle_watcher # Register the ZLE handler. 473 - break 474 - fi 475 - done 476 - fi 477 - } 478 - 479 - # 480 - # Unregister the callback for a specific worker. 481 - # 482 - # usage: 483 - # async_unregister_callback <worker_name> 484 - # 485 - async_unregister_callback() { 486 - typeset -gA ASYNC_CALLBACKS 487 - 488 - unset "ASYNC_CALLBACKS[$1]" 489 - } 490 - 491 - # 492 - # Flush all current jobs running on a worker. This will terminate any and all running processes under the worker, use 493 - # with caution. 494 - # 495 - # usage: 496 - # async_flush_jobs <worker_name> 497 - # 498 - async_flush_jobs() { 499 - setopt localoptions noshwordsplit 500 - 501 - local worker=$1; shift 502 - 503 - # Check if the worker exists 504 - zpty -t $worker &>/dev/null || return 1 505 - 506 - # Send kill command to worker 507 - async_job $worker "_killjobs" 508 - 509 - # Clear the zpty buffer. 510 - local junk 511 - if zpty -r -t $worker junk '*'; then 512 - (( ASYNC_DEBUG )) && print -n "async_flush_jobs $worker: ${(V)junk}" 513 - while zpty -r -t $worker junk '*'; do 514 - (( ASYNC_DEBUG )) && print -n "${(V)junk}" 515 - done 516 - (( ASYNC_DEBUG )) && print 517 - fi 518 - 519 - # Finally, clear the process buffer in case of partially parsed responses. 520 - typeset -gA ASYNC_PROCESS_BUFFER 521 - unset "ASYNC_PROCESS_BUFFER[$worker]" 522 - } 523 - 524 - # 525 - # Start a new async worker with optional parameters, a worker can be told to only run unique tasks and to notify a 526 - # process when tasks are complete. 527 - # 528 - # usage: 529 - # async_start_worker <worker_name> [-u] [-n] [-p <pid>] 530 - # 531 - # opts: 532 - # -u unique (only unique job names can run) 533 - # -n notify through SIGWINCH signal 534 - # -p pid to notify (defaults to current pid) 535 - # 536 - async_start_worker() { 537 - setopt localoptions noshwordsplit noclobber 538 - 539 - local worker=$1; shift 540 - local -a args 541 - args=("$@") 542 - zpty -t $worker &>/dev/null && return 543 - 544 - typeset -gA ASYNC_PTYS 545 - typeset -h REPLY 546 - typeset has_xtrace=0 547 - 548 - if [[ -o interactive ]] && [[ -o zle ]]; then 549 - # Inform the worker to ignore the notify flag and that we're 550 - # using a ZLE watcher instead. 551 - args+=(-z) 552 - 553 - if (( ! ASYNC_ZPTY_RETURNS_FD )); then 554 - # When zpty doesn't return a file descriptor (on older versions of zsh) 555 - # we try to guess it anyway. 556 - integer -l zptyfd 557 - exec {zptyfd}>&1 # Open a new file descriptor (above 10). 558 - exec {zptyfd}>&- # Close it so it's free to be used by zpty. 559 - fi 560 - fi 561 - 562 - # Workaround for stderr in the main shell sometimes (incorrectly) being 563 - # reassigned to /dev/null by the reassignment done inside the async 564 - # worker. 565 - # See https://github.com/mafredri/zsh-async/issues/35. 566 - integer errfd=-1 567 - 568 - # Redirect of errfd is broken on zsh 5.0.2. 569 - if is-at-least 5.0.8; then 570 - exec {errfd}>&2 571 - fi 572 - 573 - # Make sure async worker is started without xtrace 574 - # (the trace output interferes with the worker). 575 - [[ -o xtrace ]] && { 576 - has_xtrace=1 577 - unsetopt xtrace 578 - } 579 - 580 - if (( errfd != -1 )); then 581 - zpty -b $worker _async_worker -p $$ $args 2>&$errfd 582 - else 583 - zpty -b $worker _async_worker -p $$ $args 584 - fi 585 - local ret=$? 586 - 587 - # Re-enable it if it was enabled, for debugging. 588 - (( has_xtrace )) && setopt xtrace 589 - (( errfd != -1 )) && exec {errfd}>& - 590 - 591 - if (( ret )); then 592 - async_stop_worker $worker 593 - return 1 594 - fi 595 - 596 - if ! is-at-least 5.0.8; then 597 - # For ZSH versions older than 5.0.8 we delay a bit to give 598 - # time for the worker to start before issuing commands, 599 - # otherwise it will not be ready to receive them. 600 - sleep 0.001 601 - fi 602 - 603 - if [[ -o interactive ]] && [[ -o zle ]]; then 604 - if (( ! ASYNC_ZPTY_RETURNS_FD )); then 605 - REPLY=$zptyfd # Use the guessed value for the file desciptor. 606 - fi 607 - 608 - ASYNC_PTYS[$REPLY]=$worker # Map the file desciptor to the worker. 609 - fi 610 - } 611 - 612 - # 613 - # Stop one or multiple workers that are running, all unfetched and incomplete work will be lost. 614 - # 615 - # usage: 616 - # async_stop_worker <worker_name_1> [<worker_name_2>] 617 - # 618 - async_stop_worker() { 619 - setopt localoptions noshwordsplit 620 - 621 - local ret=0 worker k v 622 - for worker in $@; do 623 - # Find and unregister the zle handler for the worker 624 - for k v in ${(@kv)ASYNC_PTYS}; do 625 - if [[ $v == $worker ]]; then 626 - zle -F $k 627 - unset "ASYNC_PTYS[$k]" 628 - fi 629 - done 630 - async_unregister_callback $worker 631 - zpty -d $worker 2>/dev/null || ret=$? 632 - 633 - # Clear any partial buffers. 634 - typeset -gA ASYNC_PROCESS_BUFFER 635 - unset "ASYNC_PROCESS_BUFFER[$worker]" 636 - done 637 - 638 - return $ret 639 - } 640 - 641 - # 642 - # Initialize the required modules for zsh-async. To be called before using the zsh-async library. 643 - # 644 - # usage: 645 - # async_init 646 - # 647 - async_init() { 648 - (( ASYNC_INIT_DONE )) && return 649 - typeset -g ASYNC_INIT_DONE=1 650 - 651 - zmodload zsh/zpty 652 - zmodload zsh/datetime 653 - 654 - # Load is-at-least for reliable version check. 655 - autoload -Uz is-at-least 656 - 657 - # Check if zsh/zpty returns a file descriptor or not, 658 - # shell must also be interactive with zle enabled. 659 - typeset -g ASYNC_ZPTY_RETURNS_FD=0 660 - [[ -o interactive ]] && [[ -o zle ]] && { 661 - typeset -h REPLY 662 - zpty _async_test : 663 - (( REPLY )) && ASYNC_ZPTY_RETURNS_FD=1 664 - zpty -d _async_test 665 - } 666 - } 667 - 668 - async() { 669 - async_init 670 - } 671 - 672 - async "$@"
···
-84
zsh/prompt.zsh
··· 1 - source "$HOME/projects/dotfiles/zsh/async.zsh" 2 - 3 - # init zsh-async 4 - async_init 5 - 6 - jj_status() { 7 - template="' (' ++ change_id.shortest(8) ++ ')'" 8 - if repo_root=$(jj root --ignore-working-copy 2>/dev/null); then 9 - jj log -r @ -T $template --no-graph --no-pager --color never --ignore-working-copy -R $repo_root 10 - fi 11 - } 12 - 13 - fetch_data() { 14 - # refetch data 15 - date_string=$(date +"%Y-%m-%d %H:%M:%S") 16 - JJ_ST=$(jj_status) 17 - } 18 - reset_prompt() { 19 - # reset prompt 20 - PROMPT="%F{green}%n@%m %F{blue}%~%f$JJ_ST %# " 21 - # re-render prompt 22 - zle reset-prompt 23 - } 24 - 25 - RUNNING= 26 - update_prompt() { 27 - if [[ -n $RUNNING ]]; then 28 - return 0 29 - fi 30 - RUNNING=1 31 - 32 - fetch_data 33 - 34 - reset_prompt 35 - 36 - # NOTE: don't make recursive loop. use TMOUT & TRAPALRM for 1s throttling 37 - # # call fetch job again 38 - # async_job prompt_worker 39 - RUNNING= 40 - } 41 - 42 - # define status_worker to run asynchronously 43 - async_start_worker prompt_worker -n 44 - async_register_callback prompt_worker update_prompt 45 - async_job prompt_worker 46 - 47 - # reset_prompt() { 48 - # # reset prompt 49 - # PROMPT="%F{green}%n@%m %F{blue}%~%f$JJ_ST %# " 50 - # # re-render prompt 51 - # zle reset-prompt 52 - # 53 - # async_job prompt_render_worker 54 - # } 55 - # async_start_worker prompt_render_worker -n 56 - # async_register_callback prompt_render_worker reset_prompt 57 - # async_job prompt_render_worker 58 - 59 - # call do_update on every seconds 60 - TMOUT=1 61 - TRAPALRM() { 62 - async_job prompt_worker 63 - } 64 - 65 - # NOTE: don't do heavy jobs here. It will make prompt slower 66 - precmd() { 67 - PROMPT="%F{green}%n@%m %F{blue}%~%f$JJ_ST %# " 68 - # call fetch job again 69 - async_job prompt_worker 70 - } 71 - 72 - zle_reset_prompt_widget() { 73 - # async_job prompt_worker 74 - } 75 - # register it under the name โ€œzle_reset_prompt_widgetโ€ 76 - zle -N zle_reset_prompt_widget 77 - autoload -Uz add-zle-hook-widget 78 - add-zle-hook-widget line-init zle_reset_prompt_widget 79 - 80 - PROMPT="%n@%m %~%f$JJ_ST %# " 81 - 82 - # references: 83 - # - https://medium.com/@henrebotha/how-to-write-an-asynchronous-zsh-prompt-b53e81720d32 84 - # - https://void-shana.moe/posts/customize-your-zsh-prompt
···