diff --git a/README.md b/README.md index b46178d..e19e639 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ lse * `-d` - *display dirs first* * `-s` - *display real file/dir size* (makes command slower) * `-R` - *display recursively content directories* +* `-t` - *display files as a tree* # license diff --git a/main.go b/main.go index 861cd90..810b761 100644 --- a/main.go +++ b/main.go @@ -2,8 +2,6 @@ package main import ( "flag" - "fmt" - "lse/ansi" "lse/config" "lse/util" ) @@ -14,6 +12,7 @@ var ( showAll bool realDirSize bool recurse bool + showTree bool ) func init() { @@ -22,6 +21,7 @@ func init() { flag.BoolVar(&showAll, "a", false, "show hidden files") flag.BoolVar(&realDirSize, "s", false, "show real dir size") flag.BoolVar(&recurse, "R", false, "display recursively content directories") + flag.BoolVar(&showTree, "t", false, "shows a tree structure") flag.Parse() } @@ -33,19 +33,10 @@ func main() { cfg := config.LoadConfig(configFile) - entries := util.CollectEntries(pattern, showAll) - util.ShowOutput(cfg, dirsFirst, realDirSize, entries, false) - - if recurse { - fmt.Println() - - subEntries := util.RecurseScan(pattern, showAll) - - for _, sl := range subEntries { - path := fmt.Sprintf(" %s%s %s/%s", cfg.FileTypes.Directory, cfg.Icons.Directory, sl.Path, ansi.Reset) - fmt.Println(path) - util.ShowOutput(cfg, dirsFirst, realDirSize, sl.Entries, recurse) - fmt.Println() - } + if showTree { + util.ShowOutput(cfg, dirsFirst, realDirSize, nil, recurse, showTree, pattern, showAll) + } else { + entries := util.CollectEntries(pattern, showAll) + util.ShowOutput(cfg, dirsFirst, realDirSize, entries, recurse, showTree, pattern, showAll) } } diff --git a/util/util.go b/util/util.go index 943284e..1018f16 100644 --- a/util/util.go +++ b/util/util.go @@ -21,6 +21,58 @@ type DirBlock struct { Entries []FileEntry } +type TreeEntry struct { + FileEntry + Prefix string +} + +func BuildPrefix(isLastAncestor []bool, isLastCurrent bool) string { + var prefix string + for _, isLast := range isLastAncestor { + if isLast { + prefix += " " + } else { + prefix += "│ " + } + } + + if len(isLastAncestor) > 0 || len(prefix) > 0 { + if isLastCurrent { + prefix += "└── " + } else { + prefix += "├── " + } + } + + return prefix +} + +func BuildTreeEntries(cwd string, isLastAncestor []bool, showAll bool, dirsFirst bool) []TreeEntry { + var treeEntries []TreeEntry + + entries := CollectEntries(cwd, showAll) + SortEntries(entries, dirsFirst) + + for i, e := range entries { + isLast := (i == len(entries)-1) + + prefix := BuildPrefix(isLastAncestor, isLast) + + treeEntries = append(treeEntries, TreeEntry{ + FileEntry: e, + Prefix: prefix, + }) + + if e.Info.IsDir() { + nextIsLastAncestor := append(isLastAncestor, isLast) + subEntries := BuildTreeEntries(e.Path, nextIsLastAncestor, showAll, dirsFirst) + treeEntries = append(treeEntries, subEntries...) + } + } + + return treeEntries +} + func RecurseScan(cwd string, showAll bool) []DirBlock { entries := CollectEntries(cwd, showAll) var result []DirBlock @@ -41,7 +93,26 @@ func RecurseScan(cwd string, showAll bool) []DirBlock { return result } -func ShowOutput(cfg config.Config, dirsFirst bool, realDirSize bool, entries []FileEntry, recurse bool) { +func ShowOutput(cfg config.Config, dirsFirst bool, realDirSize bool, entries []FileEntry, recurse bool, showTree bool, cwd string, showAll bool) { + if showTree { + // Modo ÁRBOL: Recolecta toda la estructura con prefijos + // Nota: El modo árbol implica recursión, por lo que 'recurse' es irrelevante aquí. + treeEntries := BuildTreeEntries(cwd, []bool{}, showAll, dirsFirst) + + if len(treeEntries) == 0 { + fmt.Printf("No entries found in '%s'\n", cwd) + return + } + + fmt.Println(cwd) + + var rows [][]string + for _, te := range treeEntries { + rows = append(rows, FormatEntry(te.FileEntry, realDirSize, cfg, true, te.Prefix)) + } + PrintTable(rows, recurse, true) + return + } if len(entries) == 0 { return @@ -51,10 +122,10 @@ func ShowOutput(cfg config.Config, dirsFirst bool, realDirSize bool, entries []F var rows [][]string for _, e := range entries { - rows = append(rows, FormatEntry(e, realDirSize, cfg)) + rows = append(rows, FormatEntry(e, realDirSize, cfg, false, "")) } - PrintTable(rows, recurse) + PrintTable(rows, recurse, false) } func CollectEntries(pattern string, showAll bool) []FileEntry { @@ -90,7 +161,13 @@ func SortEntries(entries []FileEntry, dirsFirst bool) { }) } -func FormatEntry(e FileEntry, realDirSize bool, cfg config.Config) []string { +func FormatEntry(e FileEntry, realDirSize bool, cfg config.Config, showTree bool, prefix string) []string { + fullName := color.Name(e.Info.Name(), e.Info.Mode(), cfg.Icons, cfg.FileTypes) + + if showTree { + return []string{prefix + fullName} + } + perm := color.Permissions(e.Info.Mode().String(), cfg.Permissions) var sizeBytes int64 @@ -102,16 +179,22 @@ func FormatEntry(e FileEntry, realDirSize bool, cfg config.Config) []string { size := color.Size(sizeBytes, cfg.Size) date := color.Date(e.Info.ModTime(), cfg.Date) - fullName := color.Name(e.Info.Name(), e.Info.Mode(), cfg.Icons, cfg.FileTypes) return []string{perm, size, date, fullName} } -func PrintTable(rows [][]string, recurse bool) { +func PrintTable(rows [][]string, recurse bool, showTree bool) { if len(rows) == 0 { return } + if showTree && len(rows[0]) == 1 { + for _, row := range rows { + fmt.Println(row[0]) + } + return + } + colWidths := make([]int, len(rows[0])) for _, row := range rows { for i, col := range row { @@ -126,9 +209,12 @@ func PrintTable(rows [][]string, recurse bool) { fmt.Print(" ") } for i, col := range row { - fmt.Print(ansi.PadString(col, colWidths[i])) + format := ansi.PadString(col, colWidths[i]) if i < len(row)-1 { + fmt.Printf("%-*s", colWidths[i], format) fmt.Print(" ") + } else { + fmt.Print(format) } } fmt.Println()