import * as vscode from 'vscode' ; import * as path from 'path' ; import * as fs from 'fs' ; import { Diagram } from "./diagram" ; import { config } from "../config" ; const INCLUDE_REG = /^\s*!(include(?:sub)?)\s+(.+?)(?:!(\w+))?$/i ; const STARTSUB_TEST_REG = /^\s*!startsub\s+(\w+)/i ; const ENDSUB_TEST_REG = /^\s*!endsub\b/i ; const START_DIAGRAM_REG = /(^|\r?\n)\s*@start.*\r?\n/i ; const END_DIAGRAM_REG = /\r?\n\s*@end.*(\r?\n|$)(?!.*\r?\n\s*@end.*(\r?\n|$))/i ; interface FileSubBlocks { [ key ] : string [ ] ; } interface DictIncluded { [ key ] : boolean ; } let _included = { } ; let _route = [ ] ; export function getContentWithInclude ( diagram ) { _included = { } ; if ( diagram . parentUri ) { _route = [ diagram . parentUri . fsPath ] ; } else { _route = [ ] ; } let searchPaths = getSearchPaths ( diagram . parentUri ) ; return resolveInclude ( diagram . lines , searchPaths ) ; } function resolveInclude ( content , searchPaths ) { let lines = content instanceof Array ? content : content . replace ( /\r\n|\r/g , "\n" ) . split ( '\n' ) ; let processedLines = lines . map ( line => line . replace ( INCLUDE_REG , ( match , ... args ) => { let Action = args [ 0 ] . toLowerCase ( ) ; let target = args [ 1 ] . trim ( ) ; let sub = args [ 2 ] ; let file = path . isAbsolute ( target ) ? target : findFile ( target , searchPaths ) ; let result ; if ( Action == "include" ) { result = getIncludeContent ( file ) ; } else { result = getIncludesubContent ( file , sub ) ; } return result === undefined ? match : result ; } ) ) ; return processedLines . join ( '\n' ) ; } function getSearchPaths ( uri ) { let searchPaths = [ ] ; if ( uri ) { searchPaths . push ( path . dirname ( uri . fsPath ) ) ; } searchPaths . push ( ... config . includepaths ( uri ) ) ; let diagramsRoot = config . diagramsRoot ( uri ) ; if ( diagramsRoot ) searchPaths . push ( diagramsRoot . fsPath ) ; return Array . from ( new Set ( searchPaths ) ) ; } function findFile ( file , searchPaths ) { let found ; for ( let dir of searchPaths ) { found = path . join ( dir , file ) ; if ( fs . existsSync ( found ) ) return found ; } return undefined ; } function getIncludeContent ( file ) { if ( ! file || ! fs . existsSync ( file ) || ! fs . statSync ( file ) . isFile ( ) ) return undefined ; if ( _included [ file ] ) { return "" ; } _route . push ( file ) ; let content = fs . readFileSync ( file ) . toString ( ) ; _included [ file ] = true ; let result = resolveInclude ( content , getSearchPaths ( vscode . Uri . file ( file ) ) ) ; _route . pop ( ) ; result = result . replace ( START_DIAGRAM_REG , "$1" ) ; result = result . replace ( END_DIAGRAM_REG , "$1" ) ; return result ; } function getIncludesubContent ( file , sub ) { if ( ! file || ! sub ) return undefined ; let identifier = `${ file }!${ sub }` ; let find = findInArray ( _route , identifier ) ; if ( find >= 0 ) { throw 'Include loop detected!' + '\n\n' + makeLoopInfo ( find ) ; } _route . push ( identifier ) ; let result = undefined ; let blocks = getSubBlocks ( file ) ; if ( blocks ) { result = resolveInclude ( blocks [ sub ] , getSearchPaths ( vscode . Uri . file ( file ) ) ) ; } _route . pop ( ) ; return result ; } function getSubBlocks ( file ) { if ( ! file ) return { } ; let blocks = { } ; let lines = fs . readFileSync ( file ) . toString ( ) . split ( '\n' ) ; let subName = "" ; let match ; for ( let line of lines ) { match = STARTSUB_TEST_REG . exec ( line ) ; if ( match ) { subName = match [ 1 ] ; continue ; } else if ( ENDSUB_TEST_REG . test ( line ) ) { subName = "" ; continue ; } else { if ( subName ) { if ( ! blocks [ subName ] ) blocks [ subName ] = [ ] ; blocks [ subName ] . push ( line ) ; } } } return blocks ; } function findInArray < T > ( arr , find ) { for ( let i = 0 ; i < arr . length ; i ++ ) { if ( arr [ i ] == find ) return i ; } return - 1 ; } function makeLoopInfo ( loopID ) { let lines = [ ] ; for ( let i = 0 ; i < loopID ; i ++ ) { lines . push ( _route [ i ] ) ; } lines . push ( '|-> ' + _route [ loopID ] ) ; for ( let i = loopID + 1 ; i < _route . length - 1 ; i ++ ) { lines . push ( '|   ' + _route [ loopID ] ) ; } lines . push ( '|<- ' + _route [ _route . length - 1 ] ) ; return lines . join ( '\n' ) ; }