license import type { Chunk } from './chunker.js' ; import type { Seeker } from './seeker.js' ; /**
 * Seeks through text counting Unicode *code points* instead of *code units*.
 *
 * Javascript characters correspond to 16 bits *code units*, hence two such
 * ‘characters’ might together constitute a single Unicode character (i.e. a
 * *code point*). The  CodePointSeeker allows to ignore this
 * variable-length encoding, by counting code points instead.
 *
 * It is made to wrap a  Seeker that counts code units (presumably a
 *  TextSeeker ), which must be passed to its  constructor .
 *
 * When reading from the `CodePointSeeker`, the returned values is not a string
 * but an array of strings, each containing one code point (thus each having a
 * `length` that is either 1 or 2).
 *
 *  public export class CodePointSeeker < TChunk extends Chunk < string > > implements Seeker < TChunk , string [ ] > { position = 0 ; param raw The  Seeker to wrap, which counts in code *units* (e.g.
     * a  TextSeeker ). It should have  Seeker . position * `0` and its methods must no longer be used directly if the
     * `CodePointSeeker`’s position is to remain correct.
      constructor ( public readonly raw ) { } seekBy ( length ) { this . seekTo ( this . position + length ) ; } seekTo ( target ) { this . _readOrSeekTo ( false , target ) ; } read ( length , roundUp ? ) { return this . readTo ( this . position + length , roundUp ) ; } readTo ( target , roundUp ? ) { return this . _readOrSeekTo ( true , target , roundUp ) ; } get currentChunk ( ) : TChunk { return this . raw . currentChunk ; } get offsetInChunk ( ) : number { return this . raw . offsetInChunk ; } seekToChunk ( target , offset = 0 ) { this . _readOrSeekToChunk ( false , target , offset ) ; } readToChunk ( target , offset = 0 ) { return this . _readOrSeekToChunk ( true , target , offset ) ; } private _readOrSeekToChunk ( read , target , offset ? ) ; private _readOrSeekToChunk ( read , target , offset ? ) ; private _readOrSeekToChunk ( read , target , offset = 0 ) { const oldRawPosition = this . raw . position ; let s = this . raw . readToChunk ( target , offset ) ; const movedForward = this . raw . position >= oldRawPosition ; if ( movedForward && endsWithinCharacter ( s ) ) { this . raw . seekBy ( - 1 ) ; s = s . slice ( 0 , - 1 ) ; } else if ( ! movedForward && startsWithinCharacter ( s ) ) { this . raw . seekBy ( 1 ) ; s = s . slice ( 1 ) ; } const result = [ ... s ] ; this . position = movedForward ? this . position + result . length : this . position - result . length ; if ( read ) return result ; } private _readOrSeekTo ( read , target , roundUp ? ) ; private _readOrSeekTo ( read , target , roundUp ? ) ; private _readOrSeekTo ( read , target , roundUp = false ) { let result = [ ] ; if ( this . position < target ) { let unpairedSurrogate = '' ; let characters = [ ] ; while ( this . position < target ) { let s = unpairedSurrogate + this . raw . read ( 1 , true ) ; if ( endsWithinCharacter ( s ) ) { unpairedSurrogate = s . slice ( - 1 ) ; s = s . slice ( 0 , - 1 ) ; } else { unpairedSurrogate = '' ; } characters = [ ... s ] ; this . position += characters . length ; if ( read ) result = result . concat ( characters ) ; } if ( unpairedSurrogate ) this . raw . seekBy ( - 1 ) ; if ( ! roundUp && this . position > target ) { const overshootInCodePoints = this . position - target ; const overshootInCodeUnits = characters . slice ( - overshootInCodePoints ) . join ( '' ) . length ; this . position -= overshootInCodePoints ; this . raw . seekBy ( - overshootInCodeUnits ) ; } } else { let unpairedSurrogate = '' ; let characters = [ ] ; while ( this . position > target ) { let s = this . raw . read ( - 1 , true ) + unpairedSurrogate ; if ( startsWithinCharacter ( s ) ) { unpairedSurrogate = s [ 0 ] ; s = s . slice ( 1 ) ; } else { unpairedSurrogate = '' ; } characters = [ ... s ] ; this . position -= characters . length ; if ( read ) result = characters . concat ( result ) ; } if ( unpairedSurrogate ) this . raw . seekBy ( 1 ) ; if ( ! roundUp && this . position < target ) { const overshootInCodePoints = target - this . position ; const overshootInCodeUnits = characters . slice ( 0 , overshootInCodePoints ) . join ( '' ) . length ; this . position += overshootInCodePoints ; this . raw . seekBy ( overshootInCodeUnits ) ; } } if ( read ) return result ; } } function endsWithinCharacter ( s ) { const codeUnit = s . charCodeAt ( s . length - 1 ) ; return 0xd800 <= codeUnit && codeUnit <= 0xdbff ; } function startsWithinCharacter ( s ) { const codeUnit = s . charCodeAt ( 0 ) ; return 0xdc00 <= codeUnit && codeUnit <= 0xdfff ; }