1// Copyright 2016 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5package text 6 7// TODO: do we care about "\n" vs "\r" vs "\r\n"? We only recognize "\n" for 8// now. 9 10import ( 11 "bytes" 12 "errors" 13 "io" 14 "strings" 15 "unicode/utf8" 16 17 "golang.org/x/image/math/fixed" 18) 19 20// Caret is a location in a Frame's text, and is the mechanism for adding and 21// removing bytes of text. Conceptually, a Caret and a Frame's text is like an 22// int c and a []byte t such that the text before and after that Caret is t[:c] 23// and t[c:]. That byte-count location remains unchanged even when a Frame is 24// re-sized and laid out into a new tree of Paragraphs, Lines and Boxes. 25// 26// A Frame can have multiple open Carets. For example, the beginning and end of 27// a text selection can be represented by two Carets. Multiple Carets for the 28// one Frame are not safe to use concurrently, but it is valid to interleave 29// such operations sequentially. For example, if two Carets c0 and c1 for the 30// one Frame are positioned at the 10th and 20th byte, and 4 bytes are written 31// to c0, inserting what becomes the equivalent of text[10:14], then c0's 32// position is updated to be 14 but c1's position is also updated to be 24. 33type Caret struct { 34 f *Frame 35 36 // caretsIndex is the index of this Caret in the f.carets slice. 37 caretsIndex int 38 39 // seqNum is the Frame f's sequence number for which this Caret's cached p, 40 // l, b and k fields are valid. If f has been modified since then, those 41 // fields will have to be re-calculated based on the pos field (which is 42 // always valid). 43 // 44 // TODO: when re-calculating p, l, b and k, be more efficient than a linear 45 // scan from the start or end? 46 seqNum uint64 47 48 // p, l and b cache the index of the Caret's Paragraph, Line and Box. None 49 // of these values can be zero. 50 p, l, b int32 51 52 // k caches the Caret's position in the text, in Frame.text order. It is 53 // valid to index the Frame.text slice with k, analogous to the Box.i and 54 // Box.j fields. For a Caret c, letting bb := c.f.boxes[c.b], an invariant 55 // is that bb.i <= c.k && c.k <= bb.j if the cache is valid (i.e. the 56 // Caret's seqNum equals the Frame's seqNum). 57 k int32 58 59 // pos is the Caret's position in the text, in layout order. It is the "c" 60 // as in "t[:c]" in the doc comment for type Caret above. It is not valid 61 // to index the Frame.text slice with pos, since the Frame.text slice does 62 // not necessarily hold the textual content in layout order. 63 pos int32 64 65 tmp [utf8.UTFMax]byte 66} 67 68// Close closes the Caret. 69func (c *Caret) Close() error { 70 i, j := c.caretsIndex, len(c.f.carets)-1 71 72 // Swap c with the last element of c.f.carets. 73 if i != j { 74 other := c.f.carets[j] 75 other.caretsIndex = i 76 c.f.carets[i] = other 77 } 78 79 c.f.carets[j] = nil 80 c.f.carets = c.f.carets[:j] 81 *c = Caret{} 82 return nil 83} 84 85type leanResult int 86 87const ( 88 // leanOK means that the lean changed the Caret's Box. 89 leanOK leanResult = iota 90 // leanFailedEOF means that the lean did not change the Caret's Box, 91 // because the Caret was already at the end / beginning of the Frame (when 92 // leaning forwards / backwards). 93 leanFailedEOF 94 // leanFailedNotEOB means that the lean did not change the Caret's Box, 95 // because the Caret was not placed at the end / beginning of the Box (when 96 // leaning forwards / backwards). 97 leanFailedNotEOB 98) 99 100// leanForwards moves the Caret from the right end of one Box to the left end 101// of the next Box, crossing Lines and Paragraphs to find that next Box. It 102// returns whether the Caret moved to a different Box. 103// 104// Diagramatically, suppose we have two adjacent boxes (shown by square 105// brackets below), with the Caret (an integer location called Caret.pos in the 106// Frame's text) in the middle of the "foo2bar3" word: 107// [foo0 foo1 foo2]^[bar3 bar4 bar5] 108// leanForwards moves Caret.k from fooBox.j to barBox.i, also updating the 109// Caret's p, l and b. Caret.pos remains unchanged. 110func (c *Caret) leanForwards() leanResult { 111 if c.k != c.f.boxes[c.b].j { 112 return leanFailedNotEOB 113 } 114 if nextB := c.f.boxes[c.b].next; nextB != 0 { 115 c.b = nextB 116 c.k = c.f.boxes[c.b].i 117 return leanOK 118 } 119 if nextL := c.f.lines[c.l].next; nextL != 0 { 120 c.l = nextL 121 c.b = c.f.lines[c.l].firstB 122 c.k = c.f.boxes[c.b].i 123 return leanOK 124 } 125 if nextP := c.f.paragraphs[c.p].next; nextP != 0 { 126 c.p = nextP 127 c.l = c.f.paragraphs[c.p].firstL 128 c.b = c.f.lines[c.l].firstB 129 c.k = c.f.boxes[c.b].i 130 return leanOK 131 } 132 return leanFailedEOF 133} 134 135// leanBackwards is like leanForwards but in the other direction. 136func (c *Caret) leanBackwards() leanResult { 137 if c.k != c.f.boxes[c.b].i { 138 return leanFailedNotEOB 139 } 140 if prevB := c.f.boxes[c.b].prev; prevB != 0 { 141 c.b = prevB 142 c.k = c.f.boxes[c.b].j 143 return leanOK 144 } 145 if prevL := c.f.lines[c.l].prev; prevL != 0 { 146 c.l = prevL 147 c.b = c.f.lines[c.l].lastBox(c.f) 148 c.k = c.f.boxes[c.b].j 149 return leanOK 150 } 151 if prevP := c.f.paragraphs[c.p].prev; prevP != 0 { 152 c.p = prevP 153 c.l = c.f.paragraphs[c.p].lastLine(c.f) 154 c.b = c.f.lines[c.l].lastBox(c.f) 155 c.k = c.f.boxes[c.b].j 156 return leanOK 157 } 158 return leanFailedEOF 159} 160 161func (c *Caret) seekStart() { 162 c.p = c.f.firstP 163 c.l = c.f.paragraphs[c.p].firstL 164 c.b = c.f.lines[c.l].firstB 165 c.k = c.f.boxes[c.b].i 166 c.pos = 0 167} 168 169func (c *Caret) seekEnd() { 170 c.p = c.f.lastParagraph() 171 c.l = c.f.paragraphs[c.p].lastLine(c.f) 172 c.b = c.f.lines[c.l].lastBox(c.f) 173 c.k = c.f.boxes[c.b].j 174 c.pos = int32(c.f.len) 175} 176 177// calculatePLBK ensures that the Caret's cached p, l, b and k fields are 178// valid. 179func (c *Caret) calculatePLBK() { 180 if c.seqNum != c.f.seqNum { 181 c.seek(c.pos) 182 } 183} 184 185// Seek satisfies the io.Seeker interface. 186func (c *Caret) Seek(offset int64, whence int) (int64, error) { 187 switch whence { 188 case SeekSet: 189 // No-op. 190 case SeekCur: 191 offset += int64(c.pos) 192 case SeekEnd: 193 offset += int64(c.f.len) 194 default: 195 return 0, errors.New("text: invalid seek whence") 196 } 197 if offset < 0 { 198 return 0, errors.New("text: negative seek position") 199 } 200 if offset > int64(c.f.len) { 201 offset = int64(c.f.len) 202 } 203 c.seek(int32(offset)) 204 return offset, nil 205} 206 207func (c *Caret) seek(off int32) { 208 delta := off - c.pos 209 // If the new offset is closer to the start or the end than to the current 210 // c.pos, or if c's cached {p,l,b,k} values are invalid, move to the start 211 // or end first. In case of a tie, we prefer to seek forwards (i.e. set 212 // delta > 0). 213 if (delta < 0 && -delta >= off) || (c.seqNum != c.f.seqNum) { 214 c.seekStart() 215 delta = off - c.pos 216 } 217 if delta > 0 && delta > int32(c.f.len)-off { 218 c.seekEnd() 219 delta = off - c.pos 220 } 221 222 if delta != 0 { 223 // Seek forwards. 224 for delta > 0 { 225 if n := c.f.boxes[c.b].j - c.k; n > 0 { 226 if n > delta { 227 n = delta 228 } 229 c.pos += n 230 c.k += n 231 delta -= n 232 } else if c.leanForwards() != leanOK { 233 panic("text: invalid state") 234 } 235 } 236 237 // Seek backwards. 238 for delta < 0 { 239 if n := c.f.boxes[c.b].i - c.k; n < 0 { 240 if n < delta { 241 n = delta 242 } 243 c.pos += n 244 c.k += n 245 delta -= n 246 } else if c.leanBackwards() != leanOK { 247 panic("text: invalid state") 248 } 249 } 250 251 // A Caret can't be placed at the end of a Paragraph, unless it is the 252 // final Paragraph. A simple way to enforce this is to lean forwards. 253 c.leanForwards() 254 } 255 256 c.seqNum = c.f.seqNum 257} 258 259// Read satisfies the io.Reader interface by copying those bytes after the 260// Caret and incrementing the Caret. 261func (c *Caret) Read(buf []byte) (n int, err error) { 262 c.calculatePLBK() 263 for len(buf) > 0 { 264 if j := c.f.boxes[c.b].j; c.k < j { 265 nn := copy(buf, c.f.text[c.k:j]) 266 buf = buf[nn:] 267 n += nn 268 c.pos += int32(nn) 269 c.k += int32(nn) 270 } 271 // A Caret can't be placed at the end of a Paragraph, unless it is the 272 // final Paragraph. A simple way to enforce this is to lean forwards. 273 if c.leanForwards() == leanFailedEOF { 274 break 275 } 276 } 277 if int(c.pos) == c.f.len { 278 err = io.EOF 279 } 280 return n, err 281} 282 283// ReadByte returns the next byte after the Caret and increments the Caret. 284func (c *Caret) ReadByte() (x byte, err error) { 285 c.calculatePLBK() 286 for { 287 if j := c.f.boxes[c.b].j; c.k < j { 288 x = c.f.text[c.k] 289 c.pos++ 290 c.k++ 291 // A Caret can't be placed at the end of a Paragraph, unless it is 292 // the final Paragraph. A simple way to enforce this is to lean 293 // forwards. 294 c.leanForwards() 295 return x, nil 296 } 297 if c.leanForwards() == leanFailedEOF { 298 return 0, io.EOF 299 } 300 } 301} 302 303// ReadRune returns the next rune after the Caret and increments the Caret. 304func (c *Caret) ReadRune() (r rune, size int, err error) { 305 c.calculatePLBK() 306 for { 307 if c.k < c.f.boxes[c.b].j { 308 r, size, c.b, c.k = c.f.readRune(c.b, c.k) 309 c.pos += int32(size) 310 // A Caret can't be placed at the end of a Paragraph, unless it is 311 // the final Paragraph. A simple way to enforce this is to lean 312 // forwards. 313 c.leanForwards() 314 return r, size, nil 315 } 316 if c.leanForwards() == leanFailedEOF { 317 return 0, 0, io.EOF 318 } 319 } 320} 321 322// WriteByte inserts x into the Frame's text at the Caret and increments the 323// Caret. 324func (c *Caret) WriteByte(x byte) error { 325 c.tmp[0] = x 326 return c.write(c.tmp[:1], "") 327} 328 329// WriteRune inserts r into the Frame's text at the Caret and increments the 330// Caret. 331func (c *Caret) WriteRune(r rune) (size int, err error) { 332 size = utf8.EncodeRune(c.tmp[:], r) 333 if err = c.write(c.tmp[:size], ""); err != nil { 334 return 0, err 335 } 336 return size, nil 337} 338 339// WriteString inserts s into the Frame's text at the Caret and increments the 340// Caret. 341func (c *Caret) WriteString(s string) (n int, err error) { 342 for len(s) > 0 { 343 i := 1 + strings.IndexByte(s, '\n') 344 if i == 0 { 345 i = len(s) 346 } 347 if err = c.write(nil, s[:i]); err != nil { 348 break 349 } 350 n += i 351 s = s[i:] 352 } 353 return n, err 354} 355 356// Write inserts s into the Frame's text at the Caret and increments the Caret. 357func (c *Caret) Write(s []byte) (n int, err error) { 358 for len(s) > 0 { 359 i := 1 + bytes.IndexByte(s, '\n') 360 if i == 0 { 361 i = len(s) 362 } 363 if err = c.write(s[:i], ""); err != nil { 364 break 365 } 366 n += i 367 s = s[i:] 368 } 369 return n, err 370} 371 372// write inserts a []byte or string into the Frame's text at the Caret. 373// 374// Exactly one of s0 and s1 must be non-empty. That non-empty argument must 375// contain at most one '\n' and if it does contain one, it must be the final 376// byte. 377func (c *Caret) write(s0 []byte, s1 string) error { 378 if m := maxLen - len(c.f.text); len(s0) > m || len(s1) > m { 379 return errors.New("text: insufficient space for writing") 380 } 381 382 // Ensure that the Caret is at the end of its Box, and that Box's text is 383 // at the end of the Frame's buffer. 384 c.calculatePLBK() 385 for { 386 bb, n := &c.f.boxes[c.b], int32(len(c.f.text)) 387 if c.k == bb.j && c.k == n { 388 break 389 } 390 391 // If the Box's text is empty, move its empty i:j range to the 392 // equivalent empty range at the end of c.f.text. 393 if bb.i == bb.j { 394 bb.i = n 395 bb.j = n 396 for _, cc := range c.f.carets { 397 if cc.b == c.b { 398 cc.k = n 399 } 400 } 401 continue 402 } 403 404 // Make the Caret be at the end of its Box. 405 if c.k != bb.j { 406 c.splitBox(true) 407 continue 408 } 409 410 // Make a new empty Box and move the Caret to it. 411 c.splitBox(true) 412 c.leanForwards() 413 } 414 415 c.f.invalidateCaches() 416 c.f.paragraphs[c.p].invalidateCaches() 417 c.f.lines[c.l].invalidateCaches() 418 419 length, nl := len(s0), false 420 if length > 0 { 421 nl = s0[length-1] == '\n' 422 c.f.text = append(c.f.text, s0...) 423 } else { 424 length = len(s1) 425 nl = s1[length-1] == '\n' 426 c.f.text = append(c.f.text, s1...) 427 } 428 c.f.len += length 429 c.f.boxes[c.b].j += int32(length) 430 c.k += int32(length) 431 for _, cc := range c.f.carets { 432 if cc.pos > c.pos { 433 cc.pos += int32(length) 434 } 435 } 436 c.pos += int32(length) 437 oldL := c.l 438 439 if nl { 440 breakParagraph(c.f, c.p, c.l, c.b) 441 c.p = c.f.paragraphs[c.p].next 442 c.l = c.f.paragraphs[c.p].firstL 443 c.b = c.f.lines[c.l].firstB 444 c.k = c.f.boxes[c.b].i 445 } 446 447 // TODO: re-layout the new c.p paragraph, if we saw '\n'. 448 layout(c.f, oldL) 449 450 c.f.seqNum++ 451 return nil 452} 453 454// breakParagraph breaks the Paragraph p into two Paragraphs, just after Box b 455// in Line l in Paragraph p. b's text must end with a '\n'. The new Paragraph 456// is inserted after p. 457func breakParagraph(f *Frame, p, l, b int32) { 458 // Assert that the Box b's text ends with a '\n'. 459 if j := f.boxes[b].j; j == 0 || f.text[j-1] != '\n' { 460 panic("text: invalid state") 461 } 462 463 // Make a new, empty Paragraph after this Paragraph p. 464 newP, _ := f.newParagraph() 465 nextP := f.paragraphs[p].next 466 if nextP != 0 { 467 f.paragraphs[nextP].prev = newP 468 } 469 f.paragraphs[newP].next = nextP 470 f.paragraphs[newP].prev = p 471 f.paragraphs[p].next = newP 472 473 // Any Lines in this Paragraph after the break point's Line l move to the 474 // newP Paragraph. 475 if nextL := f.lines[l].next; nextL != 0 { 476 f.lines[l].next = 0 477 f.lines[nextL].prev = 0 478 f.paragraphs[newP].firstL = nextL 479 } 480 481 // Any Boxes in this Line after the break point's Box b move to a new Line 482 // at the start of the newP Paragraph. 483 if nextB := f.boxes[b].next; nextB != 0 { 484 f.boxes[b].next = 0 485 f.boxes[nextB].prev = 0 486 newL, _ := f.newLine() 487 f.lines[newL].firstB = nextB 488 if newPFirstL := f.paragraphs[newP].firstL; newPFirstL != 0 { 489 f.lines[newL].next = newPFirstL 490 f.lines[newPFirstL].prev = newL 491 } 492 f.paragraphs[newP].firstL = newL 493 } 494 495 // Make the newP Paragraph's first Line and first Box explicit, since 496 // Carets require an explicit p, l and b. 497 { 498 pp := &f.paragraphs[newP] 499 if pp.firstL == 0 { 500 pp.firstL, _ = f.newLine() 501 } 502 ll := &f.lines[pp.firstL] 503 if ll.firstB == 0 { 504 ll.firstB, _ = f.newBox() 505 } 506 } 507 508 // TODO: re-layout the newP paragraph. 509} 510 511// breakLine breaks the Line l at text index k in Box b. The b-and-k index must 512// not be at the start or end of the Line. Text to the right of b-and-k in the 513// Line l will be moved to the start of the next Line in the Paragraph, with 514// that next Line being created if it didn't already exist. 515func breakLine(f *Frame, l, b, k int32) { 516 // Split this Box into two if necessary, so that k equals a Box's j end. 517 bb := &f.boxes[b] 518 if k != bb.j { 519 if k == bb.i { 520 panic("TODO: degenerate split left, possibly adjusting the Line's firstB??") 521 } 522 newB, realloc := f.newBox() 523 if realloc { 524 bb = &f.boxes[b] 525 } 526 nextB := bb.next 527 if nextB != 0 { 528 f.boxes[nextB].prev = newB 529 } 530 f.boxes[newB].next = nextB 531 f.boxes[newB].prev = b 532 f.boxes[newB].i = k 533 f.boxes[newB].j = bb.j 534 bb.next = newB 535 bb.j = k 536 } 537 538 // Assert that the break point isn't already at the start or end of the Line. 539 if bb.next == 0 || (bb.prev == 0 && k == bb.i) { 540 panic("text: invalid state") 541 } 542 543 // Insert a line after this one, if one doesn't already exist. 544 ll := &f.lines[l] 545 if ll.next == 0 { 546 newL, realloc := f.newLine() 547 if realloc { 548 ll = &f.lines[l] 549 } 550 f.lines[newL].prev = l 551 ll.next = newL 552 } 553 554 // Move the remaining boxes to the next line. 555 nextB, nextL := bb.next, ll.next 556 bb.next = 0 557 f.boxes[nextB].prev = 0 558 fb := f.lines[nextL].firstB 559 f.lines[nextL].firstB = nextB 560 561 // If the next Line already contained Boxes, append them to the end of the 562 // nextB chain, and join the two newly linked Boxes if possible. 563 if fb != 0 { 564 lb := f.lines[nextL].lastBox(f) 565 lbb := &f.boxes[lb] 566 fbb := &f.boxes[fb] 567 lbb.next = fb 568 fbb.prev = lb 569 f.joinBoxes(lb, fb, lbb, fbb) 570 } 571} 572 573// layout inserts a soft return in the Line l if its text measures longer than 574// f.maxWidth and a suitable line break point is found. This may spill text 575// onto the next line, which will also be laid out, and so on recursively. 576func layout(f *Frame, l int32) { 577 if f.maxWidth <= 0 || f.face == nil { 578 return 579 } 580 f.seqNum++ 581 582 for ; l != 0; l = f.lines[l].next { 583 var ( 584 firstB = f.lines[l].firstB 585 reader = f.lineReader(firstB, f.boxes[firstB].i) 586 breakPoint bAndK 587 prevR rune 588 prevRValid bool 589 advance fixed.Int26_6 590 ) 591 for { 592 r, _, err := reader.ReadRune() 593 if err != nil || r == '\n' { 594 return 595 } 596 if prevRValid { 597 advance += f.face.Kern(prevR, r) 598 } 599 // TODO: match all whitespace, not just ' '? 600 if r == ' ' { 601 breakPoint = reader.bAndK() 602 } 603 a, ok := f.face.GlyphAdvance(r) 604 if !ok { 605 panic("TODO: is falling back on the U+FFFD glyph the responsibility of the caller or the Face?") 606 } 607 advance += a 608 if r != ' ' && advance > f.maxWidth && breakPoint.b != 0 { 609 breakLine(f, l, breakPoint.b, breakPoint.k) 610 break 611 } 612 prevR, prevRValid = r, true 613 } 614 } 615} 616 617// Delete deletes nBytes bytes in the specified direction from the Caret's 618// location. It returns the number of bytes deleted, which can be fewer than 619// that requested if it hits the beginning or end of the Frame. 620func (c *Caret) Delete(dir Direction, nBytes int) (dBytes int) { 621 if nBytes <= 0 { 622 return 0 623 } 624 625 // Convert a backwards delete of n bytes from position p to a forwards 626 // delete of n bytes from position p-n. 627 // 628 // In general, it's easier to delete forwards than backwards. For example, 629 // when crossing paragraph boundaries, it's easier to find the first line 630 // of the next paragraph than the last line of the previous paragraph. 631 if dir == Backwards { 632 newPos := int(c.pos) - nBytes 633 if newPos < 0 { 634 newPos = 0 635 nBytes = int(c.pos) 636 if nBytes == 0 { 637 return 0 638 } 639 } 640 c.seek(int32(newPos)) 641 } 642 643 if int(c.pos) == c.f.len { 644 return 0 645 } 646 647 c.calculatePLBK() 648 c.leanForwards() 649 if c.f.boxes[c.b].i != c.k && c.splitBox(false) { 650 c.leanForwards() 651 } 652 for nBytes > 0 && int(c.pos) != c.f.len { 653 bb := &c.f.boxes[c.b] 654 n := bb.j - bb.i 655 newLine := n != 0 && c.f.text[bb.j-1] == '\n' 656 if int(n) > nBytes { 657 n = int32(nBytes) 658 } 659 bb.i += n 660 c.k += n 661 dBytes += int(n) 662 nBytes -= int(n) 663 c.f.len -= int(n) 664 665 if bb.i != bb.j { 666 break 667 } 668 669 if newLine { 670 c.joinNextParagraph() 671 } 672 c.leanForwards() 673 } 674 675 // The mergeIntoOneLine will shake out any empty Boxes. 676 l := c.f.mergeIntoOneLine(c.p) 677 layout(c.f, l) 678 c.f.invalidateCaches() 679 680 // Compact c.f.text if it's large enough and the fraction of deleted text 681 // is above some threshold. The actual threshold value (25%) is arbitrary. 682 // A lower value means more frequent compactions, so less memory on average 683 // but more CPU. A higher value means the opposite. 684 if len(c.f.text) > 4096 && len(c.f.text)/4 < c.f.deletedLen() { 685 c.f.compactText() 686 } 687 688 c.f.seqNum++ 689 for _, cc := range c.f.carets { 690 if cc == c { 691 continue 692 } 693 switch relPos := cc.pos - c.pos; { 694 case relPos <= 0: 695 // No-op. 696 case relPos <= int32(dBytes): 697 cc.pos = c.pos 698 default: 699 cc.pos -= int32(dBytes) 700 } 701 } 702 return dBytes 703} 704 705// DeleteRunes deletes nRunes runes in the specified direction from the Caret's 706// location. It returns the number of runes and bytes deleted, which can be 707// fewer than that requested if it hits the beginning or end of the Frame. 708func (c *Caret) DeleteRunes(dir Direction, nRunes int) (dRunes, dBytes int) { 709 // Save the current Caret position, move the Caret by nRunes runes to 710 // calculate how many bytes to delete, restore that saved Caret position, 711 // then delete that many bytes. 712 c.calculatePLBK() 713 savedC := *c 714 if dir == Forwards { 715 for dRunes < nRunes { 716 var size int 717 _, size, c.b, c.k = c.f.readRune(c.b, c.k) 718 if size != 0 { 719 dRunes++ 720 dBytes += size 721 } else if c.leanForwards() != leanOK { 722 break 723 } 724 } 725 } else { 726 for dRunes < nRunes { 727 var size int 728 _, size, c.b, c.k = c.f.readLastRune(c.b, c.k) 729 if size != 0 { 730 dRunes++ 731 dBytes += size 732 } else if c.leanBackwards() != leanOK { 733 break 734 } 735 } 736 } 737 *c = savedC 738 if dBytes != c.Delete(dir, dBytes) { 739 panic("text: invalid state") 740 } 741 return dRunes, dBytes 742} 743 744// joinNextParagraph joins c's current and next Paragraph. That next Paragraph 745// must exist, and c must be at the last Line of its current Paragraph. 746func (c *Caret) joinNextParagraph() { 747 pp0 := &c.f.paragraphs[c.p] 748 ll0 := &c.f.lines[c.l] 749 if pp0.next == 0 || ll0.next != 0 { 750 panic("text: invalid state") 751 } 752 pp1 := &c.f.paragraphs[pp0.next] 753 l1 := pp1.firstL 754 755 ll0.next = l1 756 c.f.lines[l1].prev = c.l 757 758 toFree := pp0.next 759 pp0.next = pp1.next 760 if pp0.next != 0 { 761 c.f.paragraphs[pp0.next].prev = c.p 762 } 763 764 c.f.freeParagraph(toFree) 765} 766 767// splitBox splits the Caret's Box into two, at the Caret's location. Unless 768// force is set, it does nothing if the Caret is at either edge of its Box. It 769// returns whether the Box was split. If so, the new Box is created after, not 770// before, the Caret's current Box. 771func (c *Caret) splitBox(force bool) bool { 772 bb := &c.f.boxes[c.b] 773 if !force && (c.k == bb.i || c.k == bb.j) { 774 return false 775 } 776 newB, realloc := c.f.newBox() 777 if realloc { 778 bb = &c.f.boxes[c.b] 779 } 780 nextB := bb.next 781 if nextB != 0 { 782 c.f.boxes[nextB].prev = newB 783 } 784 c.f.boxes[newB] = Box{ 785 next: nextB, 786 prev: c.b, 787 i: c.k, 788 j: bb.j, 789 } 790 bb.next = newB 791 bb.j = c.k 792 return true 793} 794