my nix dotfiles

nix/kvothe: update before wipe

+377 -2
+2 -2
programs/zed/settings.nix
··· 62 62 }; 63 63 assistant = { 64 64 default_model = { 65 - provider = "copilot_chat"; 66 - model = "claude-3-5-sonnet"; 65 + provider = "zed.dev"; 66 + model = "claude-3-7-sonnet"; 67 67 }; 68 68 version = "2"; 69 69 enabled = true;
+54
prompt/git.go
··· 1 + package main 2 + 3 + import ( 4 + "os" 5 + "path/filepath" 6 + 7 + git "github.com/libgit2/git2go/v33" 8 + ) 9 + 10 + // Recursively traverse up until we find .git 11 + // and return the git repo path. 12 + func getGitDir(cwd string) string { 13 + for { 14 + dirs, _ := os.ReadDir(cwd) 15 + for _, d := range dirs { 16 + if ".git" == d.Name() { 17 + return cwd 18 + } else if cwd == "/" { 19 + return "" 20 + } 21 + } 22 + cwd = filepath.Dir(cwd) 23 + } 24 + } 25 + 26 + // Returns the current git branch or current ref sha. 27 + func getGitBranch(repo *git.Repository) string { 28 + ref, _ := repo.Head() 29 + // Quick hack to fix crash when ref is nil; 30 + // i.e., new repo with no commits. 31 + if ref == nil { 32 + return "no commit" 33 + } 34 + if ref.IsBranch() { 35 + name, _ := ref.Branch().Name() 36 + return name 37 + } else { 38 + return ref.Target().String()[:8] 39 + } 40 + } 41 + 42 + // Returns • if clean, else ×. 43 + func getGitStatus(repo *git.Repository, clean, dirty string) string { 44 + sl, _ := repo.StatusList(&git.StatusOptions{ 45 + Show: git.StatusShowIndexAndWorkdir, 46 + Flags: git.StatusOptIncludeUntracked, 47 + }) 48 + n, _ := sl.EntryCount() 49 + if n != 0 { 50 + return dirty 51 + } else { 52 + return clean 53 + } 54 + }
+32
prompt/go.mod
··· 1 + module git.icyphox.sh/dotfiles/prompt 2 + 3 + go 1.21 4 + 5 + toolchain go1.23.4 6 + 7 + require github.com/go-git/go-git/v5 v5.13.2 8 + 9 + require ( 10 + dario.cat/mergo v1.0.0 // indirect 11 + github.com/Microsoft/go-winio v0.6.1 // indirect 12 + github.com/ProtonMail/go-crypto v1.1.5 // indirect 13 + github.com/cloudflare/circl v1.3.7 // indirect 14 + github.com/cyphar/filepath-securejoin v0.3.6 // indirect 15 + github.com/emirpasic/gods v1.18.1 // indirect 16 + github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect 17 + github.com/go-git/go-billy/v5 v5.6.2 // indirect 18 + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect 19 + github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect 20 + github.com/kevinburke/ssh_config v1.2.0 // indirect 21 + github.com/pjbgf/sha1cd v0.3.2 // indirect 22 + github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect 23 + github.com/skeema/knownhosts v1.3.0 // indirect 24 + github.com/xanzy/ssh-agent v0.3.3 // indirect 25 + golang.org/x/crypto v0.32.0 // indirect 26 + golang.org/x/mod v0.19.0 // indirect 27 + golang.org/x/net v0.34.0 // indirect 28 + golang.org/x/sync v0.10.0 // indirect 29 + golang.org/x/sys v0.29.0 // indirect 30 + golang.org/x/tools v0.23.0 // indirect 31 + gopkg.in/warnings.v0 v0.1.2 // indirect 32 + )
+108
prompt/go.sum
··· 1 + dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= 2 + dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= 3 + github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= 4 + github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= 5 + github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= 6 + github.com/ProtonMail/go-crypto v1.1.5 h1:eoAQfK2dwL+tFSFpr7TbOaPNUbPiJj4fLYwwGE1FQO4= 7 + github.com/ProtonMail/go-crypto v1.1.5/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= 8 + github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= 9 + github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= 10 + github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= 11 + github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= 12 + github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU= 13 + github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA= 14 + github.com/cyphar/filepath-securejoin v0.3.6 h1:4d9N5ykBnSp5Xn2JkhocYDkOpURL/18CYMpo6xB9uWM= 15 + github.com/cyphar/filepath-securejoin v0.3.6/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI= 16 + github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 17 + github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 18 + github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 19 + github.com/elazarl/goproxy v1.4.0 h1:4GyuSbFa+s26+3rmYNSuUVsx+HgPrV1bk1jXI0l9wjM= 20 + github.com/elazarl/goproxy v1.4.0/go.mod h1:X/5W/t+gzDyLfHW4DrMdpjqYjpXsURlBt9lpBDxZZZQ= 21 + github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= 22 + github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= 23 + github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c= 24 + github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU= 25 + github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= 26 + github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= 27 + github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UNbRM= 28 + github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU= 29 + github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4= 30 + github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= 31 + github.com/go-git/go-git/v5 v5.13.2 h1:7O7xvsK7K+rZPKW6AQR1YyNhfywkv7B8/FsP3ki6Zv0= 32 + github.com/go-git/go-git/v5 v5.13.2/go.mod h1:hWdW5P4YZRjmpGHwRH2v3zkWcNl6HeXaXQEMGb3NJ9A= 33 + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= 34 + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 35 + github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= 36 + github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 37 + github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= 38 + github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= 39 + github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= 40 + github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= 41 + github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 42 + github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= 43 + github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= 44 + github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 45 + github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 46 + github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 47 + github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 48 + github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= 49 + github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY= 50 + github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4= 51 + github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A= 52 + github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 53 + github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 54 + github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 55 + github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 56 + github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= 57 + github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= 58 + github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8= 59 + github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= 60 + github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= 61 + github.com/skeema/knownhosts v1.3.0 h1:AM+y0rI04VksttfwjkSTNQorvGqmwATnvnAHpSgc0LY= 62 + github.com/skeema/knownhosts v1.3.0/go.mod h1:sPINvnADmT/qYH1kfv+ePMmOBTH6Tbl7b5LvTDjFK7M= 63 + github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 64 + github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 65 + github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 66 + github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= 67 + github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 68 + github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= 69 + github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= 70 + golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= 71 + golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= 72 + golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= 73 + golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= 74 + golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= 75 + golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8= 76 + golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= 77 + golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 78 + golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= 79 + golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= 80 + golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= 81 + golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 82 + golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 83 + golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 84 + golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 85 + golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 86 + golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 87 + golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 88 + golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= 89 + golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 90 + golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 91 + golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg= 92 + golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek= 93 + golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 94 + golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= 95 + golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= 96 + golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 97 + golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg= 98 + golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI= 99 + gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 100 + gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 101 + gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= 102 + gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 103 + gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= 104 + gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= 105 + gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 106 + gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 107 + gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 108 + gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+181
prompt/prompt.go
··· 1 + package main 2 + 3 + import ( 4 + "fmt" 5 + "os" 6 + "path/filepath" 7 + "strings" 8 + 9 + "github.com/go-git/go-git/v5" 10 + "github.com/go-git/go-git/v5/plumbing" 11 + "github.com/go-git/go-git/v5/plumbing/revlist" 12 + ) 13 + 14 + func main() { 15 + if len(os.Args) < 3 { 16 + return 17 + } 18 + 19 + command := os.Args[1] 20 + target := os.Args[2] 21 + 22 + switch command { 23 + case "cwd": 24 + fmt.Print(cwd(target)) 25 + case "vcs": 26 + if status := vcs(target); status != "" { 27 + fmt.Print(status) 28 + } 29 + } 30 + } 31 + 32 + func cwd(target string) string { 33 + home, _ := os.UserHomeDir() 34 + absTarget, _ := filepath.Abs(target) 35 + absHome, _ := filepath.Abs(home) 36 + 37 + // Replace home directory with ~ 38 + if strings.HasPrefix(absTarget, absHome) { 39 + if absTarget == absHome { 40 + return "~" 41 + } 42 + absTarget = "~" + strings.TrimPrefix(absTarget, absHome) 43 + } 44 + 45 + // Truncate path components 46 + parts := strings.Split(filepath.ToSlash(absTarget), "/") 47 + for i := 0; i < len(parts)-1; i++ { 48 + if parts[i] != "" && parts[i] != "~" { 49 + parts[i] = parts[i][:1] 50 + } 51 + } 52 + return strings.Join(parts, "/") 53 + } 54 + 55 + func vcs(target string) string { 56 + repo := findRepo(target) 57 + if repo == nil { 58 + return "" 59 + } 60 + 61 + status := &strings.Builder{} 62 + 63 + // Get branch information 64 + if branch, ok := getBranch(repo); ok { 65 + status.WriteString(fmt.Sprintf("#[fg=colour8]%s ", branch)) 66 + } 67 + 68 + // Get ahead/behind information 69 + if dist := getDistance(repo); dist != "" { 70 + status.WriteString(fmt.Sprintf("#[fg=colour8]%s", dist)) 71 + } 72 + 73 + // Get repository status 74 + statusSymbol, statusColor := getRepoStatus(repo) 75 + status.WriteString(fmt.Sprintf("%s%s#[fg=colour7]", statusColor, statusSymbol)) 76 + 77 + return status.String() 78 + } 79 + 80 + func findRepo(target string) *git.Repository { 81 + dir := filepath.Clean(target) 82 + for { 83 + repo, err := git.PlainOpen(dir) 84 + if err == nil { 85 + return repo 86 + } 87 + 88 + parent := filepath.Dir(dir) 89 + if parent == dir { 90 + return nil 91 + } 92 + dir = parent 93 + } 94 + } 95 + 96 + func getBranch(repo *git.Repository) (string, bool) { 97 + head, err := repo.Head() 98 + if err != nil { 99 + return "", false 100 + } 101 + 102 + if head.Name().IsBranch() { 103 + return head.Name().Short(), true 104 + } 105 + 106 + // Check for detached HEAD 107 + if commit, err := repo.CommitObject(head.Hash()); err == nil { 108 + return commit.Hash.String()[:7], true 109 + } 110 + 111 + return "", false 112 + } 113 + 114 + func getDistance(repo *git.Repository) string { 115 + head, err := repo.Head() 116 + if err != nil || !head.Name().IsBranch() { 117 + return "" 118 + } 119 + 120 + local := head.Hash() 121 + 122 + branch, err := repo.Branch(head.Name().Short()) 123 + if err != nil { 124 + return "" 125 + } 126 + 127 + remoteName := "origin" 128 + 129 + remote, err := repo.Remote(remoteName) 130 + if err != nil { 131 + return "" 132 + } 133 + 134 + err = remote.Fetch(&git.FetchOptions{RemoteName: remoteName}) 135 + if err != nil { 136 + return "" 137 + } 138 + 139 + refName := plumbing.Re 140 + 141 + ahead, _ := revlist.Objects(repo.Storer, []plumbing.Hash{local}, []plumbing.Hash{remote}) 142 + behind, _ := revlist.Objects(repo.Storer, []plumbing.Hash{remote}, []plumbing.Hash{local}) 143 + 144 + switch { 145 + case len(ahead) > 0 && len(behind) > 0: 146 + return "↑↓ " 147 + case len(ahead) > 0: 148 + return "↑ " 149 + case len(behind) > 0: 150 + return "↓ " 151 + default: 152 + return "" 153 + } 154 + } 155 + 156 + func getRepoStatus(repo *git.Repository) (string, string) { 157 + wt, _ := repo.Worktree() 158 + stat, _ := wt.Status() 159 + 160 + hasStaged := false 161 + hasUnstaged := false 162 + 163 + for _, s := range stat { 164 + 165 + if s.Staging != git.Unmodified { 166 + hasStaged = true 167 + } 168 + if s.Worktree != git.Unmodified { 169 + hasUnstaged = true 170 + } 171 + } 172 + 173 + switch { 174 + case hasUnstaged: 175 + return "×", "#[fg=colour1]" 176 + case hasStaged: 177 + return "±", "#[fg=colour3]" 178 + default: 179 + return "·", "#[fg=colour2]" 180 + } 181 + }