Giter Club home page Giter Club logo

uilive's Introduction

uilive GoDoc Build Status

uilive is a go library for updating terminal output in realtime. It provides a buffered io.Writer that is flushed at a timed interval. uilive powers uiprogress.

Usage Example

Calling uilive.New() will create a new writer. To start rendering, simply call writer.Start() and update the ui by writing to the writer. Full source for the below example is in example/main.go.

writer := uilive.New()
// start listening for updates and render
writer.Start()

for i := 0; i <= 100; i++ {
  fmt.Fprintf(writer, "Downloading.. (%d/%d) GB\n", i, 100)
  time.Sleep(time.Millisecond * 5)
}

fmt.Fprintln(writer, "Finished: Downloaded 100GB")
writer.Stop() // flush and stop rendering

The above will render

example

Installation

$ go get -v github.com/gosuri/uilive

uilive's People

Contributors

asahasrabuddhe avatar boz avatar gosuri avatar henvic avatar kothar avatar ljfranklin avatar maplain avatar mattn avatar mistiara avatar vnlitvinov avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

uilive's Issues

Broken lines

Hi,
I thought I've finally found a lib which makes it easy to use single-line prints, but this lib suffers from the same issue I've seen in many other libs.

demo:

writer := uilive.New()
// start listening for updates and render
writer.Start()

for i := 0; i <= 100; i++ {
  fmt.Fprintf(writer, "Downloading.. (%d/%d) GB\n", i, 100)
  time.Sleep(time.Millisecond * 5)
}

fmt.Fprintln(writer, "Finished")
writer.Stop() // flush and stop rendering

try fmt.Fprintln(writer, "Finished") instead of fmt.Fprintln(writer, "Finished: Downloaded 100GB").
meaning: print a last message which is shorter than the previous one.

I get:

Finisheding.. (100/100) GB

Combining with uitable causes leading whitespace

Just come across your trio of console UI libraries, they look awesome!

One bug I've found is when combining this with uitable. Here's my code:

	writer := uilive.New()
	bufferedWriter := writer.Bypass()
	writer.Start()

	for i := 0; i <= 100; i++ {
		table := uitable.New()
		table.MaxColWidth = 50

		table.AddRow("Operation", "Progress", "%%")
		table.AddRow("Downloading", fmt.Sprintf("%d/%d", i, 100), fmt.Sprintf("%d", i/100))
		table.AddRow("And another", fmt.Sprintf("%d/%d", i*2, 1000), fmt.Sprintf("%d", (i*2)/1000))

		fmt.Fprintf(bufferedWriter, table.String())
		writer.Flush()
		time.Sleep(time.Millisecond * 500)
	}

	fmt.Fprintln(writer, "Finished: Downloaded 100GB")
	writer.Stop() // flush and stop rendering

And here's some output:

                                  Operation  	Progress	%
Downloading	35/100  	0 
And another	70/1000 	0

As you can see the top row of the table is indented by spaces. Is this easy to fix? As you can see above I am using .Newline(). I did also try writing direct to writer, but that still causes the bug. If I use .Bypass() then the indent goes away but then the table is echoed many times to the terminal as it updates. Removing .Flush() makes no difference.

The last line is being overwritten when width of the buffer exceeds screen width

The last line seems to be overwritten when width of the buffer exceeds screen width:

screen shot 2015-12-08 at 11 11 19

Code:

package main

import (
    "fmt"
    "time"

    "github.com/gosuri/uilive"
)

func main() {
    writer := uilive.New()

    // start listening for updates and render
    writer.Start()

    str := ""
    for i := 0; i <= 100; i++ {
        str = str + "s"
    }
    str = str + "[end]"

    for i := 0; i <= 100; i++ {
        fmt.Fprintf(writer, "%d %s\n", i, str)
        time.Sleep(time.Second)
    }

    writer.Stop() // flush and stop rendering
}

