github.com/markusbkk/elvish@v0.0.0-20231204143114-91dc52438621/pkg/cli/term/read_rune.go (about) 1 package term 2 3 import ( 4 "time" 5 ) 6 7 type byteReaderWithTimeout interface { 8 // ReadByteWithTimeout reads a single byte with a timeout. A negative 9 // timeout means no timeout. 10 ReadByteWithTimeout(timeout time.Duration) (byte, error) 11 } 12 13 const badRune = '\ufffd' 14 15 var utf8SeqTimeout = 10 * time.Millisecond 16 17 // Reads a rune from the reader. The timeout applies to the first byte; a 18 // negative value means no timeout. 19 func readRune(rd byteReaderWithTimeout, timeout time.Duration) (rune, error) { 20 leader, err := rd.ReadByteWithTimeout(timeout) 21 if err != nil { 22 return badRune, err 23 } 24 var r rune 25 pending := 0 26 switch { 27 case leader>>7 == 0: 28 r = rune(leader) 29 case leader>>5 == 0x6: 30 r = rune(leader & 0x1f) 31 pending = 1 32 case leader>>4 == 0xe: 33 r = rune(leader & 0xf) 34 pending = 2 35 case leader>>3 == 0x1e: 36 r = rune(leader & 0x7) 37 pending = 3 38 } 39 for i := 0; i < pending; i++ { 40 b, err := rd.ReadByteWithTimeout(utf8SeqTimeout) 41 if err != nil { 42 return badRune, err 43 } 44 r = r<<6 + rune(b&0x3f) 45 } 46 return r, nil 47 }