Skip to content

Instantly share code, notes, and snippets.

@pwwang
Last active July 5, 2018 19:55
Show Gist options
  • Save pwwang/5ad96bbfb034a91af851c411f0b12a23 to your computer and use it in GitHub Desktop.
Save pwwang/5ad96bbfb034a91af851c411f0b12a23 to your computer and use it in GitHub Desktop.
Fish cd with symbolic path kept.
# for prevd and nextd, cd -
function __fish_move_last -d "Move the last element of a directory history from src to dest"
set -l src $argv[1]
set -l dest $argv[2]
set -l size_src (count $$src)
if test $size_src = 0
# Cannot make this step
printf (_ "Hit end of history…\n")
return 1
end
# Append current dir to the end of the destination
set -g (echo $dest) $$dest $PWD2
set ssrc $$src
# Change dir to the last entry in the source dir-hist
builtin cd $ssrc[$size_src]
set PWD2 $ssrc[$size_src]
# Keep all but the last from the source dir-hist
set -e (echo $src)\[$size_src]
# All ok, return success
return 0
end
function __is_parent -S -a path1 path2 -d 'Tell if path1 is the parent of path2 directly'
# /home/user = /home/user
if test "$path1" = "$path2"
echo -n 'yes'
return
end
# /home/user /home/user/a/b
set -l len1 (string length "$path1/")
if test (string sub -s 1 -l $len1 "$path2") = "$path1/"
echo -n 'yes'
return
end
echo -n 'no'
end
function __type_of_path -S -d 'Get the type of path: abs, rel or home'
# test home fiist
# empty is also going to home
if test -z "$argv" -o (__is_parent "$HOME" "$argv") = 'yes' -o (__is_parent '~' "$argv") = 'yes'
echo "home"
else if test (string sub -s 1 -l 1 "$argv") = "/"
echo "abs"
else
echo "rel"
end
end
function __sanitize_path -S -d 'Sanitize concatenated path'
set -l ret "$argv"
# remove repeated /
set ret (string replace -ar '/{2,}' '/' -- "$ret")
# remove /./
set ret (string replace -ar '/\./' '/' -- "$ret")
# change a/b/c/../d to a/b/d
while string match -q -r '[^/]+/\.\.' "$ret"
set ret (string replace -r '[^/]+/\.\.' '' -- "$ret")
set ret (string replace -ar '/{2,}' '/' -- "$ret")
end
# remove repeated /
# change a/c/. to a/c
set ret (string replace -ar '/\.$' '' -- $ret)
#set ret (string replace -ar '\./' '' -- $ret)
# remove last /
set ret (string replace -r '/$' '' -- $ret)
echo -n $ret
end
# Change directory and keep symbols in path
function cd --description "Change directory"
set -l MAX_DIR_HIST 25
set -l previous
set -l cd_status
if test (count $argv) -gt 1
printf "%s\n" (_ "Too many args for cd command")
return 1
end
# Skip history in subshells.
if status --is-command-substitution
builtin cd $argv
return $status
end
# Avoid set completions.
if test "$PWD2"
set previous $PWD2
else
set -xg PWD2
set previous $PWD
end
if test "$argv" = "-"
if test "$__fish_cd_direction" = "next"
nextd
else
prevd
end
return $status
end
switch (__type_of_path $argv)
case "abs"
set PWD2 (__sanitize_path "$argv")
case "rel"
set PWD2 (__sanitize_path "$previous/$argv")
if test -d "$PWD2"
# if realpath is a different directory, use the real one
test (realpath "$PWD2") != (realpath "$PWD/$argv")
and set PWD2 (__sanitize_path "$PWD/$argv")
else
set PWD2 (__sanitize_path "$PWD/$argv")
end
case "home"
test -z "$argv"
and set PWD2 "$HOME"
or set PWD2 (__sanitize_path "$argv")
end
builtin cd $PWD2
set cd_status $status
if test $cd_status -eq 0 -a "$PWD" != (realpath "$previous")
set -q dirprev
or set -l dirprev
set -q dirprev[$MAX_DIR_HIST]
and set -e dirprev[1]
# nextd, prevd cannot recogize ~
set -g dirprev $previous
set -e dirnext
set -g __fish_cd_direction prev
else if test $cd_status -ne 0
set PWD2 $previous
end
return $cd_status
end
@pwwang
Copy link
Author

pwwang commented May 11, 2018

Introduction

In fish shell, $PWD or pwd is always absolute path. But sometimes this is annoying:

~> cd
~> pwd
/home/user
~> ln -s /this/is/a/very/very/very/very/very/very/long/path .
~> ll -1
lrwxrwxrwx 1 user group   87 Apr 18 14:23 path -> /this/is/a/very/very/very/very/very/very/long/path
~> cd path
/this/is/a/very/very/very/very/very/very/long/path>
# but in most cases we need:
# ~/path>

This function overwrites fish's cd function and save the shortened path in $PWD2 (as $PWD is readonly 😞 )
You can use it in the any powerline themes.

It also works when you jump back and forth:

~> pwd
/home/user
~> cd path
~/path> cd -
~> cd -
~/path> prevd
~> nextd
~/path>

How does it work?

Basically, it depends on the $argv you pass to cd. If it is an absolute path, then go to that path (assign the path to $PWD2);, if it is a path with $HOME prefix, then replace the prefix with ~, otherwise, it should be a relative path, then concatenate it with current path.

Install

Download this file and put it in ~/.config/fish/functions/ or via fisherman:
fisher https://gist.github.com/pwwang/5ad96bbfb034a91af851c411f0b12a23

Compatible fish themes

fisher pwwang/theme-bobthefish

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment