prompt.go (2547B)
1 package prompt 2 3 import ( 4 "fmt" 5 "unicode" 6 "unicode/utf8" 7 8 "code.dwrz.net/src/pkg/editor/buffer/glyph" 9 "code.dwrz.net/src/pkg/editor/buffer/line" 10 "code.dwrz.net/src/pkg/log" 11 ) 12 13 type Prompt struct { 14 log *log.Logger 15 16 err error 17 cb CB 18 cursor cursor 19 line *line.Line 20 text string 21 } 22 23 type CB func(l line.Line) error 24 25 type cursor struct { 26 glyph int 27 index int 28 } 29 30 type Parameters struct { 31 Log *log.Logger 32 } 33 34 func New(p Parameters) *Prompt { 35 return &Prompt{log: p.Log} 36 } 37 38 func (p *Prompt) Complete() { 39 if err := p.cb(*p.line); err != nil { 40 p.log.Error.Printf("callback error: %v", err) 41 p.err = err 42 43 return 44 } 45 46 // No error -- we should be done. 47 p.cb = nil 48 p.err = nil 49 p.line = nil 50 p.text = "" 51 p.cursor = cursor{} 52 } 53 54 func (p *Prompt) Cursor() int { 55 return p.cursor.glyph 56 } 57 58 func (p *Prompt) Escape() { 59 p.cb = nil 60 p.err = nil 61 p.line = nil 62 p.text = "" 63 p.cursor = cursor{} 64 } 65 66 func (p *Prompt) IsPrompt() bool { 67 return p.line != nil 68 } 69 70 func (p *Prompt) Prompt(s string, cb CB) { 71 p.text = s 72 p.line = &line.Line{} 73 p.cb = cb 74 75 p.log.Debug.Printf("new prompt: %#v", p) 76 } 77 78 func (p *Prompt) SetText(s string) { 79 p.text = s 80 } 81 82 func (p *Prompt) Text() string { 83 if p.err != nil { 84 return fmt.Sprintf("[%v] %s", p.err, p.text) 85 } 86 87 return p.text 88 } 89 90 func (p *Prompt) Line(offset, width int) string { 91 if p.line == nil { 92 return "" 93 } 94 95 return p.line.Render(offset, width) 96 } 97 98 func (p *Prompt) CursorLeft() { 99 if p.cursor.index > 0 { 100 var ( 101 r rune 102 size int 103 ) 104 // Reverse until we hit the start of a rune. 105 for i := p.cursor.index - 1; i >= 0; i-- { 106 r, size = p.line.DecodeRune(i) 107 if r != utf8.RuneError { 108 p.cursor.index -= size 109 p.cursor.glyph -= glyph.Width(r) 110 break 111 } 112 } 113 } 114 } 115 116 func (p *Prompt) CursorRight() { 117 if p.cursor.index < p.line.Length() { 118 r, size := p.line.DecodeRune(p.cursor.index) 119 if r == utf8.RuneError { 120 p.cursor.index++ 121 p.cursor.glyph += glyph.Width(r) 122 } 123 124 p.cursor.index += size 125 p.cursor.glyph += glyph.Width(r) 126 } 127 } 128 129 func (p *Prompt) CursorHome() { 130 p.cursor.glyph = 0 131 p.cursor.index = 0 132 } 133 134 func (p *Prompt) CursorEnd() { 135 p.cursor.index = p.line.Length() 136 p.cursor.glyph = p.line.Width() 137 } 138 139 func (p *Prompt) Backspace() { 140 if p.cursor.index > 0 { 141 p.CursorLeft() 142 p.line.DeleteRune(p.cursor.index) 143 } 144 } 145 146 func (p *Prompt) Insert(r rune) { 147 switch { 148 // Ignore all other non-printable characters. 149 case !unicode.IsPrint(r): 150 return 151 152 default: 153 p.line.Insert(p.cursor.index, r) 154 p.cursor.index += utf8.RuneLen(r) 155 p.cursor.glyph += glyph.Width(r) 156 } 157 }