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  }