// Package prompt provides an implementation of the cli.Prompt interface.
package prompt import ( ) // Prompt implements a prompt that is executed asynchronously. type Prompt struct { config Config // Working directory when prompt was last updated. lastWd string // Channel for update requests. updateReq chan struct{} // Channel on which prompt contents are delivered. ch chan struct{} // Last computed prompt content. last ui.Text // Mutex for guarding access to the last field. lastMutex sync.RWMutex } // Config keeps configurations for the prompt. type Config struct { // The function that computes the prompt. Compute func() ui.Text // Function to transform stale prompts. StaleTransform func(ui.Text) ui.Text // Threshold for a prompt to be considered as stale. StaleThreshold func() time.Duration // How eager the prompt should be updated. When >= 5, updated when directory // is changed. When >= 10, always update. Default is 5. Eagerness func() int } func ( ui.Text) ui.Text { return ui.StyleText(, ui.Inverse) } const defaultStaleThreshold = 200 * time.Millisecond const defaultEagerness = 5 var unknownContent = ui.T("???> ") // New makes a new prompt. func ( Config) *Prompt { if .Compute == nil { .Compute = func() ui.Text { return unknownContent } } if .StaleTransform == nil { .StaleTransform = defaultStaleTransform } if .StaleThreshold == nil { .StaleThreshold = func() time.Duration { return defaultStaleThreshold } } if .Eagerness == nil { .Eagerness = func() int { return defaultEagerness } } := &Prompt{ , "", make(chan struct{}, 1), make(chan struct{}, 1), unknownContent, sync.RWMutex{}} // TODO: Don't keep a goroutine running. go .loop() return } func ( *Prompt) () { := unknownContent := make(chan ui.Text) for range .updateReq { go func() { <- .config.Compute() }() select { case <-time.After(.config.StaleThreshold()): // The prompt callback did not finish within the threshold. Send the // previous content, marked as stale. .update(.config.StaleTransform()) = <- select { case <-.updateReq: // If another update is already requested by the time we finish, // keep marking the prompt as stale. This reduces flickering. .update(.config.StaleTransform()) .queueUpdate() default: .update() } case = <-: .update() } } } // Trigger triggers an update to the prompt. func ( *Prompt) ( bool) { if || .shouldUpdate() { .queueUpdate() } } // Get returns the current content of the prompt. func ( *Prompt) () ui.Text { .lastMutex.RLock() defer .lastMutex.RUnlock() return .last } // LateUpdates returns a channel on which late updates are made available. func ( *Prompt) () <-chan struct{} { return .ch } func ( *Prompt) () { select { case .updateReq <- struct{}{}: default: } } func ( *Prompt) ( ui.Text) { .lastMutex.Lock() .last = .lastMutex.Unlock() .ch <- struct{}{} } func ( *Prompt) () bool { := .config.Eagerness() if >= 10 { return true } if >= 5 { , := os.Getwd() if != nil { = "error" } := .lastWd .lastWd = return != } return false }