github.com/kujenga/gash@v0.0.0-20230321210531-bed9f4dc58b3/cmd/smart-pwd/main.go (about) 1 // Command smart-pwd intelligently prints the current working directory. 2 // 3 // The inital intended use case is a shell prompt, so speed is important. The 4 // intended user experience is that just enough information is provided to 5 // indicate the current location, while maintaining a level of brevity 6 // appropriate for a prompt. 7 package main 8 9 import ( 10 "fmt" 11 "log" 12 "os" 13 "path/filepath" 14 "strings" 15 ) 16 17 // This code was originally a bash function from: 18 // https://github.com/kujenga/init/blob/526cf35e1aecfdc614fcff45f39dac0f91f11b73/lib/profile.sh#L33-L53 19 // 20 // smart_pwd() { 21 // # set -x 22 // # git prefix with repo name or pwd. 23 // GIT=$(git rev-parse --show-prefix 2> /dev/null) 24 // if [ $? -eq 0 ]; then 25 // DIR="$(basename "$(git rev-parse --show-toplevel)")/$GIT" 26 // else 27 // DIR="$(pwd)" 28 // fi 29 // # strip trailing slash, replace $HOME with ~, shorten all but last. 30 // echo "$DIR" | \ 31 // sed 's?/$??g' | \ 32 // sed "s?$HOME?~?g" | \ 33 // perl -F/ -ane 'print join( "/", map { $i++ < @F - 1 ? substr $_,0,1 : $_ } @F)' 34 // # set +x 35 // } 36 37 var pathSeparator = string([]rune{os.PathSeparator}) 38 39 func main() { 40 fmt.Println(getSmart()) 41 } 42 43 func getSmart() string { 44 dir := getDir() 45 return smartenUp(dir) 46 } 47 48 // getDir gets the string representing the current directory in it's fully 49 // qualified form with no abbreviations. 50 func getDir() string { 51 wd, err := os.Getwd() 52 check(err, "getting current working directory") 53 // Walk up the directory tree looking for a .git directory, for which 54 // we would customize the printout. 55 dir := wd 56 // We terminate the loop when we are at the root, indicated by a 57 // following spash as documented: 58 // https://golang.org/pkg/path/filepath/#Dir 59 for !strings.HasSuffix(dir, pathSeparator) { 60 if _, err := os.Stat(filepath.Join(dir, ".git")); err == nil { 61 // If we find a directory with a .git directory, we 62 // return the relative path from it's containing 63 // directory. 64 gitParent := filepath.Dir(dir) 65 rel, err := filepath.Rel(gitParent, wd) 66 check(err, "getting relative path to working directory") 67 return rel 68 } 69 dir = filepath.Dir(dir) 70 } 71 return wd 72 } 73 74 // smartenUp takes a path and shortens all the components to just their 75 // first character, except for the last component. This is intended to present 76 // enough information that viewing the string would give the reader an 77 // indication where they are in their directory tree with minimal length, 78 // aiming at use in shell prompts 79 func smartenUp(s string) string { 80 // Strip trailing slashes from the path. 81 s = strings.TrimSuffix(s, "/") 82 // Replace $HOME with ~ for brevity. 83 home := os.Getenv("HOME") 84 if strings.HasPrefix(s, home) { 85 s = strings.Replace(s, home, "~", 1) 86 } 87 // Shorten all path components but the last. 88 components := strings.Split(s, pathSeparator) 89 for i := range components { 90 if i == len(components)-1 { 91 break 92 } 93 if components[i] != "" { 94 components[i] = string([]rune(components[i])[0]) 95 } 96 } 97 // Use strings.Join here to preserve preceeding slash 98 return strings.Join(components, pathSeparator) 99 } 100 101 // check exits the program with the specified message if the error is non-nil. 102 func check(err error, msg string) { 103 if err != nil { 104 log.Fatalf("smart-pwd: %s: %v", msg, err) 105 } 106 }