Strip line numbers from write_to_file

This commit is contained in:
Matt Rubens
2024-12-17 09:04:46 -05:00
parent 3fcfc8d354
commit 800db618bb
4 changed files with 109 additions and 15 deletions

View File

@@ -12,7 +12,7 @@ import { ApiHandler, buildApiHandler } from "../api"
import { ApiStream } from "../api/transform/stream" import { ApiStream } from "../api/transform/stream"
import { DiffViewProvider } from "../integrations/editor/DiffViewProvider" import { DiffViewProvider } from "../integrations/editor/DiffViewProvider"
import { findToolName, formatContentBlockToMarkdown } from "../integrations/misc/export-markdown" import { findToolName, formatContentBlockToMarkdown } from "../integrations/misc/export-markdown"
import { extractTextFromFile, addLineNumbers } from "../integrations/misc/extract-text" import { extractTextFromFile, addLineNumbers, stripLineNumbers, everyLineHasLineNumbers } from "../integrations/misc/extract-text"
import { TerminalManager } from "../integrations/terminal/TerminalManager" import { TerminalManager } from "../integrations/terminal/TerminalManager"
import { UrlContentFetcher } from "../services/browser/UrlContentFetcher" import { UrlContentFetcher } from "../services/browser/UrlContentFetcher"
import { listFiles } from "../services/glob/list-files" import { listFiles } from "../services/glob/list-files"
@@ -1090,7 +1090,7 @@ export class Cline {
await this.diffViewProvider.open(relPath) await this.diffViewProvider.open(relPath)
} }
// editor is open, stream content in // editor is open, stream content in
await this.diffViewProvider.update(newContent, false) await this.diffViewProvider.update(everyLineHasLineNumbers(newContent) ? stripLineNumbers(newContent) : newContent, false)
break break
} else { } else {
if (!relPath) { if (!relPath) {
@@ -1116,7 +1116,7 @@ export class Cline {
await this.ask("tool", partialMessage, true).catch(() => {}) // sending true for partial even though it's not a partial, this shows the edit row before the content is streamed into the editor await this.ask("tool", partialMessage, true).catch(() => {}) // sending true for partial even though it's not a partial, this shows the edit row before the content is streamed into the editor
await this.diffViewProvider.open(relPath) await this.diffViewProvider.open(relPath)
} }
await this.diffViewProvider.update(newContent, true) await this.diffViewProvider.update(everyLineHasLineNumbers(newContent) ? stripLineNumbers(newContent) : newContent, true)
await delay(300) // wait for diff view to update await delay(300) // wait for diff view to update
this.diffViewProvider.scrollToFirstDiff() this.diffViewProvider.scrollToFirstDiff()

View File

@@ -1,5 +1,5 @@
import { DiffStrategy, DiffResult } from "../types" import { DiffStrategy, DiffResult } from "../types"
import { addLineNumbers } from "../../../integrations/misc/extract-text" import { addLineNumbers, everyLineHasLineNumbers, stripLineNumbers } from "../../../integrations/misc/extract-text"
const BUFFER_LINES = 20; // Number of extra context lines to show before and after matches const BUFFER_LINES = 20; // Number of extra context lines to show before and after matches
@@ -140,16 +140,7 @@ Your search/replace content here
const lineEnding = originalContent.includes('\r\n') ? '\r\n' : '\n'; const lineEnding = originalContent.includes('\r\n') ? '\r\n' : '\n';
// Strip line numbers from search and replace content if every line starts with a line number // Strip line numbers from search and replace content if every line starts with a line number
const hasLineNumbers = (content: string) => { if (everyLineHasLineNumbers(searchContent) && everyLineHasLineNumbers(replaceContent)) {
const lines = content.split(/\r?\n/);
return lines.length > 0 && lines.every(line => /^\s*\d+\s+\|(?!\|)/.test(line));
};
if (hasLineNumbers(searchContent) && hasLineNumbers(replaceContent)) {
const stripLineNumbers = (content: string) => {
return content.replace(/^\s*\d+\s+\|(?!\|)/gm, '');
};
searchContent = stripLineNumbers(searchContent); searchContent = stripLineNumbers(searchContent);
replaceContent = stripLineNumbers(replaceContent); replaceContent = stripLineNumbers(replaceContent);
} }

View File

@@ -1,4 +1,4 @@
import { addLineNumbers } from '../extract-text'; import { addLineNumbers, everyLineHasLineNumbers, stripLineNumbers } from '../extract-text';
describe('addLineNumbers', () => { describe('addLineNumbers', () => {
it('should add line numbers starting from 1 by default', () => { it('should add line numbers starting from 1 by default', () => {
@@ -30,3 +30,80 @@ describe('addLineNumbers', () => {
expect(addLineNumbers(input, 99)).toBe(expected); expect(addLineNumbers(input, 99)).toBe(expected);
}); });
}); });
describe('everyLineHasLineNumbers', () => {
it('should return true for content with line numbers', () => {
const input = '1 | line one\n2 | line two\n3 | line three';
expect(everyLineHasLineNumbers(input)).toBe(true);
});
it('should return true for content with padded line numbers', () => {
const input = ' 1 | line one\n 2 | line two\n 3 | line three';
expect(everyLineHasLineNumbers(input)).toBe(true);
});
it('should return false for content without line numbers', () => {
const input = 'line one\nline two\nline three';
expect(everyLineHasLineNumbers(input)).toBe(false);
});
it('should return false for mixed content', () => {
const input = '1 | line one\nline two\n3 | line three';
expect(everyLineHasLineNumbers(input)).toBe(false);
});
it('should handle empty content', () => {
expect(everyLineHasLineNumbers('')).toBe(false);
});
it('should return false for content with pipe but no line numbers', () => {
const input = 'a | b\nc | d';
expect(everyLineHasLineNumbers(input)).toBe(false);
});
});
describe('stripLineNumbers', () => {
it('should strip line numbers from content', () => {
const input = '1 | line one\n2 | line two\n3 | line three';
const expected = 'line one\nline two\nline three';
expect(stripLineNumbers(input)).toBe(expected);
});
it('should strip padded line numbers', () => {
const input = ' 1 | line one\n 2 | line two\n 3 | line three';
const expected = 'line one\nline two\nline three';
expect(stripLineNumbers(input)).toBe(expected);
});
it('should handle content without line numbers', () => {
const input = 'line one\nline two\nline three';
expect(stripLineNumbers(input)).toBe(input);
});
it('should handle empty content', () => {
expect(stripLineNumbers('')).toBe('');
});
it('should preserve content with pipe but no line numbers', () => {
const input = 'a | b\nc | d';
expect(stripLineNumbers(input)).toBe(input);
});
it('should handle windows-style line endings', () => {
const input = '1 | line one\r\n2 | line two\r\n3 | line three';
const expected = 'line one\r\nline two\r\nline three';
expect(stripLineNumbers(input)).toBe(expected);
});
it('should handle content with varying line number widths', () => {
const input = ' 1 | line one\n 10 | line two\n100 | line three';
const expected = 'line one\nline two\nline three';
expect(stripLineNumbers(input)).toBe(expected);
});
it('should preserve indentation after line numbers', () => {
const input = '1 | indented line\n2 | another indented';
const expected = ' indented line\n another indented';
expect(stripLineNumbers(input)).toBe(expected);
});
});

View File

@@ -53,6 +53,7 @@ async function extractTextFromIPYNB(filePath: string): Promise<string> {
return addLineNumbers(extractedText) return addLineNumbers(extractedText)
} }
export function addLineNumbers(content: string, startLine: number = 1): string { export function addLineNumbers(content: string, startLine: number = 1): string {
const lines = content.split('\n') const lines = content.split('\n')
const maxLineNumberWidth = String(startLine + lines.length - 1).length const maxLineNumberWidth = String(startLine + lines.length - 1).length
@@ -62,3 +63,28 @@ export function addLineNumbers(content: string, startLine: number = 1): string {
return `${lineNumber} | ${line}` return `${lineNumber} | ${line}`
}).join('\n') }).join('\n')
} }
// Checks if every line in the content has line numbers prefixed (e.g., "1 | content" or "123 | content")
// Line numbers must be followed by a single pipe character (not double pipes)
export function everyLineHasLineNumbers(content: string): boolean {
const lines = content.split(/\r?\n/)
return lines.length > 0 && lines.every(line => /^\s*\d+\s+\|(?!\|)/.test(line))
}
// Strips line numbers from content while preserving the actual content
// Handles formats like "1 | content", " 12 | content", "123 | content"
// Preserves content that naturally starts with pipe characters
export function stripLineNumbers(content: string): string {
// Split into lines to handle each line individually
const lines = content.split(/\r?\n/)
// Process each line
const processedLines = lines.map(line => {
// Match line number pattern and capture everything after the pipe
const match = line.match(/^\s*\d+\s+\|(?!\|)\s?(.*)$/)
return match ? match[1] : line
})
// Join back with original line endings
const lineEnding = content.includes('\r\n') ? '\r\n' : '\n'
return processedLines.join(lineEnding)
}