might fix repl bugs

This commit is contained in:
Robin Olsen
2026-03-22 17:12:22 +01:00
parent ecabaf796e
commit cdb3d1dcd9
2 changed files with 39 additions and 30 deletions

View File

@@ -2,7 +2,6 @@
package repl package repl
import ( import (
"bufio"
"context" "context"
"fmt" "fmt"
"os" "os"
@@ -52,7 +51,7 @@ func (r *REPL) Run(ctx context.Context, config app.Config) error {
r.currentFeedback = ValidationFeedback{} r.currentFeedback = ValidationFeedback{}
r.completionChan = make(chan struct{}, 1) r.completionChan = make(chan struct{}, 1)
// Save terminal state to restore on exit (go-prompt v0.2.6 doesn't restore properly) // Save terminal state to restore on exit
oldState, err := term.GetState(int(os.Stdin.Fd())) oldState, err := term.GetState(int(os.Stdin.Fd()))
if err == nil { if err == nil {
defer term.Restore(int(os.Stdin.Fd()), oldState) defer term.Restore(int(os.Stdin.Fd()), oldState)
@@ -82,12 +81,20 @@ func (r *REPL) Run(ctx context.Context, config app.Config) error {
go r.watchValidation() go r.watchValidation()
} }
// Goroutine to inject newline when task completes to wake up blocked prompt
go func() {
for range r.completionChan {
if tty, err := os.OpenFile("/dev/tty", os.O_WRONLY, 0); err == nil {
tty.Write([]byte("\n"))
tty.Close()
}
}
}()
// history keeps track of command history. // history keeps track of command history.
// This enables navigating through previous commands.
var history []string var history []string
// Start the REPL loop, which continues until the user types "exit" or "quit" or task completes. // Start the REPL loop
reader := bufio.NewReader(os.Stdin)
replLoop: replLoop:
for !r.exitRequested { for !r.exitRequested {
// Check if we should exit before prompting (non-blocking check) // Check if we should exit before prompting (non-blocking check)
@@ -95,27 +102,22 @@ replLoop:
break break
} }
// Prompt the user for input, and provide suggestions. // Check if task completed before showing prompt
inputChan := make(chan string, 1) if r.taskCompleted {
go func(hist []string) { fmt.Println("\nTask completed successfully!")
inputChan <- prompt.Input( break replLoop
PromptPrefix, }
completer,
promptOptions(hist)...,
)
}(history)
var input string // Prompt the user for input, and provide suggestions.
select { input := prompt.Input(
case input = <-inputChan: PromptPrefix,
case <-r.completionChan: completer,
fmt.Println(style.TitleStyle.Render("Task completed successfully!")) promptOptions(history)...,
fmt.Print("Press Enter to exit...") )
// Restore terminal before reading final input (go-prompt doesn't restore properly)
if oldState != nil { // Check if task completed while at prompt (will be true if newline was injected)
term.Restore(int(os.Stdin.Fd()), oldState) if r.taskCompleted {
} fmt.Println("\nTask completed successfully!")
reader.ReadString('\n')
break replLoop break replLoop
} }
@@ -124,8 +126,7 @@ replLoop:
break break
} }
// Check if task completed while waiting at prompt // Trim whitespace from the input
// Trim whitespace from the input to ensure consistent command processing.
input = strings.TrimSpace(input) input = strings.TrimSpace(input)
// If the user types "exit" or "quit", break the loop and exit the REPL. // If the user types "exit" or "quit", break the loop and exit the REPL.
@@ -139,7 +140,7 @@ replLoop:
r.logAction("repl command: " + input) r.logAction("repl command: " + input)
} }
// Add the input to the history for future navigation. // Add the input to the history
history = append(history, input) history = append(history, input)
output, err := r.execute(input) output, err := r.execute(input)
@@ -153,11 +154,11 @@ replLoop:
} }
if err != nil { if err != nil {
// Show command output (even on error) in normal text style // Show command output (even on error)
if output != "" { if output != "" {
fmt.Println(style.TextStyle.Render(output)) fmt.Println(style.TextStyle.Render(output))
} }
// Show error message in red if no output was captured // Show error message in red if no output
if output == "" { if output == "" {
fmt.Println(style.ErrorStyle.Render(err.Error())) fmt.Println(style.ErrorStyle.Render(err.Error()))
} }

View File

@@ -4,9 +4,11 @@ import (
"context" "context"
"fmt" "fmt"
"log/slog" "log/slog"
"os"
"charm.land/bubbletea/v2" "charm.land/bubbletea/v2"
"github.com/LazyBachelor/LazyPM/internal/models" "github.com/LazyBachelor/LazyPM/internal/models"
"golang.org/x/term"
) )
type App = models.App type App = models.App
@@ -123,6 +125,12 @@ func (r *TaskRunner) Run(ctx context.Context, t Tasker, i Interface, iType Inter
} }
} }
if oldState, err := term.GetState(int(os.Stdin.Fd())); err == nil {
term.Restore(int(os.Stdin.Fd()), oldState)
}
fmt.Print("\033[0m\033[?25h")
// Questionnaire // Questionnaire
if err := runQuestionnaire(t, iType, collector); err != nil { if err := runQuestionnaire(t, iType, collector); err != nil {
return err return err