mirror of
https://github.com/johnkerl/miller.git
synced 2026-07-25 08:53:55 +00:00
control-C handler
This commit is contained in:
parent
f9a6be44f0
commit
5da3c2b426
5 changed files with 47 additions and 4 deletions
|
|
@ -18,7 +18,6 @@ const ENV_SECONDARY_PROMPT = "MLR_REPL_PS2"
|
|||
const DEFAULT_PRIMARY_PROMPT = "[mlr] "
|
||||
const DEFAULT_SECONDARY_PROMPT = ""
|
||||
|
||||
|
||||
func getInputIsTerminal() bool {
|
||||
return term.IsTerminal(int(os.Stdin.Fd()))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,9 @@ import (
|
|||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"miller/cliutil"
|
||||
"miller/dsl/cst"
|
||||
|
|
@ -63,6 +65,12 @@ func NewRepl(
|
|||
// types.MlrvalFromVoid().
|
||||
runtimeState.FilterExpression = types.MlrvalFromVoid()
|
||||
|
||||
// For control-C handling
|
||||
sysToSignalHandlerChannel := make(chan os.Signal, 1) // Our signal handler reads system notification here
|
||||
appSignalNotificationChannel := make(chan bool, 1) // Our signal handler writes this for our app to poll
|
||||
signal.Notify(sysToSignalHandlerChannel, os.Interrupt, syscall.SIGTERM)
|
||||
go controlCHandler(sysToSignalHandlerChannel, appSignalNotificationChannel)
|
||||
|
||||
return &Repl{
|
||||
exeName: exeName,
|
||||
replName: replName,
|
||||
|
|
@ -80,10 +88,23 @@ func NewRepl(
|
|||
recordReader: recordReader,
|
||||
recordWriter: recordWriter,
|
||||
|
||||
runtimeState: runtimeState,
|
||||
runtimeState: runtimeState,
|
||||
sysToSignalHandlerChannel: sysToSignalHandlerChannel,
|
||||
appSignalNotificationChannel: appSignalNotificationChannel,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// When the user types control-C, immediately print something to the screen,
|
||||
// then also write to a channel while long-running things like :skip and
|
||||
// :process can check it.
|
||||
func controlCHandler(sysToSignalHandlerChannel chan os.Signal, appSignalNotificationChannel chan bool) {
|
||||
for {
|
||||
<-sysToSignalHandlerChannel // Block until control-C notification is sent by system
|
||||
fmt.Println("^C") // Acknowledge for user reassurance
|
||||
appSignalNotificationChannel <- true // Let our app poll this
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
func (this *Repl) handleSession(istream *os.File) {
|
||||
this.printStartupBanner()
|
||||
|
|
@ -103,6 +124,14 @@ func (this *Repl) handleSession(istream *os.File) {
|
|||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Acknowledge any control-C's, even if typed at a ready prompt
|
||||
select {
|
||||
case _ = <-this.appSignalNotificationChannel:
|
||||
continue
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
// This trims the trailing newline, as well as leading/trailing whitespace:
|
||||
trimmedLine := strings.TrimSpace(line)
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@
|
|||
package repl
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"miller/cliutil"
|
||||
"miller/dsl/cst"
|
||||
"miller/input"
|
||||
|
|
@ -47,4 +49,8 @@ type Repl struct {
|
|||
recordWriter output.IRecordWriter
|
||||
|
||||
runtimeState *runtime.State
|
||||
|
||||
// For control-C handling
|
||||
sysToSignalHandlerChannel chan os.Signal // Our signal handler reads system notification here
|
||||
appSignalNotificationChannel chan bool // Our signal handler writes this for our app to poll
|
||||
}
|
||||
|
|
|
|||
|
|
@ -350,6 +350,8 @@ func handleSkipOrProcessN(this *Repl, n int, processingNotSkipping bool) {
|
|||
break
|
||||
case err = <-this.errorChannel:
|
||||
break
|
||||
case _ = <-this.appSignalNotificationChannel: // user typed control-C
|
||||
break
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
|
|
@ -398,11 +400,18 @@ func handleSkipOrProcessUntil(this *Repl, dslString string, processingNotSkippin
|
|||
var recordAndContext *types.RecordAndContext = nil
|
||||
|
||||
for {
|
||||
doubleBreak := false
|
||||
select {
|
||||
case recordAndContext = <-this.inputChannel:
|
||||
break
|
||||
case err = <-this.errorChannel:
|
||||
break
|
||||
case _ = <-this.appSignalNotificationChannel: // user typed control-C
|
||||
doubleBreak = true
|
||||
break
|
||||
}
|
||||
if doubleBreak {
|
||||
break
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
|
|
@ -715,7 +724,7 @@ func handleHelpSingle(this *Repl, arg string) {
|
|||
|
||||
func showREPLIntro(this *Repl) {
|
||||
fmt.Printf(
|
||||
`The Miller REPL is an interactive counterpart to record-processing using the
|
||||
`The Miller REPL is an interactive counterpart to record-processing using the
|
||||
put/filter DSL.
|
||||
|
||||
Using put and filter, you can do the following:
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
o make a README.md
|
||||
- rlwrap note
|
||||
|
||||
! for ^C -- need a :skip xxx / :process xxx -- also online help
|
||||
! output-specifier: write or append.
|
||||
- applies to all record-writer stuff incl strings
|
||||
- update ':help intro'
|
||||
|
|
@ -35,7 +36,6 @@
|
|||
o tilde-expand for load/open ...
|
||||
- if '~' is in the string, run it though sh -c echo ...
|
||||
|
||||
? :continue / ^C -- ?!?
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue