aboutsummaryrefslogtreecommitdiffstats
path: root/internal
diff options
context:
space:
mode:
Diffstat (limited to 'internal')
-rw-r--r--internal/cp/cp.go18
-rw-r--r--internal/render/render.go74
2 files changed, 79 insertions, 13 deletions
diff --git a/internal/cp/cp.go b/internal/cp/cp.go
index 7ad80f7..7d32163 100644
--- a/internal/cp/cp.go
+++ b/internal/cp/cp.go
@@ -10,7 +10,6 @@ import (
"path"
"slices"
"strings"
- "time"
"github.com/rhogenson/ccp/internal/wfs"
"github.com/rhogenson/ccp/internal/wfs/sftpfs"
@@ -127,9 +126,8 @@ func (p FSPath) exists() bool {
}
type copier struct {
- p Progress
- force bool
- fileStartRateLimit *time.Ticker
+ p Progress
+ force bool
}
func (c *copier) openWithRetry(path FSPath, fn func() error) error {
@@ -143,11 +141,7 @@ func (c *copier) openWithRetry(path FSPath, fn func() error) error {
}
func (c *copier) copyRegularFile(src, dst FSPath) error {
- select {
- case <-c.fileStartRateLimit.C:
- c.p.FileStart(src.String(), dst.String())
- default:
- }
+ c.p.FileStart(src.String(), dst.String())
in, err := src.open()
if err != nil {
@@ -220,11 +214,9 @@ func Copy(progress Progress, srcs []FSPath, dstRoot FSPath, force bool) {
// sem acts as a semaphore to limit the number of concurrent file copies
sem := make(chan struct{}, maxConcurrency)
c := &copier{
- p: progress,
- force: force,
- fileStartRateLimit: time.NewTicker(500 * time.Millisecond),
+ p: progress,
+ force: force,
}
- defer c.fileStartRateLimit.Stop()
type roDir struct {
path FSPath
mode fs.FileMode
diff --git a/internal/render/render.go b/internal/render/render.go
new file mode 100644
index 0000000..877e2bd
--- /dev/null
+++ b/internal/render/render.go
@@ -0,0 +1,74 @@
+// Package render renders to the terminal using ANSI escape sequences.
+package render
+
+import (
+ "bufio"
+ "bytes"
+ "fmt"
+ "os"
+)
+
+// Renderer updates a terminal UI. Typical usage looks like
+//
+// r := render.New()
+// // Game loop
+// for {
+// // Update state
+//
+// r.Clear()
+// fmt.Fprintf(r, "Render UI by writing to r using io.Writer")
+// r.Flush()
+// }
+type Renderer struct {
+ w bufio.Writer
+ prevLines int
+}
+
+// New creates a new Renderer
+func New() *Renderer {
+ r := &Renderer{}
+ r.w.Reset(os.Stderr)
+ return r
+}
+
+// Clear clears the screen before rendering a new frame.
+func (r *Renderer) Clear() {
+ if r.prevLines > 0 {
+ fmt.Fprintf(&r.w, "\033[%dA", r.prevLines)
+ }
+ r.prevLines = 0
+ r.w.WriteString("\r")
+}
+
+// Write implements io.Writer.
+func (r *Renderer) Write(buf []byte) (int, error) {
+ totalBytes := 0
+ for len(buf) > 0 {
+ i := bytes.IndexByte(buf, '\n')
+ if i < 0 {
+ n, err := r.w.Write(buf)
+ totalBytes += n
+ return totalBytes, err
+ }
+ line := buf[:i]
+ buf = buf[i+1:]
+ n, err := r.w.Write(line)
+ totalBytes += n
+ if err != nil {
+ return totalBytes, err
+ }
+ if _, err := r.w.WriteString("\033[K\n"); err != nil {
+ return totalBytes, err
+ }
+ totalBytes++
+ r.prevLines++
+ }
+ return totalBytes, nil
+}
+
+// Flush flushes the internal buffer to stdout. Flush should be called at the
+// end of every frame.
+func (r *Renderer) Flush() {
+ r.w.WriteString("\033[J")
+ r.w.Flush()
+}