mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-18 02:40:45 +00:00
parent
2e545959dd
commit
395071e9f6
1 changed files with 48 additions and 36 deletions
|
|
@ -309,62 +309,62 @@ func (c *Console) Evaluate(statement string) {
|
||||||
// the configured user prompter.
|
// the configured user prompter.
|
||||||
func (c *Console) Interactive() {
|
func (c *Console) Interactive() {
|
||||||
var (
|
var (
|
||||||
prompt = c.prompt // Current prompt line (used for multi-line inputs)
|
prompt = c.prompt // Current prompt line (used for multi-line inputs)
|
||||||
indents = 0 // Current number of input indents (used for multi-line inputs)
|
indents = 0 // Current number of input indents (used for multi-line inputs)
|
||||||
input = "" // Current user input
|
input = "" // Current user input
|
||||||
scheduler = make(chan string) // Channel to send the next prompt on and receive the input
|
inputLine = make(chan string, 1) // receives user input
|
||||||
|
inputErr = make(chan error, 1) // receives liner errors
|
||||||
|
requestLine = make(chan string) // requests a line of input
|
||||||
|
interrupt = make(chan os.Signal, 1)
|
||||||
)
|
)
|
||||||
// Start a goroutine to listen for promt requests and send back inputs
|
|
||||||
go func() {
|
// Monitor Ctrl-C. While liner does turn on the relevant terminal mode bits to avoid
|
||||||
for {
|
// the signal, a signal can still be received for unsupported terminals. Unfortunately
|
||||||
// Read the next user input
|
// there is no way to cancel the line reader when this happens. The readLines
|
||||||
line, err := c.prompter.PromptInput(<-scheduler)
|
// goroutine will be leaked in this case.
|
||||||
if err != nil {
|
signal.Notify(interrupt, syscall.SIGINT, syscall.SIGTERM)
|
||||||
// In case of an error, either clear the prompt or fail
|
defer signal.Stop(interrupt)
|
||||||
if err == liner.ErrPromptAborted { // ctrl-C
|
|
||||||
prompt, indents, input = c.prompt, 0, ""
|
// The line reader runs in a separate goroutine.
|
||||||
scheduler <- ""
|
go c.readLines(inputLine, inputErr, requestLine)
|
||||||
continue
|
defer close(requestLine)
|
||||||
}
|
|
||||||
close(scheduler)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// User input retrieved, send for interpretation and loop
|
|
||||||
scheduler <- line
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
// Monitor Ctrl-C too in case the input is empty and we need to bail
|
|
||||||
abort := make(chan os.Signal, 1)
|
|
||||||
signal.Notify(abort, syscall.SIGINT, syscall.SIGTERM)
|
|
||||||
|
|
||||||
// Start sending prompts to the user and reading back inputs
|
// Start sending prompts to the user and reading back inputs
|
||||||
for {
|
for {
|
||||||
// Send the next prompt, triggering an input read and process the result
|
// Send the next prompt, triggering an input read.
|
||||||
scheduler <- prompt
|
requestLine <- prompt
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case <-abort:
|
case <-interrupt:
|
||||||
// User forcefully quite the console
|
|
||||||
fmt.Fprintln(c.printer, "caught interrupt, exiting")
|
fmt.Fprintln(c.printer, "caught interrupt, exiting")
|
||||||
return
|
return
|
||||||
|
|
||||||
case line, ok := <-scheduler:
|
case err := <-inputErr:
|
||||||
// User input was returned by the prompter, handle special cases
|
if err == liner.ErrPromptAborted && indents > 0 {
|
||||||
if !ok || (indents <= 0 && exit.MatchString(line)) {
|
// When prompting for multi-line input, the first Ctrl-C resets
|
||||||
|
// the multi-line state.
|
||||||
|
prompt, indents, input = c.prompt, 0, ""
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return
|
||||||
|
|
||||||
|
case line := <-inputLine:
|
||||||
|
// User input was returned by the prompter, handle special cases.
|
||||||
|
if indents <= 0 && exit.MatchString(line) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if onlyWhitespace.MatchString(line) {
|
if onlyWhitespace.MatchString(line) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Append the line to the input and check for multi-line interpretation
|
// Append the line to the input and check for multi-line interpretation.
|
||||||
input += line + "\n"
|
input += line + "\n"
|
||||||
|
|
||||||
indents = countIndents(input)
|
indents = countIndents(input)
|
||||||
if indents <= 0 {
|
if indents <= 0 {
|
||||||
prompt = c.prompt
|
prompt = c.prompt
|
||||||
} else {
|
} else {
|
||||||
prompt = strings.Repeat(".", indents*3) + " "
|
prompt = strings.Repeat(".", indents*3) + " "
|
||||||
}
|
}
|
||||||
// If all the needed lines are present, save the command and run
|
// If all the needed lines are present, save the command and run it.
|
||||||
if indents <= 0 {
|
if indents <= 0 {
|
||||||
if len(input) > 0 && input[0] != ' ' && !passwordRegexp.MatchString(input) {
|
if len(input) > 0 && input[0] != ' ' && !passwordRegexp.MatchString(input) {
|
||||||
if command := strings.TrimSpace(input); len(c.history) == 0 || command != c.history[len(c.history)-1] {
|
if command := strings.TrimSpace(input); len(c.history) == 0 || command != c.history[len(c.history)-1] {
|
||||||
|
|
@ -381,6 +381,18 @@ func (c *Console) Interactive() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// readLines runs in its own goroutine, prompting for input.
|
||||||
|
func (c *Console) readLines(input chan<- string, errc chan<- error, prompt <-chan string) {
|
||||||
|
for p := range prompt {
|
||||||
|
line, err := c.prompter.PromptInput(p)
|
||||||
|
if err != nil {
|
||||||
|
errc <- err
|
||||||
|
} else {
|
||||||
|
input <- line
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// countIndents returns the number of identations for the given input.
|
// countIndents returns the number of identations for the given input.
|
||||||
// In case of invalid input such as var a = } the result can be negative.
|
// In case of invalid input such as var a = } the result can be negative.
|
||||||
func countIndents(input string) int {
|
func countIndents(input string) int {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue