gioui.org/ui@v0.0.0-20190926171558-ce74bc0cbaea/text/buffer.go (about)

     1  // SPDX-License-Identifier: Unlicense OR MIT
     2  
     3  package text
     4  
     5  import (
     6  	"fmt"
     7  	"io"
     8  	"strings"
     9  	"unicode/utf8"
    10  )
    11  
    12  const bufferDebug = false
    13  
    14  // editBuffer implements a gap buffer for text editing.
    15  type editBuffer struct {
    16  	// caret is the caret position in bytes.
    17  	caret int
    18  	// pos is the byte position for Read and ReadRune.
    19  	pos int
    20  
    21  	// The gap start and end in bytes.
    22  	gapstart, gapend int
    23  	text             []byte
    24  
    25  	// changed tracks whether the buffer content
    26  	// has changed since the last call to Changed.
    27  	changed bool
    28  }
    29  
    30  const minSpace = 5
    31  
    32  func (e *editBuffer) Changed() bool {
    33  	c := e.changed
    34  	e.changed = false
    35  	return c
    36  }
    37  
    38  func (e *editBuffer) deleteRuneForward() {
    39  	e.moveGap(0)
    40  	_, s := utf8.DecodeRune(e.text[e.gapend:])
    41  	e.gapend += s
    42  	e.changed = e.changed || s > 0
    43  	e.dump()
    44  }
    45  
    46  func (e *editBuffer) deleteRune() {
    47  	e.moveGap(0)
    48  	_, s := utf8.DecodeLastRune(e.text[:e.gapstart])
    49  	e.gapstart -= s
    50  	e.caret -= s
    51  	e.changed = e.changed || s > 0
    52  	e.dump()
    53  }
    54  
    55  // moveGap moves the gap to the caret position. After returning,
    56  // the gap is guaranteed to be at least space bytes long.
    57  func (e *editBuffer) moveGap(space int) {
    58  	if e.gapLen() < space {
    59  		if space < minSpace {
    60  			space = minSpace
    61  		}
    62  		txt := make([]byte, e.len()+space)
    63  		// Expand to capacity.
    64  		txt = txt[:cap(txt)]
    65  		gaplen := len(txt) - e.len()
    66  		if e.caret > e.gapstart {
    67  			copy(txt, e.text[:e.gapstart])
    68  			copy(txt[e.caret+gaplen:], e.text[e.caret:])
    69  			copy(txt[e.gapstart:], e.text[e.gapend:e.caret+e.gapLen()])
    70  		} else {
    71  			copy(txt, e.text[:e.caret])
    72  			copy(txt[e.gapstart+gaplen:], e.text[e.gapend:])
    73  			copy(txt[e.caret+gaplen:], e.text[e.caret:e.gapstart])
    74  		}
    75  		e.text = txt
    76  		e.gapstart = e.caret
    77  		e.gapend = e.gapstart + gaplen
    78  	} else {
    79  		if e.caret > e.gapstart {
    80  			copy(e.text[e.gapstart:], e.text[e.gapend:e.caret+e.gapLen()])
    81  		} else {
    82  			copy(e.text[e.caret+e.gapLen():], e.text[e.caret:e.gapstart])
    83  		}
    84  		l := e.gapLen()
    85  		e.gapstart = e.caret
    86  		e.gapend = e.gapstart + l
    87  	}
    88  	e.dump()
    89  }
    90  
    91  func (e *editBuffer) len() int {
    92  	return len(e.text) - e.gapLen()
    93  }
    94  
    95  func (e *editBuffer) gapLen() int {
    96  	return e.gapend - e.gapstart
    97  }
    98  
    99  func (e *editBuffer) Read(p []byte) (int, error) {
   100  	if e.pos == e.len() {
   101  		return 0, io.EOF
   102  	}
   103  	var n int
   104  	if e.pos < e.gapstart {
   105  		n += copy(p, e.text[e.pos:e.gapstart])
   106  		p = p[n:]
   107  	}
   108  	n += copy(p, e.text[e.gapend:])
   109  	e.pos += n
   110  	return n, nil
   111  }
   112  
   113  func (e *editBuffer) ReadRune() (rune, int, error) {
   114  	if e.pos == e.len() {
   115  		return 0, 0, io.EOF
   116  	}
   117  	r, s := e.runeAt(e.pos)
   118  	e.pos += s
   119  	return r, s, nil
   120  }
   121  
   122  func (e *editBuffer) String() string {
   123  	var b strings.Builder
   124  	b.Grow(e.len())
   125  	b.Write(e.text[:e.gapstart])
   126  	b.Write(e.text[e.gapend:])
   127  	return b.String()
   128  }
   129  
   130  func (e *editBuffer) prepend(s string) {
   131  	e.moveGap(len(s))
   132  	copy(e.text[e.caret:], s)
   133  	e.gapstart += len(s)
   134  	e.changed = e.changed || len(s) > 0
   135  	e.dump()
   136  }
   137  
   138  func (e *editBuffer) dump() {
   139  	if bufferDebug {
   140  		fmt.Printf("len(e.text) %d e.len() %d e.gapstart %d e.gapend %d e.caret %d txt:\n'%+x'<-%d->'%+x'\n", len(e.text), e.len(), e.gapstart, e.gapend, e.caret, e.text[:e.gapstart], e.gapLen(), e.text[e.gapend:])
   141  	}
   142  }
   143  
   144  func (e *editBuffer) moveLeft() {
   145  	_, s := e.runeBefore(e.caret)
   146  	e.caret -= s
   147  	e.dump()
   148  }
   149  
   150  func (e *editBuffer) moveRight() {
   151  	_, s := e.runeAt(e.caret)
   152  	e.caret += s
   153  	e.dump()
   154  }
   155  
   156  func (e *editBuffer) runeBefore(idx int) (rune, int) {
   157  	if idx >= e.gapstart {
   158  		idx += e.gapLen()
   159  	}
   160  	return utf8.DecodeLastRune(e.text[:idx])
   161  }
   162  
   163  func (e *editBuffer) runeAt(idx int) (rune, int) {
   164  	if idx >= e.gapstart {
   165  		idx += e.gapLen()
   166  	}
   167  	return utf8.DecodeRune(e.text[idx:])
   168  }