MCPcopy
hub / github.com/modelcontextprotocol/servers / applyFileEdits

Function applyFileEdits

src/filesystem/lib.ts:194–282  ·  view source on GitHub ↗
(
  filePath: string,
  edits: FileEdit[],
  dryRun: boolean = false
)

Source from the content-addressed store, hash-verified

192}
193
194export async function applyFileEdits(
195 filePath: string,
196 edits: FileEdit[],
197 dryRun: boolean = false
198): Promise<string> {
199 // Read file content and normalize line endings
200 const content = normalizeLineEndings(await fs.readFile(filePath, 'utf-8'));
201
202 // Apply edits sequentially
203 let modifiedContent = content;
204 for (const edit of edits) {
205 const normalizedOld = normalizeLineEndings(edit.oldText);
206 const normalizedNew = normalizeLineEndings(edit.newText);
207
208 // If exact match exists, use it
209 if (modifiedContent.includes(normalizedOld)) {
210 modifiedContent = modifiedContent.replace(normalizedOld, () => normalizedNew);
211 continue;
212 }
213
214 // Otherwise, try line-by-line matching with flexibility for whitespace
215 const oldLines = normalizedOld.split('\n');
216 const contentLines = modifiedContent.split('\n');
217 let matchFound = false;
218
219 for (let i = 0; i <= contentLines.length - oldLines.length; i++) {
220 const potentialMatch = contentLines.slice(i, i + oldLines.length);
221
222 // Compare lines with normalized whitespace
223 const isMatch = oldLines.every((oldLine, j) => {
224 const contentLine = potentialMatch[j];
225 return oldLine.trim() === contentLine.trim();
226 });
227
228 if (isMatch) {
229 // Preserve original indentation of first line
230 const originalIndent = contentLines[i].match(/^\s*/)?.[0] || '';
231 const newLines = normalizedNew.split('\n').map((line, j) => {
232 if (j === 0) return originalIndent + line.trimStart();
233 // For subsequent lines, try to preserve relative indentation
234 const oldIndent = oldLines[j]?.match(/^\s*/)?.[0] || '';
235 const newIndent = line.match(/^\s*/)?.[0] || '';
236 if (oldIndent && newIndent) {
237 const relativeIndent = newIndent.length - oldIndent.length;
238 return originalIndent + ' '.repeat(Math.max(0, relativeIndent)) + line.trimStart();
239 }
240 return line;
241 });
242
243 contentLines.splice(i, oldLines.length, ...newLines);
244 modifiedContent = contentLines.join('\n');
245 matchFound = true;
246 break;
247 }
248 }
249
250 if (!matchFound) {
251 throw new Error(`Could not find exact match for edit:\n${edit.oldText}`);

Callers 2

index.tsFile · 0.85
lib.test.tsFile · 0.85

Calls 2

normalizeLineEndingsFunction · 0.85
createUnifiedDiffFunction · 0.85

Tested by

no test coverage detected