Open PrincessRTFM opened 4 years ago
Hi, thanks for the detailed description.
As a bash user, I'm not familiar with this "directory aliasing" feature. Do you have a working version for zsh? If yes, we can put the zsh-specific login behind an if
like this one
Unfortunately, I don't think I have the skill to put that together. I can spend a while testing things out and see what I can come up with though.
The quick rundown of zsh's static named directories feature is that you can define an alias using hash -d "alias_name=target_directory"
much like the standard alias
command. However, instead of making an actual alias, it extends the ~username
-style directory expansion so that you can use ~your_alias_here
at the start of an unquoted directory path, like home expansion, and it will expand to the targeted location. For instance, in my example above, I directory-aliased ~steam
to my steam games installation directory, which allows me to cd ~steam
instead of using the long path.
But as it happens, this doesn't actually need to be zsh-specific, except in how the current path is acquired. The print
builtin for zsh might not be compatible with bash, dash, fish, etc, but once the path is retrieved, splitting it is shell-agnostic - just don't treat a leading character of ~
specially.
Okay, I may have been wrong about not being able to make a working version for my request. I've narrowed things down a little, and I think the core fix is that $first_char
shouldn't be... well, that. Instead of paying special attention to the first character like that, check the first path segment, before the very first /
- either there's nothing there, so /
is the first thing, or there's something there which suggests a directory alias. Instead of using $first_char
, maybe use $leader
or something, for clarity. I'm doing some scratchwork right now, and I think this looks promising. If it has to cut out (because it hit the $dir_limit
) then there's no issue, it can still just set leader="$truncation"
as now, but otherwise it would be printing the whole "leading text" which would be either /
or (presumably) a tilde-prefixed directory alias. I'll tinker a little more and tell you what I can figure out.
Hm. Another thing I notice, though I'm not sure how to solve, is that the truncation string is used even if the only piece being chopped off is a ~
or a /
, which strikes me a little odd. Is that just me? I feel like ~/dir1/dir2
shouldn't be formatted into <truncation> | dir1 | dir2
in the prompt, since it's the same number of path segments either way.
Okay, I think I fixed the original issue, and also found a workaround for the truncation thing. I'll leave it up to you whether you think the truncation thing is desired behaviour or not, but here's the output of my test showing the difference so you can get a better idea what I mean. If you want to do some more testing yourself, here's the code I used for my tests:
#!/bin/bash
# Your current method of getting the cwd
cwd="${PWD/#$HOME/"~"}"
formatted_cwd=""
part_count=0
dir_sep=" | " # Just for testing, use a pipe instead
leader="" # NOTE: this replaces first_char for clarity!
dir_limit=2 # I made this a number instead of a string, too
truncation="…"
# If you want to test a specific path, you can pass it to this testing script
[[ -n "$*" ]] && cwd="$*"
# Liberally applied debug statements
printf "Operating on '%s'\n\n" "$cwd"
# Grab the first character, same as the original did it
[[ -n ${ZSH_VERSION-} ]] && leader=$cwd[1,1] || leader=${cwd::1}
# If the first character is a tilde, grab the entire first segment - $cwd, with the LONGEST TRAILING string matching "/*" removed
[[ "$leader" == "~" ]] && leader="${cwd%%/*}"
# The original loop condition works fine
while [[ "$cwd" == */* && "$cwd" != "/" ]]; do
# Gets the value of $cwd with the longest starting string matching "*/" removed
part="${cwd##*/}"
printf "Handling part %d: '%s'\n" $(($part_count + 1)) "$part"
# Gets the value of $cwd with the shortest trailing string matching "/*" removed
cwd="${cwd%/*}"
printf "Remainder: '%s'\n" "$cwd"
formatted_cwd="$dir_sep$part$formatted_cwd"
printf "Currently handled: '%s'\n" "$formatted_cwd"
part_count=$((part_count+1))
printf "\n"
[[ $part_count -eq $dir_limit ]] && leader="$truncation" && break
done
# If we're truncating, but the remaining path has ONLY ONE SEGMENT, just use the remaining path
[[ "$leader" == "$truncation" && "$cwd" != */* ]] && leader="$cwd"
printf "First character: '%s'\n" "$leader"
printf "Formatted CWD: '%s'\n" "$formatted_cwd"
printf "Remainder: '%s'\n" "$cwd"
printf "Final product: '%s%s'\n" "$leader" "$formatted_cwd"
As you can see, it was tested using plain bash, but running it through zsh ./prompts "~steam/Stardew Valley/Mods"
produced the same output. I don't have any other shells, but given that I only used syntax that was already present in the original code, I would reasonably expect them to work too, unless the original code didn't work on them.
I've manually edited my promptline exported file to set the __promptline_cwd
function to the tweaked version, and using this code seems to work perfectly:
function __promptline_cwd {
local dir_limit=2
local truncation="…"
local leader
local part_count=0
local formatted_cwd=""
local dir_sep=" "
local tilde="~"
local cwd="${PWD/#$HOME/$tilde}"
[[ -n ${ZSH_VERSION-} ]] && cwd=`print -P "%~"` # ADDITION: get zsh's cwd with its own expansion, to apply static named directory aliases
# get first char of the path, i.e. tilde or slash
[[ -n ${ZSH_VERSION-} ]] && leader=$cwd[1,1] || leader=${cwd::1}
[[ "$leader" == "~" ]] && leader="${cwd%%/*}" # ADDITION: if the cwd starts with a tilde, grab the entire first segment in case of static named directory aliases
# CHANGE: do not remove leading tilde
#cwd="${cwd#\~}"
while [[ "$cwd" == */* && "$cwd" != "/" ]]; do
# pop off last part of cwd
local part="${cwd##*/}"
cwd="${cwd%/*}"
formatted_cwd="$dir_sep$part$formatted_cwd"
part_count=$((part_count+1))
[[ $part_count -eq $dir_limit ]] && leader="$truncation" && break
done
[[ "$leader" == "$truncation" && "$cwd" != */* ]] && leader="$cwd" # ADDITION: if the truncated path is only one segment, don't truncate it
printf "%s" "$leader$formatted_cwd"
}
The
cwd
slice directly replaces$HOME
with~
, which is nice but unfortunately ignores zsh static named directories (zsh manual, section 14.7 Filename Expansion, see subsection 2 Static named directories). Usingprint -P "%~"
in zsh provides the cwd with such directories replaced, but this cannot simply be used in place of the existing line (local cwd="${PWD/#$HOME/$tilde}"
) because then any static named directory in the current directory path will be replaced with a single tilde, not including the static name:It looks like the function was written with the assumption that if the current path starts with a
~
then it must indicate the user's home directory, which is reasonable given the way the current path is acquired. However, this means that one of the shell's built-in features (a default prompt will expand to use static directory names) is unavailable. Would it be possible to make the function intelligently determine if static named directories are in use from the shell-provided expanded prompt instead?