func (ui *Edit) SetCursor(c Cursor) {
	if c.Start < 0 {
		c.Start = c.Cur
	}
	ui.cursor = c
}
SetCursor sets the new cursor or selection.
Current is the new cursor. Start is the start of the selection.
If start < 0, it is set to current.
package duit
import (
	"bufio"
	"bytes"
	"fmt"
	"image"
	"io"
	"io/ioutil"
	"log"
	"regexp"
	"strings"
	"unicode"
	"unicode/utf8"
	"9fans.net/go/draw"
)
type SeekReaderAt interface {
	io.Seeker
	io.ReaderAt
}
var (
	EditPadding = Space{0, 3, 0, 3}  
)
type editMode int
const (
	modeInsert     editMode = iota  
	modeCommand                     
	modeVisual                      
	modeVisualLine                  
)
type EditColors struct {
	Fg, Bg,
	SelFg, SelBg,
	ScrollVis, ScrollBg,
	HoverScrollVis, HoverScrollBg,
	CommandBorder, VisualBorder *draw.Image
}
type Cursor struct {
	Cur   int64  
	Start int64  
}
type Edit struct {
	NoScrollbar  bool                                        
	LastSearch   string                                      
	Error        chan error                                  
	Colors       *EditColors                                `json:"-"`  
	Font         *draw.Font                                 `json:"-"`  
	Keys         func(k rune, m draw.Mouse) (e Event)       `json:"-"`  
	Click        func(m draw.Mouse, offset int64) (e Event) `json:"-"`  
	DirtyChanged func(dirty bool)                           `json:"-"`  
	dui *DUI  
	text   *text   
	offset int64   
	cursor Cursor  
	lastSearchRegexpString string  
	lastSearchRegexp       *regexp.Regexp
	mode    editMode
	command string  
	visual  string  
	lastCommand         string
	needLastCommandText bool
	lastCommandText     []byte  
	dirty bool
	r,
	barR,
	barActiveR,
	textR image.Rectangle
	textM,
	prevTextB1 draw.Mouse
	lastCursorPoint image.Point
}
func (c Cursor) Ordered() (int64, int64) {
	if c.Cur > c.Start {
		return c.Start, c.Cur
	}
	return c.Cur, c.Start
}
func (c Cursor) size() int64 {
	c0, c1 := c.Ordered()
	return c1 - c0
}
func (ui *Edit) error(err error, msg string) bool {
	if err == nil {
		return false
	}
	err = fmt.Errorf("%s: %s", msg, err)
	go func() {
		if ui.Error != nil {
			ui.Error <- err
		} else {
			ui.dui.Error <- err
		}
	}()
	return true
}
func (ui *Edit) colors() EditColors {
	if ui.Colors != nil {
		return *ui.Colors
	}
	dui := ui.dui
	return EditColors{
		Fg:             dui.Regular.Normal.Text,
		Bg:             dui.Regular.Normal.Background,
		SelFg:          dui.Inverse.Text,
		SelBg:          dui.Inverse.Background,
		ScrollVis:      dui.ScrollVisibleNormal,
		ScrollBg:       dui.ScrollBGNormal,
		HoverScrollVis: dui.ScrollVisibleHover,
		HoverScrollBg:  dui.ScrollBGHover,
		CommandBorder:  dui.CommandMode,
		VisualBorder:   dui.VisualMode,
	}
}
func NewEdit(f SeekReaderAt) (ui *Edit, err error) {
	size, err := f.Seek(0, io.SeekEnd)
	if err != nil {
		return
	}
	parts := []textPart{}
	if size > 0 {
		parts = append(parts, &file{f, 0, size})
	}
	ui = &Edit{
		text: &text{file: f, l: parts},
	}
	return
}
type reverseReader struct {
	src    io.ReaderAt
	offset int64  
}
var _ io.Reader = &reverseReader{}
func readAtFull(src io.ReaderAt, buf []byte, offset int64) (read int, err error) {
	want := len(buf)
	for want > 0 {
		var n int
		n, err = src.ReadAt(buf, offset)
		if n > 0 {
			read += n
			offset += int64(n)
			want -= n
			buf = buf[n:]
		}
		if err == io.EOF {
			return
		}
	}
	return
}
func (r *reverseReader) Read(buf []byte) (int, error) {
	want := int64(len(buf))
	want = minimum64(want, r.offset)
	if want == 0 {
		return 0, io.EOF
	}
	have, err := readAtFull(r.src, buf[:want], r.offset-want)
	if err != nil && err != io.EOF {
		return have, err
	}
	buf = buf[:have]
	orignbuf := make([]byte, have)
	nbuf := orignbuf
	obuf := buf
	for len(buf) > 0 {
		_, size := utf8.DecodeLastRune(buf)
		if size == 0 {
			break
		}
		copy(nbuf[:], buf[len(buf)-size:])
		buf = buf[:len(buf)-size]
		nbuf = nbuf[size:]
	}
	have -= len(buf)
	copy(obuf[:], orignbuf[:have])
	if have > 0 {
		r.offset -= int64(have)
	}
	return have, err
}
type reader struct {
	ui      *Edit
	n       int64  
	r       *bufio.Reader
	offset  int64
	forward bool
}
func (r *reader) Offset() int64 {
	if r.forward {
		return r.offset + r.n
	}
	return r.offset - r.n
}
func (r *reader) Peek() (rune, bool) {
	c, size, err := r.r.ReadRune()
	if size <= 0 && err == io.EOF {
		return 0, true
	}
	if r.ui.error(err, "readrune") {
		return -1, true
	}
	r.r.UnreadRune()
	return c, false
}
func (r *reader) Get() rune {
	c, size, err := r.r.ReadRune()
	if r.ui.error(err, "readrune") {
		return -1
	}
	r.n += int64(size)
	return c
}
func (r *reader) TryGet() (rune, error) {
	c, size, err := r.r.ReadRune()
	if err != nil {
		return 0, err
	}
	r.n += int64(size)
	return c, nil
}
func (r *reader) Line(includeNewline bool) (runes int, s string, eof bool) {
	var c rune
	for {
		c, eof = r.Peek()
		if eof {
			eof = s == ""
			break
		}
		if c == '\n' {
			if includeNewline {
				runes++
				r.Get()
			}
			break
		}
		r.Get()
		runes++
		s += string(c)
	}
	return
}
func (r *reader) gather(keep func(c rune) bool) (s string, eof bool) {
	var c rune
	for {
		c, eof = r.Peek()
		if eof {
			eof = s == ""
			break
		}
		if !keep(c) {
			break
		}
		r.Get()
		s += string(c)
	}
	return
}
func (r *reader) Whitespace(newline bool) (s string, eof bool) {
	return r.gather(func(c rune) (keep bool) {
		return (c != '\n' || newline) && unicode.IsSpace(c)
	})
}
func (r *reader) Nonwhitespace() (s string, eof bool) {
	return r.gather(func(c rune) (keep bool) {
		return !unicode.IsSpace(c)
	})
}
func (r *reader) Whitespacepunct(newline bool) (s string, eof bool) {
	return r.gather(func(c rune) (keep bool) {
		return (c != '\n' || newline) && (unicode.IsSpace(c) || unicode.IsPunct(c))
	})
}
func (r *reader) Nonwhitespacepunct() (s string, eof bool) {
	return r.gather(func(c rune) (keep bool) {
		return !unicode.IsSpace(c) && !unicode.IsPunct(c)
	})
}
func (r *reader) Punctuation() (s string, eof bool) {
	return r.gather(func(c rune) (keep bool) {
		return unicode.IsPunct(c)
	})
}
func (ui *Edit) ensureInit() {
	if ui.text == nil {
		ui.text = &text{}
	}
}
type EditReader interface {
	Peek() (r rune, eof bool)                                  
	TryGet() (r rune, err error)                               
	Get() (r rune)                                             
	Offset() (offset int64)                                    
	Whitespace(newline bool) (s string, eof bool)              
	Nonwhitespace() (s string, eof bool)                       
	Whitespacepunct(newline bool) (s string, eof bool)         
	Nonwhitespacepunct() (s string, eof bool)                  
	Punctuation() (s string, eof bool)                         
	Line(includeNewline bool) (runes int, s string, eof bool)  
}
type ReaderReaderAt interface {
	io.Reader
	io.ReaderAt
}
func (ui *Edit) Text() ([]byte, error) {
	return ioutil.ReadAll(ui.Reader())
}
func (ui *Edit) Reader() ReaderReaderAt {
	ui.ensureInit()
	return io.NewSectionReader(ui.text, 0, ui.text.Size()-0)
}
func (ui *Edit) EditReader(offset int64) EditReader {
	ui.ensureInit()
	return ui.reader(offset, ui.text.Size())
}
func (ui *Edit) ReverseEditReader(offset int64) EditReader {
	ui.ensureInit()
	return ui.revReader(offset)
}
func (ui *Edit) reader(offset, size int64) *reader {
	return &reader{ui, 0, bufio.NewReader(io.NewSectionReader(ui.text, offset, size-offset)), offset, true}
}
func (ui *Edit) revReader(offset int64) *reader {
	return &reader{ui, 0, bufio.NewReader(&reverseReader{ui.text, offset}), offset, false}
}
func (ui *Edit) font() *draw.Font {
	return ui.dui.Font(ui.Font)
}
func (ui *Edit) Layout(dui *DUI, self *Kid, sizeAvail image.Point, force bool) {
	ui.dui = dui
	dui.debugLayout(self)
	ui.ensureInit()
	ui.r = rect(sizeAvail)
	ui.barR = ui.r
	if ui.NoScrollbar {
		ui.barR.Max.X = ui.barR.Min.X
	} else {
		ui.barR.Max.X = ui.barR.Min.X + dui.Scale(ScrollbarSize)
	}
	ui.barActiveR = ui.barR  
	ui.textR = ui.r
	ui.textR.Min.X = ui.barR.Max.X
	ui.textR = dui.ScaleSpace(EditPadding).Inset(ui.textR)
	self.R = ui.r
}
func (ui *Edit) Draw(dui *DUI, self *Kid, img *draw.Image, orig image.Point, m draw.Mouse, force bool) {
	ui.dui = dui
	dui.debugDraw(self)
	ui.ensureInit()
	if ui.r.Empty() {
		return
	}
	colors := ui.colors()
	pad := dui.ScaleSpace(EditPadding).Mul(-1)
	switch ui.mode {
	case modeInsert:
		img.Draw(pad.Inset(ui.textR.Add(orig)), colors.Bg, nil, image.ZP)
	case modeCommand:
		img.Draw(pad.Inset(ui.textR.Add(orig)), colors.CommandBorder, nil, image.ZP)
	case modeVisual, modeVisualLine:
		img.Draw(pad.Inset(ui.textR.Add(orig)), colors.VisualBorder, nil, image.ZP)
	}
	img.Draw(ui.textR.Add(orig), colors.Bg, nil, image.ZP)
	font := ui.font()
	s := ""
	sdx := 0
	lineWidth := ui.textR.Dx()
	line := 0
	size := ui.text.Size()
	rd := ui.reader(ui.offset, size)
	lines := ui.textR.Dy() / font.Height
	dropNewline := func(s string) string {
		if s != "" && s[len(s)-1] == '\n' {
			return s[:len(s)-1]
		}
		return s
	}
	c0, c1 := ui.cursor.Ordered()
	drawLine := func(offsetEnd int64, eof bool) {
		origS := s
		n := len(s)
		offset := offsetEnd - int64(n)
		p := orig.Add(ui.textR.Min).Add(image.Pt(0, line*font.Height))
		cursorp := image.Pt(-1, -1)
		drawCursor := func(haveSel, cursorAtBegin bool) {
			p0 := cursorp
			p1 := p0
			p1.Y += font.Height
			thick := 0
			if dui.Scale(1) > 1 {
				thick = 1
			}
			img.Line(p0, p1, 0, 0, thick, dui.Display.Black, image.ZP)
			ui.lastCursorPoint = p1.Sub(orig)
			if haveSel {
				pp := pt(dui.Scale(-2))
				if cursorAtBegin {
					pp.X *= -1
				}
				ui.lastCursorPoint = ui.lastCursorPoint.Add(pp)
			}
		}
		if offset < c0 {
			nn := minimum64(int64(n), c0-offset)
			pp := img.String(p, colors.Fg, image.ZP, font, dropNewline(s[:nn]))
			p.X = pp.X
			s = s[nn:]
			offset += nn
		}
		if offset == ui.cursor.Cur && ui.cursor.Cur == c0 && c0 != c1 && offset < offsetEnd {
			cursorp = p
		}
		haveSelection := offset >= c0 && offset < c1 && c1-c0 > 0 && offset < offsetEnd
		if haveSelection {
			nn := minimum64(c1, offsetEnd) - offset
			sels := s[:nn]
			toEnd := sels[len(sels)-1] == '\n'
			if toEnd {
				sels = sels[:len(sels)-1]
			}
			s = s[nn:]
			offset += nn
			if !(offset >= c1 && offsetEnd > offset) {
				toEnd = true
			}
			seldx := font.StringWidth(sels)
			selR := rect(image.Pt(seldx, font.Height)).Add(p)
			if toEnd {
				selR.Max.X = ui.textR.Max.X + orig.X
			}
			img.Draw(selR, colors.SelBg, nil, image.ZP)
			pp := img.String(p, colors.SelFg, image.ZP, font, sels)
			p.X = pp.X
		}
		if offset == ui.cursor.Cur && ui.cursor.Cur == c1 && (offset < offsetEnd || (offset == size && eof)) {
			cursorp = p
		}
		if cursorp.X >= 0 {
			drawCursor(haveSelection, ui.cursor.Cur < ui.cursor.Start)
		}
		if offset >= c1 && offsetEnd > offset {
			nn := int(offsetEnd - offset)
			pp := img.String(p, colors.Fg, image.ZP, font, dropNewline(s))
			p.X = pp.X
			s = s[nn:]
			offset += int64(nn)
		}
		if s != "" || offset != offsetEnd {
			panic(fmt.Sprintf("bug in drawLine, s %v, offset %d, offsetEnd %d, c0 %d c1 %d, line %d, sdx %d, origS %s", s, offset, offsetEnd, c0, c1, line, sdx, origS))
		}
		s = ""
		sdx = 0
		line++
	}
	for line < lines {
		c, eof := rd.Peek()
		if eof {
			drawLine(rd.Offset(), eof)
			break
		}
		if c == '\n' {
			s += string(rd.Get())
			drawLine(rd.Offset(), false)
			continue
		}
		dx := font.StringWidth(string(c))
		if sdx+dx < lineWidth {
			sdx += dx
			s += string(rd.Get())
			continue
		}
		drawLine(rd.Offset(), false)
		rd.Get()
		s = string(c)
		sdx = dx
	}
	barHover := m.In(ui.barR)
	bg := colors.ScrollBg
	vis := colors.ScrollVis
	if barHover {
		bg = colors.HoverScrollBg
		vis = colors.HoverScrollVis
	}
	if size == 0 {
		ui.barActiveR = ui.barR
	} else {
		ui.barActiveR.Min.Y = int(int64(ui.barR.Dy()) * ui.offset / size)
		ui.barActiveR.Max.Y = int(int64(ui.barR.Dy()) * rd.Offset() / size)
	}
	if ui.barR.Dx() > 0 {
		barActiveR := ui.barActiveR.Add(orig)
		barActiveR.Max.X -= 1  
		img.Draw(ui.barR.Add(orig), bg, nil, image.ZP)
		img.Draw(barActiveR, vis, nil, image.ZP)
	}
}
func (ui *Edit) scroll(lines int, self *Kid) {
	offset := ui.offset
	if lines > 0 {
		rd := ui.reader(ui.offset, ui.text.Size())
		eof := false
		for ; lines > 0 && !eof; lines-- {
			_, _, eof = rd.Line(true)
		}
		offset = rd.Offset()
	} else if lines < 0 {
		rd := ui.revReader(ui.offset)
		eof := false
		for ; lines < 0 && !eof; lines++ {
			rd.TryGet()
			_, _, eof = rd.Line(false)
		}
		offset = rd.Offset()
	}
	if offset != ui.offset {
		self.Draw = Dirty
	}
	ui.offset = offset
}
func (ui *Edit) expandNested(r *reader, up, down rune) int64 {
	nested := 1
	for {
		c, eof := r.Peek()
		if eof {
			return 0
		}
		if c == down {
			nested--
		} else if c == up {
			nested++
		}
		if nested == 0 {
			return r.n
		}
		r.Get()
	}
}
func (ui *Edit) ExpandedText() ([]byte, error) {
	ui.ensureInit()
	br := ui.revReader(ui.cursor.Cur)
	br.Nonwhitespace()
	fr := ui.reader(ui.cursor.Cur, ui.text.Size())
	fr.Nonwhitespace()
	return ui.readText(Cursor{br.Offset(), fr.Offset()})
}
func (ui *Edit) expand(offset int64, fr, br *reader) Cursor {
	const (
		Starts = "[{(<\"'`"
		Ends   = "]})>\"'`"
	)
	c, eof := br.Peek()
	index := strings.IndexRune(Starts, c)
	if !eof && index >= 0 {
		br.Get()
		n := ui.expandNested(fr, rune(Starts[index]), rune(Ends[index]))
		return Cursor{offset + n, offset}
	}
	c, eof = fr.Peek()
	index = strings.IndexRune(Ends, c)
	if !eof && index >= 0 {
		fr.Get()
		n := ui.expandNested(br, rune(Ends[index]), rune(Starts[index]))
		return Cursor{offset, offset - n}
	}
	c, eof = br.Peek()
	if c == '\n' {
		fr.Line(true)
		return Cursor{fr.Offset(), offset}
	} else if c, eof = fr.Peek(); c == '\n' || eof {
		if !eof {
			fr.Get()
		}
		br.Line(false)
		return Cursor{fr.Offset(), br.Offset()}
	}
	bc, _ := br.Peek()
	fc, _ := fr.Peek()
	if unicode.IsSpace(bc) && unicode.IsSpace(fc) {
		br.Whitespace(true)
		fr.Whitespace(true)
	} else {
		br.Nonwhitespacepunct()
		fr.Nonwhitespacepunct()
	}
	return Cursor{fr.Offset(), br.Offset()}
}
func (ui *Edit) checkDirty(odirty bool) {
	if odirty != ui.dirty && ui.DirtyChanged != nil {
		ui.DirtyChanged(ui.dirty)
	}
}
func (ui *Edit) Mouse(dui *DUI, self *Kid, m draw.Mouse, origM draw.Mouse, orig image.Point) (r Result) {
	ui.dui = dui
	ui.ensureInit()
	font := ui.font()
	scrollLines := func(y int) int {
		lines := ui.textR.Dy() / font.Height
		n := lines * y / ui.textR.Dy()
		if n == 0 {
			return 1
		}
		return n
	}
	if origM.In(ui.barR) {
		o := ui.offset
		switch m.Buttons {
		case Button1:
			ui.scroll(-scrollLines(origM.Y), self)
		case Button2:
			y := maximum(0, minimum(m.Y, ui.textR.Dy()))
			rd := ui.revReader(ui.text.Size() * int64(y) / int64(ui.textR.Dy()))
			for {
				c, eof := rd.Peek()
				if eof || c == '\n' {
					break
				}
				rd.Get()
			}
			if rd.Offset() != ui.offset {
				self.Draw = Dirty
			}
			ui.offset = rd.Offset()
		case Button3:
			ui.scroll(scrollLines(origM.Y), self)
		case Button4:
			ui.scroll(-scrollLines(origM.Y/4), self)
		case Button5:
			ui.scroll(scrollLines(origM.Y/4), self)
		}
		r.Consumed = o != ui.offset
		return
	}
	if !origM.In(dui.ScaleSpace(EditPadding).Mul(-1).Inset(ui.textR)) {
		return
	}
	defer ui.checkDirty(ui.dirty)
	origM.Point = origM.Point.Sub(ui.textR.Min)
	origM.Point.X = maximum(0, origM.Point.X)
	m.Point = m.Point.Sub(ui.textR.Min)
	m.Point.X = maximum(0, m.Point.X)
	om := ui.textM
	ui.textM = m
	switch m.Buttons {
	case Button4, Button5:
		o := ui.offset
		switch m.Buttons {
		case Button4:
			ui.scroll(-scrollLines(m.Y/4), self)
		case Button5:
			ui.scroll(scrollLines(m.Y/4), self)
		}
		r.Consumed = ui.offset != o
		return
	}
	mouseOffset := func() int64 {
		line := m.Y / ui.font().Height
		xmax := ui.textR.Dx()
		if line < 0 {
			rd := ui.revReader(ui.offset)
			var s []rune  
			x := 0
			finishLine := func() (offset int64, done bool) {
				if line < 0 {
					line++
					s = nil
					x = 0
					return rd.Offset(), false
				}
				x := 0
				o := 0
				for i := len(s) - 1; i >= 0; i-- {
					dx := font.StringWidth(string(s[i]))
					if x+2*dx/3 > xmax {
						break
					}
					o += len(string(s[i]))
				}
				return rd.Offset() + int64(o), true
			}
			for {
				c, eof := rd.Peek()
				if eof {
					r, _ := finishLine()
					return r
				}
				if c == '\n' {
					if r, done := finishLine(); done {
						return r
					}
					rd.Get()
					continue
				}
				dx := font.StringWidth(string(c))
				if x+dx > xmax {
					if r, done := finishLine(); done {
						return r
					}
				}
				rd.Get()
				s = append(s, c)
				x += dx
			}
		}
		rd := ui.reader(ui.offset, ui.text.Size())
		x := 0
		mX := m.X
		for {
			c, eof := rd.Peek()
			if eof {
				break
			}
			if c == '\n' {
				if line == 0 {
					break
				}
				rd.Get()
				line--
				x = 0
				continue
			}
			dx := font.StringWidth(string(c))
			if line == 0 && (x+2*dx/3 > mX || x+dx > xmax) {
				break
			}
			x += dx
			if x > xmax {
				line--
				x = 0
			} else {
				rd.Get()
			}
		}
		return rd.Offset()
	}
	if m.Buttons^om.Buttons != 0 && ui.mode != modeInsert {
		ui.mode = modeInsert
		ui.command = ""
		ui.visual = ""
	}
	if m.Buttons == Button1 {
		ui.cursor.Cur = mouseOffset()
		ui.ScrollCursor(dui)
		if om.Buttons == 0 {
			if m.Msec-ui.prevTextB1.Msec < 350 {
				ui.cursor = ui.expand(ui.cursor.Cur, ui.reader(ui.cursor.Cur, ui.text.Size()), ui.revReader(ui.cursor.Cur))
			} else {
				ui.cursor.Start = ui.cursor.Cur
			}
			ui.prevTextB1 = m
		}
		self.Draw = Dirty
		r.Consumed = true
		return
	}
	if m.Buttons == 0 && om.Buttons&(Button1|Button2|Button3) != 0 && ui.Click != nil {
		e := ui.Click(om, mouseOffset())
		propagateEvent(self, &r, e)
	}
	if m.Buttons^om.Buttons != 0 {
		ui.text.closeHist(ui)
	} else if m.Buttons != 0 && m.Buttons == om.Buttons {
	} else if om.Buttons != 0 && m.Buttons == 0 {
	}
	r.Consumed = true
	return
}
func (ui *Edit) readText(c Cursor) ([]byte, error) {
	c0, c1 := c.Ordered()
	r := io.NewSectionReader(ui.text, c0, c1-c0)
	return ioutil.ReadAll(r)
}
func (ui *Edit) selectionText() ([]byte, error) {
	c0, c1 := ui.cursor.Ordered()
	r := io.NewSectionReader(ui.text, c0, c1-c0)
	return ioutil.ReadAll(r)
}
func (ui *Edit) Selection() ([]byte, error) {
	ui.ensureInit()
	return ui.selectionText()
}
func (ui *Edit) Cursor() Cursor {
	return ui.cursor
}