export class TextSelectionRange { start:number; length:number; get end():number { return this.start + this.length; } set end( value:number ) { this.length = value - this.start; } get( source:string ) { return source.substring( this.start, this.end ); } replace( source:string, replacement:string ) { let before = source.substring( 0, this.start ); let after = source.substring( this.end ); console.log( "start:", this.start, "end:", this.end) return [ before, replacement, after ].join( "" ); } static fromWithLength( start:number, length:number ) { let range = new TextSelectionRange(); range.start = start; range.length = length; return range; } static fromToExclusive( start:number, end:number ) { let range = new TextSelectionRange(); range.start = start; range.end = end; return range; } static fromString( source:string ) { let range = new TextSelectionRange(); range.start = 0; range.length = source.length; return range; } static replaceRanges( source:string, ranges:TextSelectionRange[], replacements:string[] ) { let fragments:string[] = []; let lastEnd = 0; for ( let i = 0; i < ranges.length; i++ ) { let range = ranges[ i ]; let replacement = replacements[ i ]; let beforeRange = source.substring( lastEnd, range.start ); fragments.push( beforeRange ); fragments.push( replacement ); lastEnd = range.end; if ( i !== ( ranges.length - 1 ) ) { continue; } let afterRange = source.substring( lastEnd, source.length ); fragments.push( afterRange ); } let replaced = fragments.join( "" ); if ( replaced === undefined ) { console.log( "Merging ranges failed:", ranges.length, fragments.length ); } return replaced; } }