From 1ddeb9e19065a65de55f852cc2b346f18435ae58 Mon Sep 17 00:00:00 2001 From: Daniel Milde Date: Mon, 11 Jan 2021 23:58:20 +0100 Subject: [PATCH 1/5] basic non interactive output --- main.go | 17 ++++-- stdout/stdout.go | 135 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 149 insertions(+), 3 deletions(-) create mode 100644 stdout/stdout.go diff --git a/main.go b/main.go index 3c69273b8..a9ce64e03 100644 --- a/main.go +++ b/main.go @@ -10,6 +10,7 @@ import ( "github.com/dundee/gdu/analyze" "github.com/dundee/gdu/cli" + "github.com/dundee/gdu/stdout" "github.com/gdamore/tcell/v2" "github.com/rivo/tview" ) @@ -22,6 +23,7 @@ func main() { ignoreDirPaths := flag.String("ignore-dir", "/proc,/dev,/sys,/run", "Absolute paths to ignore (separated by comma)") showVersion := flag.Bool("v", false, "Prints version") noColor := flag.Bool("no-color", false, "Do not use colorized output") + nonInteractive := flag.Bool("noninteractive", false, "Do not run in interactive mode") flag.Usage = func() { fmt.Fprintf(flag.CommandLine.Output(), "Usage of gdu: [flags] [directory to scan]\n") @@ -29,6 +31,7 @@ func main() { } flag.Parse() + args := flag.Args() if *showVersion { fmt.Println("Version:\t", AppVersion) @@ -40,9 +43,19 @@ func main() { log.Fatalf("error opening file: %v", err) } defer f.Close() - log.SetOutput(f) + if *nonInteractive { + if len(args) == 1 { + ui := stdout.CreateStdoutUI(!*noColor) + ui.SetIgnoreDirPaths(strings.Split(*ignoreDirPaths, ",")) + ui.AnalyzePath(args[0], analyze.ProcessDir) + } else { + flag.Usage() + } + return + } + screen, err := tcell.NewScreen() if err != nil { panic(err) @@ -56,8 +69,6 @@ func main() { ui := cli.CreateUI(screen, !*noColor) ui.SetIgnoreDirPaths(strings.Split(*ignoreDirPaths, ",")) - args := flag.Args() - if len(args) == 1 { ui.AnalyzePath(args[0], analyze.ProcessDir, nil) } else { diff --git a/stdout/stdout.go b/stdout/stdout.go new file mode 100644 index 000000000..75e003f39 --- /dev/null +++ b/stdout/stdout.go @@ -0,0 +1,135 @@ +package stdout + +import ( + "fmt" + "math" + "path/filepath" + "sort" + "sync" + "time" + + "github.com/dundee/gdu/analyze" +) + +// UI struct +type UI struct { + ignoreDirPaths map[string]bool + useColors bool +} + +// CreateStdoutUI creates UI for stdout +func CreateStdoutUI(useColors bool) *UI { + ui := &UI{ + useColors: useColors, + } + return ui +} + +// AnalyzePath analyzes recursively disk usage in given path +func (ui *UI) AnalyzePath(path string, analyzer analyze.Analyzer) { + abspath, _ := filepath.Abs(path) + var dir *analyze.File + + progress := &analyze.CurrentProgress{ + Mutex: &sync.Mutex{}, + Done: false, + ItemCount: 0, + TotalSize: int64(0), + } + var wait sync.WaitGroup + wait.Add(2) + + go func() { + ui.updateProgress(progress) + wait.Done() + }() + + go func() { + dir = analyzer(abspath, progress, ui.ShouldDirBeIgnored) + wait.Done() + }() + wait.Wait() + + sort.Sort(dir.Files) + + print("\r") + prefix := "" + for _, file := range dir.Files { + if file.IsDir { + prefix = "/" + } + fmt.Printf("%10s %s%s\n", + formatSize(file.Size), + prefix, + file.Name) + } +} + +// SetIgnoreDirPaths sets paths to ignore +func (ui *UI) SetIgnoreDirPaths(paths []string) { + ui.ignoreDirPaths = make(map[string]bool, len(paths)) + for _, path := range paths { + ui.ignoreDirPaths[path] = true + } +} + +// ShouldDirBeIgnored returns true if given path should be ignored +func (ui *UI) ShouldDirBeIgnored(path string) bool { + return ui.ignoreDirPaths[path] +} + +func (ui *UI) updateProgress(progress *analyze.CurrentProgress) { + emptyRow := "\r" + for j := 0; j < 100; j++ { + emptyRow += " " + } + + i := 0 + for { + progress.Mutex.Lock() + + print(emptyRow) + + if progress.Done { + return + } + + switch i { + case 0: + print("\r | ") + break + case 1: + print("\r / ") + break + case 2: + print("\r - ") + break + case 3: + print("\r \\ ") + break + } + + print("Scanning... Total items: " + + fmt.Sprint(progress.ItemCount) + + " size: " + + formatSize(progress.TotalSize)) + progress.Mutex.Unlock() + + time.Sleep(100 * time.Millisecond) + i++ + i %= 4 + } +} + +func formatSize(size int64) string { + if size > 1e12 { + return fmt.Sprintf("%.1f TiB", float64(size)/math.Pow(2, 40)) + } else if size > 1e9 { + return fmt.Sprintf("%.1f GiB", float64(size)/math.Pow(2, 30)) + } else if size > 1e6 { + return fmt.Sprintf("%.1f MiB", float64(size)/math.Pow(2, 20)) + } else if size > 1e3 { + return fmt.Sprintf("%.1f KiB", float64(size)/math.Pow(2, 10)) + } + return fmt.Sprintf("%d B", size) +} From ceedf07ece33839c469106c6b938ee1f8f7113e6 Mon Sep 17 00:00:00 2001 From: Daniel Milde Date: Tue, 12 Jan 2021 20:51:55 +0100 Subject: [PATCH 2/5] detect TTY, added no-progress flag --- README.md | 17 +++++++++++------ go.mod | 1 + go.sum | 3 +++ main.go | 10 +++++++--- stdout/stdout.go | 20 +++++++++++++------- 5 files changed, 35 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index efa8ddf24..eee6f7e6e 100644 --- a/README.md +++ b/README.md @@ -49,12 +49,17 @@ Debian: ## Usage - gdu # show all mounted disks - gdu some_dir_to_analyze # analyze given dir - gdu -log-file=./gdu.log some_dir # write errors to log file - gdu -ignore-dir=/sys,/proc / # ignore some paths - gdu -no-color / # use only white/gray/black colors + gdu # show all mounted disks + gdu some_dir_to_analyze # analyze given dir + gdu -log-file=./gdu.log some_dir # write errors to log file + gdu -ignore-dir=/sys,/proc / # ignore some paths + gdu -no-color / # use only white/gray/black colors + gdu -non-interactive / # only print stats, do not start interactive mode + gdu -non-interactive -no-progress / # do not show progress either + gdu / > file # write stats to file, do not start interactive mode + +Non interactive mode is started automtically when TTY is not detected (using [go-isatty](https://github.com/mattn/go-isatty)), for example if the output is being piped to a file. ## Running tests @@ -76,4 +81,4 @@ duc index / | 34 | 4.5 | 11.3 (2.5 + 8.8) baobab / | 38 | 12 | 25 (16 + 9) ncdu / | 43 | 13 | 18.5 (1.5 + 17) -Gdu is inspired by [ncdu](https://dev.yorhel.nl/ncdu), [godu](https://github.com/viktomas/godu), [dua](https://github.com/Byron/dua-cli) and [df](https://www.gnu.org/software/coreutils/manual/html_node/df-invocation.html). +Gdu is inspired by [ncdu](https://dev.yorhel.nl/ncdu), [godu](https://github.com/viktomas/godu), [dua](https://github.com/Byron/dua-cli) and [df](https://www.gnu.org/software/coreutils/manual/html_node/df-invocation.html). \ No newline at end of file diff --git a/go.mod b/go.mod index abb1c8b26..ebf232c99 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.15 require ( github.com/gdamore/tcell/v2 v2.1.0 + github.com/mattn/go-isatty v0.0.12 github.com/rivo/tview v0.0.0-20201204190810-5406288b8e4e github.com/stretchr/testify v1.6.1 golang.org/x/sys v0.0.0-20201223074533-0d417f636930 // indirect diff --git a/go.sum b/go.sum index 0e2bfb9b1..98a425118 100644 --- a/go.sum +++ b/go.sum @@ -7,6 +7,8 @@ github.com/gdamore/tcell/v2 v2.1.0 h1:UnSmozHgBkQi2PGsFr+rpdXuAPRRucMegpQp3Z3kDr github.com/gdamore/tcell/v2 v2.1.0/go.mod h1:vSVL/GV5mCSlPC6thFP5kfOFdM9MGZcalipmpTxTgQA= github.com/lucasb-eyer/go-colorful v1.0.3 h1:QIbQXiugsb+q10B+MI+7DI1oQLdmnep86tWFlaaUAac= github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= +github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= @@ -20,6 +22,7 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201017003518-b09fb700fbb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201223074533-0d417f636930 h1:vRgIt+nup/B/BwIS0g2oC0haq0iqbV3ZA+u6+0TlNCo= golang.org/x/sys v0.0.0-20201223074533-0d417f636930/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/main.go b/main.go index a9ce64e03..4d44f4d33 100644 --- a/main.go +++ b/main.go @@ -12,6 +12,7 @@ import ( "github.com/dundee/gdu/cli" "github.com/dundee/gdu/stdout" "github.com/gdamore/tcell/v2" + "github.com/mattn/go-isatty" "github.com/rivo/tview" ) @@ -23,7 +24,8 @@ func main() { ignoreDirPaths := flag.String("ignore-dir", "/proc,/dev,/sys,/run", "Absolute paths to ignore (separated by comma)") showVersion := flag.Bool("v", false, "Prints version") noColor := flag.Bool("no-color", false, "Do not use colorized output") - nonInteractive := flag.Bool("noninteractive", false, "Do not run in interactive mode") + nonInteractive := flag.Bool("non-interactive", false, "Do not run in interactive mode") + noProgress := flag.Bool("no-progress", false, "Do not show progress") flag.Usage = func() { fmt.Fprintf(flag.CommandLine.Output(), "Usage of gdu: [flags] [directory to scan]\n") @@ -33,6 +35,8 @@ func main() { flag.Parse() args := flag.Args() + istty := isatty.IsTerminal(os.Stdout.Fd()) + if *showVersion { fmt.Println("Version:\t", AppVersion) return @@ -45,9 +49,9 @@ func main() { defer f.Close() log.SetOutput(f) - if *nonInteractive { + if *nonInteractive || !istty { if len(args) == 1 { - ui := stdout.CreateStdoutUI(!*noColor) + ui := stdout.CreateStdoutUI(!*noColor, !*noProgress && istty) ui.SetIgnoreDirPaths(strings.Split(*ignoreDirPaths, ",")) ui.AnalyzePath(args[0], analyze.ProcessDir) } else { diff --git a/stdout/stdout.go b/stdout/stdout.go index 75e003f39..28dfd5943 100644 --- a/stdout/stdout.go +++ b/stdout/stdout.go @@ -15,12 +15,14 @@ import ( type UI struct { ignoreDirPaths map[string]bool useColors bool + showProgress bool } // CreateStdoutUI creates UI for stdout -func CreateStdoutUI(useColors bool) *UI { +func CreateStdoutUI(useColors bool, showProgress bool) *UI { ui := &UI{ - useColors: useColors, + useColors: useColors, + showProgress: showProgress, } return ui } @@ -37,17 +39,21 @@ func (ui *UI) AnalyzePath(path string, analyzer analyze.Analyzer) { TotalSize: int64(0), } var wait sync.WaitGroup - wait.Add(2) - go func() { - ui.updateProgress(progress) - wait.Done() - }() + if ui.showProgress { + wait.Add(1) + go func() { + ui.updateProgress(progress) + wait.Done() + }() + } + wait.Add(1) go func() { dir = analyzer(abspath, progress, ui.ShouldDirBeIgnored) wait.Done() }() + wait.Wait() sort.Sort(dir.Files) From f8caf07cc3052163e009f9510c541d01a4ad07f2 Mon Sep 17 00:00:00 2001 From: Daniel Milde Date: Tue, 12 Jan 2021 21:50:30 +0100 Subject: [PATCH 3/5] write result to io.Writer --- Makefile | 5 ++++- main.go | 2 +- stdout/stdout.go | 26 +++++++++++++++----------- stdout/stdout_test.go | 37 +++++++++++++++++++++++++++++++++++++ 4 files changed, 57 insertions(+), 13 deletions(-) create mode 100644 stdout/stdout_test.go diff --git a/Makefile b/Makefile index 0c38294a5..5f9f3f3d8 100644 --- a/Makefile +++ b/Makefile @@ -25,6 +25,9 @@ test: coverage: go test -v -race -coverprofile=coverage.txt -covermode=atomic $(PACKAGES) +coverage-html: coverage + go tool cover -html=coverage.txt + benchnmark: go test -bench=. $(PACKAGES) @@ -34,4 +37,4 @@ clean: -rm -r build -sudo rm -r obj-x86_64-linux-gnu on *.deb -.PHONY: run build test coverage clean +.PHONY: run build test coverage coverage-html clean diff --git a/main.go b/main.go index 4d44f4d33..2f81ec16f 100644 --- a/main.go +++ b/main.go @@ -51,7 +51,7 @@ func main() { if *nonInteractive || !istty { if len(args) == 1 { - ui := stdout.CreateStdoutUI(!*noColor, !*noProgress && istty) + ui := stdout.CreateStdoutUI(os.Stdout, !*noColor, !*noProgress && istty) ui.SetIgnoreDirPaths(strings.Split(*ignoreDirPaths, ",")) ui.AnalyzePath(args[0], analyze.ProcessDir) } else { diff --git a/stdout/stdout.go b/stdout/stdout.go index 28dfd5943..f82bd4e34 100644 --- a/stdout/stdout.go +++ b/stdout/stdout.go @@ -2,6 +2,7 @@ package stdout import ( "fmt" + "io" "math" "path/filepath" "sort" @@ -13,14 +14,16 @@ import ( // UI struct type UI struct { + output io.Writer ignoreDirPaths map[string]bool useColors bool showProgress bool } // CreateStdoutUI creates UI for stdout -func CreateStdoutUI(useColors bool, showProgress bool) *UI { +func CreateStdoutUI(output io.Writer, useColors bool, showProgress bool) *UI { ui := &UI{ + output: output, useColors: useColors, showProgress: showProgress, } @@ -58,13 +61,14 @@ func (ui *UI) AnalyzePath(path string, analyzer analyze.Analyzer) { sort.Sort(dir.Files) - print("\r") + fmt.Fprint(ui.output, "\r") prefix := "" for _, file := range dir.Files { if file.IsDir { prefix = "/" } - fmt.Printf("%10s %s%s\n", + fmt.Fprintf(ui.output, + "%10s %s%s\n", formatSize(file.Size), prefix, file.Name) @@ -94,7 +98,7 @@ func (ui *UI) updateProgress(progress *analyze.CurrentProgress) { for { progress.Mutex.Lock() - print(emptyRow) + fmt.Fprint(ui.output, emptyRow) if progress.Done { return @@ -102,22 +106,22 @@ func (ui *UI) updateProgress(progress *analyze.CurrentProgress) { switch i { case 0: - print("\r | ") + fmt.Fprint(ui.output, "\r | ") break case 1: - print("\r / ") + fmt.Fprint(ui.output, "\r / ") break case 2: - print("\r - ") + fmt.Fprint(ui.output, "\r - ") break case 3: - print("\r \\ ") + fmt.Fprint(ui.output, "\r \\ ") break } - print("Scanning... Total items: " + - fmt.Sprint(progress.ItemCount) + - " size: " + + fmt.Fprint(ui.output, "Scanning... Total items: "+ + fmt.Sprint(progress.ItemCount)+ + " size: "+ formatSize(progress.TotalSize)) progress.Mutex.Unlock() diff --git a/stdout/stdout_test.go b/stdout/stdout_test.go new file mode 100644 index 000000000..572e15af8 --- /dev/null +++ b/stdout/stdout_test.go @@ -0,0 +1,37 @@ +package stdout + +import ( + "bytes" + "testing" + + "github.com/dundee/gdu/analyze" + "github.com/stretchr/testify/assert" +) + +func TestAnalyzePath(t *testing.T) { + fin := analyze.CreateTestDir() + defer fin() + + buff := make([]byte, 10) + output := bytes.NewBuffer(buff) + + ui := CreateStdoutUI(output, false, false) + ui.SetIgnoreDirPaths([]string{"/xxx"}) + ui.AnalyzePath("test_dir", analyze.ProcessDir) + + assert.Equal(t, "nested", output.String()[23:29]) +} + +func TestAnalyzePathWithProgress(t *testing.T) { + fin := analyze.CreateTestDir() + defer fin() + + buff := make([]byte, 10) + output := bytes.NewBuffer(buff) + + ui := CreateStdoutUI(output, false, true) + ui.SetIgnoreDirPaths([]string{"/xxx"}) + ui.AnalyzePath("test_dir", analyze.ProcessDir) + + assert.Contains(t, output.String(), "nested") +} From 018aadc7062a776e032e8b9f4025d59148928621 Mon Sep 17 00:00:00 2001 From: Daniel Milde Date: Tue, 12 Jan 2021 22:41:05 +0100 Subject: [PATCH 4/5] added colors to non-interactive mode --- go.mod | 1 + go.sum | 3 +++ main.go | 2 +- stdout/stdout.go | 54 ++++++++++++++++++++++++++++++------------- stdout/stdout_test.go | 23 ++++++++++++++++-- 5 files changed, 64 insertions(+), 19 deletions(-) diff --git a/go.mod b/go.mod index ebf232c99..153ef4fc4 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.15 require ( github.com/gdamore/tcell/v2 v2.1.0 + github.com/gookit/color v1.3.6 github.com/mattn/go-isatty v0.0.12 github.com/rivo/tview v0.0.0-20201204190810-5406288b8e4e github.com/stretchr/testify v1.6.1 diff --git a/go.sum b/go.sum index 98a425118..74a472b94 100644 --- a/go.sum +++ b/go.sum @@ -5,6 +5,8 @@ github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo github.com/gdamore/tcell/v2 v2.0.1-0.20201017141208-acf90d56d591/go.mod h1:vSVL/GV5mCSlPC6thFP5kfOFdM9MGZcalipmpTxTgQA= github.com/gdamore/tcell/v2 v2.1.0 h1:UnSmozHgBkQi2PGsFr+rpdXuAPRRucMegpQp3Z3kDro= github.com/gdamore/tcell/v2 v2.1.0/go.mod h1:vSVL/GV5mCSlPC6thFP5kfOFdM9MGZcalipmpTxTgQA= +github.com/gookit/color v1.3.6 h1:Rgbazd4JO5AgSTVGS3o0nvaSdwdrS8bzvIXwtK6OiMk= +github.com/gookit/color v1.3.6/go.mod h1:R3ogXq2B9rTbXoSHJ1HyUVAZ3poOJHpd9nQmyGZsfvQ= github.com/lucasb-eyer/go-colorful v1.0.3 h1:QIbQXiugsb+q10B+MI+7DI1oQLdmnep86tWFlaaUAac= github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= @@ -19,6 +21,7 @@ github.com/rivo/tview v0.0.0-20201204190810-5406288b8e4e/go.mod h1:0ha5CGekam8ZV github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/main.go b/main.go index 2f81ec16f..852a2f791 100644 --- a/main.go +++ b/main.go @@ -51,7 +51,7 @@ func main() { if *nonInteractive || !istty { if len(args) == 1 { - ui := stdout.CreateStdoutUI(os.Stdout, !*noColor, !*noProgress && istty) + ui := stdout.CreateStdoutUI(os.Stdout, !*noColor && istty, !*noProgress && istty) ui.SetIgnoreDirPaths(strings.Split(*ignoreDirPaths, ",")) ui.AnalyzePath(args[0], analyze.ProcessDir) } else { diff --git a/stdout/stdout.go b/stdout/stdout.go index f82bd4e34..721748e54 100644 --- a/stdout/stdout.go +++ b/stdout/stdout.go @@ -10,6 +10,7 @@ import ( "time" "github.com/dundee/gdu/analyze" + "github.com/gookit/color" ) // UI struct @@ -18,6 +19,9 @@ type UI struct { ignoreDirPaths map[string]bool useColors bool showProgress bool + red color.Style + orange color.Style + blue color.Style } // CreateStdoutUI creates UI for stdout @@ -27,6 +31,15 @@ func CreateStdoutUI(output io.Writer, useColors bool, showProgress bool) *UI { useColors: useColors, showProgress: showProgress, } + + ui.red = color.Style{color.FgRed, color.OpBold} + ui.orange = color.Style{color.FgYellow, color.OpBold} + ui.blue = color.Style{color.FgBlue, color.OpBold} + + if !useColors { + color.Disable() + } + return ui } @@ -61,17 +74,25 @@ func (ui *UI) AnalyzePath(path string, analyzer analyze.Analyzer) { sort.Sort(dir.Files) - fmt.Fprint(ui.output, "\r") - prefix := "" + var lineFormat string + if ui.useColors { + lineFormat = "%20s %s\n" + } else { + lineFormat = "%9s %s\n" + } + for _, file := range dir.Files { if file.IsDir { - prefix = "/" + fmt.Fprintf(ui.output, + lineFormat, + ui.formatSize(file.Size), + ui.blue.Sprintf("/"+file.Name)) + } else { + fmt.Fprintf(ui.output, + lineFormat, + ui.formatSize(file.Size), + file.Name) } - fmt.Fprintf(ui.output, - "%10s %s%s\n", - formatSize(file.Size), - prefix, - file.Name) } } @@ -101,6 +122,7 @@ func (ui *UI) updateProgress(progress *analyze.CurrentProgress) { fmt.Fprint(ui.output, emptyRow) if progress.Done { + fmt.Fprint(ui.output, "\r") return } @@ -120,9 +142,9 @@ func (ui *UI) updateProgress(progress *analyze.CurrentProgress) { } fmt.Fprint(ui.output, "Scanning... Total items: "+ - fmt.Sprint(progress.ItemCount)+ + ui.red.Sprint(progress.ItemCount)+ " size: "+ - formatSize(progress.TotalSize)) + ui.formatSize(progress.TotalSize)) progress.Mutex.Unlock() time.Sleep(100 * time.Millisecond) @@ -131,15 +153,15 @@ func (ui *UI) updateProgress(progress *analyze.CurrentProgress) { } } -func formatSize(size int64) string { +func (ui *UI) formatSize(size int64) string { if size > 1e12 { - return fmt.Sprintf("%.1f TiB", float64(size)/math.Pow(2, 40)) + return ui.orange.Sprintf("%.1f", float64(size)/math.Pow(2, 40)) + " TiB" } else if size > 1e9 { - return fmt.Sprintf("%.1f GiB", float64(size)/math.Pow(2, 30)) + return ui.orange.Sprintf("%.1f", float64(size)/math.Pow(2, 30)) + " GiB" } else if size > 1e6 { - return fmt.Sprintf("%.1f MiB", float64(size)/math.Pow(2, 20)) + return ui.orange.Sprintf("%.1f", float64(size)/math.Pow(2, 20)) + " MiB" } else if size > 1e3 { - return fmt.Sprintf("%.1f KiB", float64(size)/math.Pow(2, 10)) + return ui.orange.Sprintf("%.1f", float64(size)/math.Pow(2, 10)) + " KiB" } - return fmt.Sprintf("%d B", size) + return ui.orange.Sprintf("%d", size) + " B" } diff --git a/stdout/stdout_test.go b/stdout/stdout_test.go index 572e15af8..397934265 100644 --- a/stdout/stdout_test.go +++ b/stdout/stdout_test.go @@ -19,19 +19,38 @@ func TestAnalyzePath(t *testing.T) { ui.SetIgnoreDirPaths([]string{"/xxx"}) ui.AnalyzePath("test_dir", analyze.ProcessDir) - assert.Equal(t, "nested", output.String()[23:29]) + assert.Contains(t, output.String(), "nested") } -func TestAnalyzePathWithProgress(t *testing.T) { +func TestAnalyzePathWithColors(t *testing.T) { fin := analyze.CreateTestDir() defer fin() buff := make([]byte, 10) output := bytes.NewBuffer(buff) + ui := CreateStdoutUI(output, true, false) + ui.SetIgnoreDirPaths([]string{"/xxx"}) + ui.AnalyzePath("test_dir/nested", analyze.ProcessDir) + + assert.Contains(t, output.String(), "subnested") +} + +func TestAnalyzePathWithProgress(t *testing.T) { + fin := analyze.CreateTestDir() + defer fin() + + output := bytes.NewBuffer(make([]byte, 10)) + ui := CreateStdoutUI(output, false, true) ui.SetIgnoreDirPaths([]string{"/xxx"}) ui.AnalyzePath("test_dir", analyze.ProcessDir) assert.Contains(t, output.String(), "nested") } + +func printBuffer(buff *bytes.Buffer) { + for i, x := range buff.String() { + println(i, string(x)) + } +} From 15c29bf8f06f1db69649ffadd96eb0f290d53607 Mon Sep 17 00:00:00 2001 From: Daniel Milde Date: Tue, 12 Jan 2021 22:45:26 +0100 Subject: [PATCH 5/5] modes in readme --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index eee6f7e6e..673427298 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,9 @@ Debian: gdu -non-interactive -no-progress / # do not show progress either gdu / > file # write stats to file, do not start interactive mode -Non interactive mode is started automtically when TTY is not detected (using [go-isatty](https://github.com/mattn/go-isatty)), for example if the output is being piped to a file. +Gdu has two modes: interactive (default) and non-interactive. + +Non-interactive mode is started automtically when TTY is not detected (using [go-isatty](https://github.com/mattn/go-isatty)), for example if the output is being piped to a file, or it can be started explicitly by using a flag. ## Running tests