9fans.net/go@v0.0.5/acme/acme.go (about)

     1  // Package acme is a simple interface for interacting with acme windows.
     2  //
     3  // Many of the functions in this package take a format string and optional
     4  // parameters.  In the documentation, the notation format, ... denotes the result
     5  // of formatting the string and arguments using fmt.Sprintf.
     6  package acme // import "9fans.net/go/acme"
     7  
     8  import (
     9  	"bufio"
    10  	"bytes"
    11  	"errors"
    12  	"fmt"
    13  	"io"
    14  	"io/ioutil"
    15  	"log"
    16  	"os"
    17  	"path"
    18  	"reflect"
    19  	"sort"
    20  	"strconv"
    21  	"strings"
    22  	"sync"
    23  	"time"
    24  
    25  	"9fans.net/go/draw"
    26  	"9fans.net/go/plan9"
    27  	"9fans.net/go/plan9/client"
    28  )
    29  
    30  // A Win represents a single acme window and its control files.
    31  type Win struct {
    32  	id         int
    33  	ctl        *client.Fid
    34  	tag        *client.Fid
    35  	body       *client.Fid
    36  	addr       *client.Fid
    37  	event      *client.Fid
    38  	data       *client.Fid
    39  	xdata      *client.Fid
    40  	errors     *client.Fid
    41  	ebuf       *bufio.Reader
    42  	c          chan *Event
    43  	next, prev *Win
    44  	buf        []byte
    45  	e2, e3, e4 Event
    46  	name       string
    47  
    48  	errorPrefix string
    49  }
    50  
    51  var windowsMu sync.Mutex
    52  var windows, last *Win
    53  var autoExit bool
    54  
    55  var fsys *client.Fsys
    56  var fsysErr error
    57  var fsysOnce sync.Once
    58  
    59  // AutoExit sets whether to call os.Exit the next time the last managed acme window is deleted.
    60  // If there are no acme windows at the time of the call, the exit does not happen until one
    61  // is created and then deleted.
    62  func AutoExit(exit bool) {
    63  	windowsMu.Lock()
    64  	defer windowsMu.Unlock()
    65  	autoExit = exit
    66  }
    67  
    68  // New creates a new window.
    69  func New() (*Win, error) {
    70  	fsysOnce.Do(mountAcme)
    71  	if fsysErr != nil {
    72  		return nil, fsysErr
    73  	}
    74  	fid, err := fsys.Open("new/ctl", plan9.ORDWR)
    75  	if err != nil {
    76  		return nil, err
    77  	}
    78  	buf := make([]byte, 100)
    79  	n, err := fid.Read(buf)
    80  	if err != nil {
    81  		fid.Close()
    82  		return nil, err
    83  	}
    84  	a := strings.Fields(string(buf[0:n]))
    85  	if len(a) == 0 {
    86  		fid.Close()
    87  		return nil, errors.New("short read from acme/new/ctl")
    88  	}
    89  	id, err := strconv.Atoi(a[0])
    90  	if err != nil {
    91  		fid.Close()
    92  		return nil, errors.New("invalid window id in acme/new/ctl: " + a[0])
    93  	}
    94  	return Open(id, fid)
    95  }
    96  
    97  type WinInfo struct {
    98  	ID int
    99  	// TagLen holds the length of the tag in runes.
   100  	TagLen int
   101  	// TagLen holds the length of the body in runes.
   102  	BodyLen    int
   103  	IsDir      bool
   104  	IsModified bool
   105  	// Name and Tag are only populated when the
   106  	// WinInfo has been obtained by calling the Windows
   107  	// function, as they're not available by reading the ctl file.
   108  
   109  	// Name holds the filename of the window.
   110  	Name string
   111  
   112  	// Tag holds the rest of the tag after the filename.
   113  	Tag string
   114  
   115  	// The Size and History fields can only be non-nil
   116  	// when WinInfo has been obtained by calling
   117  	// the Win.Info method, because that information
   118  	// isn't available as part of the index file.
   119  	Size *WinSizeInfo
   120  }
   121  
   122  type WinSizeInfo struct {
   123  	Width    int
   124  	Font     string
   125  	TabWidth int
   126  }
   127  
   128  // A LogReader provides read access to the acme log file.
   129  type LogReader struct {
   130  	f   *client.Fid
   131  	buf [8192]byte
   132  }
   133  
   134  func (r *LogReader) Close() error {
   135  	return r.f.Close()
   136  }
   137  
   138  // A LogEvent is a single event in the acme log file.
   139  type LogEvent struct {
   140  	ID   int
   141  	Op   string
   142  	Name string
   143  }
   144  
   145  // Read reads an event from the acme log file.
   146  func (r *LogReader) Read() (LogEvent, error) {
   147  	n, err := r.f.Read(r.buf[:])
   148  	if err != nil {
   149  		return LogEvent{}, err
   150  	}
   151  	f := strings.SplitN(string(r.buf[:n]), " ", 3)
   152  	if len(f) != 3 {
   153  		return LogEvent{}, fmt.Errorf("malformed log event")
   154  	}
   155  	id, _ := strconv.Atoi(f[0])
   156  	op := f[1]
   157  	name := f[2]
   158  	name = strings.TrimSpace(name)
   159  	return LogEvent{id, op, name}, nil
   160  }
   161  
   162  // Log returns a reader reading the acme/log file.
   163  func Log() (*LogReader, error) {
   164  	fsysOnce.Do(mountAcme)
   165  	if fsysErr != nil {
   166  		return nil, fsysErr
   167  	}
   168  	f, err := fsys.Open("log", plan9.OREAD)
   169  	if err != nil {
   170  		return nil, err
   171  	}
   172  	return &LogReader{f: f}, nil
   173  }
   174  
   175  // Windows returns a list of the existing acme windows.
   176  func Windows() ([]WinInfo, error) {
   177  	fsysOnce.Do(mountAcme)
   178  	if fsysErr != nil {
   179  		return nil, fsysErr
   180  	}
   181  	index, err := fsys.Open("index", plan9.OREAD)
   182  	if err != nil {
   183  		return nil, err
   184  	}
   185  	defer index.Close()
   186  	data, err := ioutil.ReadAll(index)
   187  	if err != nil {
   188  		return nil, err
   189  	}
   190  	var infos []WinInfo
   191  	for _, line := range strings.Split(string(data), "\n") {
   192  		if len(line) == 0 {
   193  			continue
   194  		}
   195  		var info WinInfo
   196  		tag, err := splitFields(line,
   197  			&info.ID,
   198  			&info.TagLen,
   199  			&info.BodyLen,
   200  			&info.IsDir,
   201  			&info.IsModified,
   202  		)
   203  		if err != nil {
   204  			return nil, fmt.Errorf("invalid index line %q: %v", line, err)
   205  		}
   206  		i := strings.Index(tag, " Del Snarf ")
   207  		if i == -1 {
   208  			return nil, fmt.Errorf("cannot determine filename in tag %q", tag)
   209  		}
   210  		info.Name = tag[:i]
   211  		info.Tag = tag[i:]
   212  		infos = append(infos, info)
   213  	}
   214  	return infos, nil
   215  }
   216  
   217  // Show looks and causes acme to show the window with the given name,
   218  // returning that window.
   219  // If this process has not created a window with the given name
   220  // (or if any such window has since been deleted),
   221  // Show returns nil.
   222  func Show(name string) *Win {
   223  	windowsMu.Lock()
   224  	defer windowsMu.Unlock()
   225  
   226  	for w := windows; w != nil; w = w.next {
   227  		if w.name == name {
   228  			if err := w.Ctl("show"); err != nil {
   229  				w.dropLocked()
   230  				return nil
   231  			}
   232  			return w
   233  		}
   234  	}
   235  	return nil
   236  }
   237  
   238  // Open connects to the existing window with the given id.
   239  // If ctl is non-nil, Open uses it as the window's control file
   240  // and takes ownership of it.
   241  func Open(id int, ctl *client.Fid) (*Win, error) {
   242  	fsysOnce.Do(mountAcme)
   243  	if fsysErr != nil {
   244  		return nil, fsysErr
   245  	}
   246  	if ctl == nil {
   247  		var err error
   248  		ctl, err = fsys.Open(fmt.Sprintf("%d/ctl", id), plan9.ORDWR)
   249  		if err != nil {
   250  			return nil, err
   251  		}
   252  	}
   253  
   254  	w := new(Win)
   255  	w.id = id
   256  	w.ctl = ctl
   257  	w.next = nil
   258  	w.prev = last
   259  	if last != nil {
   260  		last.next = w
   261  	} else {
   262  		windows = w
   263  	}
   264  	last = w
   265  	return w, nil
   266  }
   267  
   268  // Addr writes format, ... to the window's addr file.
   269  func (w *Win) Addr(format string, args ...interface{}) error {
   270  	return w.Fprintf("addr", format, args...)
   271  }
   272  
   273  // CloseFiles closes all the open files associated with the window w.
   274  // (These file descriptors are cached across calls to Ctl, etc.)
   275  func (w *Win) CloseFiles() {
   276  	w.ctl.Close()
   277  	w.ctl = nil
   278  
   279  	w.body.Close()
   280  	w.body = nil
   281  
   282  	w.addr.Close()
   283  	w.addr = nil
   284  
   285  	w.tag.Close()
   286  	w.tag = nil
   287  
   288  	w.event.Close()
   289  	w.event = nil
   290  	w.ebuf = nil
   291  
   292  	w.data.Close()
   293  	w.data = nil
   294  
   295  	w.xdata.Close()
   296  	w.xdata = nil
   297  
   298  	w.errors.Close()
   299  	w.errors = nil
   300  }
   301  
   302  // Ctl writes the command format, ... to the window's ctl file.
   303  func (w *Win) Ctl(format string, args ...interface{}) error {
   304  	return w.Fprintf("ctl", format+"\n", args...)
   305  }
   306  
   307  // Winctl deletes the window, writing `del' (or, if sure is true, `delete') to the ctl file.
   308  func (w *Win) Del(sure bool) error {
   309  	cmd := "del"
   310  	if sure {
   311  		cmd = "delete"
   312  	}
   313  	return w.Ctl(cmd)
   314  }
   315  
   316  // DeleteAll deletes all windows.
   317  func DeleteAll() {
   318  	for w := windows; w != nil; w = w.next {
   319  		w.Ctl("delete")
   320  	}
   321  }
   322  
   323  func (w *Win) OpenEvent() error {
   324  	_, err := w.fid("event")
   325  	return err
   326  }
   327  
   328  func (w *Win) fid(name string) (*client.Fid, error) {
   329  	var f **client.Fid
   330  	var mode uint8 = plan9.ORDWR
   331  	switch name {
   332  	case "addr":
   333  		f = &w.addr
   334  	case "body":
   335  		f = &w.body
   336  	case "ctl":
   337  		f = &w.ctl
   338  	case "data":
   339  		f = &w.data
   340  	case "event":
   341  		f = &w.event
   342  	case "tag":
   343  		f = &w.tag
   344  	case "xdata":
   345  		f = &w.xdata
   346  	case "errors":
   347  		f = &w.errors
   348  		mode = plan9.OWRITE
   349  	default:
   350  		return nil, errors.New("unknown acme file: " + name)
   351  	}
   352  	if *f == nil {
   353  		var err error
   354  		*f, err = fsys.Open(fmt.Sprintf("%d/%s", w.id, name), mode)
   355  		if err != nil {
   356  			return nil, err
   357  		}
   358  	}
   359  	return *f, nil
   360  }
   361  
   362  // ReadAll
   363  func (w *Win) ReadAll(file string) ([]byte, error) {
   364  	f, err := w.fid(file)
   365  	if err != nil {
   366  		return nil, err
   367  	}
   368  	f.Seek(0, 0)
   369  	return ioutil.ReadAll(f)
   370  }
   371  
   372  func (w *Win) ID() int {
   373  	return w.id
   374  }
   375  
   376  func (w *Win) Name(format string, args ...interface{}) error {
   377  	name := fmt.Sprintf(format, args...)
   378  	if err := w.Ctl("name %s", name); err != nil {
   379  		return err
   380  	}
   381  	w.name = name
   382  	return nil
   383  }
   384  
   385  func (w *Win) Fprintf(file, format string, args ...interface{}) error {
   386  	f, err := w.fid(file)
   387  	if err != nil {
   388  		return err
   389  	}
   390  	var buf bytes.Buffer
   391  	fmt.Fprintf(&buf, format, args...)
   392  	_, err = f.Write(buf.Bytes())
   393  	return err
   394  }
   395  
   396  func (w *Win) Read(file string, b []byte) (n int, err error) {
   397  	f, err := w.fid(file)
   398  	if err != nil {
   399  		return 0, err
   400  	}
   401  	return f.Read(b)
   402  }
   403  
   404  func (w *Win) ReadAddr() (q0, q1 int, err error) {
   405  	f, err := w.fid("addr")
   406  	if err != nil {
   407  		return 0, 0, err
   408  	}
   409  	buf := make([]byte, 40)
   410  	n, err := f.ReadAt(buf, 0)
   411  	if err != nil && err != io.EOF {
   412  		return 0, 0, err
   413  	}
   414  	a := strings.Fields(string(buf[0:n]))
   415  	if len(a) < 2 {
   416  		return 0, 0, errors.New("short read from acme addr")
   417  	}
   418  	q0, err0 := strconv.Atoi(a[0])
   419  	q1, err1 := strconv.Atoi(a[1])
   420  	if err0 != nil || err1 != nil {
   421  		return 0, 0, errors.New("invalid read from acme addr")
   422  	}
   423  	return q0, q1, nil
   424  }
   425  
   426  func (w *Win) Info() (WinInfo, error) {
   427  	f, err := w.fid("ctl")
   428  	if err != nil {
   429  		return WinInfo{}, err
   430  	}
   431  	buf := make([]byte, 8192)
   432  	n, err := f.ReadAt(buf, 0)
   433  	if err != nil && err != io.EOF {
   434  		return WinInfo{}, err
   435  	}
   436  	line := string(buf[:n])
   437  	info := WinInfo{
   438  		Size: new(WinSizeInfo),
   439  	}
   440  	if _, err := splitFields(line,
   441  		&info.ID,
   442  		&info.TagLen,
   443  		&info.BodyLen,
   444  		&info.IsDir,
   445  		&info.IsModified,
   446  		&info.Size.Width,
   447  		&info.Size.Font,
   448  		&info.Size.TabWidth,
   449  	); err != nil {
   450  		return WinInfo{}, fmt.Errorf("invalid ctl contents %q: %v", line, err)
   451  	}
   452  	return info, nil
   453  }
   454  
   455  func (w *Win) Seek(file string, offset int64, whence int) (int64, error) {
   456  	f, err := w.fid(file)
   457  	if err != nil {
   458  		return 0, err
   459  	}
   460  	return f.Seek(offset, whence)
   461  }
   462  
   463  func (w *Win) Write(file string, b []byte) (n int, err error) {
   464  	f, err := w.fid(file)
   465  	if err != nil {
   466  		return 0, err
   467  	}
   468  	return f.Write(b)
   469  }
   470  
   471  const eventSize = 256
   472  
   473  // An Event represents an event originating in a particular window.
   474  // The fields correspond to the fields in acme's event messages.
   475  // See http://swtch.com/plan9port/man/man4/acme.html for details.
   476  type Event struct {
   477  	// The two event characters, indicating origin and type of action
   478  	C1, C2 rune
   479  
   480  	// The character addresses of the action.
   481  	// If the original event had an empty selection (OrigQ0=OrigQ1)
   482  	// and was accompanied by an expansion (the 2 bit is set in Flag),
   483  	// then Q0 and Q1 will indicate the expansion rather than the
   484  	// original event.
   485  	Q0, Q1 int
   486  
   487  	// The Q0 and Q1 of the original event, even if it was expanded.
   488  	// If there was no expansion, OrigQ0=Q0 and OrigQ1=Q1.
   489  	OrigQ0, OrigQ1 int
   490  
   491  	// The flag bits.
   492  	Flag int
   493  
   494  	// The number of bytes in the optional text.
   495  	Nb int
   496  
   497  	// The number of characters (UTF-8 sequences) in the optional text.
   498  	Nr int
   499  
   500  	// The optional text itself, encoded in UTF-8.
   501  	Text []byte
   502  
   503  	// The chorded argument, if present (the 8 bit is set in the flag).
   504  	Arg []byte
   505  
   506  	// The chorded location, if present (the 8 bit is set in the flag).
   507  	Loc []byte
   508  }
   509  
   510  // ReadEvent reads the next event from the window's event file.
   511  func (w *Win) ReadEvent() (e *Event, err error) {
   512  	defer func() {
   513  		if v := recover(); v != nil {
   514  			e = nil
   515  			err = errors.New("malformed acme event: " + v.(string))
   516  		}
   517  	}()
   518  
   519  	if _, err = w.fid("event"); err != nil {
   520  		return nil, err
   521  	}
   522  
   523  	e = new(Event)
   524  	w.gete(e)
   525  	e.OrigQ0 = e.Q0
   526  	e.OrigQ1 = e.Q1
   527  
   528  	// expansion
   529  	if e.Flag&2 != 0 {
   530  		e2 := new(Event)
   531  		w.gete(e2)
   532  		if e.Q0 == e.Q1 {
   533  			e2.OrigQ0 = e.Q0
   534  			e2.OrigQ1 = e.Q1
   535  			e2.Flag = e.Flag
   536  			e = e2
   537  		}
   538  	}
   539  
   540  	// chorded argument
   541  	if e.Flag&8 != 0 {
   542  		e3 := new(Event)
   543  		e4 := new(Event)
   544  		w.gete(e3)
   545  		w.gete(e4)
   546  		e.Arg = e3.Text
   547  		e.Loc = e4.Text
   548  	}
   549  
   550  	return e, nil
   551  }
   552  
   553  func (w *Win) gete(e *Event) {
   554  	if w.ebuf == nil {
   555  		w.ebuf = bufio.NewReader(w.event)
   556  	}
   557  	e.C1 = w.getec()
   558  	e.C2 = w.getec()
   559  	e.Q0 = w.geten()
   560  	e.Q1 = w.geten()
   561  	e.Flag = w.geten()
   562  	e.Nr = w.geten()
   563  	if e.Nr > eventSize {
   564  		panic("event string too long")
   565  	}
   566  	r := make([]rune, e.Nr)
   567  	for i := 0; i < e.Nr; i++ {
   568  		r[i] = w.getec()
   569  	}
   570  	e.Text = []byte(string(r))
   571  	if w.getec() != '\n' {
   572  		panic("phase error")
   573  	}
   574  }
   575  
   576  func (w *Win) getec() rune {
   577  	c, _, err := w.ebuf.ReadRune()
   578  	if err != nil {
   579  		panic(err.Error())
   580  	}
   581  	return c
   582  }
   583  
   584  func (w *Win) geten() int {
   585  	var (
   586  		c rune
   587  		n int
   588  	)
   589  	for {
   590  		c = w.getec()
   591  		if c < '0' || c > '9' {
   592  			break
   593  		}
   594  		n = n*10 + int(c) - '0'
   595  	}
   596  	if c != ' ' {
   597  		panic("event number syntax")
   598  	}
   599  	return n
   600  }
   601  
   602  // WriteEvent writes an event back to the window's event file,
   603  // indicating to acme that the event should be handled internally.
   604  func (w *Win) WriteEvent(e *Event) error {
   605  	var buf bytes.Buffer
   606  	fmt.Fprintf(&buf, "%c%c%d %d \n", e.C1, e.C2, e.OrigQ0, e.OrigQ1)
   607  	_, err := w.Write("event", buf.Bytes())
   608  	return err
   609  }
   610  
   611  // EventChan returns a channel on which events can be read.
   612  // The first call to EventChan allocates a channel and starts a
   613  // new goroutine that loops calling ReadEvent and sending
   614  // the result into the channel.  Subsequent calls return the
   615  // same channel.  Clients should not call ReadEvent after calling
   616  // EventChan.
   617  func (w *Win) EventChan() <-chan *Event {
   618  	if w.c == nil {
   619  		w.c = make(chan *Event, 0)
   620  		go w.eventReader()
   621  	}
   622  	return w.c
   623  }
   624  
   625  func (w *Win) eventReader() {
   626  	for {
   627  		e, err := w.ReadEvent()
   628  		if err != nil {
   629  			break
   630  		}
   631  		w.c <- e
   632  	}
   633  	w.c <- new(Event) // make sure event reader is done processing last event; drop might exit
   634  	w.drop()
   635  	close(w.c)
   636  }
   637  
   638  func (w *Win) drop() {
   639  	windowsMu.Lock()
   640  	defer windowsMu.Unlock()
   641  	w.dropLocked()
   642  }
   643  
   644  func (w *Win) dropLocked() {
   645  	if w.prev == nil && w.next == nil && windows != w {
   646  		return
   647  	}
   648  	if w.prev != nil {
   649  		w.prev.next = w.next
   650  	} else {
   651  		windows = w.next
   652  	}
   653  	if w.next != nil {
   654  		w.next.prev = w.prev
   655  	} else {
   656  		last = w.prev
   657  	}
   658  	w.prev = nil
   659  	w.next = nil
   660  	if autoExit && windows == nil {
   661  		os.Exit(0)
   662  	}
   663  }
   664  
   665  var fontCache struct {
   666  	sync.Mutex
   667  	m map[string]*draw.Font
   668  }
   669  
   670  // Font returns the window's current tab width (in zeros) and font.
   671  func (w *Win) Font() (tab int, font *draw.Font, err error) {
   672  	ctl := make([]byte, 1000)
   673  	w.Seek("ctl", 0, 0)
   674  	n, err := w.Read("ctl", ctl)
   675  	if err != nil {
   676  		return 0, nil, err
   677  	}
   678  	f := strings.Fields(string(ctl[:n]))
   679  	if len(f) < 8 {
   680  		return 0, nil, fmt.Errorf("malformed ctl file")
   681  	}
   682  	tab, _ = strconv.Atoi(f[7])
   683  	if tab == 0 {
   684  		return 0, nil, fmt.Errorf("malformed ctl file")
   685  	}
   686  	name := f[6]
   687  
   688  	fontCache.Lock()
   689  	font = fontCache.m[name]
   690  	fontCache.Unlock()
   691  
   692  	if font != nil {
   693  		return tab, font, nil
   694  	}
   695  
   696  	var disp *draw.Display = nil
   697  	font, err = disp.OpenFont(name)
   698  	if err != nil {
   699  		return tab, nil, err
   700  	}
   701  
   702  	fontCache.Lock()
   703  	if fontCache.m == nil {
   704  		fontCache.m = make(map[string]*draw.Font)
   705  	}
   706  	if fontCache.m[name] != nil {
   707  		font = fontCache.m[name]
   708  	} else {
   709  		fontCache.m[name] = font
   710  	}
   711  	fontCache.Unlock()
   712  
   713  	return tab, font, nil
   714  }
   715  
   716  // Blink starts the window tag blinking and returns a function that stops it.
   717  // When stop returns, the blinking is over and the window state is clean.
   718  func (w *Win) Blink() (stop func()) {
   719  	c := make(chan struct{})
   720  	go func() {
   721  		t := time.NewTicker(1000 * time.Millisecond)
   722  		defer t.Stop()
   723  		dirty := false
   724  		for {
   725  			select {
   726  			case <-t.C:
   727  				dirty = !dirty
   728  				if dirty {
   729  					w.Ctl("dirty")
   730  				} else {
   731  					w.Ctl("clean")
   732  				}
   733  			case <-c:
   734  				w.Ctl("clean")
   735  				c <- struct{}{}
   736  				return
   737  			}
   738  		}
   739  	}()
   740  	return func() {
   741  		c <- struct{}{}
   742  		<-c
   743  	}
   744  }
   745  
   746  // Sort sorts the lines in the current address range
   747  // according to the comparison function.
   748  func (w *Win) Sort(less func(x, y string) bool) error {
   749  	q0, q1, err := w.ReadAddr()
   750  	if err != nil {
   751  		return err
   752  	}
   753  	data, err := w.ReadAll("xdata")
   754  	if err != nil {
   755  		return err
   756  	}
   757  	suffix := ""
   758  	lines := strings.Split(string(data), "\n")
   759  	if lines[len(lines)-1] == "" {
   760  		suffix = "\n"
   761  		lines = lines[:len(lines)-1]
   762  	}
   763  	sort.SliceStable(lines, func(i, j int) bool { return less(lines[i], lines[j]) })
   764  	w.Addr("#%d,#%d", q0, q1)
   765  	w.Write("data", []byte(strings.Join(lines, "\n")+suffix))
   766  	return nil
   767  }
   768  
   769  // PrintTabbed prints tab-separated columnated text to body,
   770  // replacing single tabs with runs of tabs as needed to align columns.
   771  func (w *Win) PrintTabbed(text string) {
   772  	tab, font, _ := w.Font()
   773  
   774  	lines := strings.SplitAfter(text, "\n")
   775  	var allRows [][]string
   776  	for _, line := range lines {
   777  		if line == "" {
   778  			continue
   779  		}
   780  		line = strings.TrimSuffix(line, "\n")
   781  		allRows = append(allRows, strings.Split(line, "\t"))
   782  	}
   783  
   784  	var buf bytes.Buffer
   785  	for len(allRows) > 0 {
   786  		if row := allRows[0]; len(row) <= 1 {
   787  			if len(row) > 0 {
   788  				buf.WriteString(row[0])
   789  			}
   790  			buf.WriteString("\n")
   791  			allRows = allRows[1:]
   792  			continue
   793  		}
   794  
   795  		i := 0
   796  		for i < len(allRows) && len(allRows[i]) > 1 {
   797  			i++
   798  		}
   799  
   800  		rows := allRows[:i]
   801  		allRows = allRows[i:]
   802  
   803  		var wid []int
   804  		if font != nil {
   805  			for _, row := range rows {
   806  				for len(wid) < len(row) {
   807  					wid = append(wid, 0)
   808  				}
   809  				for i, col := range row {
   810  					n := font.StringWidth(col)
   811  					if wid[i] < n {
   812  						wid[i] = n
   813  					}
   814  				}
   815  			}
   816  		}
   817  
   818  		for _, row := range rows {
   819  			for i, col := range row {
   820  				buf.WriteString(col)
   821  				if i == len(row)-1 {
   822  					break
   823  				}
   824  				if font == nil || tab == 0 {
   825  					buf.WriteString("\t")
   826  					continue
   827  				}
   828  				pos := font.StringWidth(col)
   829  				for pos <= wid[i] {
   830  					buf.WriteString("\t")
   831  					pos += tab - pos%tab
   832  				}
   833  			}
   834  			buf.WriteString("\n")
   835  		}
   836  	}
   837  
   838  	w.Write("body", buf.Bytes())
   839  }
   840  
   841  // Clear clears the window body.
   842  func (w *Win) Clear() {
   843  	w.Addr(",")
   844  	w.Write("data", nil)
   845  }
   846  
   847  type EventHandler interface {
   848  	Execute(cmd string) bool
   849  	Look(arg string) bool
   850  }
   851  
   852  func (w *Win) loadText(e *Event, h EventHandler) {
   853  	if len(e.Text) == 0 && e.Q0 < e.Q1 {
   854  		w.Addr("#%d,#%d", e.Q0, e.Q1)
   855  		data, err := w.ReadAll("xdata")
   856  		if err != nil {
   857  			w.Err(err.Error())
   858  		}
   859  		e.Text = data
   860  	}
   861  }
   862  
   863  func (w *Win) EventLoop(h EventHandler) {
   864  	for e := range w.EventChan() {
   865  		switch e.C2 {
   866  		case 'x', 'X': // execute
   867  			cmd := strings.TrimSpace(string(e.Text))
   868  			if !w.execute(h, cmd) {
   869  				w.WriteEvent(e)
   870  			}
   871  		case 'l', 'L': // look
   872  			// TODO(rsc): Expand selection, especially for URLs.
   873  			w.loadText(e, h)
   874  			if !h.Look(string(e.Text)) {
   875  				w.WriteEvent(e)
   876  			}
   877  		}
   878  	}
   879  }
   880  
   881  func (w *Win) execute(h EventHandler, cmd string) bool {
   882  	verb, arg := cmd, ""
   883  	if i := strings.IndexAny(verb, " \t"); i >= 0 {
   884  		verb, arg = verb[:i], strings.TrimSpace(verb[i+1:])
   885  	}
   886  
   887  	// Look for specific method.
   888  	m := reflect.ValueOf(h).MethodByName("Exec" + verb)
   889  	if !m.IsValid() {
   890  		// Fall back to general Execute.
   891  		return h.Execute(cmd)
   892  	}
   893  
   894  	// Found method.
   895  	// Committed to handling the event.
   896  	// All returns below should be return true.
   897  
   898  	// Check method signature.
   899  	t := m.Type()
   900  	switch t.NumOut() {
   901  	default:
   902  		w.Errf("bad method %s: too many results", cmd)
   903  		return true
   904  	case 0:
   905  		// ok
   906  	case 1:
   907  		if t.Out(0) != reflect.TypeOf((*error)(nil)).Elem() {
   908  			w.Errf("bad method %s: return type %v, not error", cmd, t.Out(0))
   909  			return true
   910  		}
   911  	}
   912  	varg := reflect.ValueOf(arg)
   913  	switch t.NumIn() {
   914  	default:
   915  		w.Errf("bad method %s: too many arguments", cmd)
   916  		return true
   917  	case 0:
   918  		if arg != "" {
   919  			w.Errf("%s takes no arguments", cmd)
   920  			return true
   921  		}
   922  	case 1:
   923  		if t.In(0) != varg.Type() {
   924  			w.Errf("bad method %s: argument type %v, not string", cmd, t.In(0))
   925  			return true
   926  		}
   927  	}
   928  
   929  	args := []reflect.Value{}
   930  	if t.NumIn() > 0 {
   931  		args = append(args, varg)
   932  	}
   933  	out := m.Call(args)
   934  	var err error
   935  	if len(out) == 1 {
   936  		err, _ = out[0].Interface().(error)
   937  	}
   938  	if err != nil {
   939  		w.Errf("%v", err)
   940  	}
   941  
   942  	return true
   943  }
   944  
   945  func (w *Win) Selection() string {
   946  	w.Ctl("addr=dot")
   947  	data, err := w.ReadAll("xdata")
   948  	if err != nil {
   949  		w.Err(err.Error())
   950  	}
   951  	return string(data)
   952  }
   953  
   954  func (w *Win) SetErrorPrefix(p string) {
   955  	w.errorPrefix = p
   956  }
   957  
   958  // Err finds or creates a window appropriate for showing errors related to w
   959  // and then prints msg to that window.
   960  // It adds a final newline to msg if needed.
   961  func (w *Win) Err(msg string) {
   962  	Err(w.errorPrefix, msg)
   963  }
   964  
   965  func (w *Win) Errf(format string, args ...interface{}) {
   966  	w.Err(fmt.Sprintf(format, args...))
   967  }
   968  
   969  // Err finds or creates a window appropriate for showing errors related to a window titled src
   970  // and then prints msg to that window. It adds a final newline to msg if needed.
   971  func Err(src, msg string) {
   972  	if !strings.HasSuffix(msg, "\n") {
   973  		msg = msg + "\n"
   974  	}
   975  	prefix, _ := path.Split(src)
   976  	if prefix == "/" || prefix == "." {
   977  		prefix = ""
   978  	}
   979  	name := prefix + "+Errors"
   980  	w1 := Show(name)
   981  	if w1 == nil {
   982  		var err error
   983  		w1, err = New()
   984  		if err != nil {
   985  			time.Sleep(100 * time.Millisecond)
   986  			w1, err = New()
   987  			if err != nil {
   988  				log.Fatalf("cannot create +Errors window")
   989  			}
   990  		}
   991  		w1.Name("%s", name)
   992  	}
   993  	w1.Addr("$")
   994  	w1.Ctl("dot=addr")
   995  	w1.Fprintf("body", "%s", msg)
   996  	w1.Addr(".,")
   997  	w1.Ctl("dot=addr")
   998  	w1.Ctl("show")
   999  }
  1000  
  1001  // Errf is like Err but accepts a printf-style formatting.
  1002  func Errf(src, format string, args ...interface{}) {
  1003  	Err(src, fmt.Sprintf(format, args...))
  1004  }
  1005  
  1006  // splitFields parses the line into fields.
  1007  // Each element of fields must be one of *int, *string or *bool
  1008  // which are set to the respective field value.
  1009  // Boolean and numeric fields are expected to numbers formatted
  1010  // in 11 characters followed by a space.
  1011  // String fields are expected to be space terminated.
  1012  //
  1013  // It returns the rest of line after all the fields have been parsed.
  1014  func splitFields(line string, fields ...interface{}) (string, error) {
  1015  	n := 0
  1016  	for len(fields) > 0 {
  1017  		switch f := fields[0].(type) {
  1018  		case *int, *bool:
  1019  			if len(line) < 12 {
  1020  				return "", fmt.Errorf("field %d is too short", n)
  1021  			}
  1022  			if line[11] != ' ' {
  1023  				return "", fmt.Errorf("field %d doesn't terminate in a space", n)
  1024  			}
  1025  			fn, err := strconv.Atoi(strings.TrimSpace(line[:11]))
  1026  			if err != nil {
  1027  				return "", fmt.Errorf("field %d is invalid: %v", n, err)
  1028  			}
  1029  			switch f := f.(type) {
  1030  			case *int:
  1031  				*f = fn
  1032  			case *bool:
  1033  				if fn != 0 && fn != 1 {
  1034  					return "", fmt.Errorf("field %d should be either 0 or 1", n)
  1035  				}
  1036  				*f = fn != 0
  1037  			}
  1038  			line = line[12:]
  1039  		case *string:
  1040  			i := strings.IndexByte(line, ' ')
  1041  			if i == -1 {
  1042  				return "", fmt.Errorf("no space found at end of string field %d", n)
  1043  			}
  1044  			*f = line[:i]
  1045  			line = line[i+1:]
  1046  		}
  1047  		fields = fields[1:]
  1048  	}
  1049  	return line, nil
  1050  }