func (er *EventReader) ExtraArg() (path string, s, e int, txt string) {
	return er.xpath, er.xs, er.xe, er.xtxt
}
Returns the extra argument as: path of the buffer containing it, start and end points of the selection and text
package util
import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"strconv"
	"strings"
	"time"
	"unicode"
	"unicode/utf8"
)
type EventOrigin rune
type EventType rune
type EventFlag int
const MAX_EVENT_TEXT_LENGTH = 256
const (
	EO_BODYTAG = EventOrigin('E')
	EO_FILES   = EventOrigin('F')
	EO_KBD     = EventOrigin('K')
	EO_MOUSE   = EventOrigin('M')
)
const (
	ET_BODYDEL  = EventType('D')
	ET_TAGDEL   = EventType('d')
	ET_BODYINS  = EventType('I')
	ET_TAGINS   = EventType('i')
	ET_BODYLOAD = EventType('L')
	ET_TAGLOAD  = EventType('l')
	ET_BODYEXEC = EventType('X')
	ET_TAGEXEC  = EventType('x')
)
const (
	EFX_BUILTIN  = EventFlag(1)
	EFX_EXPANDED = EventFlag(2)
	EFX_EXTRAARG = EventFlag(8)
	EFL_EXPANDED = EventFlag(2)
)
func fmteventEx(eventChan chan string, origin EventOrigin, istag bool, etype EventType, s, e int, flags EventFlag, arg string, onfail func()) bool {
	defer func() {
		ierr := recover()
		if ierr != nil {
			fmt.Fprintf(os.Stderr, "fmteventEx: %v\n", ierr)
		}
	}()
	if istag {
		etype = EventType(unicode.ToLower(rune(etype)))
	}
	if utf8.RuneCountInString(arg) >= MAX_EVENT_TEXT_LENGTH {
		arg = ""
	}
	t := time.NewTimer(100 * time.Millisecond)
	defer t.Stop()
	select {
	case eventChan <- fmt.Sprintf("%c%c%d %d %d %d %s\n", origin, etype, s, e, flags, utf8.RuneCountInString(arg), arg):
		return true
	case <-t.C:
		onfail()
		return false
	}
}
func FmteventBase(eventChan chan string, origin EventOrigin, istag bool, etype EventType, s, e int, arg string, onfail func()) {
	fmteventEx(eventChan, origin, istag, etype, s, e, 0, arg, onfail)
}
func Fmtevent2(eventChan chan string, origin EventOrigin, istag, isbltin, hasextra bool, p, s, e int, arg string, onfail func()) {
	flags := EventFlag(0)
	if isbltin {
		flags = EFX_BUILTIN
	}
	if hasextra {
		flags |= EFX_EXTRAARG
	}
	if p >= 0 {
		ok := fmteventEx(eventChan, origin, istag, ET_BODYEXEC, p, p, flags|EFX_EXPANDED, "", onfail)
		if !ok {
			return
		}
		fmteventEx(eventChan, origin, istag, ET_BODYEXEC, s, e, 0, arg, onfail)
	} else {
		fmteventEx(eventChan, origin, istag, ET_BODYEXEC, s, e, flags, arg, onfail)
	}
}
func Fmtevent2extra(eventChan chan string, origin EventOrigin, istag bool, s, e int, originPath, arg string, onfail func()) {
	ok := fmteventEx(eventChan, origin, istag, ET_BODYEXEC, 0, 0, 0, arg, onfail)
	if !ok {
		return
	}
	fmteventEx(eventChan, origin, istag, ET_BODYEXEC, 0, 0, 0, fmt.Sprintf("%s:#%d,#%d", originPath, s, e), onfail)
}
func Fmtevent3(eventChan chan string, origin EventOrigin, istag bool, p, s, e int, arg string, onfail func()) {
	if p >= 0 {
		ok := fmteventEx(eventChan, origin, istag, ET_BODYLOAD, p, p, EFL_EXPANDED, "", onfail)
		if !ok {
			return
		}
	}
	fmteventEx(eventChan, origin, istag, ET_BODYLOAD, s, e, 0, arg, onfail)
}
type eventReaderState func(er *EventReader, msg string) eventReaderState
type EventReader struct {
	rbuf     []byte
	insertfn eventReaderState
	done     bool
	perr     string
	etype  EventType
	origin EventOrigin
	flags  EventFlag
	bltin  bool
	p, s, e int
	txt     string
	xs, xe      int
	xpath, xtxt string
	compat bool
}
func (er *EventReader) String() string {
	return fmt.Sprintf("done:%v etype:%v origin:%v flags:%v bltin:%v p:%v s:%v e:%v txt:[%s] xs:%v xe:%v xtxt:[%s]", er.done, er.etype, er.origin, er.flags, er.bltin, er.p, er.s, er.e, er.txt, er.xs, er.xe, er.xtxt)
}
func (er *EventReader) ReadFrom(in io.Reader) error {
	if er.rbuf == nil {
		er.rbuf = make([]byte, 2*MAX_EVENT_TEXT_LENGTH)
	}
	er.Reset()
	for !er.Done() {
		n, err := in.Read(er.rbuf)
		if err != nil {
			return err
		}
		if n < 1 {
			return fmt.Errorf("Short read from input")
		}
		er.Insert(string(er.rbuf[:n]))
	}
	return nil
}
func (er *EventReader) Insert(msg string) {
	if er.Done() {
		er.Reset()
	}
	er.insertfn = er.insertfn(er, msg)
}
func (er *EventReader) Done() bool {
	return er.done
}
func (er *EventReader) Valid() (bool, string) {
	return er.perr == "", er.perr
}
func (er *EventReader) Reset() {
	er.done = false
	er.insertfn = erBaseInsert
	er.perr = ""
	er.flags = 0
	er.etype = EventType(0)
	er.origin = EventOrigin(0)
	er.bltin = false
	er.compat = false
	er.p = -1
	er.s = -1
	er.e = -1
	er.txt = ""
	er.xs = -1
	er.xe = -1
	er.xtxt = ""
	er.xpath = ""
}
func (er *EventReader) SendBack(fh io.Writer) error {
	if !er.Done() {
		panic(fmt.Errorf("Tried to send back an incompletely read event"))
	}
	eventChan := make(chan string, 10)
	done := make(chan struct{})
	go func() {
		for e := range eventChan {
			_, err := fh.Write([]byte(e))
			if err != nil {
				return
			}
		}
		close(done)
	}()
	switch er.etype {
	case ET_BODYDEL, ET_TAGDEL, ET_BODYINS, ET_TAGINS:
		FmteventBase(eventChan, er.origin, false, er.etype, er.s, er.e, er.txt, func() {})
	case ET_BODYEXEC, ET_TAGEXEC:
		Fmtevent2(eventChan, er.origin, er.etype == ET_TAGEXEC, er.bltin, er.xs != -1, er.p, er.s, er.e, er.txt, func() {})
		if er.xs != -1 {
			Fmtevent2extra(eventChan, er.origin, er.etype == ET_TAGEXEC, er.xs, er.xe, er.xpath, er.xtxt, func() {})
		}
	case ET_BODYLOAD, ET_TAGLOAD:
		Fmtevent3(eventChan, er.origin, er.etype == ET_TAGLOAD, er.p, er.s, er.e, er.txt, func() {})
	}
	close(eventChan)
	<-done
	return nil
}
func (er *EventReader) Type() EventType {
	return er.etype
}
func (er *EventReader) Flags() EventFlag {
	return er.flags
}
func (er *EventReader) Origin() EventOrigin {
	return er.origin
}
func (er *EventReader) Points() (p, s, e int) {
	return er.p, er.s, er.e
}
func (er *EventReader) ShouldFetchText() bool {
	return (er.etype != ET_BODYDEL) && (er.etype != ET_TAGDEL) && (((er.s != er.e) && (er.txt == "")) || er.compat)
}
func (er *EventReader) IsCompat() bool {
	return er.compat
}
func (er *EventReader) BuiltIn() bool {
	return er.bltin
}
func (er *EventReader) Text(addrRead io.ReadSeeker, addrWrite io.Writer, xdata io.ReadSeeker) (txt string, err error) {
	txt = er.txt
	if !er.ShouldFetchText() {
		return
	}
	if (addrRead == nil) || (addrWrite == nil) || (xdata == nil) {
		return
	}
	b := make([]byte, 1024)
	addrRead.Seek(0, 0)
	n, err := addrRead.Read(b)
	if err != nil && n == 0 {
		return
	}
	oldaddr := b[:n]
	_, err = addrWrite.Write([]byte(fmt.Sprintf("#%d,#%d", er.s, er.e)))
	if err != nil {
		return
	}
	xdata.Seek(0, 0)
	bytes, err := ioutil.ReadAll(xdata)
	er.txt = string(bytes)
	txt = er.txt
	_, err = addrWrite.Write(oldaddr)
	if err != nil {
		return
	}
	return
}
func (er *EventReader) MissingExtraArg() bool {
	return (er.xs != er.xe) && (er.xtxt == "")
}