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  }