import { TxtNode , TxtParentNode } from "@textlint/ast-node-types" ; import { AbstractParser } from "./AbstractParser" ; import StructureSource from "structured-source" ; export class SourceCode { private index : number = 0 ; private source : any ; private textCharacters : string [ ] ; private sourceNode ? : TxtParentNode ; private contexts : string [ ] = [ ] ; private contextRanges : [ number , number ] [ ] = [ ] ; private firstChildPadding : number ; private startOffset : number ; constructor ( input ) { if ( typeof input === "string" ) { this . textCharacters = input . split ( "" ) ; this . source = new StructureSource ( input ) ; this . startOffset = 0 ; this . firstChildPadding = 0 ; } else { this . sourceNode = input ; this . startOffset = this . sourceNode . range [ 0 ] ; this . index = this . startOffset ; const lineBreaks = Array . from ( new Array ( this . sourceNode . loc . start . line - 1 ) ) . fill ( "\n" ) ; const offset = Array . from ( new Array ( this . startOffset - lineBreaks . length ) ) . fill ( "∯" ) ; this . textCharacters = offset . concat ( lineBreaks , input . raw . split ( "" ) ) ; this . source = new StructureSource ( this . textCharacters . join ( "" ) ) ; if ( this . sourceNode . children [ 0 ] ) { this . firstChildPadding = this . sourceNode . children [ 0 ] . range [ 0 ] - this . startOffset ; } else { this . firstChildPadding = 0 ; } } } markContextRange ( range ) { this . contextRanges . push ( range ) ; } isInContextRange ( ) { const offset = this . offset ; return this . contextRanges . some ( range => { return range [ 0 ] <= offset && offset < range [ 1 ] ; } ) ; } enterContext ( context ) { this . contexts . push ( context ) ; } isInContext ( context ? ) { if ( ! context ) { return this . contexts . length > 0 ; } return this . contexts . some ( targetContext => targetContext === context ) ; } leaveContext ( context ) { const index = this . contexts . lastIndexOf ( context ) ; if ( index !== - 1 ) { this . contexts . splice ( index , 1 ) ; } } returns { number } get offset ( ) { return this . index + this . firstChildPadding ; } /**
     * Return current position object.
     * It includes line, column, offset.
     */ now ( ) { const indexWithChildrenOffset = this . offset ; const position = this . source . indexToPosition ( indexWithChildrenOffset ) ; return { line : position . line as number , column : position . column as number , offset : indexWithChildrenOffset } ; } /**
     * Return true, no more read char
     */ get hasEnd ( ) { return this . read ( ) === false ; } returns { string } read ( over = 0 ) { const index = this . offset + over ; if ( index < this . startOffset ) { return false ; } if ( 0 <= index && index < this . textCharacters . length ) { return this . textCharacters [ index ] ; } return false ; } returns { node } readNode ( over = 0 ) { if ( ! this . sourceNode ) { return false ; } const index = this . offset + over ; if ( index < this . startOffset ) { return false ; } const matchNodeList = this . sourceNode . children . filter ( node => { return node . range [ 0 ] <= index && index < node . range [ 1 ] ; } ) ; if ( matchNodeList . length > 0 ) { return matchNodeList [ matchNodeList . length - 1 ] ; } return false ; } /**
     * Increment current index
     */ peek ( ) { this . index += 1 ; } /**
     * Increment node range
     */ peekNode ( node ) { this . index += node . range [ 1 ] - node . range [ 0 ] ; } /**
     * Seek and Peek
     */ seekNext ( parser ) { const startPosition = this . now ( ) ; parser . seek ( this ) ; const endPosition = this . now ( ) ; const value = this . sliceRange ( startPosition . offset , endPosition . offset ) ; return { value , startPosition , endPosition } ; } param { number } start param { number } end returns { string } sliceRange ( start , end ) { return this . textCharacters . slice ( start , end ) . join ( "" ) ; } }