mirror of
https://github.com/chenasraf/watchr.git
synced 2026-05-17 17:28:06 +00:00
feat: stream new output in-place without resetting existing output
This commit is contained in:
@@ -178,11 +178,13 @@ func (r *Runner) Run(ctx context.Context) (Result, error) {
|
||||
|
||||
// StreamingResult holds the state of a streaming command
|
||||
type StreamingResult struct {
|
||||
Lines *[]Line
|
||||
ExitCode int
|
||||
Done bool
|
||||
Error error
|
||||
mu sync.RWMutex
|
||||
Lines *[]Line
|
||||
ExitCode int
|
||||
Done bool
|
||||
Error error
|
||||
PrevLineCount int // Number of lines from previous run (for trimming)
|
||||
CurrentLineCount int // Number of lines written by current run
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
// GetLines returns a copy of the current lines (thread-safe)
|
||||
@@ -214,14 +216,27 @@ func (s *StreamingResult) IsDone() bool {
|
||||
return s.Done
|
||||
}
|
||||
|
||||
// GetCurrentLineCount returns the number of lines written by the current run (thread-safe)
|
||||
func (s *StreamingResult) GetCurrentLineCount() int {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
return s.CurrentLineCount
|
||||
}
|
||||
|
||||
// RunStreaming executes the command and streams output lines in the background.
|
||||
// Returns a StreamingResult that can be polled for updates.
|
||||
// The command runs until ctx is cancelled or it completes naturally.
|
||||
func (r *Runner) RunStreaming(ctx context.Context) *StreamingResult {
|
||||
// If prevLines is provided, lines are updated in place rather than starting fresh.
|
||||
func (r *Runner) RunStreaming(ctx context.Context, prevLines []Line) *StreamingResult {
|
||||
// Copy previous lines to allow in-place updates
|
||||
lines := make([]Line, len(prevLines))
|
||||
copy(lines, prevLines)
|
||||
|
||||
result := &StreamingResult{
|
||||
Lines: &[]Line{},
|
||||
ExitCode: -1,
|
||||
Done: false,
|
||||
Lines: &lines,
|
||||
ExitCode: -1,
|
||||
Done: false,
|
||||
PrevLineCount: len(prevLines),
|
||||
}
|
||||
|
||||
go func() {
|
||||
@@ -268,14 +283,27 @@ func (r *Runner) RunStreaming(ctx context.Context) *StreamingResult {
|
||||
for scanner.Scan() {
|
||||
lineNumMu.Lock()
|
||||
currentLineNum := lineNum
|
||||
lineIdx := lineNum - 1 // 0-indexed
|
||||
lineNum++
|
||||
lineNumMu.Unlock()
|
||||
|
||||
result.mu.Lock()
|
||||
*result.Lines = append(*result.Lines, Line{
|
||||
newLine := Line{
|
||||
Number: currentLineNum,
|
||||
Content: sanitizeLine(scanner.Text()),
|
||||
})
|
||||
}
|
||||
|
||||
result.mu.Lock()
|
||||
if lineIdx < len(*result.Lines) {
|
||||
// Update existing line in place
|
||||
(*result.Lines)[lineIdx] = newLine
|
||||
} else {
|
||||
// Append new line
|
||||
*result.Lines = append(*result.Lines, newLine)
|
||||
}
|
||||
// Track how many lines this run has produced
|
||||
if currentLineNum > result.CurrentLineCount {
|
||||
result.CurrentLineCount = currentLineNum
|
||||
}
|
||||
result.mu.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,10 +157,11 @@ func (m *model) startStreaming() tea.Cmd {
|
||||
}
|
||||
m.ctx, m.cancel = context.WithCancel(context.Background())
|
||||
|
||||
m.streamResult = m.runner.RunStreaming(m.ctx)
|
||||
// Pass previous lines for in-place updates
|
||||
m.streamResult = m.runner.RunStreaming(m.ctx, m.lines)
|
||||
m.streaming = true
|
||||
m.loading = true
|
||||
m.lastLineCount = 0
|
||||
m.lastLineCount = len(m.lines)
|
||||
m.exitCode = -1
|
||||
m.errorMsg = ""
|
||||
m.userScrolled = false
|
||||
@@ -223,6 +224,13 @@ func (m *model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
m.errorMsg = m.streamResult.Error.Error()
|
||||
}
|
||||
|
||||
// Trim excess lines from previous run
|
||||
currentCount := m.streamResult.GetCurrentLineCount()
|
||||
if currentCount < len(m.lines) {
|
||||
m.lines = m.lines[:currentCount]
|
||||
m.updateFiltered()
|
||||
}
|
||||
|
||||
// If auto-refresh is enabled, schedule the next run
|
||||
if m.config.RefreshInterval > 0 {
|
||||
return m, m.tickCmd()
|
||||
|
||||
Reference in New Issue
Block a user