github.com/haraldrudell/parl@v0.4.176/mains/keystrokes-thread.go (about) 1 /* 2 © 2018–present Harald Rudell <harald.rudell@gmail.com> (https://haraldrudell.github.io/haraldrudell/) 3 ISC License 4 */ 5 6 package mains 7 8 import ( 9 "bufio" 10 "sync/atomic" 11 12 "github.com/haraldrudell/parl" 13 "github.com/haraldrudell/parl/perrors" 14 ) 15 16 // keystrokesThread reads blocking from [os.Stdin] therefore cannot be canceled 17 // - therefore, keystrokesThread is a top-level function not waited upon 18 // - on [Keystrokes.CloseNow], keystrokesThread exits on the following return keypress 19 // - on [os.Stdin] closing, keystrokesThread closes the Keystrokes channel 20 // - [StdinReader] converts any error to [io.EOF] 21 // - [parl.Infallible] prints any errors to standard error, should not be any 22 // - — 23 // - -verbose=mains.keystrokesThread 24 func keystrokesThread(silent bool, addError parl.AddError, stdin *parl.NBChan[string]) { 25 var err error 26 defer parl.Debug("keystrokes.scannerThread exiting: err: %s", perrors.Short(err)) 27 // if a panic is recovered, or err holds an error, both are printed to standard error 28 defer parl.Recover(func() parl.DA { return parl.A() }, &err, parl.Infallible) 29 // ensure string-channel closes on exit without discarding any input 30 defer stdin.Close() 31 32 var isError atomic.Bool 33 var scanner = bufio.NewScanner(NewStdinReader(addError, &isError)) 34 parl.Debug("keystrokes.scannerThread scanning: stdin.Ch: 0x%x", stdin.Ch()) 35 36 // blocks here 37 for scanner.Scan() { 38 // DidClose is true if close was invoked 39 // - stdin.Ch may not be closed yet 40 if stdin.DidClose() { 41 return // terminated by Keystrokes.CloseNow 42 } 43 if parl.IsThisDebug() { 44 parl.Log("keystrokes.Send %q", scanner.Text()) 45 } 46 stdin.Send(scanner.Text()) 47 } 48 49 // scanner had end of input or error 50 // - caused by a closing event like user pressing ^D or by 51 // - error during os.Stdin.Read or 52 // - error raised in Scanner 53 err = scanner.Err() 54 // do not print: 55 if silent || // - if silent is configured or 56 err != nil || // - the scanner had error or 57 isError.Load() { // - close is caused by an error handled by StdinReader 58 return 59 } 60 // echoed to standard error 61 // - echoed if: 62 // - stdin closed without error, eg. from user pressing ^D 63 // - silent is false 64 parl.Log("%s standard input closed", perrors.PackFunc()) 65 }