might fix repl bugs
This commit is contained in:
@@ -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()))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user