github.com/gop9/olt@v0.0.0-20200202132135-d956aad50b08/gio/widget/buffer.go (about)

     1  // SPDX-License-Identifier: Unlicense OR MIT
     2  
     3  package widget
     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) deleteRunes(runes int) {
    39  	e.moveGap(0)
    40  	for ; runes < 0 && e.gapstart > 0; runes++ {
    41  		_, s := utf8.DecodeLastRune(e.text[:e.gapstart])
    42  		e.gapstart -= s
    43  		e.caret -= s
    44  		e.changed = e.changed || s > 0
    45  	}
    46  	for ; runes > 0 && e.gapend < len(e.text); runes-- {
    47  		_, s := utf8.DecodeRune(e.text[e.gapend:])
    48  		e.gapend += s
    49  		e.changed = e.changed || s > 0
    50  	}
    51  	e.dump()
    52  }
    53  
    54  // moveGap moves the gap to the caret position. After returning,
    55  // the gap is guaranteed to be at least space bytes long.
    56  func (e *editBuffer) moveGap(space int) {
    57  	if e.gapLen() < space {
    58  		if space < minSpace {
    59  			space = minSpace
    60  		}
    61  		txt := make([]byte, e.len()+space)
    62  		// Expand to capacity.
    63  		txt = txt[:cap(txt)]
    64  		gaplen := len(txt) - e.len()
    65  		if e.caret > e.gapstart {
    66  			copy(txt, e.text[:e.gapstart])
    67  			copy(txt[e.caret+gaplen:], e.text[e.caret:])
    68  			copy(txt[e.gapstart:], e.text[e.gapend:e.caret+e.gapLen()])
    69  		} else {
    70  			copy(txt, e.text[:e.caret])
    71  			copy(txt[e.gapstart+gaplen:], e.text[e.gapend:])
    72  			copy(txt[e.caret+gaplen:], e.text[e.caret:e.gapstart])
    73  		}
    74  		e.text = txt
    75  		e.gapstart = e.caret
    76  		e.gapend = e.gapstart + gaplen
    77  	} else {
    78  		if e.caret > e.gapstart {
    79  			copy(e.text[e.gapstart:], e.text[e.gapend:e.caret+e.gapLen()])
    80  		} else {
    81  			copy(e.text[e.caret+e.gapLen():], e.text[e.caret:e.gapstart])
    82  		}
    83  		l := e.gapLen()
    84  		e.gapstart = e.caret
    85  		e.gapend = e.gapstart + l
    86  	}
    87  	e.dump()
    88  }
    89  
    90  func (e *editBuffer) len() int {
    91  	return len(e.text) - e.gapLen()
    92  }
    93  
    94  func (e *editBuffer) gapLen() int {
    95  	return e.gapend - e.gapstart
    96  }
    97  
    98  func (e *editBuffer) Read(p []byte) (int, error) {
    99  	if e.pos == e.len() {
   100  		return 0, io.EOF
   101  	}
   102  	var n int
   103  	if e.pos < e.gapstart {
   104  		n += copy(p, e.text[e.pos:e.gapstart])
   105  		p = p[n:]
   106  	}
   107  	n += copy(p, e.text[e.gapend:])
   108  	e.pos += n
   109  	return n, nil
   110  }
   111  
   112  func (e *editBuffer) ReadRune() (rune, int, error) {
   113  	if e.pos == e.len() {
   114  		return 0, 0, io.EOF
   115  	}
   116  	r, s := e.runeAt(e.pos)
   117  	e.pos += s
   118  	return r, s, nil
   119  }
   120  
   121  func (e *editBuffer) String() string {
   122  	var b strings.Builder
   123  	b.Grow(e.len())
   124  	b.Write(e.text[:e.gapstart])
   125  	b.Write(e.text[e.gapend:])
   126  	return b.String()
   127  }
   128  
   129  func (e *editBuffer) prepend(s string) {
   130  	e.moveGap(len(s))
   131  	copy(e.text[e.caret:], s)
   132  	e.gapstart += len(s)
   133  	e.changed = e.changed || len(s) > 0
   134  	e.dump()
   135  }
   136  
   137  func (e *editBuffer) dump() {
   138  	if bufferDebug {
   139  		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:])
   140  	}
   141  }
   142  
   143  func (e *editBuffer) move(runes int) {
   144  	for ; runes < 0 && e.caret > 0; runes++ {
   145  		_, s := e.runeBefore(e.caret)
   146  		e.caret -= s
   147  	}
   148  	for ; runes > 0 && e.caret < len(e.text); runes-- {
   149  		_, s := e.runeAt(e.caret)
   150  		e.caret += s
   151  	}
   152  	e.dump()
   153  }
   154  
   155  func (e *editBuffer) runeBefore(idx int) (rune, int) {
   156  	if idx > e.gapstart {
   157  		idx += e.gapLen()
   158  	}
   159  	return utf8.DecodeLastRune(e.text[:idx])
   160  }
   161  
   162  func (e *editBuffer) runeAt(idx int) (rune, int) {
   163  	if idx >= e.gapstart {
   164  		idx += e.gapLen()
   165  	}
   166  	return utf8.DecodeRune(e.text[idx:])
   167  }