UNPKG

editcodewithai

Version:
169 lines (168 loc) 6.25 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.shouldDeleteFile = shouldDeleteFile; exports.prepareFilesForPrompt = prepareFilesForPrompt; exports.mergeFileChanges = mergeFileChanges; exports.parseDiffs = parseDiffs; exports.applyDiffs = applyDiffs; exports.parseDiffFenced = parseDiffFenced; exports.parseUdiffs = parseUdiffs; exports.applyUdiffs = applyUdiffs; const viz_utils_1 = require("@vizhub/viz-utils"); /** * If the LLM outputs empty text for a file, we interpret this * as a request to delete the file. */ function shouldDeleteFile(file) { if (!file) return false; return file.text.trim() === ""; } /** * Processes files for the prompt by truncating large files */ function prepareFilesForPrompt(files) { const result = {}; Object.values(files).forEach((file) => { // Example: truncate large files, etc. result[file.name] = file.text .split("\n") .slice(0, file.name.endsWith(".csv") || file.name.endsWith(".json") ? 50 : 500) .map((line) => line.slice(0, 200)) .join("\n"); }); return result; } /** * Merges original files with changes from the LLM */ function mergeFileChanges(originalFiles, parsedFiles) { // Start with existing files let changedFiles = Object.keys(originalFiles).reduce((acc, fileId) => { const original = originalFiles[fileId]; const changedText = parsedFiles[original.name]; const changedFile = changedText !== undefined ? { name: original.name, text: changedText } : undefined; if (shouldDeleteFile(changedFile)) { // Exclude from new set return acc; } // If changedFile is present, use it; otherwise use original acc[fileId] = { ...original, text: changedFile ? changedFile.text : original.text, }; return acc; }, {}); // Handle newly-created files Object.entries(parsedFiles).forEach(([fileName, fileText]) => { const existingFile = Object.values(changedFiles).find((file) => file.name === fileName); // If no existing file and not empty => it's a new file if (!existingFile && fileText.trim() !== "") { const newFileId = (0, viz_utils_1.generateVizFileId)(); changedFiles[newFileId] = { name: fileName, text: fileText, }; } }); return changedFiles; } function parseDiffs(responseText) { const diffs = []; // This regex captures the file path, and the content of the SEARCH and REPLACE blocks. const diffRegex = /^(.+)\n```\n<<<<<<< SEARCH\n([\s\S]*?)\n=======\n([\s\S]*?)\n>>>>>>> REPLACE\n```/gm; const matches = responseText.matchAll(diffRegex); for (const match of matches) { const [_, fileName, search, replace] = match; diffs.push({ fileName: fileName.trim(), search, replace, }); } return diffs; } function applyDiffs(originalFiles, diffs) { // Create a mutable copy of the files to avoid side effects. const changedFiles = JSON.parse(JSON.stringify(originalFiles)); for (const diff of diffs) { const fileId = Object.keys(changedFiles).find((id) => changedFiles[id].name === diff.fileName); if (!fileId) { throw new Error(`File not found: ${diff.fileName}`); } const file = changedFiles[fileId]; if (!file.text.includes(diff.search)) { throw new Error(`Search block not found in file: ${diff.fileName}`); } // Replace only the first occurrence, which is the standard behavior of string.replace. file.text = file.text.replace(diff.search, diff.replace); } return changedFiles; } function parseDiffFenced(responseText) { const diffs = []; const diffRegex = /^```\n(.+)\n<<<<<<< SEARCH\n([\s\S]*?)\n=======\n([\s\S]*?)\n>>>>>>> REPLACE\n```/gm; const matches = responseText.matchAll(diffRegex); for (const match of matches) { const [_, fileName, search, replace] = match; diffs.push({ fileName: fileName.trim(), search, replace, }); } return diffs; } function parseUdiffs(responseText) { const hunks = []; const udiffFileRegex = /```diff\n--- (.+?)\n\+\+\+ \1\n([\s\S]+?)```/g; let fileMatch; while ((fileMatch = udiffFileRegex.exec(responseText)) !== null) { const fileName = fileMatch[1].trim(); const allHunksContent = fileMatch[2]; const hunkParts = allHunksContent.split(/^@@ .* @@$/m).slice(1); for (const part of hunkParts) { if (part.trim() === "") continue; const lines = part.trim().split("\n"); const original = []; const updated = []; for (const line of lines) { if (line.startsWith("+")) { updated.push(line.substring(1)); } else if (line.startsWith("-")) { original.push(line.substring(1)); } else { const content = line.startsWith(" ") ? line.substring(1) : line; original.push(content); updated.push(content); } } hunks.push({ fileName: fileName, original: original.join("\n"), updated: updated.join("\n"), }); } } return hunks; } function applyUdiffs(originalFiles, hunks) { const changedFiles = JSON.parse(JSON.stringify(originalFiles)); for (const hunk of hunks) { const fileId = Object.keys(changedFiles).find((id) => changedFiles[id].name === hunk.fileName); if (!fileId) { throw new Error(`File not found: ${hunk.fileName}`); } const file = changedFiles[fileId]; if (!file.text.includes(hunk.original)) { throw new Error(`Original content for hunk not found in file: ${hunk.fileName}`); } file.text = file.text.replace(hunk.original, hunk.updated); } return changedFiles; }