github.com/julianthome/gore@v0.0.0-20231109011145-b3a6bbe6fe55/liner.go (about) 1 package gore 2 3 import ( 4 "bytes" 5 "fmt" 6 "io" 7 "strings" 8 "text/scanner" 9 10 "github.com/peterh/liner" 11 ) 12 13 const ( 14 promptDefault = "gore> " 15 promptContinue = "..... " 16 indent = " " 17 ) 18 19 type contLiner struct { 20 *liner.State 21 buffer string 22 depth int 23 defaultPrompt string 24 } 25 26 func newContLiner(defaultPrompt string) *contLiner { 27 rl := liner.NewLiner() 28 rl.SetCtrlCAborts(true) 29 return &contLiner{State: rl, defaultPrompt: defaultPrompt} 30 } 31 32 func (cl *contLiner) promptString() string { 33 if cl.buffer != "" { 34 return promptContinue + strings.Repeat(indent, cl.depth) 35 } 36 37 return cl.defaultPrompt 38 } 39 40 func (cl *contLiner) Prompt() (string, error) { 41 line, err := cl.State.Prompt(cl.promptString()) 42 if err == io.EOF { 43 if cl.buffer != "" { 44 // cancel line continuation 45 cl.Accepted() 46 fmt.Println() 47 err = nil 48 } 49 } else if err == liner.ErrPromptAborted { 50 err = nil 51 if cl.buffer != "" { 52 cl.Accepted() 53 } else { 54 fmt.Println("(^D to quit)") 55 } 56 } else if err == nil { 57 if cl.buffer != "" { 58 cl.buffer = cl.buffer + "\n" + line 59 } else { 60 cl.buffer = line 61 } 62 } 63 64 return cl.buffer, err 65 } 66 67 func (cl *contLiner) Accepted() { 68 cl.State.AppendHistory(cl.buffer) 69 cl.buffer = "" 70 } 71 72 func (cl *contLiner) Clear() { 73 cl.buffer = "" 74 cl.depth = 0 75 } 76 77 var errUnmatchedBraces = fmt.Errorf("unmatched braces") 78 79 func (cl *contLiner) Reindent() error { 80 oldDepth := cl.depth 81 cl.depth = cl.countDepth() 82 83 if cl.depth < 0 { 84 return errUnmatchedBraces 85 } 86 87 if cl.depth < oldDepth { 88 lines := strings.Split(cl.buffer, "\n") 89 if len(lines) > 1 { 90 lastLine := lines[len(lines)-1] 91 92 cursorUp() 93 fmt.Printf("\r%s%s", cl.promptString(), lastLine) 94 eraseInLine() 95 fmt.Print("\n") 96 } 97 } 98 99 return nil 100 } 101 102 func (cl *contLiner) countDepth() int { 103 reader := bytes.NewBufferString(cl.buffer) 104 sc := new(scanner.Scanner) 105 sc.Init(reader) 106 sc.Error = func(_ *scanner.Scanner, msg string) { 107 debugf("scanner: %s", msg) 108 } 109 110 depth := 0 111 for { 112 switch sc.Scan() { 113 case '{', '(': 114 depth++ 115 case '}', ')': 116 depth-- 117 case scanner.EOF: 118 return depth 119 } 120 } 121 }