import Delta from '../delta/Delta' ; import AttributeMap from '../delta/AttributeMap' ; import { EditorRange } from './EditorRange' ; import isEqual from '../util/isEqual' ; const EMPTY_MAP = new Map ( ) ; const INFINITY = { id : '' , attributes : { } , content : new Delta ( [ { retain : Infinity } ] ) , length : Infinity } ; export type LineRanges = Map < Line , EditorRange > ; export type LineIds = Map < string , Line > ; export type LineInfo = { ranges : LineRanges ; ids : LineIds ; } ; interface Line { id : string ; attributes : AttributeMap ; content : Delta ; length : number ; } namespace Line { export function iterator ( lines , lineIds ? ) { return new LineIterator ( lines , lineIds ) ; } export function linesToLineIds ( lines ) { const lineIds = new Map ( ) ; lines . forEach ( line => lineIds . set ( line . id || Line . createId ( lineIds ) , line ) ) ; return lineIds ; } export function length ( line ) { return line . length ; } export function getId ( line ) { console . warn ( 'getId() is deprecated' ) ; return line . id ; } export function equal ( value , other ) { return isEqual ( value . attributes , other . attributes ) && isEqual ( value . content . ops , other . content . ops ) ; } export function fromDelta ( delta , existing ? ) { const lines = [ ] ; const ids = new Map ( existing || [ ] ) ; delta . eachLine ( ( content , attr ) => { const line = Line . create ( content , Object . keys ( attr ) . length ? attr : undefined , ids ) ; ids . set ( line . id , line ) ; lines . push ( line ) ; } ) ; return lines ; } export function toDelta ( lines ) { let delta = new Delta ( ) ; lines . forEach ( line => { delta = delta . concat ( line . content ) ; delta . insert ( '\n' , line . attributes ) ; } ) ; return delta ; } export function create ( content = new Delta ( ) , attributes = { } , id ? ) { const length = content . length ( ) + 1 ; if ( typeof id !== 'string' ) id = createId ( id ) ; return { id , attributes , content : content , length } ; } export function createFrom ( line ? , content = new Delta ( ) , lineIds ? ) { const id = line ? line . id : createId ( lineIds ) ; const attributes = line ? line . attributes : { } ; return { id , attributes , content , length : 1 } ; } export function getLineRanges ( lines ) { const ranges = new Map < Line , EditorRange > ( ) as LineRanges ; let pos = 0 ; lines . forEach ( line => { ranges . set ( line , [ pos , pos += line . length ] ) ; } ) ; return ranges ; } export function createId ( existing = EMPTY_MAP ) { let id ; while ( existing [ ( id = Math . random ( ) . toString ( 36 ) . slice ( 2 ) ) ] ) ; return id ; } } export default Line ; export class LineIterator { lines : Line [ ] ; index : number ; offset : number ; lineIds : LineIds ; constructor ( lines , lineIds ? ) { this . lines = lines ; this . index = 0 ; this . offset = 0 ; this . lineIds = lineIds ? new Map ( lineIds ) : Line . linesToLineIds ( lines ) ; } hasNext ( ) { return ! ! this . peek ( ) ; } next ( length ? ) { if ( ! length ) { length = Infinity ; } const nextLine = this . lines [ this . index ] ; if ( nextLine ) { const offset = this . offset ; const lineLength = nextLine . length ; if ( length >= lineLength - offset ) { length = lineLength - offset ; this . index += 1 ; this . offset = 0 ; } else { this . offset += length ; } if ( offset === 0 && length >= nextLine . length ) { return nextLine ; } else { const id = offset === 0 ? nextLine . id : Line . createId ( this . lineIds ) ; const partialLine = { id , attributes : nextLine . attributes , content : nextLine . content . slice ( offset , length ) , length : length - offset } ; if ( offset !== 0 ) this . lineIds . set ( id , partialLine ) ; return partialLine ; } } else { return INFINITY ; } } peek ( ) { return this . lines [ this . index ] ; } peekLength ( ) { if ( this . lines [ this . index ] ) { return this . lines [ this . index ] . length - this . offset ; } else { return Infinity ; } } rest ( ) { if ( ! this . hasNext ( ) ) { return [ ] ; } else if ( this . offset === 0 ) { return this . lines . slice ( this . index ) ; } else { const offset = this . offset ; const index = this . index ; const next = this . next ( ) ; const rest = this . lines . slice ( this . index ) ; this . offset = offset ; this . index = index ; return [ next ] . concat ( rest ) ; } } }