import { StyleSpan } from './style-map' ; import EventEmitter from '../utils/emitter' ; /**
 * Represents a single line, including rendering information.
 */ export class Line { public cursors : number [ ] = [ ] ; public styles : StyleSpan [ ] = [ ] ; public utf8ToChIndices : number [ ] = [ ] ; public chTo8Indices : number [ ] = [ ] ; public utf16ToChIndices : number [ ] = [ ] ; public chTo16Indices : number [ ] = [ ] ; param text param utf8Styles param cursors constructor ( public text , utf8Styles , cursors ) { let pos = 0 , pos8 = 0 , pos16 = 0 ; for ( const ch of text ) { const codePoint = ( < number > ch . codePointAt ( 0 ) ) ; this . utf8ToChIndices [ pos8 ] = pos ; this . chTo8Indices . push ( pos8 ) ; if ( codePoint < 0x80 ) { pos8 += 1 ; } else if ( codePoint < 0x800 ) { pos8 += 2 ; } else if ( codePoint < 0x10000 ) { pos8 += 3 ; } else if ( codePoint < 0x110000 ) { pos8 += 4 ; } else { throw new Error ( 'Char cannot be represented in JS.' ) ; } this . utf16ToChIndices [ pos16 ] = pos ; this . chTo16Indices . push ( pos16 ) ; pos16 += ch . length ; pos += 1 ; } this . utf8ToChIndices [ pos8 ] = pos ; this . chTo8Indices . push ( pos8 ) ; this . utf16ToChIndices [ pos16 ] = pos ; this . chTo16Indices . push ( pos16 ) ; this . cursors = cursors . map ( ( x ) => this . utf8ToChIndices [ x ] ) ; this . styles = StyleSpan . stylesFromCore ( this . utf8ToChIndices , utf8Styles , pos ) ; } param { Object } data return { Line } public static fromJSON ( data ) { const { text = '' , cursor = [ ] , styles = [ ] } = data ; const line = new Line ( text , styles , cursor ) ; return line ; } param { Line } line param { Object } data return { Line } public static updateFromJSON ( data , line ? ) { const { cursor , styles } = data ; if ( ! line ) { return null ; } if ( cursor ) { line . cursors = cursor ; } if ( styles ) { line . styles = styles ; } return line ; } return { Boolean } containsCursor ( ) { return this . cursors . length > 0 ; } return { Boolean } containsReservedStyle ( ) { return this . styles . length > 0 && this . styles . some ( ( { style } ) => style . isReservedStyle ( ) ) ; } toJSON ( ) { const { cursors , styles } = this ; return { cursor : cursors , styles } ; } } /**
 * Keeps a "window" (or cache) of a view which is inside xi-core.
 */ export default class LineCache extends EventEmitter { private nInvalidBefore : number = 0 ; private nInvalidAfter : number = 0 ; public lines : Array < Line | null > = [ ] ; return { Number } height ( ) { return this . nInvalidBefore + this . lines . length + this . nInvalidAfter ; } return { Boolean } isEmpty ( ) { return this . lines . length == 0 || ( this . lines . length == 1 && ( < Line > this . lines [ 0 ] ) . text == '' ) ; } param { Number } i return { String } get ( i ) { if ( i < this . nInvalidBefore ) { return null ; } i -= this . nInvalidBefore ; if ( i < this . lines . length ) { return this . lines [ i ] ; } return null ; } param { Object } update applyUpdate ( data ) { if ( ! data . ops ) { return ; } let newInvalidBefore = 0 ; let newInvalidAfter = 0 ; let newLines = [ ] ; let oldIndex = 0 ; const ops = data . ops ; for ( let i = 0 ; i < ops . length ; ++ i ) { const { n , op : op_type } = ops [ i ] ; if ( ! op_type || ! n ) { return ; } switch ( op_type ) { case 'invalidate' : { if ( newLines . length == 0 ) { newInvalidBefore += n ; } else { newInvalidAfter += n ; } break ; } case 'ins' : { for ( let i = 0 ; i < newInvalidAfter ; ++ i ) { newLines . push ( null ) ; } newInvalidAfter = 0 ; const { lines } = ops [ i ] ; if ( ! lines ) { return ; } for ( let i = 0 ; i < lines . length ; ++ i ) { const line = Line . fromJSON ( lines [ i ] ) ; newLines . push ( line ) ; } break ; } case 'copy' : case 'update' : { let nRemaining = n ; if ( oldIndex < this . nInvalidBefore ) { let nInvalid = Math . min ( n , this . nInvalidBefore - oldIndex ) ; if ( newLines . length == 0 ) { newInvalidBefore += nInvalid ; } else { newInvalidAfter += nInvalid ; } oldIndex += nInvalid ; nRemaining -= nInvalid ; } if ( nRemaining > 0 && oldIndex < this . nInvalidBefore + this . lines . length ) { for ( let i = 0 ; i < newInvalidAfter ; ++ i ) { newLines . push ( null ) ; } newInvalidAfter = 0 ; let nCopy = Math . min ( nRemaining , this . nInvalidBefore + this . lines . length - oldIndex ) ; let start = oldIndex - this . nInvalidBefore ; if ( op_type == 'copy' ) { newLines . push ( ... this . lines . slice ( start , start + nCopy ) ) ; } else { const { lines } = ops [ i ] ; if ( ! lines ) { return ; } let jsonIndex = n - nRemaining ; for ( let i = start ; i < start + nCopy ; ++ i ) { const line = this . lines [ i ] ; if ( ! line ) { continue ; } newLines . push ( Line . updateFromJSON ( line . toJSON ( ) , lines [ jsonIndex ] ) ) ; jsonIndex ++ ; } } oldIndex += nCopy ; nRemaining -= nCopy ; } if ( newLines . length == 0 ) { newInvalidBefore += nRemaining ; } else { newInvalidAfter += nRemaining ; } oldIndex += nRemaining ; break ; } case 'skip' : { oldIndex += n ; break ; } default : { console . error ( `Unknown op type: "${ op_type }".` ) ; } } } this . nInvalidBefore = newInvalidBefore ; this . nInvalidAfter = newInvalidAfter ; this . lines = newLines ; this . emit ( 'update' ) ; } param { Number } first param { Number } last return { Array } computeMissing ( first , last ) { const ranges = [ ] ; last = Math . min ( last , this . height ( ) ) ; if ( first >= last ) { console . error ( `compute missing called with first (${ first }) >= last (${ last })` ) ; return ranges ; } for ( let i = first ; i < last ; ++ i ) { if ( i < this . nInvalidBefore || i >= ( this . nInvalidBefore + this . lines . length ) || this . lines [ i - this . nInvalidBefore ] == null ) { if ( ranges . length == 0 || ranges [ ranges . length - 1 ] [ 1 ] != i ) { ranges . push ( [ i , i + 1 ] ) ; } else { ranges [ ranges . length - 1 ] [ 1 ] = i + 1 ; } } } ranges . forEach ( ( [ _first , _last ] ) => { } ) ; return ranges ; } }