(Ref #2)

Library should not call os.exit()

With the latest commit if the tool fails to open /dev/tty (to get window size) then it calls os.exit(). A library should not call os.exit! When my command line tool (that doesn't use this as a direct dependency) was run from an environment where there wasn't a tty it just exited. No error, no problem. I had to do an strace just to see that it was right after trying to open /dev/tty.

constant scrolling when writing a line of size 2 * columns + 1

When writing a line of a very specific size, the next line will be written one line too low leading to undesired scrolling.
The following code will trigger the bug:

package main

import (
	"fmt"
	"os"
	"runtime"
	"strings"
	"syscall"
	"time"
	"unsafe"

	"github.com/gosuri/uilive"
)

type windowSize struct {
	rows uint16
	cols uint16
}

func getTermSize() (int, int) {
	var out *os.File
	var err error
	var sz windowSize
	if runtime.GOOS == "openbsd" {
		out, err = os.OpenFile("/dev/tty", os.O_RDWR, 0)
		if err != nil {
			return 0, 0
		}

	} else {
		out, err = os.OpenFile("/dev/tty", os.O_WRONLY, 0)
		if err != nil {
			return 0, 0
		}
	}
	_, _, _ = syscall.Syscall(syscall.SYS_IOCTL,
		out.Fd(), uintptr(syscall.TIOCGWINSZ), uintptr(unsafe.Pointer(&sz)))
	return int(sz.cols), int(sz.rows)
}

func main() {
	cols, _ := getTermSize()
	writer := uilive.New()

	for {
		_, _ = fmt.Fprintf(writer, "%s\n", strings.Repeat("b", cols*2+1))
		writer.Flush()
		time.Sleep(1000 * time.Millisecond)
	}
}

Latest commit breaks Windows build

# github.com/gosuri/uilive
..\..\..\go\pkg\mod\github.com\gosuri\[email protected]\terminal_size.go:34:27: not enough arguments in call to syscall.Syscall
..\..\..\go\pkg\mod\github.com\gosuri\[email protected]\terminal_size.go:34:28: undefined: syscall.SYS_IOCTL
..\..\..\go\pkg\mod\github.com\gosuri\[email protected]\terminal_size.go:35:21: undefined: syscall.TIOCGWINSZ
..\..\..\go\pkg\mod\github.com\gosuri\[email protected]\writer_windows.go:49:12: undefined: isatty
..\..\..\go\pkg\mod\github.com\gosuri\[email protected]\writer_windows.go:53:28: undefined: strings

clearLines not working

Hi Greg,

I was playing with uilive and am having troubles, not sure if it is my terminal or somethings else is wrong, perhaps you have an idea.

Basically when I run go run ./example/main.go I end up with:

$ go run ./example/main.go
Finished: Downloaded 100GB50) GB

So the previous output is not actually getting cleared, despite the \033[2K CSI code being printed and I still see the 50) GB from the "Downloading" prints.

Oddly the \033[J code (clear to end of screen) seems to work, so when I change clearLines to look like this it works fine:

func (w *Writer) clearLines() {
    fmt.Fprint(w.Out, strings.Repeat("\033[A", w.lineCount), "\033[J")
}

I am running on OSX Sierra, but I tried in iterm2 and terminal both with and without tmux and I get the same results with the original code.

Any ideas? Thanks!
-Cory

Event based flush of buffer

Is it possible to expose some methods to add event based flushing of buffer, along with time based approach?

New goroutine

How do you make this work with new go routines?

progressWrite := uilive.New()
progressWrite.Start()

for i := 0; i <= 100; i++ {
	go func() {
		fmt.Fprintf(progressWrite, "Downloading.. (%d/%d) GB\n", i, 100)
		time.Sleep(time.Millisecond * 20)
	}()
}

glitch when the writer RefreshInterval is too little

When the RefreshInterval is too little like time.Nanosecond little or 0, you should use writer.Wait() to be able to use the library, but still the text glitches a little. And when you do not use the writer.Wait(), the library isn't able to replace the text and start to act almost like a regular fmt.Println.

package main

import (
        "fmt"

        "github.com/gosuri/uilive"
)

func main() {
        writer := uilive.New()

        // start listening for updates and render
        writer.RefreshInterval = 0

        writer.Start()

        for _, f := range []string{"Foo.zip", "Bar.iso"} {
                for i := 0; i <= 5000000; i++ {
                        fmt.Fprintf(writer, "Downloading %s.. (%d/%d) GB\n", f, i, 50)
                        writer.Wait()
                }
                fmt.Fprintf(writer.Bypass(), "Downloaded %s\n", f)
        }

        fmt.Fprintln(writer, "Finished: Downloaded 100GB")
        writer.Stop() // flush and stop rendering
}

Panic on Stop() with nil channel

Hello!

As example without writer

package main

import (
	"github.com/gosuri/uilive"
)

func main() {
	writer := uilive.New()

	// start listening for updates and render
	//writer.Start()

	writer.Stop() // flush and stop rendering
}

we go to panic on Stop().
How I see befor 57b6e55 we not go to panic without Start().
Was it done on purpose?
Or it can be ignored in library like mrqwer88@7da1a80 as example?

Thanks in advance!

Windows 10 black screen

When using uilive I noticed that on windows 10 the CMD window becomes black without any data on it.

I used to notice it only after long run times but I finally found a way to reproduce it accurately hence I finally open a issue for it.

The way I was able to reproduce it is by double clicking the CMD window (in any of the black area), then all text disappears and stops showing up until either restarting the program or fixing it by pressing ENTER once or twice.

I will try to get some sample code and maybe a GIF or video of this in action tomorrow.

Syscall in getTermSize() corrupts package variable of a different library

Hi.

I am developing a CLI application which uses uilive v0.0.4 to output execution statistics to the console at runtime. Everything was fine until I decided to use sniper as a key-value storage for my app. It turned out that one of the sniper package variables got corrupted (changes it's value from 12 to 0) after calling uilive.New(). Without calling uilive.New() sniper works perfectly fine.

I traced the issue down to the IOCTL syscall in getTermSize() function in terminal_size.go:

//...
var sz windowSize

func getTermSize() (int, int) {
       //...
       // `sniper` package variable is fine here
	_, _, _ = syscall.Syscall(syscall.SYS_IOCTL,
		out.Fd(), uintptr(syscall.TIOCGWINSZ), uintptr(unsafe.Pointer(&sz)))
        // `sniper` package variable is reset to 0 
	return int(sz.cols), int(sz.rows)
}Ubuntu 20.04 LTS x64

I am not sure what goes wrong exactly, but it looks like using an unsafe.Pointer to the sz package variable somehow messes up the memory contents. I can't say if any other package variables are affected.

I managed to fix the issue by simply moving package variables from terminal_size.go into the getTermSize() function body, making them local (and they kinda should be local, there are no usages of them outside of terminal_size.go):

// +build !windows

package uilive

import (
	"os"
	"runtime"
	"syscall"
	"unsafe"
)

type windowSize struct {
	rows    uint16
	cols    uint16
}

func getTermSize() (int, int) {
	var (
		out *os.File
		err error
		sz  windowSize
	)
	if runtime.GOOS == "openbsd" {
		out, err = os.OpenFile("/dev/tty", os.O_RDWR, 0)
		if err != nil {
			return 0, 0
		}
	} else {
		out, err = os.OpenFile("/dev/tty", os.O_WRONLY, 0)
		if err != nil {
			return 0, 0
		}
	}
	_, _, _ = syscall.Syscall(syscall.SYS_IOCTL,
		out.Fd(), uintptr(syscall.TIOCGWINSZ), uintptr(unsafe.Pointer(&sz)))
	return int(sz.cols), int(sz.rows)
}

What do you think? Please help me out.

Env specs:

OS Ubuntu 20.04 LTS x64
Kernel 5.7.7-050707-generic
Go 1.15.7

PR: #39

P.S.: big thanks for the library, it's awesome :)

Outputting to multiple lines

This is more a question than an issue.

I'd like to output and update the status of a list of tasks:

e.g.

Task1... Done
Task2... Done
Task3... Running
Task4... 

I've attempted to do this using multiple Fprintf statements as follows:

w := uilive.New()
w.Start()

for i := 0; i <= 60; i++ {
  var t1, t2, t3, t4 string

  if i > 2 {
    t1 = "Done     "
    t2 = "Running"
  }
  if i > 5 {
    t1 = "Done     "
    t2 = "Done     "
    t3 = "Running"
  }
  if i > 7 {
    t1 = "Done     "
    t2 = "Done     "
    t3 = "Done     "
    t4 = "Running"
  }
  if i > 9 {
    t1 = "Done     "
    t2 = "Done     "
    t3 = "Done     "
    t4 = "Done     "
  }

  fmt.Fprintf(w, "Task1...  %s\n", t1)
  fmt.Fprintf(w, "Task2...  %s\n", t2)
  fmt.Fprintf(w, "Task3...  %s\n", t3)
  fmt.Fprintf(w, "Task4...  %s\n", t4)
  time.Sleep(time.Second)
}

w.Stop()

This initially appears to work fine however on some iterations various tasks disappear and then reappear.

e.g.

# I get this
Task3...  Done
Task4...  Done

# instead of
Task1...  Done
Task2...  Done
Task3...  Done
Task4...  Done

Am I using this incorrectly or could this possibly be a bug?

Is this package maintained?

As stated - is anyone still maintaining this package? I see a lot of PRs and forks, but none of them seems to be a leading one...

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.