]> git.quilime.com - cfg.git/commitdiff
Update dotfiles
authorGabriel Dunne <gdunne@quilime.com>
Sat, 19 Sep 2026 19:36:13 +0000 (12:36 -0700)
committerGabriel Dunne <gdunne@quilime.com>
Sat, 19 Sep 2026 19:36:13 +0000 (12:36 -0700)
14 files changed:
.Xresources [deleted file]
.tmux.conf
.zsh-config
.zsh-darwin
.zsh-prompt
.zsh/cd-smart.README.md [new file with mode: 0644]
.zsh/cd-smart.tsv [new file with mode: 0644]
.zsh/cd-smart.zsh [new file with mode: 0644]
.zsh/common.zsh [new file with mode: 0644]
.zsh/fzf-tab.zsh [new file with mode: 0644]
.zsh/linux.zsh [new file with mode: 0644]
.zsh/local.zsh.example [new file with mode: 0644]
.zsh/macos.zsh [new file with mode: 0644]
.zshrc

diff --git a/.Xresources b/.Xresources
deleted file mode 100644 (file)
index 118b0a2..0000000
+++ /dev/null
@@ -1,238 +0,0 @@
-Xft.dpi:       96
-Xft.antialias: true
-Xft.hinting:   true
-Xft.rgba:      rgb
-Xft.autohint:  false
-Xft.hintstyle: hintslight
-Xft.lcdfilter: lcddefault
-
-XTerm*background:        #222D31
-XTerm*foreground:        #d8d8d8
-XTerm*pointerColor:      #1ABB9B
-XTerm*faceName:          Fixed
-XTerm*faceSize:          11
-XTerm*reverseVideo:      on
-XTerm*selectToClipboard: true
-
-*background:                      #222D31
-*foreground:                      #d8d8d8
-*fading:                          8
-*fadeColor:                       black
-*cursorColor:                     #1ABB9B
-*pointerColorBackground:          #2B2C2B
-*pointerColorForeground:          #16A085
-
-!! black dark/light
-*color0:                          #222D31
-*color8:                          #585858
-
-!! red dark/light
-*color1:                          #ab4642
-*color9:                          #ab4642
-
-!! green dark/light
-*color2:                          #7E807E
-*color10:                         #8D8F8D
-
-!! yellow dark/light
-*color3:                          #f7ca88
-*color11:                         #f7ca88
-
-!! blue dark/light
-*color4:                          #7cafc2
-*color12:                         #7cafc2
-
-!! magenta dark/light
-*color5:                          #ba8baf
-*color13:                         #ba8baf
-
-!! cyan dark/light
-*color6:                          #1ABB9B
-*color14:                         #1ABB9B
-
-!! white dark/light
-*color7:                          #d8d8d8
-*color15:                         #f8f8f8
-
-Xcursor.theme: xcursor-breeze
-Xcursor.size:                     0
-
-URxvt*font:                       xft:DejaVuSansMono:size=10
-! URxvt.font:                       xft:TerminessTTFNerdFontMono:size:11
-! URxvt.font:                       9x15,xft:TerminessTTFNerdFontMono
-
-! alternative font settings with 'terminus':
-! URxvt.font:      -xos4-terminus-medium-r-normal--16-160-72-72-c-80-iso10646-1
-! URxvt.bold.font: -xos4-terminus-bold-r-normal--16-160-72-72-c-80-iso10646-1
-!! terminus names see end of file!
-
-URxvt.depth:                      32
-URxvt.background:                 [100]#222D31
-URxvt*scrollBar:                  false
-URxvt*mouseWheelScrollPage:       false
-URxvt*cursorBlink:                true
-URxvt*background:                 black
-URxvt*foreground:                 grey
-URxvt*saveLines:                  5000
-
-! for 'fake' transparency (without Compton) uncomment the following three lines
-! URxvt*inheritPixmap:            true
-! URxvt*transparent:              true
-! URxvt*shading:                  138
-
-! Normal copy-paste keybindings without perls
-URxvt.iso14755:                   false
-URxvt.keysym.Shift-Control-V:     eval:paste_clipboard
-URxvt.keysym.Shift-Control-C:     eval:selection_to_clipboard
-!Xterm escape codes, word by word movement
-URxvt.keysym.Control-Left:        \033[1;5D
-URxvt.keysym.Shift-Control-Left:  \033[1;6D
-URxvt.keysym.Control-Right:       \033[1;5C
-URxvt.keysym.Shift-Control-Right: \033[1;6C
-URxvt.keysym.Control-Up:          \033[1;5A
-URxvt.keysym.Shift-Control-Up:    \033[1;6A
-URxvt.keysym.Control-Down:        \033[1;5B
-URxvt.keysym.Shift-Control-Down:  \033[1;6B
-! Rxvt.perl-ext-common:             ...,clipboard
-! URxvt.keysym.M-C-c:               perl:clipboard:copy
-! URxvt.keysym.M-v:                 perl:clipboard:paste
-! URxvt.keysym.M-C-v:               perl:clipboard:paste_escaped
-! URxvt*termName:                   string
-! URxvt*geometry:                   geometry
-! URxvt*chdir:                      string
-! URxvt*loginShell:                 boolean
-! URxvt*multiClickTime:             number
-! URxvt*jumpScroll:                 boolean
-! URxvt*skipScroll:                 boolean
-! URxvt*pastableTabs:               boolean
-! URxvt*scrollstyle:                plain
-! URxvt*scrollBar_right:            boolean
-! URxvt*scrollBar_floating:         true
-! URxvt*scrollBar_align:            mode
-! URxvt*thickness:                  number
-! URxvt*scrollTtyOutput:            boolean
-! URxvt*scrollTtyKeypress:          boolean
-! URxvt*scrollWithBuffer:           boolean
-! URxvt*tintColor:                  !7DA55
-! URxvt*blurRadius:                 HxV
-! URxvt*fading:                     number
-! URxvt*fadeColor:                  color
-! URxvt*utmpInhibit:                boolean
-! URxvt*urgentOnBell:               boolean
-! URxvt*visualBell:                 boolean
-! URxvt*mapAlert:                   boolean
-! URxvt*meta8:                      boolean
-! URxvt*tripleclickwords:           boolean
-! URxvt*insecure:                   boolean
-! URxvt*cursorUnderline:            boolean
-! URxvt*pointerBlank:               boolean
-! URxvt*color0:                     color
-! URxvt*color1:                     color
-! URxvt*color2:                     color
-! URxvt*color3:                     color
-! URxvt*color4:                     color
-! URxvt*color5:                     color
-! URxvt*color6:                     color
-! URxvt*color7:                     color
-! URxvt*color8:                     color
-! URxvt*color9:                     color
-! URxvt*color10:                    color
-! URxvt*color11:                    color
-! URxvt*color12:                    color
-! URxvt*color13:                    color
-! URxvt*color14:                    color
-! URxvt*color15:                    color
-! URxvt*colorBD:                    color
-! URxvt*colorIT:                    color
-! URxvt*colorUL:                    color
-! URxvt*colorRV:                    color
-! URxvt*underlineColor:             color
-! URxvt*scrollColor:                color
-! URxvt*troughColor:                color
-! URxvt*highlightColor:             color
-! URxvt*highlightTextColor:         color
-! URxvt*cursorColor:                color
-! URxvt*cursorColor2:               color
-! URxvt*pointerColor:               color
-! URxvt*pointerColor2:              color
-! URxvt*borderColor:                color
-! URxvt*iconFile:                   file
-! URxvt*font:                       fontname
-! URxvt*boldFont:                   fontname
-! URxvt*italicFont:                 fontname
-! URxvt*boldItalicFont:             fontname
-! URxvt*intensityStyles:            boolean
-! URxvt*inputMethod:                name
-! URxvt*preeditType:                style
-! URxvt*imLocale:                   string
-! URxvt*imFont:                     fontname
-! URxvt*title:                      string
-! URxvt*iconName:                   string
-! URxvt*buffered:                   boolean
-! URxvt*depth:                      number
-! URxvt*visual:                     number
-! URxvt*transient-for:              windowid
-! URxvt*override-redirect:          boolean
-! URxvt*hold:                       boolean
-! URxvt*externalBorder:             number
-! URxvt*internalBorder:             number
-! URxvt*borderLess:                 true
-! URxvt*lineSpace:                  number
-! URxvt*letterSpace:                number
-! URxvt*skipBuiltinGlyphs:          boolean
-! URxvt*pointerBlankDelay:          number
-! URxvt*backspacekey:               string
-! URxvt*deletekey:                  string
-! URxvt*print-pipe:                 string
-! URxvt*modifier:                   modifier
-! URxvt*cutchars:                   string
-! URxvt*answerbackString:           string
-! URxvt*secondaryScreen:            boolean
-! URxvt*secondaryScroll:            boolean
-! URxvt*perl-lib:                   string
-! URxvt*perl-eval:                  perl-eval
-! URxvt*perl-ext-common:            string
-! URxvt*perl-ext:                   string
-! URxvt*iso14755:                   boolean
-! URxvt*iso14755_52:                boolean
-! URxvt*xrm:                        string
-! URxvt*keysym.sym:                 keysym
-! URxvt*background.border:          boolean
-! URxvt*background.expr:            string
-! URxvt*background.interval:        seconds
-! URxvt*bell-command:               string
-! URxvt*kuake.hotkey:               string
-! URxvt*matcher.button:             string
-! URxvt*matcher.launcher:           string
-! URxvt*matcher.launcher.*:         string
-! URxvt*matcher.pattern.*:          string
-! URxvt*matcher.rend.*:             string
-! URxvt*remote-clipboard.fetch:     string
-! URxvt*remote-clipboard.store:     string
-! URxvt*searchable-scrollback:      string
-! URxvt*selection-autotransform.*:  string
-! URxvt*selection-pastebin.cmd:     string
-! URxvt*selection-pastebin.url:     string
-! URxvt*selection.pattern-0:        string
-! URxvt*tab-bg:                     colour
-! URxvt*tab-fg:                     colour
-! URxvt*tabbar-bg:                  colour
-! URxvt*tabbar-fg:                  colour
-! URxvt*url-launcher:               string
-
-! The Terminus font uses the following X-names:
-! -xos4-terminus-medium-r-normal--12-120-72-72-c-60-iso10646-1
-! -xos4-terminus-medium-r-normal--14-140-72-72-c-80-iso10646-1
-! -xos4-terminus-medium-r-normal--16-160-72-72-c-80-iso10646-1
-! -xos4-terminus-medium-r-normal--20-200-72-72-c-100-iso10646-1
-! -xos4-terminus-medium-r-normal--22-220-72-72-c-110-iso10646-1
-! -xos4-terminus-medium-r-normal--24-240-72-72-c-120-iso10646-1
-! -xos4-terminus-medium-r-normal--28-280-72-72-c-140-iso10646-1
-! -xos4-terminus-medium-r-normal--32-320-72-72-c-160-iso10646-1
-! -xos4-terminus-bold-r-normal--12-120-72-72-c-60-iso10646-1
-! -xos4-terminus-bold-r-normal--14-140-72-72-c-80-iso10646-1
-! -xos4-terminus-bold-r-normal--16-160-72-72-c-80-iso10646-1
-! -xos4-terminus-bold-r-normal--20-200-72-72-c-100-iso10646-1
-! -xos4-terminus-bold-r-normal--24-240-72-72-c-120-iso10646-1
-! -xos4-terminus-bold-r-normal--28-280-72-72-c-140-iso10646-1
-! -xos4-terminus-bold-r-normal--32-320-72-72-c-160-iso10646-1
index f7f1f367bd9953afc85583e0c2a6e6756723baba..9126e9d94babae38ccf56644263d7e30875a91c5 100644 (file)
@@ -6,6 +6,9 @@ set -g mouse on
 # Allows for faster key repetition
 set -s escape-time 0
 
+# multi-line shift+enter
+bind -n S-Enter send-keys C-j
+
 # bind new keys for splits
 bind | split-window -hc "#{pane_current_path}"
 bind - split-window -vc "#{pane_current_path}"
@@ -16,6 +19,11 @@ bind r source-file ~/.tmux.conf \; display "Reloaded .tmux.conf"
 # constrain window size to the maximum size of any client connected to that window
 setw -g aggressive-resize on
 
+# Start window and pane numbering at 1.
+set -g base-index 1
+setw -g pane-base-index 1
+set -g renumber-windows on
+
 # Set status bar
 set -g status-bg colour0
 set -g status-fg white
@@ -24,3 +32,29 @@ set -g status-right-length 40
 set -g status-left "#[bg=colour0]#[fg=colour4] #h [#[fg=colour15]#I#[fg=colour4]] "
 # set -g status-right "#[bg=colour33]#[fg=colour255]#[fg=colour232] #[fg=colour255]#(uptime | awk {'print $8, $9, $10'})#[fg=colour232] #[bg=colour232] #[bg=colour33] #[fg=colour255]#(date '+%H:%M %d-%m-%Y')#[fg=colour232] "
 set -g status-right "#[bg=colour0]#[fg=colour255]#[fg=colour232]#[bg=colour0]#[fg=colour149]#(date '+%Y-%m-%d %a %H:%M')"
+
+# Highlight the active tab and show activity/bell notifications.
+set -g status-justify left
+setw -g window-status-separator ''
+setw -g window-status-format '#[fg=colour244,bg=colour0]  #I #[fg=colour250]#W #[fg=colour238]|'
+setw -g window-status-current-format '#[fg=colour232,bg=colour39,bold]  #I #[fg=colour255]#W #[fg=colour39] '
+setw -g window-status-last-style 'fg=colour0,bg=colour245'
+setw -g window-status-activity-style 'fg=colour232,bg=colour214,bold'
+setw -g window-status-bell-style 'fg=colour255,bg=colour160,bold'
+
+# Switch tabs and panes with Ctrl+Shift+Arrow.
+bind -n C-S-Left previous-window
+bind -n C-S-Right next-window
+bind -n C-S-Up select-pane -U
+bind -n C-S-Down select-pane -D
+
+# Jump directly to tabs 1–9.
+bind -n C-1 select-window -t 1
+bind -n C-2 select-window -t 2
+bind -n C-3 select-window -t 3
+bind -n C-4 select-window -t 4
+bind -n C-5 select-window -t 5
+bind -n C-6 select-window -t 6
+bind -n C-7 select-window -t 7
+bind -n C-8 select-window -t 8
+bind -n C-9 select-window -t 9
index d1513c29cdc32317d8cb0b55aa0921fd8c3d8035..d1e6bfb878bff61590d9f36ce6502636af9fd8bf 100644 (file)
@@ -78,9 +78,9 @@ export LESS=-r
 
 ## Plugins section: Enable fish style features
 # Use syntax highlighting
-source /usr/share/zsh/plugins/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh
+source /opt/homebrew/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh
 # Use history substring search
-source /usr/share/zsh/plugins/zsh-history-substring-search/zsh-history-substring-search.zsh
+source /opt/homebrew/share/zsh-history-substring-search/zsh-history-substring-search.zsh
 # bind UP and DOWN arrow keys to history substring search
 zmodload zsh/terminfo
 bindkey "$terminfo[kcuu1]" history-substring-search-up
index 8b1b63a940111c2d73831140ae39181c54648f3e..0b133bb0467b7a36ae7b2b81209f549e56b6f04d 100644 (file)
@@ -14,6 +14,31 @@ zstyle ':vcs_info:git:*' formats '%b'
 PROMPT='%F{cyan}%n@%m%f %F{240}%1~ %f%# '
 RPROMPT='%F{green}${vcs_info_msg_0_} %F{240}%*%f'
 
+
+
+# ZSH completion
+if type brew &>/dev/null; then
+    FPATH="$(brew --prefix)/share/zsh/site-functions:${FPATH}"
+fi
+autoload -Uz compinit
+# only rebuild the completion dump once a day
+if [[ -n ~/.zcompdump(#qN.mh+24) ]]; then compinit; else compinit -C; fi
+
+export LS_COLORS='di=34:ln=35:so=32:pi=33:ex=31:bd=34;46:cd=34;43'
+
+zstyle ':completion:*' menu select
+zstyle ':completion:*' matcher-list 'm:{a-zA-Z}={A-Za-z}' 'r:|[._-]=* r:|=*' 'l:|=* r:|=*'
+zstyle ':completion:*' list-colors "${(s.:.)LS_COLORS}"
+zstyle ':completion:*' group-name ''
+zstyle ':completion:*:descriptions' format '%F{yellow}-- %d --%f'
+zstyle ':completion:*:messages' format '%F{purple}-- %d --%f'
+zstyle ':completion:*:warnings' format '%F{red}-- no matches --%f'
+zstyle ':completion:*' verbose yes
+zstyle ':completion:*' use-cache on
+zstyle ':completion:*' cache-path ~/.zsh/cache
+setopt complete_in_word auto_menu
+
+
 # aliases
 alias ls='ls -G'
 alias cats='highlight -O ansi --force'
@@ -23,6 +48,12 @@ alias hf='defaults write com.apple.finder AppleShowAllFiles false; killall Finde
 alias shf='defaults write com.apple.finder AppleShowAllFiles true; killall Finder' # show hidden
 # alias cat='bat'
 alias grep='grep --color'
+alias ff='fastfetch'
+#alias ncdu='ncdu --color=dark'
+
+# scripts
+export PATH="/Users/gld/bin:$PATH"
+
 
 # Lima
 alias archlinux='limactl shell archlinux'
@@ -52,13 +83,16 @@ export NVM_DIR="$HOME/.nvm"
 export PATH="/etc/paths.d/TeX:/Library/TeX/texbin:$PATH"
 
 # ruby
-#export PATH="/Users/gld/.gem/ruby/3.1.1/bin:/opt/homebrew/opt/ruby/bin:/opt/homebrew/lib/ruby/gems/3.1.1/bin:$PATH"
-export PATH="/Users/gld/.gem/ruby/3.1.0/bin:/opt/homebrew/opt/ruby/bin:/opt/homebrew/lib/ruby/gems/3.1.0/bin:$PATH"
-source /opt/homebrew/opt/chruby/share/chruby/chruby.sh
-source /opt/homebrew/opt/chruby/share/chruby/auto.sh
-chruby ruby-3.1.2 # run chruby to see actual version
+export PATH="/Users/gld/.gem/ruby/3.1.1/bin:/opt/homebrew/opt/ruby/bin:/opt/homebrew/lib/ruby/gems/3.1.1/bin:$PATH"
+#export PATH="/Users/gld/.gem/ruby/3.1.0/bin:/opt/homebrew/opt/ruby/bin:/opt/homebrew/lib/ruby/gems/3.1.0/bin:$PATH"
 source /opt/homebrew/opt/chruby/share/chruby/chruby.sh
 source /opt/homebrew/opt/chruby/share/chruby/auto.sh
+# chruby ruby-3.1.2 # run chruby to see actual version
+#source /opt/homebrew/opt/chruby/share/chruby/chruby.sh
+#source /opt/homebrew/opt/chruby/share/chruby/auto.sh
+
+# mysql
+export PATH="/opt/homebrew/opt/mysql/bin:$PATH"
 
 # dotfiles
 alias cfg='/opt/homebrew/bin/git --git-dir=/Users/gld/.cfg/ --work-tree=/Users/gld'
@@ -66,7 +100,14 @@ alias cfg='/opt/homebrew/bin/git --git-dir=/Users/gld/.cfg/ --work-tree=/Users/g
 # more path
 export PATH="$HOME/.local/bin:$PATH"
 
+# fzf and fuzzy tab completion
+source <(fzf --zsh)
+source "$(brew --prefix)/opt/fzf-tab/share/fzf-tab/fzf-tab.zsh"
+zstyle ':fzf-tab:*' fzf-flags --height=40%
 
+# plugins -- syntax highlighting must stay last
+source "$(brew --prefix)/share/zsh-autosuggestions/zsh-autosuggestions.zsh"
+source "$(brew --prefix)/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh"
 
 
 
index 00b1b42ed65fb58d1ef5f034408f284097b23ed0..4c6f146492eb7161b9de5350906426c5ec1c43ae 100644 (file)
@@ -99,14 +99,14 @@ case $(basename "$(cat "/proc/$PPID/comm")") in
 #              #BASE16_SHELL="/usr/share/zsh/scripts/base16-shell/base16-$theme.$shade.sh"
 #              #[[ -s $BASE16_SHELL ]] && source $BASE16_SHELL
 #              # Use autosuggestion
-#              source /usr/share/zsh/plugins/zsh-autosuggestions/zsh-autosuggestions.zsh
+#              source /opt/homebrew/share/zsh-autosuggestions/zsh-autosuggestions.zsh
 #              ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE=20
 #              ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE='fg=8'
 #     ;;
   *)
         RPROMPT='$(git_prompt_string)'
                # Use autosuggestion
-               source /usr/share/zsh/plugins/zsh-autosuggestions/zsh-autosuggestions.zsh
+               source /opt/homebrew/share/zsh-autosuggestions/zsh-autosuggestions.zsh
                ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE=20
                ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE='fg=8'
     ;;
diff --git a/.zsh/cd-smart.README.md b/.zsh/cd-smart.README.md
new file mode 100644 (file)
index 0000000..d931a65
--- /dev/null
@@ -0,0 +1,125 @@
+# cd-smart
+
+A `cd` that learns where you go. About 250 lines of zsh, one plain-text
+database, no external processes on the `cd` path, and no dependencies beyond
+zsh itself (fzf is used by `cdi` when present).
+
+## Install
+
+```zsh
+# ~/.zshrc, after compinit
+source ~/.zsh/cd-smart.zsh
+```
+
+Files live next to the script: `cd-smart.tsv` (learned directories) and
+`cd-smart.aliases` (shortcuts defined at the prompt).
+
+## Use
+
+```zsh
+cd ~/Projects/notes   # ordinary cd; the directory is learned
+cd agents                       # jump to the best learned match for "agents"
+cd open ag                      # words must appear in order: .../Projects/notes
+cd ag<TAB>                      # normal completion plus learned matches and shortcuts
+cdi ag                          # pick from matches in fzf; ctrl-/ toggles a preview
+cdi                             # browse everything learned
+```
+
+Precedence inside the wrapper, first match wins:
+
+1. No argument, or anything with options, slashes, or a leading `.` or `~`,
+   goes straight to the builtin. `cd ..`, `cd -`, `cd ~/x` are untouched.
+2. A single word that exists as a directory here.
+3. A single word that is a shortcut.
+4. Two words that work as the builtin's `cd old new` substitution.
+5. The best-scored learned directory containing the words in order.
+6. The builtin's normal error.
+
+### Shortcuts
+
+```zsh
+cdalias osa ~/Projects/notes   # "cd osa" always goes there
+cdalias dl                               # current directory becomes "dl"
+cdalias                                  # list
+cdunalias dl                             # remove
+```
+
+Shortcuts can also be declared in `.zshrc` before sourcing, and those win over
+saved ones:
+
+```zsh
+typeset -gA CDS_ALIASES=(osa ~/Projects/notes)
+```
+
+### Maintenance
+
+```zsh
+cdlearn PATH     # learn a directory without visiting it
+cdforget PATH    # remove one
+cdlist           # learned directories with scores
+cdsmart-help     # short reminder of all of this
+```
+
+### Configuration
+
+Set before sourcing.
+
+| variable      | default                      | meaning                                   |
+|---------------|------------------------------|-------------------------------------------|
+| `CDS_DB`      | `cd-smart.tsv` beside script | learned-directory database                |
+| `CDS_ROOTS`   | `$HOME` (and `/zpool` if present) | only directories under these are learned |
+| `CDS_MAXAGE`  | `1000`                       | aging threshold, see below                |
+| `CDS_ALIASES` | empty                        | shortcuts declared in `.zshrc`            |
+
+## How ranking works
+
+Each learned directory has a score and a last-visit time. A visit adds 1 to
+the score. At lookup time a candidate's rank is its score plus a bonus for an
+exact basename match (+100) or basename prefix (+50) on the last query word,
+plus a recency bonus (20 within an hour, 15 within a day, 10 within a week,
+5 within a month).
+
+Growth is bounded by aging. When the sum of all scores exceeds `CDS_MAXAGE`,
+every score is multiplied by `0.9 × CDS_MAXAGE / total`, entries that fall
+below 1 are dropped, and directories that no longer exist are pruned. Places
+you keep returning to stay; one-off visits fade out.
+
+## Tests
+
+```zsh
+zsh ~/.zsh/cd-smart.test.zsh
+```
+
+Runs in a throwaway sandbox and never touches your real database.
+
+## Compared with zoxide
+
+[zoxide](https://github.com/ajeetdsouza/zoxide) solves the same problem and
+is the obvious alternative. The ranking model here is deliberately close to
+its design. The differences are mostly about scope.
+
+| | cd-smart | zoxide |
+|---|---|---|
+| Implementation | ~250 lines of zsh, one file | ~2,000 lines of Rust plus a shell template per shell |
+| Shells | zsh only | bash, zsh, fish, nushell, PowerShell, and others |
+| Database | plain TSV you can read and edit | binary, with file locking |
+| Command | wraps `cd` itself | separate `z` and `zi`; `--cmd cd` can replace `cd` |
+| Real directories vs. guesses | an existing local directory always wins, then shortcuts, then learned matches | with `--cmd cd`, an existing path also wins; otherwise `z` always guesses |
+| Tab completion | learned matches and shortcuts appear inside normal `cd <TAB>` | completion on the `z` command only |
+| Shortcuts | built in (`cdalias`) | none; use shell aliases |
+| Where learning happens | only under `CDS_ROOTS` | everywhere, minus `_ZO_EXCLUDE_DIRS` |
+| Multi-word queries | words in order anywhere in the path; last word gets basename bonuses | words in order; last word must match the last path component |
+| Recency | additive bonus by age bucket | multiplier by age bucket (4×, 2×, 0.5×, 0.25×) |
+| Aging | same scheme: scale by 0.9 × cap / total, drop below 1, prune dead paths | same, default cap 10,000 |
+| Concurrency | last writer wins if two shells `cd` at the same instant | locked writes |
+| External processes | none on `cd` or lookup | one binary launch per `cd` and per query |
+| Speed at 300 entries (typical after aging) | ~3.5 ms per `cd`, ~1 ms per lookup | a few ms, dominated by the launch |
+| Speed at 2,000 entries | ~17 ms per `cd`, ~7 ms per lookup | a few ms |
+| Tests | 45 smoke tests in one script | full Rust test suite |
+
+In short: zoxide is safer under concurrent shells and portable, and stays
+fast at any database size.
+cd-smart is smaller, fully inspectable, restricted to the directories you
+care about, and integrates with `cd` and its completion rather than adding a
+second command. In practice aging keeps the database in the low hundreds of
+entries, where neither tool is perceptibly slower than a plain `cd`.
diff --git a/.zsh/cd-smart.tsv b/.zsh/cd-smart.tsv
new file mode 100644 (file)
index 0000000..61d7670
--- /dev/null
@@ -0,0 +1 @@
+1.00   1789845937      /Users/gld
diff --git a/.zsh/cd-smart.zsh b/.zsh/cd-smart.zsh
new file mode 100644 (file)
index 0000000..0a69120
--- /dev/null
@@ -0,0 +1,326 @@
+# cd-smart.zsh
+#
+# A small, self-contained Zsh "smart cd" layer.
+#
+# Normal:
+#   cd /Users/gabrieldunne/Code
+#   cd ~/Downloads
+#   cd ../foo
+#
+# Smart:
+#   cd ph          -> best learned directory matching "ph"
+#   cd pro notes   -> words must appear in order: .../Projects/notes
+#   cd ph<TAB>     -> best learned matches
+#   cdi ph         -> pick from matches in fzf
+#
+# Learning happens automatically after successful directory changes.
+
+[[ -o interactive ]] || return 0
+
+zmodload zsh/datetime
+zmodload zsh/files
+
+: ${CDS_DB:="${${(%):-%x}:A:h}/cd-smart.tsv"}
+: ${CDS_MAXAGE:=1000}
+: ${CDS_ALIAS_FILE:="${CDS_DB:h}/cd-smart.aliases"}
+
+(( ${+CDS_ALIASES} )) || typeset -gA CDS_ALIASES
+typeset -gA _cds_zshrc_aliases=("${(@kv)CDS_ALIASES}")
+
+_cds_load_aliases() {
+    local name target
+    [[ -r "$CDS_ALIAS_FILE" ]] || return 0
+    while IFS=$'\t' read -r name target; do
+        [[ -n "$name" && -n "$target" ]] || continue
+        (( ${+_cds_zshrc_aliases[$name]} )) || CDS_ALIASES[$name]="$target"
+    done < "$CDS_ALIAS_FILE"
+}
+_cds_load_aliases
+
+_cds_valid_alias_name() {
+    [[ -n "$1" && "$1" != -* && "$1" != */* && "$1" != .* && "$1" != '~'* && "$1" != *[[:space:]]* ]]
+}
+
+if (( ! ${+CDS_ROOTS} )); then
+    typeset -ga CDS_ROOTS=("$HOME")
+    [[ -d /zpool ]] && CDS_ROOTS+=("/zpool")
+fi
+CDS_ROOTS=("${(@)CDS_ROOTS%/}")
+
+_cds_init() {
+    [[ -d "${CDS_DB:h}" ]] || zf_mkdir -p -- "${CDS_DB:h}" 2>/dev/null
+    [[ -f "$CDS_DB" ]] || : >| "$CDS_DB" 2>/dev/null
+}
+
+_cds_save() {
+    local tmp="${CDS_DB}.tmp.$$" line dir sc
+    local -F total=0 factor score
+    local -a lines=("${(@)@:#}") scores
+
+    scores=("${(@)lines%%$'\t'*}")
+    for sc in "${scores[@]}"; do (( total += ${sc:-0} )); done
+
+    if (( total > CDS_MAXAGE )); then
+        (( factor = 0.9 * CDS_MAXAGE / total ))
+        {
+            for line in "${lines[@]}"; do
+                dir="${line##*$'\t'}"
+                [[ -n "$dir" && -d "$dir" ]] || continue
+                (( score = ${${line%%$'\t'*}:-0} * factor ))
+                (( score >= 1 )) || continue
+                printf '%.2f\t%s\n' "$score" "${line#*$'\t'}"
+            done
+        } >| "$tmp" 2>/dev/null || { zf_rm -f -- "$tmp"; return 1 }
+    else
+        { (( ${#lines} == 0 )) || print -rl -- "${lines[@]}" } >| "$tmp" 2>/dev/null ||
+            { zf_rm -f -- "$tmp"; return 1 }
+    fi
+
+    zf_mv -f -- "$tmp" "$CDS_DB" 2>/dev/null || { zf_rm -f -- "$tmp"; return 1 }
+}
+
+_cds_allowed() {
+    local p="$1" root
+    for root in "${CDS_ROOTS[@]}"; do
+        [[ "$p" == "$root" || "$p" == "$root"/* ]] && return 0
+    done
+    return 1
+}
+
+_cds_record() {
+    local p="${1:-$PWD}" i pat
+    local -F score
+    local -a lines
+
+    _cds_allowed "$p" || return 0
+    _cds_init || return 0
+    lines=("${(@f)$(<"$CDS_DB")}")
+    lines=("${(@)lines:#}")
+    pat="*"$'\t'"${(b)p}"
+    i=${lines[(i)$pat]}
+    if (( i <= ${#lines} )); then
+        (( score = ${${lines[i]%%$'\t'*}:-0} + 1 ))
+        lines[i]="$(printf '%.2f' "$score")"$'\t'"$EPOCHSECONDS"$'\t'"$p"
+    else
+        lines+=("1.00"$'\t'"$EPOCHSECONDS"$'\t'"$p")
+    fi
+    _cds_save "${lines[@]}"
+}
+
+autoload -Uz add-zsh-hook
+add-zsh-hook chpwd _cds_record
+
+_cds_matches() {
+    setopt localoptions extendedglob
+    local now stored last dir base age recency pat w line key
+    local -F score
+    local -a words=("${(@L)@}") lines ranked
+    local q="${words[-1]}"
+
+    pat='(#i)*'
+    for w in "${words[@]}"; do pat+="${(b)w}*"; done
+
+    _cds_init || return 0
+    now=$EPOCHSECONDS
+    lines=("${(@f)$(<"$CDS_DB")}")
+    lines=("${(@M)lines:#${~pat}}")
+
+    for line in "${lines[@]}"; do
+        stored="${line%%$'\t'*}"
+        dir="${line##*$'\t'}"
+        last="${${line#*$'\t'}%%$'\t'*}"
+        [[ -n "$dir" && -d "$dir" ]] || continue
+        base="${dir:t:l}"
+        score=${stored:-0}
+
+        if [[ -n "$q" && "$base" == "$q" ]]; then
+            (( score += 100 ))
+        elif [[ -n "$q" && "$base" == "$q"* ]]; then
+            (( score += 50 ))
+        fi
+
+        age=$(( now - ${last:-now} ))
+        if (( age < 3600 )); then recency=20
+        elif (( age < 86400 )); then recency=15
+        elif (( age < 604800 )); then recency=10
+        elif (( age < 2592000 )); then recency=5
+        else recency=0
+        fi
+
+        (( score += recency ))
+        printf -v key '%012.2f' "$score"
+        ranked+=("$key"$'\t'"$dir")
+    done
+
+    (( ${#ranked} )) && print -rl -- "${(@)${(@O)ranked}#*$'\t'}"
+}
+
+_cds_simple_words() {
+    local w
+    (( $# )) || return 1
+    for w in "$@"; do
+        [[ "$w" == -* || "$w" == */* || "$w" == .* || "$w" == '~'* ]] && return 1
+    done
+    return 0
+}
+
+cd() {
+    local -a matches
+    (( $# == 0 )) && { builtin cd; return; }
+
+    if (( $# > 1 )) && [[ "${@[-1]}" == /* && -d "${@[-1]}" ]]; then
+        builtin cd -- "${@[-1]}"
+        return
+    fi
+
+    if ! _cds_simple_words "$@"; then
+        builtin cd "$@"
+        return
+    fi
+
+    if (( $# == 1 )) && [[ -d "$1" ]]; then
+        builtin cd -- "$1"
+        return
+    fi
+
+    if (( $# == 1 && ${+CDS_ALIASES[$1]} )); then
+        local target="${CDS_ALIASES[$1]}"
+        [[ -d "$target" ]] || { print -u2 "cd-smart: alias $1 -> $target: not a directory"; return 1; }
+        builtin cd -- "$target"
+        return
+    fi
+
+    if (( $# == 2 )) && builtin cd "$@" 2>/dev/null; then
+        return
+    fi
+
+    matches=(${(f)"$(_cds_matches "$@")"})
+    if (( ${#matches} )); then
+        builtin cd -- "${matches[1]}"
+        return
+    fi
+
+    if (( $# > 1 )); then
+        print -u2 "cd-smart: no learned match for ${(q)*}"
+        return 1
+    fi
+    builtin cd "$@"
+}
+
+_cds_cd_complete() {
+    (( $+functions[_cd] )) && _cd "$@"
+    local -a query learned
+    query=("${(@)words[2,CURRENT]}")
+    _cds_simple_words "${query[@]}" || return 0
+
+    if (( CURRENT == 2 && ${#CDS_ALIASES} )); then
+        local name
+        local -a names descs
+        for name in "${(@ko)CDS_ALIASES}"; do
+            names+=("$name")
+            descs+=("$name -- ${CDS_ALIASES[$name]/#$HOME/~}")
+        done
+        compadd -J shortcuts -X 'shortcuts' -d descs -- "${names[@]}"
+    fi
+
+    learned=(${(f)"$(_cds_matches "${query[@]}")"})
+    (( ${#learned} )) || return 0
+    compadd -U -Q -J learned -X 'learned directories' -- "${learned[@]}"
+}
+
+compdef _cds_cd_complete cd 2>/dev/null
+
+cdi() {
+    local q="$*" choice
+    local -a paths
+    paths=(${(f)"$(_cds_matches "$@")"})
+    if (( ! ${#paths[@]} )); then
+        print -u2 "cd-smart: no learned match for ${(q)q}"
+        return 1
+    fi
+
+    if (( $+commands[fzf] )); then
+        choice="$(print -rl -- "${paths[@]}" | fzf --height=~40% --layout=reverse --no-sort --select-1 --exit-0 --prompt='cd › ' --query="$q" --preview='CLICOLOR_FORCE=1 ls -1G {}' --preview-window=right:50%:hidden --bind=ctrl-/:toggle-preview)" || return 1
+    elif (( ${#paths[@]} == 1 )); then
+        choice="${paths[1]}"
+    else
+        print
+        select choice in "${paths[@]}" "cancel"; do
+            [[ "$choice" == cancel || -z "$choice" ]] && return 1
+            break
+        done
+    fi
+
+    [[ -n "$choice" ]] && builtin cd -- "$choice"
+}
+
+cdlearn() {
+    local p
+    (( $# == 1 )) || { print -u2 "usage: cdlearn PATH"; return 2; }
+    p="${1:A}"
+    [[ -d "$p" ]] || { print -u2 "cd-smart: not a directory: $1"; return 1; }
+    _cds_allowed "$p" || { print -u2 "cd-smart: outside CDS_ROOTS, not learned: $p"; return 1; }
+    _cds_record "$p"
+}
+
+cdforget() {
+    local target="${1:A}" score last dir
+    local -a lines
+    (( $# == 1 )) || { print -u2 "usage: cdforget PATH"; return 2; }
+    _cds_init || return 1
+    while IFS=$'\t' read -r score last dir; do
+        [[ -n "$dir" && "$dir" != "$target" ]] || continue
+        lines+=("$score"$'\t'"$last"$'\t'"$dir")
+    done < "$CDS_DB"
+    _cds_save "${lines[@]}"
+}
+
+cdlist() {
+    local score last dir
+    _cds_init || return 1
+    while IFS=$'\t' read -r score last dir; do
+        [[ -n "$dir" ]] && printf '%9.2f  %s\n' "$score" "$dir"
+    done < "$CDS_DB" | sort -rn
+}
+
+_cds_save_aliases() {
+    local name tmp="${CDS_ALIAS_FILE}.tmp.$$"
+    {
+        for name in "${(@ko)CDS_ALIASES}"; do
+            (( ${+_cds_zshrc_aliases[$name]} )) && continue
+            print -r -- "$name"$'\t'"${CDS_ALIASES[$name]}"
+        done
+    } >| "$tmp" 2>/dev/null || { zf_rm -f -- "$tmp"; return 1; }
+    zf_mv -f -- "$tmp" "$CDS_ALIAS_FILE" 2>/dev/null || { zf_rm -f -- "$tmp"; return 1; }
+}
+
+cdalias() {
+    local name="$1" target="${2:-$PWD}"
+    if (( $# == 0 )); then
+        for name in "${(@ko)CDS_ALIASES}"; do
+            printf '%-12s %s\n' "$name" "${CDS_ALIASES[$name]/#$HOME/~}"
+        done
+        return 0
+    fi
+    (( $# <= 2 )) || { print -u2 "usage: cdalias [NAME [PATH]]"; return 2; }
+    _cds_valid_alias_name "$name" || { print -u2 "cd-smart: invalid alias name: ${(q)name}"; return 2; }
+    target="${target:A}"
+    [[ -d "$target" ]] || { print -u2 "cd-smart: not a directory: $2"; return 1; }
+    (( ${+_cds_zshrc_aliases[$name]} )) && { print -u2 "cd-smart: $name is defined in your zshrc; edit it there"; return 1; }
+    CDS_ALIASES[$name]="$target"
+    _cds_save_aliases
+}
+
+cdunalias() {
+    local name="$1"
+    (( $# == 1 )) || { print -u2 "usage: cdunalias NAME"; return 2; }
+    (( ${+CDS_ALIASES[$name]} )) || { print -u2 "cd-smart: no such alias: $name"; return 1; }
+    (( ${+_cds_zshrc_aliases[$name]} )) && { print -u2 "cd-smart: $name is defined in your zshrc; remove it there"; return 1; }
+    unset "CDS_ALIASES[$name]"
+    _cds_save_aliases
+}
+
+cdsmart-help() {
+    print 'cd-smart: cd learns visited directories; cdi searches them with fzf.'
+    print 'Commands: cdlearn PATH, cdforget PATH, cdlist, cdalias [NAME [PATH]], cdunalias NAME'
+}
diff --git a/.zsh/common.zsh b/.zsh/common.zsh
new file mode 100644 (file)
index 0000000..7295d54
--- /dev/null
@@ -0,0 +1,55 @@
+# Shared Zsh settings
+
+setopt correct extendedglob nocaseglob rcexpandparam nocheckjobs
+setopt numericglobsort nobeep appendhistory histignorealldups autocd prompt_subst
+
+HISTFILE="$HOME/.zsh_history"
+HISTSIZE=100000
+SAVEHIST=100000
+setopt share_history hist_ignore_all_dups hist_ignore_space hist_reduce_blanks extended_history
+
+export LS_COLORS='di=34:ln=35:so=32:pi=33:ex=31:bd=34;46:cd=34;43'
+
+# Completion
+if command -v brew >/dev/null 2>&1; then
+    FPATH="$(brew --prefix)/share/zsh/site-functions:${FPATH}"
+fi
+autoload -Uz compinit
+if [[ -n ~/.zcompdump(#qN.mh+24) ]]; then compinit; else compinit -C; fi
+zstyle ':completion:*' menu no
+zstyle ':completion:*' matcher-list 'm:{a-zA-Z}={A-Za-z}' 'r:|[._-]=* r:|=*' 'l:|=* r:|=*'
+zstyle ':completion:*' list-colors "${(s.:.)LS_COLORS}"
+zstyle ':completion:*' group-name ''
+zstyle ':completion:*:descriptions' format '[%d]'
+zstyle ':completion:*:messages' format '[%d]'
+zstyle ':completion:*:warnings' format '[no matches]'
+zstyle ':completion:*' verbose yes use-cache on
+zstyle ':completion:*' cache-path "$HOME/.zsh/cache"
+setopt complete_in_word auto_menu
+
+# Prompt
+autoload -Uz vcs_info
+precmd_vcs_info() { vcs_info }
+precmd_functions+=(precmd_vcs_info)
+zstyle ':vcs_info:git:*' formats '%b'
+PROMPT='%F{cyan}%n@%m%f %F{240}%1~%f %F{cyan}❯%f '
+RPROMPT='%F{green}${vcs_info_msg_0_} %F{240}%*%f'
+
+bindkey -e
+WORDCHARS=${WORDCHARS//\/[&.;]}
+
+# Smart cd; its database remains local to this machine.
+export CDS_DB="$HOME/.local/state/cd-smart.tsv"
+source "$HOME/.zsh/cd-smart.zsh"
+
+alias grep='grep --color'
+alias cfg='git --git-dir="$HOME/.cfg" --work-tree="$HOME"'
+export PATH="$HOME/bin:$HOME/.local/bin:$PATH"
+
+# fzf, when installed
+if command -v fzf >/dev/null 2>&1; then
+    export FZF_CTRL_R_OPTS="--height=~15 --layout=reverse --no-info --no-separator --no-scrollbar --prompt='history › ' --scheme=history --with-nth 2.."
+    source <(fzf --zsh)
+fi
+
+# fzf-tab and optional plugins are loaded by the platform file when available.
diff --git a/.zsh/fzf-tab.zsh b/.zsh/fzf-tab.zsh
new file mode 100644 (file)
index 0000000..b77e1d0
--- /dev/null
@@ -0,0 +1,29 @@
+# fzf-tab: fuzzy tab completion; preview hidden by default, Ctrl-/ toggles it
+zstyle ':fzf-tab:*' fzf-flags --height=50% --preview-window=right:55%:wrap:hidden --bind=ctrl-/:toggle-preview
+zstyle ':fzf-tab:*' switch-group '<' '>'
+
+# Preview commands run in a separate shell. Quote the fzf-tab variables there,
+# rather than expanding them while this configuration is sourced.
+zstyle ':fzf-tab:complete:cd:*' fzf-preview 'CLICOLOR_FORCE=1 ls -1G -- "$realpath"'
+zstyle ':fzf-tab:complete:(cat|bat|less|vim|subl|code|cp|mv|rm|open):*' fzf-preview \
+  'if [[ -d "$realpath" ]]; then
+     CLICOLOR_FORCE=1 ls -1G -- "$realpath"
+   elif grep -qI . -- "$realpath" 2>/dev/null; then
+     bat --color=always --style=numbers --line-range=:200 -- "$realpath"
+   else
+     file -b -- "$realpath"
+   fi'
+zstyle ':fzf-tab:complete:git-(add|diff|restore):*' fzf-preview 'git diff --color=always -- $word | head -200'
+zstyle ':fzf-tab:complete:git-(checkout|switch|merge|rebase|log):*' fzf-preview 'git log --oneline --color=always -20 $word 2>/dev/null'
+zstyle ':fzf-tab:complete:(-parameter-|-brace-parameter-|export|unset):*' fzf-preview 'echo ${(P)word}'
+unset _ls
+
+# Complete full Git file paths instead of one directory at a time.
+autoload -Uz +X _multi_parts
+functions -c _multi_parts _multi_parts_orig
+_multi_parts() {
+    [[ $curcontext == *:git-* ]] || { _multi_parts_orig "$@"; return; }
+    local -a opts
+    while [[ $1 != - ]]; do opts+=("$1"); shift; done
+    compadd "${opts[@]}" -- "${(@P)3}"
+}
diff --git a/.zsh/linux.zsh b/.zsh/linux.zsh
new file mode 100644 (file)
index 0000000..aa2186d
--- /dev/null
@@ -0,0 +1,19 @@
+# Linux-specific Zsh settings
+
+if command -v ls >/dev/null 2>&1; then
+    alias ls='ls --color=auto'
+fi
+
+if command -v fzf-tab >/dev/null 2>&1; then
+    source "$(dirname "$(command -v fzf-tab)")/../share/fzf-tab/fzf-tab.zsh" 2>/dev/null
+fi
+
+[[ -r "$HOME/.zsh/fzf-tab.zsh" ]] && source "$HOME/.zsh/fzf-tab.zsh"
+
+for plugin in \
+    "$HOME/.local/share/zsh-autosuggestions/zsh-autosuggestions.zsh" \
+    /usr/share/zsh-autosuggestions/zsh-autosuggestions.zsh \
+    "$HOME/.local/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh" \
+    /usr/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh; do
+    [[ -r "$plugin" ]] && source "$plugin"
+done
diff --git a/.zsh/local.zsh.example b/.zsh/local.zsh.example
new file mode 100644 (file)
index 0000000..10c5c1c
--- /dev/null
@@ -0,0 +1,10 @@
+# Copy to ~/.zsh/local.zsh for machine- or work-specific settings.
+# Keep this file out of shared/public dotfiles.
+
+# Examples:
+# AWS configuration is usually best managed in ~/.aws/config.
+# If a machine-wide default is needed, set it here with generic values:
+# export AWS_PROFILE=your-profile
+# export AWS_REGION=your-region
+#
+# Put any other machine-specific paths or environment variables here.
diff --git a/.zsh/macos.zsh b/.zsh/macos.zsh
new file mode 100644 (file)
index 0000000..6361021
--- /dev/null
@@ -0,0 +1,18 @@
+# macOS-specific Zsh settings
+
+alias ls='ls -G'
+alias x86='arch -x86_64 zsh'
+
+if command -v brew >/dev/null 2>&1; then
+    eval "$(brew shellenv)"
+    if [[ -r "$(brew --prefix)/opt/fzf-tab/share/fzf-tab/fzf-tab.zsh" ]]; then
+        source "$(brew --prefix)/opt/fzf-tab/share/fzf-tab/fzf-tab.zsh"
+        [[ -r "$HOME/.zsh/fzf-tab.zsh" ]] && source "$HOME/.zsh/fzf-tab.zsh"
+    fi
+    [[ -r "$(brew --prefix)/share/zsh-autosuggestions/zsh-autosuggestions.zsh" ]] && \
+        source "$(brew --prefix)/share/zsh-autosuggestions/zsh-autosuggestions.zsh"
+    [[ -r "$(brew --prefix)/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh" ]] && \
+        source "$(brew --prefix)/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh"
+fi
+
+[[ -r "$HOME/.docker/init-zsh.sh" ]] && source "$HOME/.docker/init-zsh.sh"
diff --git a/.zshrc b/.zshrc
index 9fcca49c1f0581a3dd90ef5f393c4c8517e999fa..63fc81e6cb36dfb538ea14e26dcc84b6787f2cb0 100644 (file)
--- a/.zshrc
+++ b/.zshrc
@@ -1,20 +1,11 @@
-if [[ "$OSTYPE" == "darwin"* ]]; then
+# Shared entry point
+source "$HOME/.zsh/common.zsh"
 
-    # OSX
-    if [[ -e ~/.zsh-darwin ]]; then
-        source ~/.zsh-darwin
-    fi
+if [[ "$OSTYPE" == darwin* ]]; then
+    [[ -r "$HOME/.zsh/macos.zsh" ]] && source "$HOME/.zsh/macos.zsh"
+elif [[ "$OSTYPE" == linux* ]]; then
+    [[ -r "$HOME/.zsh/linux.zsh" ]] && source "$HOME/.zsh/linux.zsh"
+fi
 
-else
-    
-    # Linux or other
-    if [[ -e ~/.zsh-config ]]; then
-        source ~/.zsh-config
-    fi
-
-    # Use zsh prompt
-    if [[ -e ~/.zsh-prompt ]]; then
-        source ~/.zsh-prompt
-    fi
-
-fi
\ No newline at end of file
+# Optional machine- or work-specific settings.
+[[ -r "$HOME/.zsh/local.zsh" ]] && source "$HOME/.zsh/local.zsh"