github.com/jmigpin/editor@v1.6.0/util/iout/iorw/utils.go (about)

     1  package iorw
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"io"
     7  	"unicode"
     8  
     9  	"github.com/jmigpin/editor/util/iout"
    10  )
    11  
    12  //----------
    13  
    14  func NewStringReaderAt(s string) ReaderAt {
    15  	return NewBytesReadWriterAt([]byte(s))
    16  }
    17  
    18  //----------
    19  
    20  func REqual(r ReaderAt, i, n int, p []byte) (bool, error) {
    21  	if n != len(p) {
    22  		return false, nil
    23  	}
    24  	b, err := r.ReadFastAt(i, n)
    25  	if err != nil {
    26  		return false, err
    27  	}
    28  	return bytes.Equal(b, p), nil
    29  }
    30  
    31  //----------
    32  
    33  // Result might not be a copy.
    34  func ReadFastFull(rd ReaderAt) ([]byte, error) {
    35  	min, max := rd.Min(), rd.Max()
    36  	return rd.ReadFastAt(min, max-min)
    37  }
    38  
    39  // Result might not be a copy.
    40  func ReadFullCopy(rd ReaderAt) ([]byte, error) {
    41  	b, err := ReadFastFull(rd)
    42  	if err != nil {
    43  		return nil, err
    44  	}
    45  	return iout.CopyBytes(b), nil
    46  }
    47  
    48  //----------
    49  
    50  func SetBytes(rw ReadWriterAt, b []byte) error {
    51  	return rw.OverwriteAt(rw.Min(), rw.Max(), b)
    52  }
    53  func SetString(rw ReadWriterAt, s string) error {
    54  	return SetBytes(rw, []byte(s))
    55  }
    56  func Append(rw ReadWriterAt, b []byte) error {
    57  	return rw.OverwriteAt(rw.Max(), 0, b)
    58  }
    59  
    60  //----------
    61  
    62  const EndRune = -1
    63  
    64  // Iterate over n+1 runes, with the last rune being eofRune(-1).
    65  func ReaderIter(r ReaderAt, fn func(i int, ru rune) bool) error {
    66  	for i := r.Min(); ; {
    67  		ru, size, err := ReadRuneAt(r, i)
    68  		if err != nil {
    69  			if err == io.EOF {
    70  				_ = fn(i, EndRune)
    71  				return nil
    72  			}
    73  			return err
    74  		}
    75  		if !fn(i, ru) {
    76  			break
    77  		}
    78  		i += size
    79  	}
    80  	return nil
    81  }
    82  
    83  //----------
    84  
    85  func HasPrefix(r ReaderAt, i int, s []byte) bool {
    86  	if len(s) == 0 {
    87  		return true
    88  	}
    89  	b, err := r.ReadFastAt(i, len(s))
    90  	if err != nil {
    91  		return false
    92  	}
    93  	return bytes.HasPrefix(b, s)
    94  }
    95  
    96  //----------
    97  
    98  //----------
    99  
   100  // On error, returns best failing index. Use errors.Is(err, io.EOF) to handle limitedreaders.
   101  func RuneIndexFn(r ReaderAt, i int, truth bool, f func(rune) bool) (index, size int, err error) {
   102  	for {
   103  		ru, size, err := ReadRuneAt(r, i)
   104  		if err != nil {
   105  			// improve invalid index
   106  			m := r.Max()
   107  			if i > m {
   108  				i = m
   109  			}
   110  
   111  			return i, 0, err
   112  		}
   113  		if f(ru) == truth {
   114  			return i, size, nil
   115  		}
   116  		i += size
   117  	}
   118  }
   119  
   120  // On error, returns best failing index. Use errors.Is(err, io.EOF) to handle limitedreaders.
   121  func RuneLastIndexFn(r ReaderAt, i int, truth bool, f func(rune) bool) (index, size int, err error) {
   122  	for {
   123  		ru, size, err := ReadLastRuneAt(r, i)
   124  		if err != nil {
   125  			// improve invalid index
   126  			m := r.Min()
   127  			if i < m {
   128  				i = m
   129  			}
   130  
   131  			return i, 0, err
   132  		}
   133  		i -= size
   134  		if f(ru) == truth {
   135  			return i, size, nil
   136  		}
   137  	}
   138  }
   139  
   140  //----------
   141  
   142  // Returns index where truth was found.
   143  func ExpandRuneIndexFn(r ReaderAt, i int, truth bool, f func(rune) bool) int {
   144  	j, _, _ := RuneIndexFn(r, i, truth, f)
   145  	return j // found, or last known index before an err
   146  }
   147  
   148  // Returns last index before truth was found.
   149  func ExpandRuneLastIndexFn(r ReaderAt, i int, truth bool, f func(rune) bool) int {
   150  	j, size, err := RuneLastIndexFn(r, i, truth, f)
   151  	if err != nil {
   152  		return j // last known index before an err
   153  	}
   154  	return j + size
   155  }
   156  
   157  //----------
   158  
   159  func LinesIndexes(r ReaderAt, a, b int) (int, int, bool, error) {
   160  	ls, err := LineStartIndex(r, a)
   161  	if err != nil {
   162  		return 0, 0, false, err
   163  	}
   164  	le, newline, err := LineEndIndex(r, b)
   165  	if err != nil {
   166  		return 0, 0, false, err
   167  	}
   168  	return ls, le, newline, nil
   169  }
   170  
   171  func LineStartIndex(r ReaderAt, i int) (int, error) {
   172  	k, size, err := NewlineLastIndex(r, i)
   173  	if errors.Is(err, io.EOF) {
   174  		return k, nil
   175  	}
   176  	return k + size, err
   177  }
   178  
   179  // index after '\n' (with isNewLine true), or max index
   180  func LineEndIndex(r ReaderAt, i int) (int, bool, error) {
   181  	k, size, err := NewlineIndex(r, i)
   182  	if errors.Is(err, io.EOF) {
   183  		return k, false, nil
   184  	}
   185  	isNewLine := err == nil
   186  	return k + size, isNewLine, err
   187  }
   188  
   189  //----------
   190  
   191  func isNewline(ru rune) bool { return ru == '\n' }
   192  
   193  func NewlineIndex(r ReaderAt, i int) (int, int, error) {
   194  	return RuneIndexFn(r, i, true, isNewline)
   195  }
   196  
   197  func NewlineLastIndex(r ReaderAt, i int) (int, int, error) {
   198  	return RuneLastIndexFn(r, i, true, isNewline)
   199  }
   200  
   201  //----------
   202  
   203  // Also used at: selectword, movecursorjump{left,right}
   204  func IsWordRune(ru rune) bool {
   205  	return unicode.IsLetter(ru) || unicode.IsDigit(ru) || ru == '_'
   206  }
   207  
   208  func WordAtIndex(r ReaderAt, index int) ([]byte, int, error) {
   209  	// right side
   210  	i1, _, err := RuneIndexFn(r, index, false, IsWordRune)
   211  	if err != nil && !errors.Is(err, io.EOF) {
   212  		return nil, 0, err
   213  	}
   214  	if i1 == index { // don't match word at index
   215  		return nil, 0, errors.New("word not found")
   216  	}
   217  
   218  	// left side
   219  	i0, size, err := RuneLastIndexFn(r, index, false, IsWordRune)
   220  	if err != nil && !errors.Is(err, io.EOF) {
   221  		return nil, 0, err
   222  	}
   223  	i0 += size
   224  
   225  	w, err := r.ReadFastAt(i0, i1-i0)
   226  	if err != nil {
   227  		return nil, 0, err
   228  	}
   229  	return iout.CopyBytes(w), i0, nil
   230  }
   231  
   232  func WordIsolated(r ReaderAt, i, le int) bool {
   233  	// previous rune can't be a word rune
   234  	ru, _, err := ReadLastRuneAt(r, i)
   235  	if err == nil && IsWordRune(ru) {
   236  		return false
   237  	}
   238  	// next rune can't be a word rune
   239  	ru, _, err = ReadRuneAt(r, i+le)
   240  	if err == nil && IsWordRune(ru) {
   241  		return false
   242  	}
   243  	return true
   244  }