#!/usr/bin/env zsh ## Author: druckdev ## Created: 2019-10-21 ## ## An ls wrapper that adds the -A flag (show hidden files except . and ..) when ## there are no visible files or the directory matches the pattern (POSIX ERE) ## defined in $LS_SHOW_ALL_DIRS. # Do not include hidden files when globbing and expand to an empty string # instead of giving an error when no files match. builtin emulate -L zsh -o no_glob_dots -o null_glob # Overwrite here or before calling to change the directories in which hidden # files should always be listed. builtin local LS_SHOW_ALL_DIRS=${LS_SHOW_ALL_DIRS:-"dotfiles|\.config"} # ANSI escape codes to print in color builtin local ansi_bold_red=$'\e[31;1m' builtin local ansi_reset=$'\e[m' builtin local LS_COMMAND=ls # Use GNU version if available under MacOS if [[ $OSTYPE =~ darwin ]] && (( $+commands[gls] )); then LS_COMMAND=gls fi builtin local non_existing=0 no_flags_from_here=0 builtin local -a dirs files # Pop files and folders from arguments and put them in the corresponding array, # keep flags (ls takes flag-arguments behind a '=') and warn when non-existing # arguments were passed. for arg in "$@"; do shift if [[ $arg = '--' ]] && (( ! no_flags_from_here )); then no_flags_from_here=1 elif [[ ${arg#-} != $arg ]] && (( ! no_flags_from_here )); then set -- "$@" "$arg" elif [[ -d $arg ]]; then dirs+="$arg" elif [[ -e $arg || -L $arg ]]; then files+="$arg" else printf >&2 "%s%s: cannot access '%s': No such file or directory%s\n" \ "$ansi_bold_red" "$0" "$arg" "$ansi_reset" non_existing=1 fi done # Print working directory when only flags were given as arguments. if ! (( $#dirs + $#files + $non_existing )); then dirs+=. elif ! (( $#dirs + $#files )); then return 2 fi # Just pass everything to ls when the -d flag is given since then the -A flag # makes no difference. # Remove all long options because `getopts` cannot handle those and sees for # example -d in --group-directories-first. Remove the resulting empty strings # afterwards since that confuses `getopts` apparently. builtin local -a all_opts empty all_opts=("$@") empty=("") set -- "${(@)${(@)all_opts//--*}:|empty}" while getopts d flag 2>/dev/null; do [[ $flag = "d" ]] || continue command $LS_COMMAND "$all_opts[@]" -- "$files[@]" "$dirs[@]" return done # Restore options. set -- "$all_opts[@]" unset all_opts empty builtin local separator="" # Print files. if (( $#files )); then command $LS_COMMAND "$@" -- "$files[@]" # Print a newline between files and folder segment. separator="\n" fi # Print directories. builtin local all_flag builtin local -a content for dir in ${(@f)dirs}; do content=( "$dir"/* ) # If the directory contains no visible files or it matches a pattern, then # show hidden files when listing if (( ! $#content )) || [[ ${dir:A} =~ ${LS_SHOW_ALL_DIRS:-^$} ]]; then all_flag="-A" else all_flag= fi # If there are multiple items to list, print a newline (if ls was already # executed) followed by the dir-name. ! (( $#dirs + $#files + $non_existing - 1 )) || echo "$separator$dir:" # Print directory. $all_flag has to be unquoted else ls will fail. command $LS_COMMAND "$@" $all_flag -- "$dir" separator="\n" done