import { CodeBlock , insertContentsAtOffset , searchFromOffset } from '../utils/commonCodeMod' ; import { findMatchingBracketPosition } from '../utils/matchBrackets' ; interface SwiftFunctionParam { argumentLabel : string ; parameterName : string ; typeString : string ; } interface InsertContentFunctionOptions { position : 'head' | 'tail' | 'tailBeforeLastReturn' ; indent ? : number ; } param source param imports returns export function addObjcImports ( source , imports ) { const lines = source . split ( '\n' ) ; const lineIndexWithFirstImport = lines . findIndex ( line => line . match ( /^#import .*$/ ) ) ; for ( const importElement of imports ) { if ( ! source . includes ( importElement ) ) { const importStatement = `#import ${ importElement }` ; lines . splice ( lineIndexWithFirstImport + 1 , 0 , importStatement ) ; } } return lines . join ( '\n' ) ; } param contents param declaration returns export function findObjcInterfaceCodeBlock ( contents , declaration ) { const start = contents . search ( new RegExp ( `^${ declaration }\\W` , 'm' ) ) ; if ( start < 0 ) { return null ; } let end = contents . indexOf ( '\n@end' , start ) ; end += 5 ; return { start , end , code : contents . substring ( start , end ) } ; } param contents param selector returns export function findObjcFunctionCodeBlock ( contents , selector ) { const symbols = selector . split ( ':' ) ; const argsCount = symbols . length - 1 ; let pattern = '^[\\-+]\\s*\\(.+?\\)' ; if ( argsCount === 0 ) { pattern += `${ symbols [ 0 ] }\\s+` ; } else { for ( let i = 0 ; i < argsCount ; ++ i ) { const argSymbol = `${ symbols [ i ] }:\\(.+\\)\\w+` ; pattern += `${ argSymbol }\\s+` ; } } pattern += '{' ; let start = contents . search ( new RegExp ( pattern , 'm' ) ) ; if ( start < 0 ) { return null ; } start = contents . indexOf ( '{' , start ) ; const end = findMatchingBracketPosition ( contents , '{' , start ) ; return { start , end , code : contents . substring ( start , end + 1 ) } ; } param srcContents param selector param insertion param options returns export function insertContentsInsideObjcFunctionBlock ( srcContents , selector , insertion , options ) { return insertContentsInsideFunctionBlock ( srcContents , selector , insertion , options , 'objc' ) ; } param srcContents param declaration param insertion param options returns export function insertContentsInsideObjcInterfaceBlock ( srcContents , declaration , insertion , options ) { const codeBlock = findObjcInterfaceCodeBlock ( srcContents , declaration ) ; if ( ! codeBlock ) { return srcContents ; } const { position } = options ; if ( position === 'head' ) { const firstNewLineIndex = srcContents . indexOf ( '\n' , codeBlock . start ) ; srcContents = insertContentsAtOffset ( srcContents , insertion , firstNewLineIndex ) ; } else if ( position === 'tail' ) { const endLen = '@end' . length ; srcContents = insertContentsAtOffset ( srcContents , insertion , codeBlock . end - endLen ) ; } return srcContents ; } param contents param selector returns export function findSwiftFunctionCodeBlock ( contents , selector ) { const parenthesesIndex = selector . indexOf ( '(' ) ; const funcName = selector . substring ( 0 , parenthesesIndex ) ; const argLabels = selector . substring ( parenthesesIndex + 1 , selector . length - 2 ) . split ( ':' ) ; let searchOffset = 0 ; const funcCandidateRegExp = new RegExp ( `\\sfunc\\s+${ funcName }\\(` , 'm' ) ; let funcCandidateOffset = searchFromOffset ( contents , funcCandidateRegExp , searchOffset ) ; while ( funcCandidateOffset >= 0 ) { const paramsStartOffset = contents . indexOf ( '(' , funcCandidateOffset ) ; const paramsEndOffset = findMatchingBracketPosition ( contents , '(' , paramsStartOffset ) ; const paramsString = contents . substring ( paramsStartOffset + 1 , paramsEndOffset ) ; const params = paramsString . split ( ',' ) . map ( parseSwiftFunctionParam ) ; searchOffset = paramsEndOffset + 1 ; funcCandidateOffset = searchFromOffset ( contents , funcCandidateRegExp , searchOffset ) ; if ( argLabels . length !== params . length ) { continue ; } for ( let i = 0 ; i < argLabels . length ; ++ i ) { if ( argLabels [ i ] !== params [ i ] . argumentLabel ) { continue ; } } const codeBlockStart = contents . indexOf ( '{' , paramsEndOffset ) ; const codeBlockEnd = findMatchingBracketPosition ( contents , '{' , paramsEndOffset ) ; const codeBlock = contents . substring ( codeBlockStart , codeBlockEnd + 1 ) ; return { start : codeBlockStart , end : codeBlockEnd , code : codeBlock } ; } return null ; } function parseSwiftFunctionParam ( paramTuple ) { const semiIndex = paramTuple . indexOf ( ':' ) ; const [ argumentLabel , parameterName ] = paramTuple . substring ( 0 , semiIndex ) . split ( /\s+/ ) ; const typeString = paramTuple . substring ( semiIndex + 1 ) . trim ( ) ; return { argumentLabel , parameterName , typeString } ; } param srcContents param declaration param insertion param options returns export function insertContentsInsideSwiftClassBlock ( srcContents , declaration , insertion , options ) { const start = srcContents . search ( new RegExp ( `\\s*${ declaration }.*?[\\(\\{]` ) ) ; if ( start < 0 ) { throw new Error ( `Unable to find class code block - declaration[${ declaration }]` ) ; } const { position } = options ; if ( position === 'head' ) { const firstBracketIndex = srcContents . indexOf ( '{' , start ) ; srcContents = insertContentsAtOffset ( srcContents , insertion , firstBracketIndex + 1 ) ; } else if ( position === 'tail' ) { const endBracketIndex = findMatchingBracketPosition ( srcContents , '{' , start ) ; srcContents = insertContentsAtOffset ( srcContents , insertion , endBracketIndex ) ; } return srcContents ; } param srcContents param selector param insertion param options returns export function insertContentsInsideSwiftFunctionBlock ( srcContents , selector , insertion , options ) { return insertContentsInsideFunctionBlock ( srcContents , selector , insertion , options , 'swift' ) ; } function insertContentsInsideFunctionBlock ( srcContents , selector , insertion , options , language ) { const codeBlock = language === 'objc' ? findObjcFunctionCodeBlock ( srcContents , selector ) : findSwiftFunctionCodeBlock ( srcContents , selector ) ; if ( ! codeBlock ) { return srcContents ; } const { position } = options ; const indent = ' ' . repeat ( options . indent ?? 2 ) ; if ( position === 'head' ) { srcContents = insertContentsAtOffset ( srcContents , `\n${ indent }${ insertion }` , codeBlock . start + 1 ) ; } else if ( position === 'tail' ) { srcContents = insertContentsAtOffset ( srcContents , `\n${ indent }${ insertion }` , codeBlock . end - 1 ) ; } else if ( position === 'tailBeforeLastReturn' ) { let lastReturnIndex = srcContents . lastIndexOf ( ' return ' , codeBlock . end ) ; if ( lastReturnIndex < 0 ) { throw new Error ( `Cannot find last return statement:\n${ srcContents }` ) ; } lastReturnIndex += 1 ; srcContents = insertContentsAtOffset ( srcContents , `${ insertion }\n${ indent }` , lastReturnIndex ) ; } return srcContents ; }