Add a Git section to the context mentions

This commit is contained in:
Matt Rubens
2025-01-06 01:50:06 -05:00
parent 6e834d2fc3
commit 7e9ea7ac28
17 changed files with 987 additions and 207 deletions

View File

@@ -1,109 +1,176 @@
import { addLineNumbers, everyLineHasLineNumbers, stripLineNumbers } from '../extract-text';
import { addLineNumbers, everyLineHasLineNumbers, stripLineNumbers, truncateOutput } from '../extract-text';
describe('addLineNumbers', () => {
it('should add line numbers starting from 1 by default', () => {
const input = 'line 1\nline 2\nline 3';
const expected = '1 | line 1\n2 | line 2\n3 | line 3';
expect(addLineNumbers(input)).toBe(expected);
});
it('should add line numbers starting from 1 by default', () => {
const input = 'line 1\nline 2\nline 3';
const expected = '1 | line 1\n2 | line 2\n3 | line 3';
expect(addLineNumbers(input)).toBe(expected);
});
it('should add line numbers starting from specified line number', () => {
const input = 'line 1\nline 2\nline 3';
const expected = '10 | line 1\n11 | line 2\n12 | line 3';
expect(addLineNumbers(input, 10)).toBe(expected);
});
it('should add line numbers starting from specified line number', () => {
const input = 'line 1\nline 2\nline 3';
const expected = '10 | line 1\n11 | line 2\n12 | line 3';
expect(addLineNumbers(input, 10)).toBe(expected);
});
it('should handle empty content', () => {
expect(addLineNumbers('')).toBe('1 | ');
expect(addLineNumbers('', 5)).toBe('5 | ');
});
it('should handle empty content', () => {
expect(addLineNumbers('')).toBe('1 | ');
expect(addLineNumbers('', 5)).toBe('5 | ');
});
it('should handle single line content', () => {
expect(addLineNumbers('single line')).toBe('1 | single line');
expect(addLineNumbers('single line', 42)).toBe('42 | single line');
});
it('should handle single line content', () => {
expect(addLineNumbers('single line')).toBe('1 | single line');
expect(addLineNumbers('single line', 42)).toBe('42 | single line');
});
it('should pad line numbers based on the highest line number', () => {
const input = 'line 1\nline 2';
// When starting from 99, highest line will be 100, so needs 3 spaces padding
const expected = ' 99 | line 1\n100 | line 2';
expect(addLineNumbers(input, 99)).toBe(expected);
});
it('should pad line numbers based on the highest line number', () => {
const input = 'line 1\nline 2';
// When starting from 99, highest line will be 100, so needs 3 spaces padding
const expected = ' 99 | line 1\n100 | line 2';
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 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 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 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 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 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);
});
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 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 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 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 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 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 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 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);
});
});
describe('truncateOutput', () => {
it('returns original content when no line limit provided', () => {
const content = 'line1\nline2\nline3'
expect(truncateOutput(content)).toBe(content)
})
it('returns original content when lines are under limit', () => {
const content = 'line1\nline2\nline3'
expect(truncateOutput(content, 5)).toBe(content)
})
it('truncates content with 20/80 split when over limit', () => {
// Create 25 lines of content
const lines = Array.from({ length: 25 }, (_, i) => `line${i + 1}`)
const content = lines.join('\n')
// Set limit to 10 lines
const result = truncateOutput(content, 10)
// Should keep:
// - First 2 lines (20% of 10)
// - Last 8 lines (80% of 10)
// - Omission indicator in between
const expectedLines = [
'line1',
'line2',
'',
'[...15 lines omitted...]',
'',
'line18',
'line19',
'line20',
'line21',
'line22',
'line23',
'line24',
'line25'
]
expect(result).toBe(expectedLines.join('\n'))
})
it('handles empty content', () => {
expect(truncateOutput('', 10)).toBe('')
})
it('handles single line content', () => {
expect(truncateOutput('single line', 10)).toBe('single line')
})
it('handles windows-style line endings', () => {
// Create content with windows line endings
const lines = Array.from({ length: 15 }, (_, i) => `line${i + 1}`)
const content = lines.join('\r\n')
const result = truncateOutput(content, 5)
// Should keep first line (20% of 5 = 1) and last 4 lines (80% of 5 = 4)
// Split result by either \r\n or \n to normalize line endings
const resultLines = result.split(/\r?\n/)
const expectedLines = [
'line1',
'',
'[...10 lines omitted...]',
'',
'line12',
'line13',
'line14',
'line15'
]
expect(resultLines).toEqual(expectedLines)
})
})

View File

@@ -87,4 +87,38 @@ export function stripLineNumbers(content: string): string {
// Join back with original line endings
const lineEnding = content.includes('\r\n') ? '\r\n' : '\n'
return processedLines.join(lineEnding)
}
/**
* Truncates multi-line output while preserving context from both the beginning and end.
* When truncation is needed, it keeps 20% of the lines from the start and 80% from the end,
* with a clear indicator of how many lines were omitted in between.
*
* @param content The multi-line string to truncate
* @param lineLimit Optional maximum number of lines to keep. If not provided or 0, returns the original content
* @returns The truncated string with an indicator of omitted lines, or the original content if no truncation needed
*
* @example
* // With 10 line limit on 25 lines of content:
* // - Keeps first 2 lines (20% of 10)
* // - Keeps last 8 lines (80% of 10)
* // - Adds "[...15 lines omitted...]" in between
*/
export function truncateOutput(content: string, lineLimit?: number): string {
if (!lineLimit) {
return content
}
const lines = content.split('\n')
if (lines.length <= lineLimit) {
return content
}
const beforeLimit = Math.floor(lineLimit * 0.2) // 20% of lines before
const afterLimit = lineLimit - beforeLimit // remaining 80% after
return [
...lines.slice(0, beforeLimit),
`\n[...${lines.length - lineLimit} lines omitted...]\n`,
...lines.slice(-afterLimit)
].join('\n')
}