feat: add double-click to expand/collapse large paste placeholders (#17471)

This commit is contained in:
Jack Wotherspoon
2026-01-26 21:59:09 -05:00
committed by GitHub
parent 5cf06503c8
commit a79051d9f8
11 changed files with 1024 additions and 61 deletions

View File

@@ -57,6 +57,7 @@ const initialState: TextBufferState = {
transformationsByLine: [[]],
visualLayout: defaultVisualLayout,
pastedContent: {},
expandedPasteInfo: new Map(),
};
/**
@@ -531,6 +532,150 @@ describe('textBufferReducer', () => {
expect(state.cursorCol).toBe(5);
});
});
describe('toggle_paste_expansion action', () => {
const placeholder = '[Pasted Text: 6 lines]';
const content = 'line1\nline2\nline3\nline4\nline5\nline6';
it('should expand a placeholder correctly', () => {
const stateWithPlaceholder = createStateWithTransformations({
lines: ['prefix ' + placeholder + ' suffix'],
cursorRow: 0,
cursorCol: 0,
pastedContent: { [placeholder]: content },
});
const action: TextBufferAction = {
type: 'toggle_paste_expansion',
payload: { id: placeholder },
};
const state = textBufferReducer(stateWithPlaceholder, action);
expect(state.lines).toEqual([
'prefix line1',
'line2',
'line3',
'line4',
'line5',
'line6 suffix',
]);
expect(state.expandedPasteInfo.has(placeholder)).toBe(true);
const info = state.expandedPasteInfo.get(placeholder);
expect(info).toEqual({
startLine: 0,
lineCount: 6,
prefix: 'prefix ',
suffix: ' suffix',
});
// Cursor should be at the end of expanded content (before suffix)
expect(state.cursorRow).toBe(5);
expect(state.cursorCol).toBe(5); // length of 'line6'
});
it('should collapse an expanded placeholder correctly', () => {
const expandedState = createStateWithTransformations({
lines: [
'prefix line1',
'line2',
'line3',
'line4',
'line5',
'line6 suffix',
],
cursorRow: 5,
cursorCol: 5,
pastedContent: { [placeholder]: content },
expandedPasteInfo: new Map([
[
placeholder,
{
startLine: 0,
lineCount: 6,
prefix: 'prefix ',
suffix: ' suffix',
},
],
]),
});
const action: TextBufferAction = {
type: 'toggle_paste_expansion',
payload: { id: placeholder },
};
const state = textBufferReducer(expandedState, action);
expect(state.lines).toEqual(['prefix ' + placeholder + ' suffix']);
expect(state.expandedPasteInfo.has(placeholder)).toBe(false);
// Cursor should be at the end of the collapsed placeholder
expect(state.cursorRow).toBe(0);
expect(state.cursorCol).toBe(('prefix ' + placeholder).length);
});
it('should expand single-line content correctly', () => {
const singleLinePlaceholder = '[Pasted Text: 10 chars]';
const singleLineContent = 'some text';
const stateWithPlaceholder = createStateWithTransformations({
lines: [singleLinePlaceholder],
cursorRow: 0,
cursorCol: 0,
pastedContent: { [singleLinePlaceholder]: singleLineContent },
});
const state = textBufferReducer(stateWithPlaceholder, {
type: 'toggle_paste_expansion',
payload: { id: singleLinePlaceholder },
});
expect(state.lines).toEqual(['some text']);
expect(state.cursorRow).toBe(0);
expect(state.cursorCol).toBe(9);
});
it('should return current state if placeholder ID not found in pastedContent', () => {
const action: TextBufferAction = {
type: 'toggle_paste_expansion',
payload: { id: 'unknown' },
};
const state = textBufferReducer(initialState, action);
expect(state).toBe(initialState);
});
it('should preserve expandedPasteInfo when lines change from edits outside the region', () => {
// Start with an expanded paste at line 0 (3 lines long)
const placeholder = '[Pasted Text: 3 lines]';
const expandedState = createStateWithTransformations({
lines: ['line1', 'line2', 'line3', 'suffix'],
cursorRow: 3,
cursorCol: 0,
pastedContent: { [placeholder]: 'line1\nline2\nline3' },
expandedPasteInfo: new Map([
[
placeholder,
{
startLine: 0,
lineCount: 3,
prefix: '',
suffix: '',
},
],
]),
});
expect(expandedState.expandedPasteInfo.size).toBe(1);
// Insert a newline at the end - this changes lines but is OUTSIDE the expanded region
const stateAfterInsert = textBufferReducer(expandedState, {
type: 'insert',
payload: '\n',
});
// Lines changed, but expandedPasteInfo should be PRESERVED and optionally shifted (no shift here since edit is after)
expect(stateAfterInsert.expandedPasteInfo.size).toBe(1);
expect(stateAfterInsert.expandedPasteInfo.has(placeholder)).toBe(true);
});
});
});
const getBufferState = (result: { current: TextBuffer }) => {