src

Go monorepo.
git clone git://code.dwrz.net/src
Log | Files | Refs

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 }