diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 0000000..bfdc987 --- /dev/null +++ b/codecov.yml @@ -0,0 +1,8 @@ +coverage: + status: + project: + default: + informational: true + patch: + default: + informational: true diff --git a/cursor.go b/cursor.go index 80cf416..fb4c010 100644 --- a/cursor.go +++ b/cursor.go @@ -5,15 +5,14 @@ package cursor import ( "fmt" - "io" "os" ) -var target io.Writer = os.Stdout +var target Writer = os.Stdout // SetTarget allows for any arbitrary io.Writer to be used // for cursor movement (will not work on Windows). -func SetTarget(w io.Writer) { +func SetTarget(w Writer) { target = w } diff --git a/cursor_test.go b/cursor_test.go index 43c9f20..b74902c 100644 --- a/cursor_test.go +++ b/cursor_test.go @@ -1,9 +1,7 @@ package cursor import ( - "bytes" "fmt" - "runtime" "testing" ) @@ -27,75 +25,3 @@ func TestHeightCannotBeNegative(t *testing.T) { t.Errorf("height is negative: %d", height) } } - -func TestCustomIOWriter(t *testing.T) { - if runtime.GOOS == "windows" { - t.Skip("skipping these tests on windows") - } - - var w bytes.Buffer - SetTarget(&w) - - Up(2) - expected := "\x1b[2A" - actual := w.String() - if expected != actual { - t.Errorf("wanted: %v, got %v", expected, actual) - } - - w.Reset() - Down(2) - expected = "\x1b[2B" - actual = w.String() - if expected != actual { - t.Errorf("wanted: %v, got %v", expected, actual) - } - - w.Reset() - Right(2) - expected = "\x1b[2C" - actual = w.String() - if expected != actual { - t.Errorf("wanted: %v, got %v", expected, actual) - } - - w.Reset() - Left(2) - expected = "\x1b[2D" - actual = w.String() - if expected != actual { - t.Errorf("wanted: %v, got %v", expected, actual) - } - - w.Reset() - Hide() - expected = "\x1b[?25l" - actual = w.String() - if expected != actual { - t.Errorf("wanted: %v, got %v", expected, actual) - } - - w.Reset() - Show() - expected = "\x1b[?25h" - actual = w.String() - if expected != actual { - t.Errorf("wanted: %v, got %v", expected, actual) - } - - w.Reset() - ClearLine() - expected = "\x1b[2K" - actual = w.String() - if expected != actual { - t.Errorf("wanted: %v, got %v", expected, actual) - } - - w.Reset() - HorizontalAbsolute(3) - expected = "\x1b[4G" - actual = w.String() - if expected != actual { - t.Errorf("wanted: %v, got %v", expected, actual) - } -} diff --git a/cursor_test_linux.go b/cursor_test_linux.go new file mode 100644 index 0000000..25179b2 --- /dev/null +++ b/cursor_test_linux.go @@ -0,0 +1,109 @@ +package cursor + +import ( + "log" + "os" + "testing" +) + +func TestCustomIOWriter(t *testing.T) { + tmpFile, err := os.CreateTemp("", "testingTmpFile-") + if err != nil { + log.Fatal(err) + } + defer os.Remove(tmpFile.Name()) + + w := tmpFile + SetTarget(w) + + Up(2) + expected := "\x1b[2A" + actual := getFileContent(t, w.Name()) + if expected != actual { + t.Errorf("wanted: %v, got %v", expected, actual) + } + + clearFile(t, w) + Down(2) + expected = "\x1b[2B" + actual = getFileContent(t, w.Name()) + if expected != actual { + t.Errorf("wanted: %v, got %v", expected, actual) + } + + clearFile(t, w) + Right(2) + expected = "\x1b[2C" + actual = getFileContent(t, w.Name()) + if expected != actual { + t.Errorf("wanted: %v, got %v", expected, actual) + } + + clearFile(t, w) + Left(2) + expected = "\x1b[2D" + actual = getFileContent(t, w.Name()) + if expected != actual { + t.Errorf("wanted: %v, got %v", expected, actual) + } + + clearFile(t, w) + Hide() + expected = "\x1b[?25l" + actual = getFileContent(t, w.Name()) + if expected != actual { + t.Errorf("wanted: %v, got %v", expected, actual) + } + + clearFile(t, w) + Show() + expected = "\x1b[?25h" + actual = getFileContent(t, w.Name()) + if expected != actual { + t.Errorf("wanted: %v, got %v", expected, actual) + } + + clearFile(t, w) + ClearLine() + expected = "\x1b[2K" + actual = getFileContent(t, w.Name()) + if expected != actual { + t.Errorf("wanted: %v, got %v", expected, actual) + } + + clearFile(t, w) + HorizontalAbsolute(3) + expected = "\x1b[4G" + actual = getFileContent(t, w.Name()) + if expected != actual { + t.Errorf("wanted: %v, got %v", expected, actual) + } +} + +func getFileContent(t *testing.T, fileName string) string { + t.Helper() + content, err := os.ReadFile(fileName) + if err != nil { + t.Errorf("failed to read file contents: %s", err) + + return "" + } + + return string(content) +} + +func clearFile(t *testing.T, file *os.File) { + t.Helper() + err := file.Truncate(0) + if err != nil { + t.Errorf("failed to clear file") + + return + } + _, err = file.Seek(0, 0) + if err != nil { + t.Errorf("failed to clear file") + + return + } +} diff --git a/cursor_windows.go b/cursor_windows.go index 79a9cf5..0a3be0a 100644 --- a/cursor_windows.go +++ b/cursor_windows.go @@ -1,15 +1,16 @@ package cursor import ( - "io" "os" "syscall" "unsafe" ) -// SetTarget allows for any arbitrary io.Writer to be used -// for cursor movement (will not work on Windows). -func SetTarget(w io.Writer) { +var target Writer = os.Stdout + +// SetTarget allows for any arbitrary Writer to be used +func SetTarget(w Writer) { + target = w } // Up moves the cursor n lines up relative to the current position. @@ -39,7 +40,7 @@ func Left(n int) { } func move(x int, y int) { - handle := syscall.Handle(os.Stdout.Fd()) + handle := syscall.Handle(target.Fd()) var csbi consoleScreenBufferInfo _, _, _ = procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) @@ -54,7 +55,7 @@ func move(x int, y int) { // HorizontalAbsolute moves the cursor to n horizontally. // The position n is absolute to the start of the line. func HorizontalAbsolute(n int) { - handle := syscall.Handle(os.Stdout.Fd()) + handle := syscall.Handle(target.Fd()) var csbi consoleScreenBufferInfo _, _, _ = procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) @@ -74,7 +75,7 @@ func HorizontalAbsolute(n int) { // Don't forget to show the cursor at least at the end of your application. // Otherwise the user might have a terminal with a permanently hidden cursor, until he reopens the terminal. func Show() { - handle := syscall.Handle(os.Stdout.Fd()) + handle := syscall.Handle(target.Fd()) var cci consoleCursorInfo _, _, _ = procGetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&cci))) @@ -87,7 +88,7 @@ func Show() { // Don't forget to show the cursor at least at the end of your application with Show. // Otherwise the user might have a terminal with a permanently hidden cursor, until he reopens the terminal. func Hide() { - handle := syscall.Handle(os.Stdout.Fd()) + handle := syscall.Handle(target.Fd()) var cci consoleCursorInfo _, _, _ = procGetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&cci))) @@ -98,7 +99,7 @@ func Hide() { // ClearLine clears the current line and moves the cursor to it's start position. func ClearLine() { - handle := syscall.Handle(os.Stdout.Fd()) + handle := syscall.Handle(target.Fd()) var csbi consoleScreenBufferInfo _, _, _ = procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) diff --git a/utils.go b/utils.go index 819b05f..981c277 100644 --- a/utils.go +++ b/utils.go @@ -1,5 +1,7 @@ package cursor +import "io" + var height int // Bottom moves the cursor to the bottom of the terminal. @@ -71,3 +73,8 @@ func ClearLinesDown(n int) { DownAndClear(1) } } + +type Writer interface { + io.Writer + Fd() uintptr +}