function handleLoop()

in src/editor/sandbox/handleLoop.js [7:78]


function handleLoop(code) {
  let AST;
  try {
    AST = acorn.parse(code, {
      ecmaVersion: 'latest',
      sourceType: 'script'
    });
  } catch (e) {
    console.error('failed to parse code', e);
    return code;
  }

  /**
   * Temporarily store the range of positions where the code needs to be inserted
   */
  const fragments = [];
  /**
   * loopID is used to mark the loop
   */
  let loopID = 1;
  /**
   * Mark the code that needs to be inserted when looping
   */
  const insertCode = {
    setMonitor: 'LoopController.loopMonitor(%d);',
    delMonitor: ';LoopController.delLoop(%d);'
  };

  // Traverse the AST to find the loop position
  estraverse.traverse(AST, {
    enter(node) {
      switch (node.type) {
        case 'WhileStatement':
        case 'DoWhileStatement':
        case 'ForStatement':
        case 'ForInStatement':
        case 'ForOfStatement':
          // Gets the head and tail of the loop body
          let { start, end } = node.body;
          start++;
          let pre = insertCode.setMonitor.replace('%d', loopID);
          let aft = '';
          // If the body of the loop is not enveloped by {} and is indented, we need to manually add {}
          if (node.body.type !== 'BlockStatement') {
            pre = '{' + pre;
            aft = '}';
            --start;
          }
          fragments.push({ pos: start, str: pre });
          fragments.push({ pos: end, str: aft });
          fragments.push({
            pos: node.end,
            str: insertCode.delMonitor.replace('%d', loopID)
          });
          ++loopID;
          break;
        default:
          break;
      }
    }
  });

  // Insert code to corresponding position
  fragments
    .sort((a, b) => b.pos - a.pos)
    .forEach((fragment) => {
      code =
        code.slice(0, fragment.pos) + fragment.str + code.slice(fragment.pos);
    });

  return code;
}