{
  "version": 3,
  "sources": ["../../../src/core/cell-mover.ts"],
  "sourcesContent": [
    "import { parseFormula } from \"../parser/parser.cjs\";\nimport { astToString } from \"../parser/formatter.cjs\";\nimport { transformAST } from \"./ast-traverser.cjs\";\nimport type { ReferenceNode, RangeNode } from \"../parser/ast.cjs\";\n\n/**\n * Information about cells being moved\n */\nexport interface MovedCellsInfo {\n  /**\n   * Set of cell keys in format \"workbookName:sheetName:colIndex:rowIndex\"\n   * for fast O(1) lookup\n   */\n  cellsSet: Set<string>;\n  \n  /**\n   * Workbook name of the source cells (all moved cells must be in same workbook/sheet)\n   */\n  workbookName: string;\n  \n  /**\n   * Sheet name of the source cells\n   */\n  sheetName: string;\n  \n  /**\n   * Row offset for the move (targetRow - sourceRow)\n   */\n  rowOffset: number;\n  \n  /**\n   * Column offset for the move (targetCol - sourceCol)\n   */\n  colOffset: number;\n}\n\n/**\n * Creates a cell key for fast lookup\n */\nfunction createCellKey(workbookName: string, sheetName: string, colIndex: number, rowIndex: number): string {\n  return `${workbookName}:${sheetName}:${colIndex}:${rowIndex}`;\n}\n\n/**\n * Checks if a cell is in the moved cells set\n */\nfunction isCellMoved(\n  movedCells: MovedCellsInfo,\n  workbookName: string | undefined,\n  sheetName: string | undefined,\n  colIndex: number,\n  rowIndex: number\n): boolean {\n  // If no workbook/sheet specified in reference, it refers to current context\n  // We only update if it matches the moved cells' workbook/sheet\n  const refWorkbook = workbookName ?? movedCells.workbookName;\n  const refSheet = sheetName ?? movedCells.sheetName;\n  \n  // Only update references in the same workbook/sheet as the moved cells\n  if (refWorkbook !== movedCells.workbookName || refSheet !== movedCells.sheetName) {\n    return false;\n  }\n  \n  const key = createCellKey(refWorkbook, refSheet, colIndex, rowIndex);\n  return movedCells.cellsSet.has(key);\n}\n\n/**\n * Updates cell and range references in a formula when cells are moved\n * \n * @param formula - The formula string (without the leading =)\n * @param movedCells - Information about which cells were moved\n * @returns The updated formula string, or the original if no changes were made\n * \n * @example\n * // Moving A1 to D5 (offset: col+3, row+4)\n * updateReferencesForMovedCells(\"A1+B1\", {...}) // \"D5+B1\" (only A1 updated)\n * updateReferencesForMovedCells(\"SUM(A1:D5)\", {...}) // \"SUM(E4:H9)\" (if entire range moved)\n */\nexport function updateReferencesForMovedCells(\n  formula: string,\n  movedCells: MovedCellsInfo\n): string {\n  try {\n    const ast = parseFormula(formula);\n    \n    const transformedAST = transformAST(ast, (node) => {\n      // Handle cell references\n      if (node.type === \"reference\") {\n        const refNode = node as ReferenceNode;\n        \n        // Check if this cell reference points to a moved cell\n        if (isCellMoved(\n          movedCells,\n          refNode.workbookName,\n          refNode.sheetName,\n          refNode.address.colIndex,\n          refNode.address.rowIndex\n        )) {\n          // Update the reference to the new location\n          return {\n            ...refNode,\n            address: {\n              colIndex: refNode.address.colIndex + movedCells.colOffset,\n              rowIndex: refNode.address.rowIndex + movedCells.rowOffset,\n            },\n          };\n        }\n      }\n      \n      // Handle range references\n      if (node.type === \"range\") {\n        const rangeNode = node as RangeNode;\n        \n        // Only update if the range has finite bounds\n        if (\n          rangeNode.range.end.col.type === \"number\" &&\n          rangeNode.range.end.row.type === \"number\"\n        ) {\n          const startCol = rangeNode.range.start.col;\n          const startRow = rangeNode.range.start.row;\n          const endCol = rangeNode.range.end.col.value;\n          const endRow = rangeNode.range.end.row.value;\n          \n          // Check if BOTH start and end of the range are in the moved cells\n          const startMoved = isCellMoved(\n            movedCells,\n            rangeNode.workbookName,\n            rangeNode.sheetName,\n            startCol,\n            startRow\n          );\n          \n          const endMoved = isCellMoved(\n            movedCells,\n            rangeNode.workbookName,\n            rangeNode.sheetName,\n            endCol,\n            endRow\n          );\n          \n          // Only update if ENTIRE range is being moved\n          if (startMoved && endMoved) {\n            // Verify that all cells in the range are being moved together\n            // by checking if the range dimensions are preserved\n            const rangeWidth = endCol - startCol;\n            const rangeHeight = endRow - startRow;\n            \n            // Check a few cells in the middle to ensure contiguous movement\n            // For small ranges, check all cells\n            let allCellsMoved = true;\n            \n            // Sample checking strategy: check corners and center\n            const cellsToCheck: Array<[number, number]> = [\n              [startCol, startRow],     // Top-left (already checked)\n              [endCol, endRow],         // Bottom-right (already checked)\n            ];\n            \n            // Add middle cells for larger ranges\n            if (rangeWidth > 1 || rangeHeight > 1) {\n              const midCol = Math.floor((startCol + endCol) / 2);\n              const midRow = Math.floor((startRow + endRow) / 2);\n              cellsToCheck.push([midCol, midRow]);\n              \n              if (rangeWidth > 1) {\n                cellsToCheck.push([midCol, startRow]);\n                cellsToCheck.push([midCol, endRow]);\n              }\n              if (rangeHeight > 1) {\n                cellsToCheck.push([startCol, midRow]);\n                cellsToCheck.push([endCol, midRow]);\n              }\n            }\n            \n            // Check if all sampled cells are moved\n            for (const [col, row] of cellsToCheck) {\n              if (!isCellMoved(movedCells, rangeNode.workbookName, rangeNode.sheetName, col, row)) {\n                allCellsMoved = false;\n                break;\n              }\n            }\n            \n            if (allCellsMoved) {\n              // Update the entire range\n              return {\n                ...rangeNode,\n                range: {\n                  start: {\n                    col: startCol + movedCells.colOffset,\n                    row: startRow + movedCells.rowOffset,\n                  },\n                  end: {\n                    col: { type: \"number\" as const, value: endCol + movedCells.colOffset },\n                    row: { type: \"number\" as const, value: endRow + movedCells.rowOffset },\n                  },\n                },\n              };\n            }\n          }\n        }\n      }\n      \n      return node;\n    });\n\n    return astToString(transformedAST);\n  } catch (error) {\n    // If parsing fails, return the original formula\n    return formula;\n  }\n}\n\n/**\n * Checks if a formula contains a reference to a specific cell\n * \n * @param formula - The formula string (without the leading =)\n * @param workbookName - The workbook name\n * @param sheetName - The sheet name\n * @param colIndex - The column index\n * @param rowIndex - The row index\n * @returns True if the formula references the cell\n */\nexport function formulaReferencesCell(\n  formula: string,\n  workbookName: string,\n  sheetName: string,\n  colIndex: number,\n  rowIndex: number\n): boolean {\n  try {\n    const ast = parseFormula(formula);\n    let hasReference = false;\n\n    transformAST(ast, (node) => {\n      if (node.type === \"reference\") {\n        const refNode = node as ReferenceNode;\n        \n        // Match if same cell (with or without explicit sheet/workbook)\n        if (\n          refNode.address.colIndex === colIndex &&\n          refNode.address.rowIndex === rowIndex\n        ) {\n          // If reference has workbook/sheet, they must match\n          if (refNode.workbookName && refNode.workbookName !== workbookName) {\n            return node;\n          }\n          if (refNode.sheetName && refNode.sheetName !== sheetName) {\n            return node;\n          }\n          \n          hasReference = true;\n        }\n      }\n      return node;\n    });\n\n    return hasReference;\n  } catch (error) {\n    // If parsing fails, assume no reference\n    return false;\n  }\n}\n\n/**\n * Checks if a formula contains a reference to a specific range\n * \n * @param formula - The formula string (without the leading =)\n * @param workbookName - The workbook name\n * @param sheetName - The sheet name\n * @param startCol - Range start column\n * @param startRow - Range start row\n * @param endCol - Range end column\n * @param endRow - Range end row\n * @returns True if the formula references the range\n */\nexport function formulaReferencesRange(\n  formula: string,\n  workbookName: string,\n  sheetName: string,\n  startCol: number,\n  startRow: number,\n  endCol: number,\n  endRow: number\n): boolean {\n  try {\n    const ast = parseFormula(formula);\n    let hasReference = false;\n\n    transformAST(ast, (node) => {\n      if (node.type === \"range\") {\n        const rangeNode = node as RangeNode;\n        \n        // Check if range matches (must be finite)\n        if (\n          rangeNode.range.end.col.type === \"number\" &&\n          rangeNode.range.end.row.type === \"number\" &&\n          rangeNode.range.start.col === startCol &&\n          rangeNode.range.start.row === startRow &&\n          rangeNode.range.end.col.value === endCol &&\n          rangeNode.range.end.row.value === endRow\n        ) {\n          // If reference has workbook/sheet, they must match\n          if (rangeNode.workbookName && rangeNode.workbookName !== workbookName) {\n            return node;\n          }\n          if (rangeNode.sheetName && rangeNode.sheetName !== sheetName) {\n            return node;\n          }\n          \n          hasReference = true;\n        }\n      }\n      return node;\n    });\n\n    return hasReference;\n  } catch (error) {\n    // If parsing fails, assume no reference\n    return false;\n  }\n}\n\n"
  ],
  "mappings": "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",
  "debugId": "BE8D9973E30A540664756E2164756E21",
  "names": []
}