1*082815dcSEvan Baconimport { CodeBlock, insertContentsAtOffset } from '../utils/commonCodeMod'; 2*082815dcSEvan Baconimport { findMatchingBracketPosition } from '../utils/matchBrackets'; 3*082815dcSEvan Bacon 4*082815dcSEvan Bacon/** 5*082815dcSEvan Bacon * Find java or kotlin new class instance code block 6*082815dcSEvan Bacon * 7*082815dcSEvan Bacon * @param contents source contents 8*082815dcSEvan Bacon * @param classDeclaration class declaration or just a class name 9*082815dcSEvan Bacon * @param language 'java' | 'kt' 10*082815dcSEvan Bacon * @returns `CodeBlock` for start/end offset and code block contents 11*082815dcSEvan Bacon */ 12*082815dcSEvan Baconexport function findNewInstanceCodeBlock( 13*082815dcSEvan Bacon contents: string, 14*082815dcSEvan Bacon classDeclaration: string, 15*082815dcSEvan Bacon language: 'java' | 'kt' 16*082815dcSEvan Bacon): CodeBlock | null { 17*082815dcSEvan Bacon const isJava = language === 'java'; 18*082815dcSEvan Bacon let start = isJava 19*082815dcSEvan Bacon ? contents.indexOf(` new ${classDeclaration}(`) 20*082815dcSEvan Bacon : contents.search(new RegExp(` (object\\s*:\\s*)?${classDeclaration}\\(`)); 21*082815dcSEvan Bacon if (start < 0) { 22*082815dcSEvan Bacon return null; 23*082815dcSEvan Bacon } 24*082815dcSEvan Bacon // `+ 1` for the prefix space 25*082815dcSEvan Bacon start += 1; 26*082815dcSEvan Bacon let end = findMatchingBracketPosition(contents, '(', start); 27*082815dcSEvan Bacon 28*082815dcSEvan Bacon // For anonymous class, should search further to the {} block. 29*082815dcSEvan Bacon // ```java 30*082815dcSEvan Bacon // new Foo() { 31*082815dcSEvan Bacon // @Override 32*082815dcSEvan Bacon // protected void interfaceMethod {} 33*082815dcSEvan Bacon // }; 34*082815dcSEvan Bacon // ``` 35*082815dcSEvan Bacon // 36*082815dcSEvan Bacon // ```kotlin 37*082815dcSEvan Bacon // object : Foo() { 38*082815dcSEvan Bacon // override fun interfaceMethod {} 39*082815dcSEvan Bacon // } 40*082815dcSEvan Bacon // ``` 41*082815dcSEvan Bacon const nextBrace = contents.indexOf('{', end + 1); 42*082815dcSEvan Bacon const isAnonymousClass = 43*082815dcSEvan Bacon nextBrace >= end && !!contents.substring(end + 1, nextBrace).match(/^\s*$/); 44*082815dcSEvan Bacon if (isAnonymousClass) { 45*082815dcSEvan Bacon end = findMatchingBracketPosition(contents, '{', end); 46*082815dcSEvan Bacon } 47*082815dcSEvan Bacon 48*082815dcSEvan Bacon return { 49*082815dcSEvan Bacon start, 50*082815dcSEvan Bacon end, 51*082815dcSEvan Bacon code: contents.substring(start, end + 1), 52*082815dcSEvan Bacon }; 53*082815dcSEvan Bacon} 54*082815dcSEvan Bacon 55*082815dcSEvan Bacon/** 56*082815dcSEvan Bacon * Append contents to the end of code declaration block, support class or method declarations. 57*082815dcSEvan Bacon * 58*082815dcSEvan Bacon * @param srcContents source contents 59*082815dcSEvan Bacon * @param declaration class declaration or method declaration 60*082815dcSEvan Bacon * @param insertion code to append 61*082815dcSEvan Bacon * @returns updated contents 62*082815dcSEvan Bacon */ 63*082815dcSEvan Baconexport function appendContentsInsideDeclarationBlock( 64*082815dcSEvan Bacon srcContents: string, 65*082815dcSEvan Bacon declaration: string, 66*082815dcSEvan Bacon insertion: string 67*082815dcSEvan Bacon): string { 68*082815dcSEvan Bacon const start = srcContents.search(new RegExp(`\\s*${declaration}.*?[\\(\\{]`)); 69*082815dcSEvan Bacon if (start < 0) { 70*082815dcSEvan Bacon throw new Error(`Unable to find code block - declaration[${declaration}]`); 71*082815dcSEvan Bacon } 72*082815dcSEvan Bacon const end = findMatchingBracketPosition(srcContents, '{', start); 73*082815dcSEvan Bacon return insertContentsAtOffset(srcContents, insertion, end); 74*082815dcSEvan Bacon} 75*082815dcSEvan Bacon 76*082815dcSEvan Baconexport function addImports(source: string, imports: string[], isJava: boolean): string { 77*082815dcSEvan Bacon const lines = source.split('\n'); 78*082815dcSEvan Bacon const lineIndexWithPackageDeclaration = lines.findIndex((line) => line.match(/^package .*;?$/)); 79*082815dcSEvan Bacon for (const javaImport of imports) { 80*082815dcSEvan Bacon if (!source.includes(javaImport)) { 81*082815dcSEvan Bacon const importStatement = `import ${javaImport}${isJava ? ';' : ''}`; 82*082815dcSEvan Bacon lines.splice(lineIndexWithPackageDeclaration + 1, 0, importStatement); 83*082815dcSEvan Bacon } 84*082815dcSEvan Bacon } 85*082815dcSEvan Bacon return lines.join('\n'); 86*082815dcSEvan Bacon} 87