@@ -306,13 +306,7 @@ func (rs *RepoSource) loadRepo(name string, rg *git.Repository) (*Repo, error) {
// LatestFile returns the latest file at the specified path in the repository.
func (r *Repo) LatestFile(path string) (string, error) {
- lg, err := r.repository.Log(&git.LogOptions{
- From: r.GetHEAD().Hash(),
- })
- if err != nil {
- return "", err
- }
- c, err := lg.Next()
+ c, err := r.commitForHash(r.head.Hash())
if err != nil {
return "", err
}
@@ -326,3 +320,8 @@ func (r *Repo) LatestFile(path string) (string, error) {
}
return content, nil
}
+
+// LatestTree returns the latest tree at the specified path in the repository.
+func (r *Repo) LatestTree(path string) (*object.Tree, error) {
+ return r.Tree(r.head, path)
+}
@@ -3,6 +3,7 @@ package server
import (
"fmt"
"path/filepath"
+ "sort"
"strings"
"github.com/alecthomas/chroma/lexers"
@@ -11,16 +12,36 @@ import (
appCfg "github.com/charmbracelet/soft-serve/internal/config"
"github.com/charmbracelet/soft-serve/internal/tui/bubbles/git/types"
"github.com/charmbracelet/wish"
- "github.com/charmbracelet/wish/git"
+ gitwish "github.com/charmbracelet/wish/git"
"github.com/gliderlabs/ssh"
- gg "github.com/go-git/go-git/v5"
+ "github.com/go-git/go-git/v5/plumbing/filemode"
+ "github.com/go-git/go-git/v5/plumbing/object"
"github.com/muesli/termenv"
)
var (
- linenoStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("8"))
+ linenoStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("8"))
+ dirnameStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#00AAFF"))
+ filenameStyle = lipgloss.NewStyle()
+ filemodeStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#777777"))
)
+type entries []object.TreeEntry
+
+func (cl entries) Len() int { return len(cl) }
+func (cl entries) Swap(i, j int) { cl[i], cl[j] = cl[j], cl[i] }
+func (cl entries) Less(i, j int) bool {
+ if cl[i].Mode == filemode.Dir && cl[j].Mode == filemode.Dir {
+ return cl[i].Name < cl[j].Name
+ } else if cl[i].Mode == filemode.Dir {
+ return true
+ } else if cl[j].Mode == filemode.Dir {
+ return false
+ } else {
+ return cl[i].Name < cl[j].Name
+ }
+}
+
// softServeMiddleware is a middleware that handles displaying files with the
// option of syntax highlighting and line numbers.
func softServeMiddleware(ac *appCfg.Config) wish.Middleware {
@@ -48,7 +69,7 @@ func softServeMiddleware(ac *appCfg.Config) wish.Middleware {
return
}
auth := ac.AuthRepo(repo, s.PublicKey())
- if auth < git.ReadOnlyAccess {
+ if auth < gitwish.ReadOnlyAccess {
s.Write([]byte("unauthorized"))
s.Exit(1)
return
@@ -66,25 +87,52 @@ func softServeMiddleware(ac *appCfg.Config) wish.Middleware {
_ = s.Exit(1)
return
}
- fc, err := readFile(rs.Repository(), strings.Join(ps[1:], "/"))
- if err != nil {
+ p := strings.Join(ps[1:], "/")
+ t, err := rs.LatestTree(p)
+ if err != nil && err != object.ErrDirectoryNotFound {
_, _ = s.Write([]byte(err.Error()))
_ = s.Exit(1)
return
}
- if color {
- ffc, err := withFormatting(fp, fc)
+ if err == object.ErrDirectoryNotFound {
+ fc, err := rs.LatestFile(p)
if err != nil {
- s.Write([]byte(err.Error()))
- s.Exit(1)
+ _, _ = s.Write([]byte(err.Error()))
+ _ = s.Exit(1)
return
}
- fc = ffc
- }
- if lineno {
- fc = withLineNumber(fc, color)
+ if color {
+ ffc, err := withFormatting(fp, fc)
+ if err != nil {
+ s.Write([]byte(err.Error()))
+ s.Exit(1)
+ return
+ }
+ fc = ffc
+ }
+ if lineno {
+ fc = withLineNumber(fc, color)
+ }
+ s.Write([]byte(fc))
+ } else {
+ ents := entries(t.Entries)
+ sort.Sort(ents)
+ for _, e := range ents {
+ m, _ := e.Mode.ToOSFileMode()
+ if m == 0 {
+ s.Write([]byte(strings.Repeat(" ", 10)))
+ } else {
+ s.Write([]byte(filemodeStyle.Render(m.String())))
+ }
+ s.Write([]byte(" "))
+ if e.Mode.IsFile() {
+ s.Write([]byte(filenameStyle.Render(e.Name)))
+ } else {
+ s.Write([]byte(dirnameStyle.Render(e.Name)))
+ }
+ s.Write([]byte("\n"))
+ }
}
- s.Write([]byte(fc))
}()
}
sh(s)
@@ -92,26 +140,6 @@ func softServeMiddleware(ac *appCfg.Config) wish.Middleware {
}
}
-func readFile(r *gg.Repository, fp string) (string, error) {
- l, err := r.Log(&gg.LogOptions{})
- if err != nil {
- return "", err
- }
- c, err := l.Next()
- if err != nil {
- return "", err
- }
- f, err := c.File(fp)
- if err != nil {
- return "", err
- }
- fc, err := f.Contents()
- if err != nil {
- return "", err
- }
- return fc, nil
-}
-
func withLineNumber(s string, color bool) string {
lines := strings.Split(s, "\n")
mll := fmt.Sprintf("%d", len(fmt.Sprintf("%d", len(lines))))