diff options
Diffstat (limited to 'dotfiles/system/.zsh/lib/zsh-ls-colors')
| -rw-r--r-- | dotfiles/system/.zsh/lib/zsh-ls-colors/LICENSE | 21 | ||||
| -rw-r--r-- | dotfiles/system/.zsh/lib/zsh-ls-colors/README.md | 114 | ||||
| -rwxr-xr-x | dotfiles/system/.zsh/lib/zsh-ls-colors/demo | 65 | ||||
| -rw-r--r-- | dotfiles/system/.zsh/lib/zsh-ls-colors/ls-colors.zsh | 186 |
4 files changed, 386 insertions, 0 deletions
diff --git a/dotfiles/system/.zsh/lib/zsh-ls-colors/LICENSE b/dotfiles/system/.zsh/lib/zsh-ls-colors/LICENSE new file mode 100644 index 0000000..940b4c2 --- /dev/null +++ b/dotfiles/system/.zsh/lib/zsh-ls-colors/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 Gamma + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/dotfiles/system/.zsh/lib/zsh-ls-colors/README.md b/dotfiles/system/.zsh/lib/zsh-ls-colors/README.md new file mode 100644 index 0000000..7736ce6 --- /dev/null +++ b/dotfiles/system/.zsh/lib/zsh-ls-colors/README.md @@ -0,0 +1,114 @@ +# zsh-ls-colors + + + +A zsh library to use `LS_COLORS` in scripts or other plugins. + +For a simple demo, see the `demo` script in this repo. + +For more advanced usage, +instructions are located at top of the source files for `from-mode` and `from-name`. +If a use case isn't adequately covered, +please open an issue! + +## Using zsh-ls-colors in a plugin + +You can use this as a submodule or a subtree. + +### submodule: + +```sh +# Add (only once) +git submodule add git://github.com/xPMo/zsh-ls-colors.git ls-colors +git commit -m 'Add ls-colors as submodule' + +# Update +cd ls-colors +git fetch +git checkout origin/master +cd .. +git commit ls-colors -m 'Update ls-colors to latest' +``` + +### Subtree: + +```sh +# Initial add +git subtree add --prefix=ls-colors/ --squash -m 'Add ls-colors as a subtree' \ + git://github.com/xPMo/zsh-ls-colors.git master + +# Update +git subtree pull --prefix=ls-colors/ --squash -m 'Update ls-colors to latest' \ + git://github.com/xPMo/zsh-ls-colors.git master + + +# Or, after adding a remote: +git remote add ls-colors git://github.com/xPMo/zsh-ls-colors.git + +# Initial add +git subtree add --prefix=ls-colors/ --squash -m 'Add ls-colors as a subtree' ls-colors master + +# Update +git subtree pull --prefix=ls-colors/ --squash -m 'Update ls-colors to latest' ls-colors master +``` + +### Function namespacing + +Since functions are a public namespace, +this plugin allows you to customize the preifix for your plugin: + +```zsh +# load functions as my-lscolors::{init,match-by,from-name,from-mode} +source ${0:h}/ls-colors/ls-colors.zsh my-lscolors +``` + +### Parameter namespacing + +While indirect parameter expansion exists with `${(P)var}`, +it doesn't play nicely with array parameters. + +There are multiple strategies to prevent unnecessary re-parsing: + +```zsh +# Call once when loading. +# Pollutes global namespace but prevents re-parsing +ls-color::init +``` + +```zsh +# Don't call init at all and only use ::match-by. +# Doesn't pollute global namespace but reparses LS_COLORS on every call +ls-color::match-by $file lstat +``` + +```zsh +# Initialize within a scope with local parameters. +# Best for not polluting global namespace when multiple filenames need to be parsed. +(){ + local -A namecolors modecolors + ls-color::init + + for arg; do + ... + done +} +``` + +```zsh +# Serialize: +typeset -g LS_COLORS_CACHE_FILE=$(mktemp) +(){ + local -A namecolors modecolors + ls-color::init + typeset -p modecolors namecolors >| $LS_COLORS_CACHE_FILE + zcompile $LS_COLORS_CACHE_FILE +} + +my-function(){ + local -A namecolors modecolors + source $LS_COLORS_CACHE_FILE + + ... +} +``` + diff --git a/dotfiles/system/.zsh/lib/zsh-ls-colors/demo b/dotfiles/system/.zsh/lib/zsh-ls-colors/demo new file mode 100755 index 0000000..a5e468d --- /dev/null +++ b/dotfiles/system/.zsh/lib/zsh-ls-colors/demo @@ -0,0 +1,65 @@ +#!/usr/bin/env zsh +# set $0 (ref: zdharma.org/Zsh-100-Commits-Club/Zsh-Plugin-Standard.html#zero-handling) +0="${${ZERO:-${0:#$ZSH_ARGZERO}}:-${(%):-%N}}" +0="${${(M)0:#/*}:-$PWD/$0}" + +# load library functions +source ls-colors.zsh '' + +# to name the functions with a different namespace +# call source with a different argument +#source my-plugin::ls + +# init (sets modecolors and namecolors) +# You have options. Either you can pollute global namespace: +ls-color::init +# Or you can have ::match-by re-parse colors on every call +: # (do nothing) +# Or if you have multiple calls, you can parse colors once for a scope: +(){ + local -A modecolors namecolors + ls-color::init + + for arg; do + ls-color::match-by $arg lstat + : do something else + done +} + + +# colors can also be added for other globs after init as well: +namecolors[*.md]='01' # bold markdown files + +# EXTENDED_GLOB is enabled when matching, so things like this are possible: +namecolors[(#i)(*/|)license(|.*)]='04' # underline LICENSE, or license.txt, or similar + +local file reply +# color each file in the argument list +for file; do + ls-color::match-by $file all + # point to symlink resolution if it exists + print '\e['$reply[1]'m'$file'\e[0m'${reply[2]:+' → \e['$reply[3]'m'$reply[2]'\e[0m'} +done + +# ======================= +# Alternate manual method: +for file; do + ls-color::match-by $file lstat follow + if [[ $reply[2] ]]; then + # This is a symlink + symlink_color=$reply[1] + # If broken, use link color for destination + resolved_color=$reply[1] + resolved=$reply[2] + if [[ -e $file ]]; then + # Not broken, update destination color + ls-color::match-by $file stat + resolved_color=$reply[1] + fi + print '\e['$symlink_color'm'$file'\e[0m → \e['$resolved_color'm'$resolved'\e[0m' + else + # This is not a symlink + print '\e['$reply[1]'m'$file'\e[0m' + fi +done + diff --git a/dotfiles/system/.zsh/lib/zsh-ls-colors/ls-colors.zsh b/dotfiles/system/.zsh/lib/zsh-ls-colors/ls-colors.zsh new file mode 100644 index 0000000..276a7bb --- /dev/null +++ b/dotfiles/system/.zsh/lib/zsh-ls-colors/ls-colors.zsh @@ -0,0 +1,186 @@ +#!/usr/bin/env zsh + +# set the prefix for all functions +local pfx=${1:-'ls-color'} + +# {{{ From mode +# Usage: +# $1: filename +# $2: The value of struct stat st_mode +# If empty, modecolors lookup will be skipped +# $3: (If symlink) The value of struct stat st_mode +# for the target of $1's symlink. If unset, +# interpret as a broken link. +# Sets REPLY to the console code +${pfx}::from-mode () { + + emulate -L zsh + setopt cbases octalzeroes extendedglob + + [[ -z $2 ]] && return 1 + + local -i reg=0 + local -a codes + + local -i st_mode=$(($2)) + # See man 7 inode for more info + # file type + case $(( st_mode & 0170000 )) in + $(( 0140000 )) ) codes=( $modecolors[so] ) ;; + $(( 0120000 )) ) # symlink, special handling + if ! (($+3)); then + REPLY=$modecolors[or] + elif [[ $modecolors[ln] = target ]]; then + "$0" "$1" "${@:3}" + else + REPLY=$modecolors[ln] + fi + return + ;; + $(( 0100000 )) ) codes=( ); reg=1 ;; # regular file + $(( 0060000 )) ) codes=( $modecolors[bd] ) ;; + $(( 0040000 )) ) codes=( $modecolors[di] ) ;; + $(( 0020000 )) ) codes=( $modecolors[cd] ) ;; + $(( 0010000 )) ) codes=( $modecolors[pi] ) ;; + esac + + # setuid/setgid/sticky/other-writable + (( st_mode & 04000 )) && codes+=( $modecolors[su] ) + (( st_mode & 02000 )) && codes+=( $modecolors[sg] ) + (( ! reg )) && case $(( st_mode & 01002 )) in + # sticky + $(( 01000 )) ) codes+=( $modecolors[st] ) ;; + # other-writable + $(( 00002 )) ) codes+=( $modecolors[ow] ) ;; + # other-writable and sticky + $(( 01002 )) ) codes+=( $modecolors[tw] ) ;; + esac + + # executable + if (( ! $#codes )); then + (( st_mode & 0111 )) && codes+=( $modecolors[ex] ) + fi + + # return nonzero if no matching code + [[ ${REPLY::=${(j:;:)codes}} ]] +} # }}} +# {{{ From name +# Usage: +# $1: filename +# +# Sets REPLY to the console code +${pfx}::from-name () { + + emulate -L zsh + setopt extendedglob + + # Return non-zero if no keys match + [[ ${REPLY::=$namecolors[(k)$1]} ]] +} # }}} +# {{{ Init +# WARNING: initializes namecolors and modecolors in global scope +${pfx}::init () { + emulate -L zsh + + # Use $1 if provided, otherwise use LS_COLORS + # Use LSCOLORS on BSD + local LS_COLORS=${1:-${LS_COLORS:-$LSCOLORS}} + + # read in LS_COLORS + typeset -gA namecolors=(${(@s:=:)${(@s.:.)LS_COLORS}:#[[:alpha:]][[:alpha:]]=*}) + typeset -gA modecolors=(${(@Ms:=:)${(@s.:.)LS_COLORS}:#[[:alpha:]][[:alpha:]]=*}) +} +# }}} +# {{{ Match by +# Usage: +# $1: filename +# Optional (must be $2): g[lobal]: Use existing stat | lstat in parent scope +# ${@:2}: Append to reply: +# - l[stat] : Look up using lstat (don't follow symlink), if empty match name +# - s[tat] : Look up using stat (do follow symlink), if empty match name +# - n[ame] : Only match name +# - f[ollow]: Get resolution path of symlink +# - L[stat] : Same as above but don't match name +# - S[tat] : Same as above but don't match name +# - a[ll] : If a broken symlink: lstat follow lstat +# : If a symlink : lstat follow stat +# : Otherwise : lstat +# - A[ll] : If a broken symlink: Lstat follow Lstat +# : If a symlink : Lstat follow Stat +# : Otherwise : Lstat +# +# or returns non-zero +${pfx}::match-by () { + emulate -L zsh + setopt extendedglob cbases octalzeroes + + local arg REPLY name=$1 pfx=${0%::match-by} + shift + + # init in local scope if not using global params + if ! [[ -v namecolors && -v modecolors ]]; then + local -A namecolors modecolors + ${pfx}::init + fi + + if [[ ${1:l} = (g|global) ]]; then + shift + else + local -a stat lstat + declare -ga reply=() + fi + + zmodload -F zsh/stat b:zstat + for arg; do + case ${arg[1]:l} in + n|name) + ${pfx}::from-name $name + reply+=("$REPLY") + ;; + l|lstat) + (($#lstat)) || zstat -A lstat -L $name || return 1 + if ((lstat[3] & 0170000 )); then + # follow symlink + (($#stat)) || zstat -A stat $name 2>/dev/null + fi + ${pfx}::from-mode "$name" "$lstat[3]" $stat[3] + if [[ $REPLY || ${2[1]} = L ]]; then + reply+=("$REPLY") + else # fall back to name + "$0" "$name" g n + fi + ;; + s|stat) + (($#stat)) || zstat -A stat $name || return 1 + ${pfx}::from-mode $name $stat[3] + reply+=("$REPLY") + if [[ $REPLY || ${arg[1]} = S ]]; then + reply+=("$REPLY") + else # fall back to name + "$0" "$name" g n + fi + ;; + f|follow) + (($#lstat)) || zstat -A lstat -L $name || return 1 + reply+=("$lstat[14]") + ;; + a|all) + # Match case + "$0" "$name" g ${${${arg[1]%a}:+L}:-l} + # won't append if empty + reply+=($lstat[14]) + # $stat[14] will be empty if not a symlink + if [[ $lstat[14] ]]; then + if [[ -e $name ]]; then + "$0" "$name" g ${${${arg[1]%a}:+S}:-s} + else + reply+=($reply[-2]) + fi + fi + ;; + *) return 2 ;; + esac + done +} +# }}} +# vim: set foldmethod=marker: |
