Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 18 additions & 8 deletions cursor.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,27 @@ package cursor

import (
"fmt"
"io"
"os"
)

var target io.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) {
target = w
}

// Up moves the cursor n lines up relative to the current position.
func Up(n int) {
fmt.Printf("\x1b[%dA", n)
fmt.Fprintf(target, "\x1b[%dA", n)
height += n
}

// Down moves the cursor n lines down relative to the current position.
func Down(n int) {
fmt.Printf("\x1b[%dB", n)
fmt.Fprintf(target, "\x1b[%dB", n)
if height-n <= 0 {
height = 0
} else {
Expand All @@ -24,36 +34,36 @@ func Down(n int) {

// Right moves the cursor n characters to the right relative to the current position.
func Right(n int) {
fmt.Printf("\x1b[%dC", n)
fmt.Fprintf(target, "\x1b[%dC", n)
}

// Left moves the cursor n characters to the left relative to the current position.
func Left(n int) {
fmt.Printf("\x1b[%dD", n)
fmt.Fprintf(target, "\x1b[%dD", n)
}

// HorizontalAbsolute moves the cursor to n horizontally.
// The position n is absolute to the start of the line.
func HorizontalAbsolute(n int) {
n += 1 // Moves the line to the character after n
fmt.Printf("\x1b[%dG", n)
fmt.Fprintf(target, "\x1b[%dG", n)
}

// Show the cursor if it was hidden previously.
// 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 they reopen the terminal.
func Show() {
fmt.Print("\x1b[?25h")
fmt.Fprint(target, "\x1b[?25h")
}

// Hide the cursor.
// 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 they reopen the terminal.
func Hide() {
fmt.Print("\x1b[?25l")
fmt.Fprintf(target, "\x1b[?25l")
}

// ClearLine clears the current line and moves the cursor to it's start position.
func ClearLine() {
fmt.Print("\x1b[2K")
fmt.Fprintf(target, "\x1b[2K")
}
74 changes: 74 additions & 0 deletions cursor_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package cursor

import (
"bytes"
"fmt"
"runtime"
"testing"
)

Expand All @@ -25,3 +27,75 @@ 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)
}
}
6 changes: 6 additions & 0 deletions cursor_windows.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
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) {
}

// Up moves the cursor n lines up relative to the current position.
func Up(n int) {
move(0, -n)
Expand Down