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 }