@@ -268,6 +268,221 @@ Invalid diff format`
268268 const result = strategy . applyDiff ( originalContent , diffContent )
269269 expect ( result ) . toBe ( " modified\n still indented\n end\n" )
270270 } )
271+
272+ it ( 'should handle complex refactoring with multiple functions' , ( ) => {
273+ const originalContent = `export async function extractTextFromFile(filePath: string): Promise<string> {
274+ try {
275+ await fs.access(filePath)
276+ } catch (error) {
277+ throw new Error(\`File not found: \${filePath}\`)
278+ }
279+ const fileExtension = path.extname(filePath).toLowerCase()
280+ switch (fileExtension) {
281+ case ".pdf":
282+ return extractTextFromPDF(filePath)
283+ case ".docx":
284+ return extractTextFromDOCX(filePath)
285+ case ".ipynb":
286+ return extractTextFromIPYNB(filePath)
287+ default:
288+ const isBinary = await isBinaryFile(filePath).catch(() => false)
289+ if (!isBinary) {
290+ return addLineNumbers(await fs.readFile(filePath, "utf8"))
291+ } else {
292+ throw new Error(\`Cannot read text for file type: \${fileExtension}\`)
293+ }
294+ }
295+ }
296+
297+ export function addLineNumbers(content: string): string {
298+ const lines = content.split('\\n')
299+ const maxLineNumberWidth = String(lines.length).length
300+ return lines
301+ .map((line, index) => {
302+ const lineNumber = String(index + 1).padStart(maxLineNumberWidth, ' ')
303+ return \`\${lineNumber} | \${line}\`
304+ }).join('\\n')
305+ }`
306+
307+ const diffContent = `test.ts
308+ <<<<<<< SEARCH
309+ export async function extractTextFromFile(filePath: string): Promise<string> {
310+ try {
311+ await fs.access(filePath)
312+ } catch (error) {
313+ throw new Error(\`File not found: \${filePath}\`)
314+ }
315+ const fileExtension = path.extname(filePath).toLowerCase()
316+ switch (fileExtension) {
317+ case ".pdf":
318+ return extractTextFromPDF(filePath)
319+ case ".docx":
320+ return extractTextFromDOCX(filePath)
321+ case ".ipynb":
322+ return extractTextFromIPYNB(filePath)
323+ default:
324+ const isBinary = await isBinaryFile(filePath).catch(() => false)
325+ if (!isBinary) {
326+ return addLineNumbers(await fs.readFile(filePath, "utf8"))
327+ } else {
328+ throw new Error(\`Cannot read text for file type: \${fileExtension}\`)
329+ }
330+ }
331+ }
332+
333+ export function addLineNumbers(content: string): string {
334+ const lines = content.split('\\n')
335+ const maxLineNumberWidth = String(lines.length).length
336+ return lines
337+ .map((line, index) => {
338+ const lineNumber = String(index + 1).padStart(maxLineNumberWidth, ' ')
339+ return \`\${lineNumber} | \${line}\`
340+ }).join('\\n')
341+ }
342+ =======
343+ function extractLineRange(content: string, startLine?: number, endLine?: number): string {
344+ const lines = content.split('\\n')
345+ const start = startLine ? Math.max(1, startLine) : 1
346+ const end = endLine ? Math.min(lines.length, endLine) : lines.length
347+
348+ if (start > end || start > lines.length) {
349+ throw new Error(\`Invalid line range: start=\${start}, end=\${end}, total lines=\${lines.length}\`)
350+ }
351+
352+ return lines.slice(start - 1, end).join('\\n')
353+ }
354+
355+ export async function extractTextFromFile(filePath: string, startLine?: number, endLine?: number): Promise<string> {
356+ try {
357+ await fs.access(filePath)
358+ } catch (error) {
359+ throw new Error(\`File not found: \${filePath}\`)
360+ }
361+ const fileExtension = path.extname(filePath).toLowerCase()
362+ let content: string
363+
364+ switch (fileExtension) {
365+ case ".pdf": {
366+ const dataBuffer = await fs.readFile(filePath)
367+ const data = await pdf(dataBuffer)
368+ content = extractLineRange(data.text, startLine, endLine)
369+ break
370+ }
371+ case ".docx": {
372+ const result = await mammoth.extractRawText({ path: filePath })
373+ content = extractLineRange(result.value, startLine, endLine)
374+ break
375+ }
376+ case ".ipynb": {
377+ const data = await fs.readFile(filePath, "utf8")
378+ const notebook = JSON.parse(data)
379+ let extractedText = ""
380+
381+ for (const cell of notebook.cells) {
382+ if ((cell.cell_type === "markdown" || cell.cell_type === "code") && cell.source) {
383+ extractedText += cell.source.join("\\n") + "\\n"
384+ }
385+ }
386+ content = extractLineRange(extractedText, startLine, endLine)
387+ break
388+ }
389+ default: {
390+ const isBinary = await isBinaryFile(filePath).catch(() => false)
391+ if (!isBinary) {
392+ const fileContent = await fs.readFile(filePath, "utf8")
393+ content = extractLineRange(fileContent, startLine, endLine)
394+ } else {
395+ throw new Error(\`Cannot read text for file type: \${fileExtension}\`)
396+ }
397+ }
398+ }
399+
400+ return addLineNumbers(content, startLine)
401+ }
402+
403+ export function addLineNumbers(content: string, startLine: number = 1): string {
404+ const lines = content.split('\\n')
405+ const maxLineNumberWidth = String(startLine + lines.length - 1).length
406+ return lines
407+ .map((line, index) => {
408+ const lineNumber = String(startLine + index).padStart(maxLineNumberWidth, ' ')
409+ return \`\${lineNumber} | \${line}\`
410+ }).join('\\n')
411+ }
412+ >>>>>>> REPLACE`
413+
414+ const result = strategy . applyDiff ( originalContent , diffContent )
415+ const expected = `function extractLineRange(content: string, startLine?: number, endLine?: number): string {
416+ const lines = content.split('\\n')
417+ const start = startLine ? Math.max(1, startLine) : 1
418+ const end = endLine ? Math.min(lines.length, endLine) : lines.length
419+
420+ if (start > end || start > lines.length) {
421+ throw new Error(\`Invalid line range: start=\${start}, end=\${end}, total lines=\${lines.length}\`)
422+ }
423+
424+ return lines.slice(start - 1, end).join('\\n')
425+ }
426+
427+ export async function extractTextFromFile(filePath: string, startLine?: number, endLine?: number): Promise<string> {
428+ try {
429+ await fs.access(filePath)
430+ } catch (error) {
431+ throw new Error(\`File not found: \${filePath}\`)
432+ }
433+ const fileExtension = path.extname(filePath).toLowerCase()
434+ let content: string
435+
436+ switch (fileExtension) {
437+ case ".pdf": {
438+ const dataBuffer = await fs.readFile(filePath)
439+ const data = await pdf(dataBuffer)
440+ content = extractLineRange(data.text, startLine, endLine)
441+ break
442+ }
443+ case ".docx": {
444+ const result = await mammoth.extractRawText({ path: filePath })
445+ content = extractLineRange(result.value, startLine, endLine)
446+ break
447+ }
448+ case ".ipynb": {
449+ const data = await fs.readFile(filePath, "utf8")
450+ const notebook = JSON.parse(data)
451+ let extractedText = ""
452+
453+ for (const cell of notebook.cells) {
454+ if ((cell.cell_type === "markdown" || cell.cell_type === "code") && cell.source) {
455+ extractedText += cell.source.join("\\n") + "\\n"
456+ }
457+ }
458+ content = extractLineRange(extractedText, startLine, endLine)
459+ break
460+ }
461+ default: {
462+ const isBinary = await isBinaryFile(filePath).catch(() => false)
463+ if (!isBinary) {
464+ const fileContent = await fs.readFile(filePath, "utf8")
465+ content = extractLineRange(fileContent, startLine, endLine)
466+ } else {
467+ throw new Error(\`Cannot read text for file type: \${fileExtension}\`)
468+ }
469+ }
470+ }
471+
472+ return addLineNumbers(content, startLine)
473+ }
474+
475+ export function addLineNumbers(content: string, startLine: number = 1): string {
476+ const lines = content.split('\\n')
477+ const maxLineNumberWidth = String(startLine + lines.length - 1).length
478+ return lines
479+ .map((line, index) => {
480+ const lineNumber = String(startLine + index).padStart(maxLineNumberWidth, ' ')
481+ return \`\${lineNumber} | \${line}\`
482+ }).join('\\n')
483+ }`
484+ expect ( result ) . toBe ( expected )
485+ } )
271486 } )
272487
273488 describe ( 'getToolDescription' , ( ) => {
0 commit comments