diff --git a/.github/release.yml b/.github/release.yml index 219005a..4f5b8e6 100644 --- a/.github/release.yml +++ b/.github/release.yml @@ -16,4 +16,4 @@ changelog: - fix - title: Other Changes labels: - - "*" \ No newline at end of file + - "*" diff --git a/.github/workflows/atomicgo.yml b/.github/workflows/atomicgo.yml index f8d3e91..1a9de94 100644 --- a/.github/workflows/atomicgo.yml +++ b/.github/workflows/atomicgo.yml @@ -1,6 +1,9 @@ -on: push - name: AtomicGo + +on: + push: + branches: [ main ] + jobs: docs: if: "!contains(github.event.head_commit.message, 'autoupdate')" diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 597968d..d258e38 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -4,7 +4,6 @@ on: push: branches: [ main ] pull_request: - branches: [ main ] jobs: build: diff --git a/.github/workflows/golangci.yml b/.github/workflows/golangci.yml deleted file mode 100644 index 6237a22..0000000 --- a/.github/workflows/golangci.yml +++ /dev/null @@ -1,16 +0,0 @@ -name: golangci-lint -on: [ push, pull_request ] -jobs: - golangci: - name: lint - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - - name: Set up Go - uses: actions/setup-go@v2 - - - name: golangci-lint - uses: golangci/golangci-lint-action@v3 - with: - version: latest diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000..800caea --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,17 @@ +name: Code analysis + +on: [pull_request] + +jobs: + golangci-lint: + runs-on: ubuntu-latest + steps: + - name: Check out code into the Go module directory + uses: actions/checkout@v3 + with: + fetch-depth: 0 + - name: Linting with golangci-lint + uses: reviewdog/action-golangci-lint@v2 + with: + github_token: ${{ secrets.ACCESS_TOKEN }} + reporter: github-pr-review diff --git a/area.go b/area.go index 91909ba..e467b56 100644 --- a/area.go +++ b/area.go @@ -2,6 +2,7 @@ package cursor import ( "fmt" + "os" "strings" ) @@ -9,16 +10,28 @@ import ( // You can use this to create live output, charts, dropdowns, etc. type Area struct { height int + writer Writer } // NewArea returns a new Area. func NewArea() Area { - return Area{} + return Area{ + writer: os.Stdout, + height: 0, + } +} + +// WithWriter sets a custom writer for the Area. +func (area Area) WithWriter(writer Writer) Area { + area.writer = writer + + return area } // Clear clears the content of the Area. func (area *Area) Clear() { Bottom() + if area.height > 0 { ClearLinesUp(area.height) } @@ -26,9 +39,13 @@ func (area *Area) Clear() { // Update overwrites the content of the Area. func (area *Area) Update(content string) { + oldWriter := target + + SetTarget(area.writer) // Temporary set the target to the Area's writer so we can use the cursor functions area.Clear() - fmt.Println(content) - height = 0 + SetTarget(oldWriter) // Reset the target to the old writer + fmt.Fprintln(area.writer, content) + height = 0 area.height = len(strings.Split(content, "\n")) } diff --git a/cursor.go b/cursor.go index fb4c010..e59e968 100644 --- a/cursor.go +++ b/cursor.go @@ -25,6 +25,7 @@ func Up(n int) { // Down moves the cursor n lines down relative to the current position. func Down(n int) { fmt.Fprintf(target, "\x1b[%dB", n) + if height-n <= 0 { height = 0 } else { @@ -45,7 +46,7 @@ func Left(n int) { // 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 + n++ // Moves the line to the character after n fmt.Fprintf(target, "\x1b[%dG", n) } diff --git a/cursor_test.go b/cursor_test.go index b74902c..23fdeed 100644 --- a/cursor_test.go +++ b/cursor_test.go @@ -9,11 +9,15 @@ func TestHeightChanges(t *testing.T) { for i := 0; i < 4; i++ { fmt.Println() } + Up(3) + if height != 3 { t.Errorf("height should be 3 but is %d", height) } + Down(3) + if height != 0 { t.Errorf("height should be 0 but is %d", height) } @@ -21,6 +25,7 @@ func TestHeightChanges(t *testing.T) { func TestHeightCannotBeNegative(t *testing.T) { Down(10) + if height < 0 { t.Errorf("height is negative: %d", height) } diff --git a/cursor_test_linux.go b/cursor_test_linux.go index 25179b2..6a37f40 100644 --- a/cursor_test_linux.go +++ b/cursor_test_linux.go @@ -6,75 +6,93 @@ import ( "testing" ) +// TestCustomIOWriter tests the cursor functions with a custom Writer. func TestCustomIOWriter(t *testing.T) { tmpFile, err := os.CreateTemp("", "testingTmpFile-") + defer os.Remove(tmpFile.Name()) + 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) } @@ -82,6 +100,7 @@ func TestCustomIOWriter(t *testing.T) { 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) @@ -94,12 +113,14 @@ func getFileContent(t *testing.T, fileName string) string { 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") diff --git a/utils.go b/utils.go index cde3668..6bf619b 100644 --- a/utils.go +++ b/utils.go @@ -10,6 +10,7 @@ func Bottom() { if height > 0 { Down(height) StartOfLine() + height = 0 } }