Cancel context with ControlC
This commit is contained in:
parent
2eab51d9f7
commit
fbc5f10547
@ -1,16 +1,16 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"context"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/c-bata/go-prompt-toolkit"
|
"github.com/c-bata/go-prompt-toolkit"
|
||||||
)
|
)
|
||||||
|
|
||||||
func executor(ctx context.Context, t string) string {
|
func executor(ctx context.Context, t string) string {
|
||||||
ctx, cancel := context.WithTimeout(ctx, 10 * time.Second)
|
ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
if t == "sleep 5s" {
|
if t == "sleep 5s" {
|
||||||
|
70
prompt.go
70
prompt.go
@ -33,6 +33,18 @@ type Prompt struct {
|
|||||||
history []string
|
history []string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Exec struct {
|
||||||
|
input string
|
||||||
|
ctx context.Context
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Exec) Context() context.Context {
|
||||||
|
if e.ctx == nil {
|
||||||
|
e.ctx = context.Background()
|
||||||
|
}
|
||||||
|
return e.ctx
|
||||||
|
}
|
||||||
|
|
||||||
func (p *Prompt) Run() {
|
func (p *Prompt) Run() {
|
||||||
p.setUp()
|
p.setUp()
|
||||||
defer p.tearDown()
|
defer p.tearDown()
|
||||||
@ -44,7 +56,7 @@ func (p *Prompt) Run() {
|
|||||||
} else {
|
} else {
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
log.SetOutput(f)
|
log.SetOutput(f)
|
||||||
log.Println("Logging is enabled.")
|
log.Println("[INFO] Logging is enabled.")
|
||||||
}
|
}
|
||||||
|
|
||||||
p.renderer.Render(p.buf, p.completer(p.buf.Text()), p.maxCompletions, p.selected)
|
p.renderer.Render(p.buf, p.completer(p.buf.Text()), p.maxCompletions, p.selected)
|
||||||
@ -59,11 +71,11 @@ func (p *Prompt) Run() {
|
|||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case b := <-bufCh:
|
case b := <-bufCh:
|
||||||
if shouldExecute, shouldExit, input := p.feed(b); shouldExit {
|
if shouldExit, exec := p.feed(b); shouldExit {
|
||||||
return
|
return
|
||||||
} else if shouldExecute {
|
} else if exec != nil {
|
||||||
ctx, _ := context.WithCancel(context.Background())
|
p.history = append(p.history, exec.input)
|
||||||
p.renderer.RenderResult(p.executor(ctx, input))
|
p.runExecutor(exec, bufCh)
|
||||||
|
|
||||||
completions := p.completer(p.buf.Text())
|
completions := p.completer(p.buf.Text())
|
||||||
p.updateSelectedCompletion(completions)
|
p.updateSelectedCompletion(completions)
|
||||||
@ -86,8 +98,29 @@ func (p *Prompt) Run() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Prompt) feed(b []byte) (shouldExecute, shouldExit bool, input string) {
|
func (p *Prompt) runExecutor(exec *Exec, bufCh chan []byte) {
|
||||||
shouldExecute = false
|
resCh := make(chan string, 1)
|
||||||
|
ctx, cancel := context.WithCancel(exec.Context())
|
||||||
|
go func() {
|
||||||
|
resCh <- p.executor(ctx, exec.input)
|
||||||
|
}()
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case r := <-resCh:
|
||||||
|
p.renderer.RenderResult(r)
|
||||||
|
return
|
||||||
|
case b := <-bufCh:
|
||||||
|
if p.in.GetKey(b) == ControlC {
|
||||||
|
log.Println("[INFO] Executor is canceled.")
|
||||||
|
cancel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Prompt) feed(b []byte) (shouldExit bool, exec *Exec) {
|
||||||
key := p.in.GetKey(b)
|
key := p.in.GetKey(b)
|
||||||
|
|
||||||
switch key {
|
switch key {
|
||||||
@ -102,10 +135,8 @@ func (p *Prompt) feed(b []byte) (shouldExecute, shouldExit bool, input string) {
|
|||||||
}
|
}
|
||||||
p.renderer.BreakLine(p.buf)
|
p.renderer.BreakLine(p.buf)
|
||||||
|
|
||||||
shouldExecute = true
|
exec = &Exec{input: p.buf.Text()}
|
||||||
input = p.buf.Text()
|
log.Printf("[History] %s", p.buf.Text())
|
||||||
p.history = append(p.history, input)
|
|
||||||
log.Printf("History: %s", input)
|
|
||||||
p.buf = NewBuffer()
|
p.buf = NewBuffer()
|
||||||
p.selected = -1
|
p.selected = -1
|
||||||
case ControlC:
|
case ControlC:
|
||||||
@ -127,6 +158,15 @@ func (p *Prompt) feed(b []byte) (shouldExecute, shouldExit bool, input string) {
|
|||||||
case Right:
|
case Right:
|
||||||
p.buf.CursorRight(1)
|
p.buf.CursorRight(1)
|
||||||
case Backspace:
|
case Backspace:
|
||||||
|
if p.selected != -1 {
|
||||||
|
c := p.completer(p.buf.Text())[p.selected]
|
||||||
|
w := p.buf.Document().GetWordBeforeCursor()
|
||||||
|
if w != "" {
|
||||||
|
p.buf.DeleteBeforeCursor(len([]rune(w)))
|
||||||
|
}
|
||||||
|
p.buf.InsertText(c.Text, false, true)
|
||||||
|
p.selected = -1
|
||||||
|
}
|
||||||
p.buf.DeleteBeforeCursor(1)
|
p.buf.DeleteBeforeCursor(1)
|
||||||
case NotDefined:
|
case NotDefined:
|
||||||
if p.selected != -1 {
|
if p.selected != -1 {
|
||||||
@ -193,19 +233,19 @@ func handleSignals(in ConsoleParser, exitCh chan int, winSizeCh chan *WinSize) {
|
|||||||
s := <-sigCh
|
s := <-sigCh
|
||||||
switch s {
|
switch s {
|
||||||
case syscall.SIGINT: // kill -SIGINT XXXX or Ctrl+c
|
case syscall.SIGINT: // kill -SIGINT XXXX or Ctrl+c
|
||||||
log.Println("SIGNAL: Catch SIGINT")
|
log.Println("[SIGNAL] Catch SIGINT")
|
||||||
exitCh <- 0
|
exitCh <- 0
|
||||||
|
|
||||||
case syscall.SIGTERM: // kill -SIGTERM XXXX
|
case syscall.SIGTERM: // kill -SIGTERM XXXX
|
||||||
log.Println("SIGNAL: Catch SIGTERM")
|
log.Println("[SIGNAL] Catch SIGTERM")
|
||||||
exitCh <- 1
|
exitCh <- 1
|
||||||
|
|
||||||
case syscall.SIGQUIT: // kill -SIGQUIT XXXX
|
case syscall.SIGQUIT: // kill -SIGQUIT XXXX
|
||||||
log.Println("SIGNAL: Catch SIGQUIT")
|
log.Println("[SIGNAL] Catch SIGQUIT")
|
||||||
exitCh <- 0
|
exitCh <- 0
|
||||||
|
|
||||||
case syscall.SIGWINCH:
|
case syscall.SIGWINCH:
|
||||||
log.Println("SIGNAL: Catch SIGWINCH")
|
log.Println("[SIGNAL] Catch SIGWINCH")
|
||||||
winSizeCh <- in.GetWinSize()
|
winSizeCh <- in.GetWinSize()
|
||||||
|
|
||||||
// TODO: SIGUSR1 -> Reopen log file.
|
// TODO: SIGUSR1 -> Reopen log file.
|
||||||
|
Loading…
Reference in New Issue
Block a user