Files
dotfiles/.config/zsh/autoload/ls-show-hidden
Julian Prein 82fc29f9c0 zsh:ls-show-hidden: Fix non_existing arg parsing
`ls` prints a warning if a non-flag argument is specified that does not
exist.

Previously ls-show-hidden assumed that all arguments that are neither a
directory nor an otherwise existing file are flags. As all flags are
passed to the ls call the warning still got printed, but this also lead
to the current working directory (i.e. `.`) being added to the
directories to process.

If `ls` expects an argument to a flag, it is always passed in the format
`--flag=arg` where the equal sign is mandatory. This makes it possible
to simply filter out all flags (and their arguments) from the other
arguments without needing to know which flags take an argument and which
don't.

Fix this behaviour by printing the same warning as `ls` does when an
argument is neither a flag, nor a directory nor an existing file. Also
only add `.` to the directories if *really* only flags were passed.
2022-09-27 01:05:42 +02:00

98 lines
3.0 KiB
Bash
Executable File

#!/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
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#-} != $arg ]]; then
set -- "$@" "$arg"
elif [[ -d "$arg" ]]; then
dirs+="$arg"
elif [[ -e "$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+=.
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} - 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