1"use strict";
2
3Object.defineProperty(exports, "__esModule", {
4  value: true
5});
6exports.addObjcImports = addObjcImports;
7exports.findObjcFunctionCodeBlock = findObjcFunctionCodeBlock;
8exports.findObjcInterfaceCodeBlock = findObjcInterfaceCodeBlock;
9exports.findSwiftFunctionCodeBlock = findSwiftFunctionCodeBlock;
10exports.insertContentsInsideObjcFunctionBlock = insertContentsInsideObjcFunctionBlock;
11exports.insertContentsInsideObjcInterfaceBlock = insertContentsInsideObjcInterfaceBlock;
12exports.insertContentsInsideSwiftClassBlock = insertContentsInsideSwiftClassBlock;
13exports.insertContentsInsideSwiftFunctionBlock = insertContentsInsideSwiftFunctionBlock;
14function _commonCodeMod() {
15  const data = require("../utils/commonCodeMod");
16  _commonCodeMod = function () {
17    return data;
18  };
19  return data;
20}
21function _matchBrackets() {
22  const data = require("../utils/matchBrackets");
23  _matchBrackets = function () {
24    return data;
25  };
26  return data;
27}
28/**
29 * Add Objective-C import
30 * @param source source contents
31 * @param imports array of imports, e.g. ['<Foundation/Foundation.h>']
32 * @returns updated contents
33 */
34function addObjcImports(source, imports) {
35  const lines = source.split('\n');
36  // Try to insert statements after first #import where would probably not in #if block
37  const lineIndexWithFirstImport = lines.findIndex(line => line.match(/^#import .*$/));
38  for (const importElement of imports) {
39    if (!source.includes(importElement)) {
40      const importStatement = `#import ${importElement}`;
41      lines.splice(lineIndexWithFirstImport + 1, 0, importStatement);
42    }
43  }
44  return lines.join('\n');
45}
46
47/**
48 * Find code block of Objective-C interface or implementation
49 *
50 * @param contents source contents
51 * @param declaration interface/implementation, e.g. '@interface Foo'
52 * @returns found CodeBlock, or null if not found
53 */
54function findObjcInterfaceCodeBlock(contents, declaration) {
55  const start = contents.search(new RegExp(`^${declaration}\\W`, 'm'));
56  if (start < 0) {
57    return null;
58  }
59  let end = contents.indexOf('\n@end', start);
60  end += 5; // '\n@end'.length === 5
61
62  return {
63    start,
64    end,
65    code: contents.substring(start, end)
66  };
67}
68
69/**
70 * Find code block of Objective-C function without declaration, will return only {} block
71 *
72 * @param contents source contents
73 * @param selector function selector, e.g. 'doSomething:withSomeValue:'
74 * @returns found CodeBlock, or null if not found.
75 */
76function findObjcFunctionCodeBlock(contents, selector) {
77  const symbols = selector.split(':');
78  const argsCount = symbols.length - 1;
79  let pattern = '^[\\-+]\\s*\\(.+?\\)';
80  if (argsCount === 0) {
81    pattern += `${symbols[0]}\\s+`;
82  } else {
83    for (let i = 0; i < argsCount; ++i) {
84      const argSymbol = `${symbols[i]}:\\(.+\\)\\w+`;
85      pattern += `${argSymbol}\\s+`;
86    }
87  }
88  pattern += '{';
89  let start = contents.search(new RegExp(pattern, 'm'));
90  if (start < 0) {
91    return null;
92  }
93  start = contents.indexOf('{', start);
94  const end = (0, _matchBrackets().findMatchingBracketPosition)(contents, '{', start);
95  return {
96    start,
97    end,
98    code: contents.substring(start, end + 1)
99  };
100}
101
102/**
103 * Insert contents to the Objective-C function block
104 *
105 * @param srcContents source contents
106 * @param selector function selector, e.g. 'doSomething:withSomeValue:'
107 * @param insertion code to insert
108 * @param options insertion options
109 * @returns updated contents
110 */
111function insertContentsInsideObjcFunctionBlock(srcContents, selector, insertion, options) {
112  return insertContentsInsideFunctionBlock(srcContents, selector, insertion, options, 'objc');
113}
114
115/**
116 * Insert contents to the Objective-C interface/implementation block
117 *
118 * @param srcContents source contents
119 * @param declaration interface/implementation, e.g. '@interface Foo'
120 * @param insertion code to insert
121 * @param options insertion options
122 * @returns updated contents
123 */
124function insertContentsInsideObjcInterfaceBlock(srcContents, declaration, insertion, options) {
125  const codeBlock = findObjcInterfaceCodeBlock(srcContents, declaration);
126  if (!codeBlock) {
127    return srcContents;
128  }
129  const {
130    position
131  } = options;
132  if (position === 'head') {
133    const firstNewLineIndex = srcContents.indexOf('\n', codeBlock.start);
134    srcContents = (0, _commonCodeMod().insertContentsAtOffset)(srcContents, insertion, firstNewLineIndex);
135  } else if (position === 'tail') {
136    const endLen = '@end'.length;
137    srcContents = (0, _commonCodeMod().insertContentsAtOffset)(srcContents, insertion, codeBlock.end - endLen);
138  }
139  return srcContents;
140}
141
142/**
143 * Find code block of Swift function without declaration, will return only {} block
144 *
145 * @param contents source contents
146 * @param selector function selector, e.g. 'doSomething(_:withSomeValue:)'
147 * @returns found CodeBlock, or null if not found.
148 */
149function findSwiftFunctionCodeBlock(contents, selector) {
150  const parenthesesIndex = selector.indexOf('(');
151  // `functName` === 'doSomething' of 'doSomething(_:withSomeValue:)'
152  const funcName = selector.substring(0, parenthesesIndex);
153  // `argLabels` === ['_', 'withSomeValue'] 'doSomething(_:withSomeValue:)'
154  const argLabels = selector.substring(parenthesesIndex + 1, selector.length - 2).split(':');
155  let searchOffset = 0;
156  const funcCandidateRegExp = new RegExp(`\\sfunc\\s+${funcName}\\(`, 'm');
157  let funcCandidateOffset = (0, _commonCodeMod().searchFromOffset)(contents, funcCandidateRegExp, searchOffset);
158  while (funcCandidateOffset >= 0) {
159    // Parse function parameters
160    const paramsStartOffset = contents.indexOf('(', funcCandidateOffset);
161    const paramsEndOffset = (0, _matchBrackets().findMatchingBracketPosition)(contents, '(', paramsStartOffset);
162    const paramsString = contents.substring(paramsStartOffset + 1, paramsEndOffset);
163    const params = paramsString.split(',').map(parseSwiftFunctionParam);
164
165    // Prepare offset for next round
166    searchOffset = paramsEndOffset + 1;
167    funcCandidateOffset = (0, _commonCodeMod().searchFromOffset)(contents, funcCandidateRegExp, searchOffset);
168
169    // Try to match function parameters
170    if (argLabels.length !== params.length) {
171      continue;
172    }
173    for (let i = 0; i < argLabels.length; ++i) {
174      if (argLabels[i] !== params[i].argumentLabel) {
175        continue;
176      }
177    }
178
179    // This function is matched one, get the code block.
180    const codeBlockStart = contents.indexOf('{', paramsEndOffset);
181    const codeBlockEnd = (0, _matchBrackets().findMatchingBracketPosition)(contents, '{', paramsEndOffset);
182    const codeBlock = contents.substring(codeBlockStart, codeBlockEnd + 1);
183    return {
184      start: codeBlockStart,
185      end: codeBlockEnd,
186      code: codeBlock
187    };
188  }
189  return null;
190}
191function parseSwiftFunctionParam(paramTuple) {
192  const semiIndex = paramTuple.indexOf(':');
193  const [argumentLabel, parameterName] = paramTuple.substring(0, semiIndex).split(/\s+/);
194  const typeString = paramTuple.substring(semiIndex + 1).trim();
195  return {
196    argumentLabel,
197    parameterName,
198    typeString
199  };
200}
201
202/**
203 * Insert contents to the swift class block
204 *
205 * @param srcContents source contents
206 * @param declaration class/extension declaration, e.g. 'class AppDelegate'
207 * @param insertion code to append
208 * @param options insertion options
209 * @returns updated contents
210 */
211function insertContentsInsideSwiftClassBlock(srcContents, declaration, insertion, options) {
212  const start = srcContents.search(new RegExp(`\\s*${declaration}.*?[\\(\\{]`));
213  if (start < 0) {
214    throw new Error(`Unable to find class code block - declaration[${declaration}]`);
215  }
216  const {
217    position
218  } = options;
219  if (position === 'head') {
220    const firstBracketIndex = srcContents.indexOf('{', start);
221    srcContents = (0, _commonCodeMod().insertContentsAtOffset)(srcContents, insertion, firstBracketIndex + 1);
222  } else if (position === 'tail') {
223    const endBracketIndex = (0, _matchBrackets().findMatchingBracketPosition)(srcContents, '{', start);
224    srcContents = (0, _commonCodeMod().insertContentsAtOffset)(srcContents, insertion, endBracketIndex);
225  }
226  return srcContents;
227}
228
229/**
230 * Insert contents to the Swift function block
231 *
232 * @param srcContents source contents
233 * @param selector function selector, e.g. 'doSomething:withSomeValue:'
234 * @param insertion code to insert
235 * @param options insertion options
236 * @returns updated contents
237 */
238function insertContentsInsideSwiftFunctionBlock(srcContents, selector, insertion, options) {
239  return insertContentsInsideFunctionBlock(srcContents, selector, insertion, options, 'swift');
240}
241function insertContentsInsideFunctionBlock(srcContents, selector, insertion, options, language) {
242  var _options$indent;
243  const codeBlock = language === 'objc' ? findObjcFunctionCodeBlock(srcContents, selector) : findSwiftFunctionCodeBlock(srcContents, selector);
244  if (!codeBlock) {
245    return srcContents;
246  }
247  const {
248    position
249  } = options;
250  const indent = ' '.repeat((_options$indent = options.indent) !== null && _options$indent !== void 0 ? _options$indent : 2);
251  if (position === 'head') {
252    srcContents = (0, _commonCodeMod().insertContentsAtOffset)(srcContents, `\n${indent}${insertion}`, codeBlock.start + 1);
253  } else if (position === 'tail') {
254    srcContents = (0, _commonCodeMod().insertContentsAtOffset)(srcContents, `\n${indent}${insertion}`, codeBlock.end - 1);
255  } else if (position === 'tailBeforeLastReturn') {
256    let lastReturnIndex = srcContents.lastIndexOf(' return ', codeBlock.end);
257    if (lastReturnIndex < 0) {
258      throw new Error(`Cannot find last return statement:\n${srcContents}`);
259    }
260    lastReturnIndex += 1; // +1 for the prefix space
261    srcContents = (0, _commonCodeMod().insertContentsAtOffset)(srcContents, `${insertion}\n${indent}`, lastReturnIndex);
262  }
263  return srcContents;
264}
265//# sourceMappingURL=codeMod.js.map