editor.go raw

   1  // SPDX-License-Identifier: Unlicense OR MIT
   2  
   3  package widget
   4  
   5  import (
   6  	"bufio"
   7  	"bytes"
   8  	"image"
   9  	"io"
  10  	"math"
  11  	"runtime"
  12  	"sort"
  13  	"strings"
  14  	"time"
  15  	"unicode"
  16  	"unicode/utf8"
  17  
  18  	"github.com/p9c/p9/pkg/gel/gio/f32"
  19  	"github.com/p9c/p9/pkg/gel/gio/gesture"
  20  	"github.com/p9c/p9/pkg/gel/gio/io/clipboard"
  21  	"github.com/p9c/p9/pkg/gel/gio/io/event"
  22  	"github.com/p9c/p9/pkg/gel/gio/io/key"
  23  	"github.com/p9c/p9/pkg/gel/gio/io/pointer"
  24  	"github.com/p9c/p9/pkg/gel/gio/layout"
  25  	"github.com/p9c/p9/pkg/gel/gio/op"
  26  	"github.com/p9c/p9/pkg/gel/gio/op/clip"
  27  	"github.com/p9c/p9/pkg/gel/gio/op/paint"
  28  	"github.com/p9c/p9/pkg/gel/gio/text"
  29  	"github.com/p9c/p9/pkg/gel/gio/unit"
  30  
  31  	"golang.org/x/image/math/fixed"
  32  )
  33  
  34  // Editor implements an editable and scrollable text area.
  35  type Editor struct {
  36  	Alignment text.Alignment
  37  	// SingleLine force the text to stay on a single line.
  38  	// SingleLine also sets the scrolling direction to
  39  	// horizontal.
  40  	SingleLine bool
  41  	// Submit enabled translation of carriage return keys to SubmitEvents.
  42  	// If not enabled, carriage returns are inserted as newlines in the text.
  43  	Submit bool
  44  	// Mask replaces the visual display of each rune in the contents with the given rune.
  45  	// Newline characters are not masked. When non-zero, the unmasked contents
  46  	// are accessed by Len, Text, and SetText.
  47  	Mask rune
  48  
  49  	eventKey     int
  50  	font         text.Font
  51  	shaper       text.Shaper
  52  	textSize     fixed.Int26_6
  53  	blinkStart   time.Time
  54  	focused      bool
  55  	rr           editBuffer
  56  	maskReader   maskReader
  57  	lastMask     rune
  58  	maxWidth     int
  59  	viewSize     image.Point
  60  	valid        bool
  61  	lines        []text.Line
  62  	shapes       []line
  63  	dims         layout.Dimensions
  64  	requestFocus bool
  65  
  66  	caret struct {
  67  		on     bool
  68  		scroll bool
  69  		// start is the current caret position, and also the start position of
  70  		// selected text. end is the end positon of selected text. If start.ofs
  71  		// == end.ofs, then there's no selection. Note that it's possible (and
  72  		// common) that the caret (start) is after the end, e.g. after
  73  		// Shift-DownArrow.
  74  		start combinedPos
  75  		end   combinedPos
  76  	}
  77  
  78  	dragging  bool
  79  	dragger   gesture.Drag
  80  	scroller  gesture.Scroll
  81  	scrollOff image.Point
  82  
  83  	clicker gesture.Click
  84  
  85  	// events is the list of events not yet processed.
  86  	events []EditorEvent
  87  	// prevEvents is the number of events from the previous frame.
  88  	prevEvents int
  89  }
  90  
  91  type maskReader struct {
  92  	// rr is the underlying reader.
  93  	rr      io.RuneReader
  94  	maskBuf [utf8.UTFMax]byte
  95  	// mask is the utf-8 encoded mask rune.
  96  	mask []byte
  97  	// overflow contains excess mask bytes left over after the last Read call.
  98  	overflow []byte
  99  }
 100  
 101  // combinedPos is a point in the editor.
 102  type combinedPos struct {
 103  	// editorBuffer offset. The other three fields are based off of this one.
 104  	ofs int
 105  
 106  	// lineCol.Y = line (offset into Editor.lines), and X = col (offset into
 107  	// Editor.lines[Y])
 108  	lineCol screenPos
 109  
 110  	// Pixel coordinates
 111  	x fixed.Int26_6
 112  	y int
 113  
 114  	// xoff is the offset to the current position when moving between lines.
 115  	xoff fixed.Int26_6
 116  }
 117  
 118  type selectionAction int
 119  
 120  const (
 121  	selectionExtend selectionAction = iota
 122  	selectionClear
 123  )
 124  
 125  func (m *maskReader) Reset(r io.RuneReader, mr rune) {
 126  	m.rr = r
 127  	n := utf8.EncodeRune(m.maskBuf[:], mr)
 128  	m.mask = m.maskBuf[:n]
 129  }
 130  
 131  // Read reads from the underlying reader and replaces every
 132  // rune with the mask rune.
 133  func (m *maskReader) Read(b []byte) (n int, err error) {
 134  	for len(b) > 0 {
 135  		var replacement []byte
 136  		if len(m.overflow) > 0 {
 137  			replacement = m.overflow
 138  		} else {
 139  			var r rune
 140  			r, _, err = m.rr.ReadRune()
 141  			if err != nil {
 142  				break
 143  			}
 144  			if r == '\n' {
 145  				replacement = []byte{'\n'}
 146  			} else {
 147  				replacement = m.mask
 148  			}
 149  		}
 150  		nn := copy(b, replacement)
 151  		m.overflow = replacement[nn:]
 152  		n += nn
 153  		b = b[nn:]
 154  	}
 155  	return n, err
 156  }
 157  
 158  type EditorEvent interface {
 159  	isEditorEvent()
 160  }
 161  
 162  // A ChangeEvent is generated for every user change to the text.
 163  type ChangeEvent struct{}
 164  
 165  // A SubmitEvent is generated when Submit is set
 166  // and a carriage return key is pressed.
 167  type SubmitEvent struct {
 168  	Text string
 169  }
 170  
 171  // A SelectEvent is generated when the user selects some text, or changes the
 172  // selection (e.g. with a shift-click), including if they remove the
 173  // selection. The selected text is not part of the event, on the theory that
 174  // it could be a relatively expensive operation (for a large editor), most
 175  // applications won't actually care about it, and those that do can call
 176  // Editor.SelectedText() (which can be empty).
 177  type SelectEvent struct{}
 178  
 179  type line struct {
 180  	offset         image.Point
 181  	clip           op.CallOp
 182  	selected       bool
 183  	selectionYOffs int
 184  	selectionSize  image.Point
 185  }
 186  
 187  const (
 188  	blinksPerSecond  = 1
 189  	maxBlinkDuration = 10 * time.Second
 190  )
 191  
 192  // Events returns available editor events.
 193  func (e *Editor) Events() []EditorEvent {
 194  	events := e.events
 195  	e.events = nil
 196  	e.prevEvents = 0
 197  	return events
 198  }
 199  
 200  func (e *Editor) processEvents(gtx layout.Context) {
 201  	// Flush events from before the previous Layout.
 202  	n := copy(e.events, e.events[e.prevEvents:])
 203  	e.events = e.events[:n]
 204  	e.prevEvents = n
 205  
 206  	if e.shaper == nil {
 207  		// Can't process events without a shaper.
 208  		return
 209  	}
 210  	oldStart, oldLen := min(e.caret.start.ofs, e.caret.end.ofs), e.SelectionLen()
 211  	e.processPointer(gtx)
 212  	e.processKey(gtx)
 213  	// Queue a SelectEvent if the selection changed, including if it went away.
 214  	if newStart, newLen := min(e.caret.start.ofs, e.caret.end.ofs), e.SelectionLen(); oldStart != newStart || oldLen != newLen {
 215  		e.events = append(e.events, SelectEvent{})
 216  	}
 217  }
 218  
 219  func (e *Editor) makeValid(positions ...*combinedPos) {
 220  	if e.valid {
 221  		return
 222  	}
 223  	e.lines, e.dims = e.layoutText(e.shaper)
 224  	e.makeValidCaret(positions...)
 225  	e.valid = true
 226  }
 227  
 228  func (e *Editor) processPointer(gtx layout.Context) {
 229  	sbounds := e.scrollBounds()
 230  	var smin, smax int
 231  	var axis gesture.Axis
 232  	if e.SingleLine {
 233  		axis = gesture.Horizontal
 234  		smin, smax = sbounds.Min.X, sbounds.Max.X
 235  	} else {
 236  		axis = gesture.Vertical
 237  		smin, smax = sbounds.Min.Y, sbounds.Max.Y
 238  	}
 239  	sdist := e.scroller.Scroll(gtx.Metric, gtx, gtx.Now, axis)
 240  	var soff int
 241  	if e.SingleLine {
 242  		e.scrollRel(sdist, 0)
 243  		soff = e.scrollOff.X
 244  	} else {
 245  		e.scrollRel(0, sdist)
 246  		soff = e.scrollOff.Y
 247  	}
 248  	for _, evt := range e.clickDragEvents(gtx) {
 249  		switch evt := evt.(type) {
 250  		case gesture.ClickEvent:
 251  			switch {
 252  			case evt.Type == gesture.TypePress && evt.Source == pointer.Mouse,
 253  				evt.Type == gesture.TypeClick:
 254  				prevCaretPos := e.caret.start
 255  				e.blinkStart = gtx.Now
 256  				e.moveCoord(image.Point{
 257  					X: int(math.Round(float64(evt.Position.X))),
 258  					Y: int(math.Round(float64(evt.Position.Y))),
 259  				})
 260  				e.requestFocus = true
 261  				if e.scroller.State() != gesture.StateFlinging {
 262  					e.caret.scroll = true
 263  				}
 264  
 265  				if evt.Modifiers == key.ModShift {
 266  					// If they clicked closer to the end, then change the end to
 267  					// where the caret used to be (effectively swapping start & end).
 268  					if abs(e.caret.end.ofs-e.caret.start.ofs) < abs(e.caret.start.ofs-prevCaretPos.ofs) {
 269  						e.caret.end = prevCaretPos
 270  					}
 271  				} else {
 272  					e.ClearSelection()
 273  				}
 274  				e.dragging = true
 275  
 276  				// Process a double-click.
 277  				if evt.NumClicks == 2 {
 278  					e.moveWord(-1, selectionClear)
 279  					e.moveWord(1, selectionExtend)
 280  					e.dragging = false
 281  				}
 282  			}
 283  		case pointer.Event:
 284  			release := false
 285  			switch {
 286  			case evt.Type == pointer.Release && evt.Source == pointer.Mouse:
 287  				release = true
 288  				fallthrough
 289  			case evt.Type == pointer.Drag && evt.Source == pointer.Mouse:
 290  				if e.dragging {
 291  					e.blinkStart = gtx.Now
 292  					e.moveCoord(image.Point{
 293  						X: int(math.Round(float64(evt.Position.X))),
 294  						Y: int(math.Round(float64(evt.Position.Y))),
 295  					})
 296  					e.caret.scroll = true
 297  
 298  					if release {
 299  						e.dragging = false
 300  					}
 301  				}
 302  			}
 303  		}
 304  	}
 305  
 306  	if (sdist > 0 && soff >= smax) || (sdist < 0 && soff <= smin) {
 307  		e.scroller.Stop()
 308  	}
 309  }
 310  
 311  func (e *Editor) clickDragEvents(gtx layout.Context) []event.Event {
 312  	var combinedEvents []event.Event
 313  	for _, evt := range e.clicker.Events(gtx) {
 314  		combinedEvents = append(combinedEvents, evt)
 315  	}
 316  	for _, evt := range e.dragger.Events(gtx.Metric, gtx, gesture.Both) {
 317  		combinedEvents = append(combinedEvents, evt)
 318  	}
 319  	return combinedEvents
 320  }
 321  
 322  func (e *Editor) processKey(gtx layout.Context) {
 323  	if e.rr.Changed() {
 324  		e.events = append(e.events, ChangeEvent{})
 325  	}
 326  	for _, ke := range gtx.Events(&e.eventKey) {
 327  		e.blinkStart = gtx.Now
 328  		switch ke := ke.(type) {
 329  		case key.FocusEvent:
 330  			e.focused = ke.Focus
 331  		case key.Event:
 332  			if !e.focused || ke.State != key.Press {
 333  				break
 334  			}
 335  			if e.Submit && (ke.Name == key.NameReturn || ke.Name == key.NameEnter) {
 336  				if !ke.Modifiers.Contain(key.ModShift) {
 337  					e.events = append(e.events, SubmitEvent{
 338  						Text: e.Text(),
 339  					})
 340  					continue
 341  				}
 342  			}
 343  			if e.command(gtx, ke) {
 344  				e.caret.scroll = true
 345  				e.scroller.Stop()
 346  			}
 347  		case key.EditEvent:
 348  			e.caret.scroll = true
 349  			e.scroller.Stop()
 350  			e.append(ke.Text)
 351  		// Complete a paste event, initiated by Shortcut-V in Editor.command().
 352  		case clipboard.Event:
 353  			e.caret.scroll = true
 354  			e.scroller.Stop()
 355  			e.append(ke.Text)
 356  		}
 357  		if e.rr.Changed() {
 358  			e.events = append(e.events, ChangeEvent{})
 359  		}
 360  	}
 361  }
 362  
 363  func (e *Editor) moveLines(distance int, selAct selectionAction) {
 364  	e.caret.start = e.movePosToLine(e.caret.start, e.caret.start.x+e.caret.start.xoff, e.caret.start.lineCol.Y+distance)
 365  	e.updateSelection(selAct)
 366  }
 367  
 368  func (e *Editor) command(gtx layout.Context, k key.Event) bool {
 369  	modSkip := key.ModCtrl
 370  	if runtime.GOOS == "darwin" {
 371  		modSkip = key.ModAlt
 372  	}
 373  	moveByWord := k.Modifiers.Contain(modSkip)
 374  	selAct := selectionClear
 375  	if k.Modifiers.Contain(key.ModShift) {
 376  		selAct = selectionExtend
 377  	}
 378  	switch k.Name {
 379  	case key.NameReturn, key.NameEnter:
 380  		e.append("\n")
 381  	case key.NameDeleteBackward:
 382  		if moveByWord {
 383  			e.deleteWord(-1)
 384  		} else {
 385  			e.Delete(-1)
 386  		}
 387  	case key.NameDeleteForward:
 388  		if moveByWord {
 389  			e.deleteWord(1)
 390  		} else {
 391  			e.Delete(1)
 392  		}
 393  	case key.NameUpArrow:
 394  		e.moveLines(-1, selAct)
 395  	case key.NameDownArrow:
 396  		e.moveLines(+1, selAct)
 397  	case key.NameLeftArrow:
 398  		if moveByWord {
 399  			e.moveWord(-1, selAct)
 400  		} else {
 401  			if selAct == selectionClear {
 402  				e.ClearSelection()
 403  			}
 404  			e.MoveCaret(-1, -1*int(selAct))
 405  		}
 406  	case key.NameRightArrow:
 407  		if moveByWord {
 408  			e.moveWord(1, selAct)
 409  		} else {
 410  			if selAct == selectionClear {
 411  				e.ClearSelection()
 412  			}
 413  			e.MoveCaret(1, int(selAct))
 414  		}
 415  	case key.NamePageUp:
 416  		e.movePages(-1, selAct)
 417  	case key.NamePageDown:
 418  		e.movePages(+1, selAct)
 419  	case key.NameHome:
 420  		e.moveStart(selAct)
 421  	case key.NameEnd:
 422  		e.moveEnd(selAct)
 423  	// Initiate a paste operation, by requesting the clipboard contents; other
 424  	// half is in Editor.processKey() under clipboard.Event.
 425  	case "V":
 426  		if k.Modifiers != key.ModShortcut {
 427  			return false
 428  		}
 429  		clipboard.ReadOp{Tag: &e.eventKey}.Add(gtx.Ops)
 430  	// Copy or Cut selection -- ignored if nothing selected.
 431  	case "C", "X":
 432  		if k.Modifiers != key.ModShortcut {
 433  			return false
 434  		}
 435  		if text := e.SelectedText(); text != "" {
 436  			clipboard.WriteOp{Text: text}.Add(gtx.Ops)
 437  			if k.Name == "X" {
 438  				e.Delete(1)
 439  			}
 440  		}
 441  	// Select all
 442  	case "A":
 443  		if k.Modifiers != key.ModShortcut {
 444  			return false
 445  		}
 446  		e.caret.end, e.caret.start = e.offsetToScreenPos2(0, e.Len())
 447  	default:
 448  		return false
 449  	}
 450  	return true
 451  }
 452  
 453  // Focus requests the input focus for the Editor.
 454  func (e *Editor) Focus() {
 455  	e.requestFocus = true
 456  }
 457  
 458  // Focused returns whether the editor is focused or not.
 459  func (e *Editor) Focused() bool {
 460  	return e.focused
 461  }
 462  
 463  // Layout lays out the editor.
 464  func (e *Editor) Layout(gtx layout.Context, sh text.Shaper, font text.Font, size unit.Value) layout.Dimensions {
 465  	textSize := fixed.I(gtx.Px(size))
 466  	if e.font != font || e.textSize != textSize {
 467  		e.invalidate()
 468  		e.font = font
 469  		e.textSize = textSize
 470  	}
 471  	maxWidth := gtx.Constraints.Max.X
 472  	if e.SingleLine {
 473  		maxWidth = inf
 474  	}
 475  	if maxWidth != e.maxWidth {
 476  		e.maxWidth = maxWidth
 477  		e.invalidate()
 478  	}
 479  	if sh != e.shaper {
 480  		e.shaper = sh
 481  		e.invalidate()
 482  	}
 483  	if e.Mask != e.lastMask {
 484  		e.lastMask = e.Mask
 485  		e.invalidate()
 486  	}
 487  
 488  	e.makeValid()
 489  	e.processEvents(gtx)
 490  	e.makeValid()
 491  
 492  	if viewSize := gtx.Constraints.Constrain(e.dims.Size); viewSize != e.viewSize {
 493  		e.viewSize = viewSize
 494  		e.invalidate()
 495  	}
 496  	e.makeValid()
 497  
 498  	return e.layout(gtx)
 499  }
 500  
 501  func (e *Editor) layout(gtx layout.Context) layout.Dimensions {
 502  	// Adjust scrolling for new viewport and layout.
 503  	e.scrollRel(0, 0)
 504  
 505  	if e.caret.scroll {
 506  		e.caret.scroll = false
 507  		e.scrollToCaret()
 508  	}
 509  
 510  	off := image.Point{
 511  		X: -e.scrollOff.X,
 512  		Y: -e.scrollOff.Y,
 513  	}
 514  	clip := textPadding(e.lines)
 515  	clip.Max = clip.Max.Add(e.viewSize)
 516  	startSel, endSel := sortPoints(e.caret.start.lineCol, e.caret.end.lineCol)
 517  	it := segmentIterator{
 518  		startSel:  startSel,
 519  		endSel:    endSel,
 520  		Lines:     e.lines,
 521  		Clip:      clip,
 522  		Alignment: e.Alignment,
 523  		Width:     e.viewSize.X,
 524  		Offset:    off,
 525  	}
 526  	e.shapes = e.shapes[:0]
 527  	for {
 528  		layout, off, selected, yOffs, size, ok := it.Next()
 529  		if !ok {
 530  			break
 531  		}
 532  		path := e.shaper.Shape(e.font, e.textSize, layout)
 533  		e.shapes = append(e.shapes, line{off, path, selected, yOffs, size})
 534  	}
 535  
 536  	key.InputOp{Tag: &e.eventKey}.Add(gtx.Ops)
 537  	if e.requestFocus {
 538  		key.FocusOp{Tag: &e.eventKey}.Add(gtx.Ops)
 539  		key.SoftKeyboardOp{Show: true}.Add(gtx.Ops)
 540  	}
 541  	e.requestFocus = false
 542  	pointerPadding := gtx.Px(unit.Dp(4))
 543  	r := image.Rectangle{Max: e.viewSize}
 544  	r.Min.X -= pointerPadding
 545  	r.Min.Y -= pointerPadding
 546  	r.Max.X += pointerPadding
 547  	r.Max.X += pointerPadding
 548  	pointer.Rect(r).Add(gtx.Ops)
 549  	pointer.CursorNameOp{Name: pointer.CursorText}.Add(gtx.Ops)
 550  
 551  	var scrollRange image.Rectangle
 552  	if e.SingleLine {
 553  		scrollRange.Min.X = -e.scrollOff.X
 554  		scrollRange.Max.X = max(0, e.dims.Size.X-(e.scrollOff.X+e.viewSize.X))
 555  	} else {
 556  		scrollRange.Min.Y = -e.scrollOff.Y
 557  		scrollRange.Max.Y = max(0, e.dims.Size.Y-(e.scrollOff.Y+e.viewSize.Y))
 558  	}
 559  	e.scroller.Add(gtx.Ops, scrollRange)
 560  
 561  	e.clicker.Add(gtx.Ops)
 562  	e.dragger.Add(gtx.Ops)
 563  	e.caret.on = false
 564  	if e.focused {
 565  		now := gtx.Now
 566  		dt := now.Sub(e.blinkStart)
 567  		blinking := dt < maxBlinkDuration
 568  		const timePerBlink = time.Second / blinksPerSecond
 569  		nextBlink := now.Add(timePerBlink/2 - dt%(timePerBlink/2))
 570  		if blinking {
 571  			redraw := op.InvalidateOp{At: nextBlink}
 572  			redraw.Add(gtx.Ops)
 573  		}
 574  		e.caret.on = e.focused && (!blinking || dt%timePerBlink < timePerBlink/2)
 575  	}
 576  
 577  	return layout.Dimensions{Size: e.viewSize, Baseline: e.dims.Baseline}
 578  }
 579  
 580  // PaintSelection paints the contrasting background for selected text.
 581  func (e *Editor) PaintSelection(gtx layout.Context) {
 582  	cl := textPadding(e.lines)
 583  	cl.Max = cl.Max.Add(e.viewSize)
 584  	clip.Rect(cl).Add(gtx.Ops)
 585  	for _, shape := range e.shapes {
 586  		if !shape.selected {
 587  			continue
 588  		}
 589  		stack := op.Save(gtx.Ops)
 590  		offset := shape.offset
 591  		offset.Y += shape.selectionYOffs
 592  		op.Offset(layout.FPt(offset)).Add(gtx.Ops)
 593  		clip.Rect(image.Rectangle{Max: shape.selectionSize}).Add(gtx.Ops)
 594  		paint.PaintOp{}.Add(gtx.Ops)
 595  		stack.Load()
 596  	}
 597  }
 598  
 599  func (e *Editor) PaintText(gtx layout.Context) {
 600  	cl := textPadding(e.lines)
 601  	cl.Max = cl.Max.Add(e.viewSize)
 602  	clip.Rect(cl).Add(gtx.Ops)
 603  	for _, shape := range e.shapes {
 604  		stack := op.Save(gtx.Ops)
 605  		op.Offset(layout.FPt(shape.offset)).Add(gtx.Ops)
 606  		shape.clip.Add(gtx.Ops)
 607  		paint.PaintOp{}.Add(gtx.Ops)
 608  		stack.Load()
 609  	}
 610  }
 611  
 612  func (e *Editor) PaintCaret(gtx layout.Context) {
 613  	if !e.caret.on {
 614  		return
 615  	}
 616  	e.makeValid()
 617  	carWidth := fixed.I(gtx.Px(unit.Dp(1)))
 618  	carX := e.caret.start.x
 619  	carY := e.caret.start.y
 620  
 621  	defer op.Save(gtx.Ops).Load()
 622  	carX -= carWidth / 2
 623  	carAsc, carDesc := -e.lines[e.caret.start.lineCol.Y].Bounds.Min.Y, e.lines[e.caret.start.lineCol.Y].Bounds.Max.Y
 624  	carRect := image.Rectangle{
 625  		Min: image.Point{X: carX.Ceil(), Y: carY - carAsc.Ceil()},
 626  		Max: image.Point{X: carX.Ceil() + carWidth.Ceil(), Y: carY + carDesc.Ceil()},
 627  	}
 628  	carRect = carRect.Add(image.Point{
 629  		X: -e.scrollOff.X,
 630  		Y: -e.scrollOff.Y,
 631  	})
 632  	cl := textPadding(e.lines)
 633  	// Account for caret width to each side.
 634  	whalf := (carWidth / 2).Ceil()
 635  	if cl.Max.X < whalf {
 636  		cl.Max.X = whalf
 637  	}
 638  	if cl.Min.X > -whalf {
 639  		cl.Min.X = -whalf
 640  	}
 641  	cl.Max = cl.Max.Add(e.viewSize)
 642  	carRect = cl.Intersect(carRect)
 643  	if !carRect.Empty() {
 644  		st := op.Save(gtx.Ops)
 645  		clip.Rect(carRect).Add(gtx.Ops)
 646  		paint.PaintOp{}.Add(gtx.Ops)
 647  		st.Load()
 648  	}
 649  }
 650  
 651  // Len is the length of the editor contents.
 652  func (e *Editor) Len() int {
 653  	return e.rr.len()
 654  }
 655  
 656  // Text returns the contents of the editor.
 657  func (e *Editor) Text() string {
 658  	return e.rr.String()
 659  }
 660  
 661  // SetText replaces the contents of the editor, clearing any selection first.
 662  func (e *Editor) SetText(s string) {
 663  	e.rr = editBuffer{}
 664  	e.caret.start = combinedPos{}
 665  	e.caret.end = combinedPos{}
 666  	e.prepend(s)
 667  }
 668  
 669  func (e *Editor) scrollBounds() image.Rectangle {
 670  	var b image.Rectangle
 671  	if e.SingleLine {
 672  		if len(e.lines) > 0 {
 673  			b.Min.X = align(e.Alignment, e.lines[0].Width, e.viewSize.X).Floor()
 674  			if b.Min.X > 0 {
 675  				b.Min.X = 0
 676  			}
 677  		}
 678  		b.Max.X = e.dims.Size.X + b.Min.X - e.viewSize.X
 679  	} else {
 680  		b.Max.Y = e.dims.Size.Y - e.viewSize.Y
 681  	}
 682  	return b
 683  }
 684  
 685  func (e *Editor) scrollRel(dx, dy int) {
 686  	e.scrollAbs(e.scrollOff.X+dx, e.scrollOff.Y+dy)
 687  }
 688  
 689  func (e *Editor) scrollAbs(x, y int) {
 690  	e.scrollOff.X = x
 691  	e.scrollOff.Y = y
 692  	b := e.scrollBounds()
 693  	if e.scrollOff.X > b.Max.X {
 694  		e.scrollOff.X = b.Max.X
 695  	}
 696  	if e.scrollOff.X < b.Min.X {
 697  		e.scrollOff.X = b.Min.X
 698  	}
 699  	if e.scrollOff.Y > b.Max.Y {
 700  		e.scrollOff.Y = b.Max.Y
 701  	}
 702  	if e.scrollOff.Y < b.Min.Y {
 703  		e.scrollOff.Y = b.Min.Y
 704  	}
 705  }
 706  
 707  func (e *Editor) moveCoord(pos image.Point) {
 708  	var (
 709  		prevDesc fixed.Int26_6
 710  		carLine  int
 711  		y        int
 712  	)
 713  	for _, l := range e.lines {
 714  		y += (prevDesc + l.Ascent).Ceil()
 715  		prevDesc = l.Descent
 716  		if y+prevDesc.Ceil() >= pos.Y+e.scrollOff.Y {
 717  			break
 718  		}
 719  		carLine++
 720  	}
 721  	x := fixed.I(pos.X + e.scrollOff.X)
 722  	e.caret.start = e.movePosToLine(e.caret.start, x, carLine)
 723  	e.caret.start.xoff = 0
 724  }
 725  
 726  func (e *Editor) layoutText(s text.Shaper) ([]text.Line, layout.Dimensions) {
 727  	e.rr.Reset()
 728  	var r io.Reader = &e.rr
 729  	if e.Mask != 0 {
 730  		e.maskReader.Reset(&e.rr, e.Mask)
 731  		r = &e.maskReader
 732  	}
 733  	var lines []text.Line
 734  	if s != nil {
 735  		lines, _ = s.Layout(e.font, e.textSize, e.maxWidth, r)
 736  	} else {
 737  		lines, _ = nullLayout(r)
 738  	}
 739  	dims := linesDimens(lines)
 740  	for i := 0; i < len(lines)-1; i++ {
 741  		// To avoid layout flickering while editing, assume a soft newline takes
 742  		// up all available space.
 743  		if layout := lines[i].Layout; len(layout.Text) > 0 {
 744  			r := layout.Text[len(layout.Text)-1]
 745  			if r != '\n' {
 746  				dims.Size.X = e.maxWidth
 747  				break
 748  			}
 749  		}
 750  	}
 751  	return lines, dims
 752  }
 753  
 754  // CaretPos returns the line & column numbers of the caret.
 755  func (e *Editor) CaretPos() (line, col int) {
 756  	e.makeValid()
 757  	return e.caret.start.lineCol.Y, e.caret.start.lineCol.X
 758  }
 759  
 760  // CaretCoords returns the coordinates of the caret, relative to the
 761  // editor itself.
 762  func (e *Editor) CaretCoords() f32.Point {
 763  	e.makeValid()
 764  	return f32.Pt(float32(e.caret.start.x)/64, float32(e.caret.start.y))
 765  }
 766  
 767  // offsetToScreenPos2 is a utility function to shortcut the common case of
 768  // wanting the positions of exactly two offsets.
 769  func (e *Editor) offsetToScreenPos2(o1, o2 int) (combinedPos, combinedPos) {
 770  	cp1, iter := e.offsetToScreenPos(o1)
 771  	return cp1, iter(o2)
 772  }
 773  
 774  // offsetToScreenPos takes an offset into the editor text (e.g.
 775  // e.caret.end.ofs) and returns a combinedPos that corresponds to its current
 776  // screen position, as well as an iterator that lets you get the combinedPos
 777  // of a later offset. The offsets given to offsetToScreenPos and to the
 778  // returned iterator must be sorted, lowest first, and they must be valid (0
 779  // <= offset <= e.Len()).
 780  //
 781  // This function is written this way to take advantage of previous work done
 782  // for offsets after the first. Otherwise you have to start from the top each
 783  // time.
 784  func (e *Editor) offsetToScreenPos(offset int) (combinedPos, func(int) combinedPos) {
 785  	var col, line, idx int
 786  	var x fixed.Int26_6
 787  
 788  	l := e.lines[line]
 789  	y := l.Ascent.Ceil()
 790  	prevDesc := l.Descent
 791  
 792  	iter := func(offset int) combinedPos {
 793  	LOOP:
 794  		for {
 795  			for ; col < len(l.Layout.Advances); col++ {
 796  				if idx >= offset {
 797  					break LOOP
 798  				}
 799  
 800  				x += l.Layout.Advances[col]
 801  				_, s := e.rr.runeAt(idx)
 802  				idx += s
 803  			}
 804  			if lastLine := line == len(e.lines)-1; lastLine || idx > offset {
 805  				break LOOP
 806  			}
 807  
 808  			line++
 809  			x = 0
 810  			col = 0
 811  			l = e.lines[line]
 812  			y += (prevDesc + l.Ascent).Ceil()
 813  			prevDesc = l.Descent
 814  		}
 815  		return combinedPos{
 816  			lineCol: screenPos{Y: line, X: col},
 817  			x:       x + align(e.Alignment, e.lines[line].Width, e.viewSize.X),
 818  			y:       y,
 819  			ofs:     offset,
 820  		}
 821  	}
 822  	return iter(offset), iter
 823  }
 824  
 825  func (e *Editor) invalidate() {
 826  	e.valid = false
 827  }
 828  
 829  // Delete runes from the caret position. The sign of runes specifies the
 830  // direction to delete: positive is forward, negative is backward.
 831  //
 832  // If there is a selection, it is deleted and counts as a single rune.
 833  func (e *Editor) Delete(runes int) {
 834  	if runes == 0 {
 835  		return
 836  	}
 837  
 838  	if l := e.caret.end.ofs - e.caret.start.ofs; l != 0 {
 839  		e.caret.start.ofs = e.rr.deleteRunes(e.caret.start.ofs, l)
 840  		runes -= sign(runes)
 841  	}
 842  
 843  	e.caret.start.ofs = e.rr.deleteRunes(e.caret.start.ofs, runes)
 844  	e.caret.start.xoff = 0
 845  	e.ClearSelection()
 846  	e.invalidate()
 847  }
 848  
 849  // Insert inserts text at the caret, moving the caret forward. If there is a
 850  // selection, Insert overwrites it.
 851  func (e *Editor) Insert(s string) {
 852  	e.append(s)
 853  	e.caret.scroll = true
 854  }
 855  
 856  // append inserts s at the cursor, leaving the caret is at the end of s. If
 857  // there is a selection, append overwrites it.
 858  // xxx|yyy + append zzz => xxxzzz|yyy
 859  func (e *Editor) append(s string) {
 860  	e.prepend(s)
 861  	e.caret.start.ofs += len(s)
 862  	e.caret.end.ofs = e.caret.start.ofs
 863  }
 864  
 865  // prepend inserts s after the cursor; the caret does not change. If there is
 866  // a selection, prepend overwrites it.
 867  // xxx|yyy + prepend zzz => xxx|zzzyyy
 868  func (e *Editor) prepend(s string) {
 869  	if e.SingleLine {
 870  		s = strings.ReplaceAll(s, "\n", " ")
 871  	}
 872  	e.caret.start.ofs = e.rr.deleteRunes(e.caret.start.ofs, e.caret.end.ofs-e.caret.start.ofs) // Delete any selection first.
 873  	e.rr.prepend(e.caret.start.ofs, s)
 874  	e.caret.start.xoff = 0
 875  	e.invalidate()
 876  }
 877  
 878  func (e *Editor) movePages(pages int, selAct selectionAction) {
 879  	e.makeValid()
 880  	y := e.caret.start.y + pages*e.viewSize.Y
 881  	var (
 882  		prevDesc fixed.Int26_6
 883  		carLine2 int
 884  	)
 885  	y2 := e.lines[0].Ascent.Ceil()
 886  	for i := 1; i < len(e.lines); i++ {
 887  		if y2 >= y {
 888  			break
 889  		}
 890  		l := e.lines[i]
 891  		h := (prevDesc + l.Ascent).Ceil()
 892  		prevDesc = l.Descent
 893  		if y2+h-y >= y-y2 {
 894  			break
 895  		}
 896  		y2 += h
 897  		carLine2++
 898  	}
 899  	e.caret.start = e.movePosToLine(e.caret.start, e.caret.start.x+e.caret.start.xoff, carLine2)
 900  	e.updateSelection(selAct)
 901  }
 902  
 903  func (e *Editor) movePosToLine(pos combinedPos, x fixed.Int26_6, line int) combinedPos {
 904  	e.makeValid(&pos)
 905  	if line < 0 {
 906  		line = 0
 907  	}
 908  	if line >= len(e.lines) {
 909  		line = len(e.lines) - 1
 910  	}
 911  
 912  	prevDesc := e.lines[line].Descent
 913  	for pos.lineCol.Y < line {
 914  		pos = e.movePosToEnd(pos)
 915  		l := e.lines[pos.lineCol.Y]
 916  		_, s := e.rr.runeAt(pos.ofs)
 917  		pos.ofs += s
 918  		pos.y += (prevDesc + l.Ascent).Ceil()
 919  		pos.lineCol.X = 0
 920  		prevDesc = l.Descent
 921  		pos.lineCol.Y++
 922  	}
 923  	for pos.lineCol.Y > line {
 924  		pos = e.movePosToStart(pos)
 925  		l := e.lines[pos.lineCol.Y]
 926  		_, s := e.rr.runeBefore(pos.ofs)
 927  		pos.ofs -= s
 928  		pos.y -= (prevDesc + l.Ascent).Ceil()
 929  		prevDesc = l.Descent
 930  		pos.lineCol.Y--
 931  		l = e.lines[pos.lineCol.Y]
 932  		pos.lineCol.X = len(l.Layout.Advances) - 1
 933  	}
 934  
 935  	pos = e.movePosToStart(pos)
 936  	l := e.lines[line]
 937  	pos.x = align(e.Alignment, l.Width, e.viewSize.X)
 938  	// Only move past the end of the last line
 939  	end := 0
 940  	if line < len(e.lines)-1 {
 941  		end = 1
 942  	}
 943  	// Move to rune closest to x.
 944  	for i := 0; i < len(l.Layout.Advances)-end; i++ {
 945  		adv := l.Layout.Advances[i]
 946  		if pos.x >= x {
 947  			break
 948  		}
 949  		if pos.x+adv-x >= x-pos.x {
 950  			break
 951  		}
 952  		pos.x += adv
 953  		_, s := e.rr.runeAt(pos.ofs)
 954  		pos.ofs += s
 955  		pos.lineCol.X++
 956  	}
 957  	pos.xoff = x - pos.x
 958  	return pos
 959  }
 960  
 961  // MoveCaret moves the caret (aka selection start) and the selection end
 962  // relative to their current positions. Positive distances moves forward,
 963  // negative distances moves backward. Distances are in runes.
 964  func (e *Editor) MoveCaret(startDelta, endDelta int) {
 965  	e.makeValid()
 966  	keepSame := e.caret.start.ofs == e.caret.end.ofs && startDelta == endDelta
 967  	e.caret.start = e.movePos(e.caret.start, startDelta)
 968  	e.caret.start.xoff = 0
 969  	// If they were in the same place, and we're moving them the same distance,
 970  	// just assign the new position, instead of recalculating it.
 971  	if keepSame {
 972  		e.caret.end = e.caret.start
 973  	} else {
 974  		e.caret.end = e.movePos(e.caret.end, endDelta)
 975  		e.caret.end.xoff = 0
 976  	}
 977  }
 978  
 979  func (e *Editor) movePos(pos combinedPos, distance int) combinedPos {
 980  	for ; distance < 0 && pos.ofs > 0; distance++ {
 981  		if pos.lineCol.X == 0 {
 982  			// Move to end of previous line.
 983  			pos = e.movePosToLine(pos, fixed.I(e.maxWidth), pos.lineCol.Y-1)
 984  			continue
 985  		}
 986  		l := e.lines[pos.lineCol.Y].Layout
 987  		_, s := e.rr.runeBefore(pos.ofs)
 988  		pos.ofs -= s
 989  		pos.lineCol.X--
 990  		pos.x -= l.Advances[pos.lineCol.X]
 991  	}
 992  	for ; distance > 0 && pos.ofs < e.rr.len(); distance-- {
 993  		l := e.lines[pos.lineCol.Y].Layout
 994  		// Only move past the end of the last line
 995  		end := 0
 996  		if pos.lineCol.Y < len(e.lines)-1 {
 997  			end = 1
 998  		}
 999  		if pos.lineCol.X >= len(l.Advances)-end {
1000  			// Move to start of next line.
1001  			pos = e.movePosToLine(pos, 0, pos.lineCol.Y+1)
1002  			continue
1003  		}
1004  		pos.x += l.Advances[pos.lineCol.X]
1005  		_, s := e.rr.runeAt(pos.ofs)
1006  		pos.ofs += s
1007  		pos.lineCol.X++
1008  	}
1009  	return pos
1010  }
1011  
1012  func (e *Editor) moveStart(selAct selectionAction) {
1013  	e.caret.start = e.movePosToStart(e.caret.start)
1014  	e.updateSelection(selAct)
1015  }
1016  
1017  func (e *Editor) movePosToStart(pos combinedPos) combinedPos {
1018  	e.makeValid(&pos)
1019  	layout := e.lines[pos.lineCol.Y].Layout
1020  	for i := pos.lineCol.X - 1; i >= 0; i-- {
1021  		_, s := e.rr.runeBefore(pos.ofs)
1022  		pos.ofs -= s
1023  		pos.x -= layout.Advances[i]
1024  	}
1025  	pos.lineCol.X = 0
1026  	pos.xoff = -pos.x
1027  	return pos
1028  }
1029  
1030  func (e *Editor) moveEnd(selAct selectionAction) {
1031  	e.caret.start = e.movePosToEnd(e.caret.start)
1032  	e.updateSelection(selAct)
1033  }
1034  
1035  func (e *Editor) movePosToEnd(pos combinedPos) combinedPos {
1036  	e.makeValid(&pos)
1037  	l := e.lines[pos.lineCol.Y]
1038  	// Only move past the end of the last line
1039  	end := 0
1040  	if pos.lineCol.Y < len(e.lines)-1 {
1041  		end = 1
1042  	}
1043  	layout := l.Layout
1044  	for i := pos.lineCol.X; i < len(layout.Advances)-end; i++ {
1045  		adv := layout.Advances[i]
1046  		_, s := e.rr.runeAt(pos.ofs)
1047  		pos.ofs += s
1048  		pos.x += adv
1049  		pos.lineCol.X++
1050  	}
1051  	a := align(e.Alignment, l.Width, e.viewSize.X)
1052  	pos.xoff = l.Width + a - pos.x
1053  	return pos
1054  }
1055  
1056  // moveWord moves the caret to the next word in the specified direction.
1057  // Positive is forward, negative is backward.
1058  // Absolute values greater than one will skip that many words.
1059  func (e *Editor) moveWord(distance int, selAct selectionAction) {
1060  	e.makeValid()
1061  	// split the distance information into constituent parts to be
1062  	// used independently.
1063  	words, direction := distance, 1
1064  	if distance < 0 {
1065  		words, direction = distance*-1, -1
1066  	}
1067  	// atEnd if caret is at either side of the buffer.
1068  	atEnd := func() bool {
1069  		return e.caret.start.ofs == 0 || e.caret.start.ofs == e.rr.len()
1070  	}
1071  	// next returns the appropriate rune given the direction.
1072  	next := func() (r rune) {
1073  		if direction < 0 {
1074  			r, _ = e.rr.runeBefore(e.caret.start.ofs)
1075  		} else {
1076  			r, _ = e.rr.runeAt(e.caret.start.ofs)
1077  		}
1078  		return r
1079  	}
1080  	for ii := 0; ii < words; ii++ {
1081  		for r := next(); unicode.IsSpace(r) && !atEnd(); r = next() {
1082  			e.MoveCaret(direction, 0)
1083  		}
1084  		e.MoveCaret(direction, 0)
1085  		for r := next(); !unicode.IsSpace(r) && !atEnd(); r = next() {
1086  			e.MoveCaret(direction, 0)
1087  		}
1088  	}
1089  	e.updateSelection(selAct)
1090  }
1091  
1092  // deleteWord deletes the next word(s) in the specified direction.
1093  // Unlike moveWord, deleteWord treats whitespace as a word itself.
1094  // Positive is forward, negative is backward.
1095  // Absolute values greater than one will delete that many words.
1096  // The selection counts as a single word.
1097  func (e *Editor) deleteWord(distance int) {
1098  	if distance == 0 {
1099  		return
1100  	}
1101  
1102  	e.makeValid()
1103  
1104  	if e.caret.start.ofs != e.caret.end.ofs {
1105  		e.Delete(1)
1106  		distance -= sign(distance)
1107  	}
1108  	if distance == 0 {
1109  		return
1110  	}
1111  
1112  	// split the distance information into constituent parts to be
1113  	// used independently.
1114  	words, direction := distance, 1
1115  	if distance < 0 {
1116  		words, direction = distance*-1, -1
1117  	}
1118  	// atEnd if offset is at or beyond either side of the buffer.
1119  	atEnd := func(offset int) bool {
1120  		idx := e.caret.start.ofs + offset*direction
1121  		return idx <= 0 || idx >= e.rr.len()
1122  	}
1123  	// next returns the appropriate rune given the direction and offset.
1124  	next := func(offset int) (r rune) {
1125  		idx := e.caret.start.ofs + offset*direction
1126  		if idx < 0 {
1127  			idx = 0
1128  		} else if idx > e.rr.len() {
1129  			idx = e.rr.len()
1130  		}
1131  		if direction < 0 {
1132  			r, _ = e.rr.runeBefore(idx)
1133  		} else {
1134  			r, _ = e.rr.runeAt(idx)
1135  		}
1136  		return r
1137  	}
1138  	var runes = 1
1139  	for ii := 0; ii < words; ii++ {
1140  		if r := next(runes); unicode.IsSpace(r) {
1141  			for r := next(runes); unicode.IsSpace(r) && !atEnd(runes); r = next(runes) {
1142  				runes += 1
1143  			}
1144  		} else {
1145  			for r := next(runes); !unicode.IsSpace(r) && !atEnd(runes); r = next(runes) {
1146  				runes += 1
1147  			}
1148  		}
1149  	}
1150  	e.Delete(runes * direction)
1151  }
1152  
1153  func (e *Editor) scrollToCaret() {
1154  	e.makeValid()
1155  	l := e.lines[e.caret.start.lineCol.Y]
1156  	if e.SingleLine {
1157  		var dist int
1158  		if d := e.caret.start.x.Floor() - e.scrollOff.X; d < 0 {
1159  			dist = d
1160  		} else if d := e.caret.start.x.Ceil() - (e.scrollOff.X + e.viewSize.X); d > 0 {
1161  			dist = d
1162  		}
1163  		e.scrollRel(dist, 0)
1164  	} else {
1165  		miny := e.caret.start.y - l.Ascent.Ceil()
1166  		maxy := e.caret.start.y + l.Descent.Ceil()
1167  		var dist int
1168  		if d := miny - e.scrollOff.Y; d < 0 {
1169  			dist = d
1170  		} else if d := maxy - (e.scrollOff.Y + e.viewSize.Y); d > 0 {
1171  			dist = d
1172  		}
1173  		e.scrollRel(0, dist)
1174  	}
1175  }
1176  
1177  // NumLines returns the number of lines in the editor.
1178  func (e *Editor) NumLines() int {
1179  	e.makeValid()
1180  	return len(e.lines)
1181  }
1182  
1183  // SelectionLen returns the length of the selection, in bytes; it is
1184  // equivalent to len(e.SelectedText()).
1185  func (e *Editor) SelectionLen() int {
1186  	return abs(e.caret.start.ofs - e.caret.end.ofs)
1187  }
1188  
1189  // Selection returns the start and end of the selection, as offsets into the
1190  // editor text. start can be > end.
1191  func (e *Editor) Selection() (start, end int) {
1192  	return e.caret.start.ofs, e.caret.end.ofs
1193  }
1194  
1195  // SetCaret moves the caret to start, and sets the selection end to end. start
1196  // and end are in bytes, and represent offsets into the editor text. start and
1197  // end must be at a rune boundary.
1198  func (e *Editor) SetCaret(start, end int) {
1199  	e.makeValid()
1200  	// Constrain start and end to [0, e.Len()].
1201  	l := e.Len()
1202  	start = max(min(start, l), 0)
1203  	end = max(min(end, l), 0)
1204  	e.caret.start.ofs, e.caret.end.ofs = start, end
1205  	e.makeValidCaret()
1206  	e.caret.scroll = true
1207  	e.scroller.Stop()
1208  }
1209  
1210  func (e *Editor) makeValidCaret(positions ...*combinedPos) {
1211  	// Jump through some hoops to order the offsets given to offsetToScreenPos,
1212  	// but still be able to update them correctly with the results thereof.
1213  	positions = append(positions, &e.caret.start, &e.caret.end)
1214  	sort.Slice(positions, func(i, j int) bool {
1215  		return positions[i].ofs < positions[j].ofs
1216  	})
1217  	var iter func(offset int) combinedPos
1218  	*positions[0], iter = e.offsetToScreenPos(positions[0].ofs)
1219  	for _, cp := range positions[1:] {
1220  		*cp = iter(cp.ofs)
1221  	}
1222  }
1223  
1224  // SelectedText returns the currently selected text (if any) from the editor.
1225  func (e *Editor) SelectedText() string {
1226  	l := e.SelectionLen()
1227  	if l == 0 {
1228  		return ""
1229  	}
1230  	buf := make([]byte, l)
1231  	e.rr.Seek(int64(min(e.caret.start.ofs, e.caret.end.ofs)), io.SeekStart)
1232  	_, err := e.rr.Read(buf)
1233  	if err != nil {
1234  		// The only error that rr.Read can return is EOF, which just means no
1235  		// selection, but we've already made sure that shouldn't happen.
1236  		panic("impossible error because end is before e.rr.Len()")
1237  	}
1238  	return string(buf)
1239  }
1240  
1241  func (e *Editor) updateSelection(selAct selectionAction) {
1242  	if selAct == selectionClear {
1243  		e.ClearSelection()
1244  	}
1245  }
1246  
1247  // ClearSelection clears the selection, by setting the selection end equal to
1248  // the selection start.
1249  func (e *Editor) ClearSelection() {
1250  	e.caret.end = e.caret.start
1251  }
1252  
1253  func max(a, b int) int {
1254  	if a > b {
1255  		return a
1256  	}
1257  	return b
1258  }
1259  
1260  func min(a, b int) int {
1261  	if a < b {
1262  		return a
1263  	}
1264  	return b
1265  }
1266  
1267  func abs(n int) int {
1268  	if n < 0 {
1269  		return -n
1270  	}
1271  	return n
1272  }
1273  
1274  func sign(n int) int {
1275  	switch {
1276  	case n < 0:
1277  		return -1
1278  	case n > 0:
1279  		return 1
1280  	default:
1281  		return 0
1282  	}
1283  }
1284  
1285  // sortPoints returns a and b sorted such that a2 <= b2.
1286  func sortPoints(a, b screenPos) (a2, b2 screenPos) {
1287  	if b.Less(a) {
1288  		return b, a
1289  	}
1290  	return a, b
1291  }
1292  
1293  func nullLayout(r io.Reader) ([]text.Line, error) {
1294  	rr := bufio.NewReader(r)
1295  	var rerr error
1296  	var n int
1297  	var buf bytes.Buffer
1298  	for {
1299  		r, s, err := rr.ReadRune()
1300  		n += s
1301  		buf.WriteRune(r)
1302  		if err != nil {
1303  			rerr = err
1304  			break
1305  		}
1306  	}
1307  	return []text.Line{
1308  		{
1309  			Layout: text.Layout{
1310  				Text:     buf.String(),
1311  				Advances: make([]fixed.Int26_6, n),
1312  			},
1313  		},
1314  	}, rerr
1315  }
1316  
1317  func (s ChangeEvent) isEditorEvent() {}
1318  func (s SubmitEvent) isEditorEvent() {}
1319  func (s SelectEvent) isEditorEvent() {}
1320