From 1d3505e40697e0c5995d569175df80c4d2d2bcfe Mon Sep 17 00:00:00 2001 From: Brooke Hatton Date: Wed, 16 Mar 2022 18:03:36 +0000 Subject: [PATCH 1/7] proposal to add windows support to the custom target functionality --- cursor.go | 6 ++-- cursor_test.go | 72 +++++++++++++++++++++++++++++++++-------------- cursor_windows.go | 18 ++++++------ utils.go | 7 +++++ 4 files changed, 71 insertions(+), 32 deletions(-) diff --git a/cursor.go b/cursor.go index 873b8ac..fb4c010 100644 --- a/cursor.go +++ b/cursor.go @@ -1,18 +1,18 @@ +//go:build !windows // +build !windows 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..2c3cd40 100644 --- a/cursor_test.go +++ b/cursor_test.go @@ -1,9 +1,9 @@ package cursor import ( - "bytes" "fmt" - "runtime" + "log" + "os" "testing" ) @@ -29,73 +29,103 @@ func TestHeightCannotBeNegative(t *testing.T) { } func TestCustomIOWriter(t *testing.T) { - if runtime.GOOS == "windows" { - t.Skip("skipping these tests on windows") + tmpFile, err := os.CreateTemp("", "testingTmpFile-") + if err != nil { + log.Fatal(err) } + defer os.Remove(tmpFile.Name()) - var w bytes.Buffer - SetTarget(&w) + w := tmpFile + SetTarget(w) Up(2) expected := "\x1b[2A" - actual := w.String() + actual := getFileContent(t, w.Name()) if expected != actual { t.Errorf("wanted: %v, got %v", expected, actual) } - w.Reset() + clearFile(t, w) Down(2) expected = "\x1b[2B" - actual = w.String() + actual = getFileContent(t, w.Name()) if expected != actual { t.Errorf("wanted: %v, got %v", expected, actual) } - w.Reset() + clearFile(t, w) Right(2) expected = "\x1b[2C" - actual = w.String() + actual = getFileContent(t, w.Name()) if expected != actual { t.Errorf("wanted: %v, got %v", expected, actual) } - w.Reset() + clearFile(t, w) Left(2) expected = "\x1b[2D" - actual = w.String() + actual = getFileContent(t, w.Name()) if expected != actual { t.Errorf("wanted: %v, got %v", expected, actual) } - w.Reset() + clearFile(t, w) Hide() expected = "\x1b[?25l" - actual = w.String() + actual = getFileContent(t, w.Name()) if expected != actual { t.Errorf("wanted: %v, got %v", expected, actual) } - w.Reset() + clearFile(t, w) Show() expected = "\x1b[?25h" - actual = w.String() + actual = getFileContent(t, w.Name()) if expected != actual { t.Errorf("wanted: %v, got %v", expected, actual) } - w.Reset() + clearFile(t, w) ClearLine() expected = "\x1b[2K" - actual = w.String() + actual = getFileContent(t, w.Name()) if expected != actual { t.Errorf("wanted: %v, got %v", expected, actual) } - w.Reset() + clearFile(t, w) HorizontalAbsolute(3) expected = "\x1b[4G" - actual = w.String() + 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") + + 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..2d7fb0a 100644 --- a/cursor_windows.go +++ b/cursor_windows.go @@ -7,9 +7,11 @@ import ( "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 = target.Fd + +// 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 +41,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 +56,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 +76,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 +89,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 +100,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 +} From 9564c535aa169bb007153428fdaea91c244f5d7e Mon Sep 17 00:00:00 2001 From: Brooke Hatton Date: Wed, 16 Mar 2022 18:09:28 +0000 Subject: [PATCH 2/7] remove extra line --- cursor.go | 1 - 1 file changed, 1 deletion(-) diff --git a/cursor.go b/cursor.go index fb4c010..113c786 100644 --- a/cursor.go +++ b/cursor.go @@ -1,4 +1,3 @@ -//go:build !windows // +build !windows package cursor From c43595a94b8c653e63e16ccd8bf71826fe20b03e Mon Sep 17 00:00:00 2001 From: Brooke Hatton Date: Sat, 16 Apr 2022 15:36:39 +0100 Subject: [PATCH 3/7] fix windows support --- cursor_windows.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cursor_windows.go b/cursor_windows.go index 2d7fb0a..0a3be0a 100644 --- a/cursor_windows.go +++ b/cursor_windows.go @@ -1,13 +1,12 @@ package cursor import ( - "io" "os" "syscall" "unsafe" ) -var target Writer = target.Fd +var target Writer = os.Stdout // SetTarget allows for any arbitrary Writer to be used func SetTarget(w Writer) { From cea076774c036b5b90f1e8126020022b0f8cb782 Mon Sep 17 00:00:00 2001 From: Brooke Hatton Date: Mon, 18 Apr 2022 12:23:54 +0100 Subject: [PATCH 4/7] fix linting issues --- cursor.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cursor.go b/cursor.go index 113c786..22e9e53 100644 --- a/cursor.go +++ b/cursor.go @@ -1,4 +1,4 @@ -// +build !windows +//go:build !windows package cursor From 6ad74ffe55227ba0e876233ec7ee12723fb4f845 Mon Sep 17 00:00:00 2001 From: MarvinJWendt Date: Sat, 4 Jun 2022 23:15:22 +0200 Subject: [PATCH 5/7] changed CustomIOWriter tests to not run on windows --- cursor_test.go | 104 ------------------------------------------- cursor_test_linux.go | 103 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+), 104 deletions(-) create mode 100644 cursor_test_linux.go diff --git a/cursor_test.go b/cursor_test.go index 2c3cd40..b74902c 100644 --- a/cursor_test.go +++ b/cursor_test.go @@ -2,8 +2,6 @@ package cursor import ( "fmt" - "log" - "os" "testing" ) @@ -27,105 +25,3 @@ func TestHeightCannotBeNegative(t *testing.T) { t.Errorf("height is negative: %d", height) } } - -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") - - 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_test_linux.go b/cursor_test_linux.go new file mode 100644 index 0000000..575e0c1 --- /dev/null +++ b/cursor_test_linux.go @@ -0,0 +1,103 @@ +package cursor + +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 + } +} From 279034cea7ca9ecfcf8851e2ffb80562fe2002b4 Mon Sep 17 00:00:00 2001 From: MarvinJWendt Date: Sat, 4 Jun 2022 23:16:48 +0200 Subject: [PATCH 6/7] fixed imports --- cursor_test_linux.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cursor_test_linux.go b/cursor_test_linux.go index 575e0c1..25179b2 100644 --- a/cursor_test_linux.go +++ b/cursor_test_linux.go @@ -1,5 +1,11 @@ package cursor +import ( + "log" + "os" + "testing" +) + func TestCustomIOWriter(t *testing.T) { tmpFile, err := os.CreateTemp("", "testingTmpFile-") if err != nil { From d46366e4de64a5fdd426d769b05f50d1b273e939 Mon Sep 17 00:00:00 2001 From: MarvinJWendt Date: Sat, 4 Jun 2022 23:21:16 +0200 Subject: [PATCH 7/7] changed codecov settings --- codecov.yml | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 codecov.yml 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