mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-04-23 19:44:30 -07:00
feat: Ctrl+O to expand paste placeholder (#18103)
This commit is contained in:
@@ -17,6 +17,8 @@ import {
|
||||
logicalPosToOffset,
|
||||
PASTED_TEXT_PLACEHOLDER_REGEX,
|
||||
getTransformUnderCursor,
|
||||
LARGE_PASTE_LINE_THRESHOLD,
|
||||
LARGE_PASTE_CHAR_THRESHOLD,
|
||||
} from './shared/text-buffer.js';
|
||||
import {
|
||||
cpSlice,
|
||||
@@ -59,6 +61,11 @@ import { getSafeLowColorBackground } from '../themes/color-utils.js';
|
||||
import { isLowColorDepth } from '../utils/terminalUtils.js';
|
||||
import { useShellFocusState } from '../contexts/ShellFocusContext.js';
|
||||
import { useUIState } from '../contexts/UIStateContext.js';
|
||||
import {
|
||||
appEvents,
|
||||
AppEvent,
|
||||
TransientMessageType,
|
||||
} from '../../utils/events.js';
|
||||
import { useSettings } from '../contexts/SettingsContext.js';
|
||||
import { StreamingState } from '../types.js';
|
||||
import { useMouseClick } from '../hooks/useMouseClick.js';
|
||||
@@ -122,6 +129,55 @@ export const calculatePromptWidths = (mainContentWidth: number) => {
|
||||
} as const;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns true if the given text exceeds the thresholds for being considered a "large paste".
|
||||
*/
|
||||
export function isLargePaste(text: string): boolean {
|
||||
const pasteLineCount = text.split('\n').length;
|
||||
return (
|
||||
pasteLineCount > LARGE_PASTE_LINE_THRESHOLD ||
|
||||
text.length > LARGE_PASTE_CHAR_THRESHOLD
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to toggle expansion of a paste placeholder in the buffer.
|
||||
* Returns true if a toggle action was performed or hint was shown, false otherwise.
|
||||
*/
|
||||
export function tryTogglePasteExpansion(buffer: TextBuffer): boolean {
|
||||
if (!buffer.pastedContent || Object.keys(buffer.pastedContent).length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const [row, col] = buffer.cursor;
|
||||
|
||||
// 1. Check if cursor is on or immediately after a collapsed placeholder
|
||||
const transform = getTransformUnderCursor(
|
||||
row,
|
||||
col,
|
||||
buffer.transformationsByLine,
|
||||
{ includeEdge: true },
|
||||
);
|
||||
if (transform?.type === 'paste' && transform.id) {
|
||||
buffer.togglePasteExpansion(transform.id, row, col);
|
||||
return true;
|
||||
}
|
||||
|
||||
// 2. Check if cursor is inside an expanded paste region — collapse it
|
||||
const expandedId = buffer.getExpandedPasteAtLine(row);
|
||||
if (expandedId) {
|
||||
buffer.togglePasteExpansion(expandedId, row, col);
|
||||
return true;
|
||||
}
|
||||
|
||||
// 3. Placeholders exist but cursor isn't on one — show hint
|
||||
appEvents.emit(AppEvent.TransientMessage, {
|
||||
message: 'Move cursor within placeholder to expand',
|
||||
type: TransientMessageType.Hint,
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
export const InputPrompt: React.FC<InputPromptProps> = ({
|
||||
buffer,
|
||||
onSubmit,
|
||||
@@ -402,6 +458,12 @@ export const InputPrompt: React.FC<InputPromptProps> = ({
|
||||
} else {
|
||||
const textToInsert = await clipboardy.read();
|
||||
buffer.insert(textToInsert, { paste: true });
|
||||
if (isLargePaste(textToInsert)) {
|
||||
appEvents.emit(AppEvent.TransientMessage, {
|
||||
message: 'Press Ctrl+O to expand pasted text',
|
||||
type: TransientMessageType.Hint,
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
debugLogger.error('Error handling paste:', error);
|
||||
@@ -455,6 +517,7 @@ export const InputPrompt: React.FC<InputPromptProps> = ({
|
||||
logicalPos.row,
|
||||
logicalPos.col,
|
||||
buffer.transformationsByLine,
|
||||
{ includeEdge: true },
|
||||
);
|
||||
if (transform?.type === 'paste' && transform.id) {
|
||||
buffer.togglePasteExpansion(
|
||||
@@ -591,6 +654,12 @@ export const InputPrompt: React.FC<InputPromptProps> = ({
|
||||
}
|
||||
// Ensure we never accidentally interpret paste as regular input.
|
||||
buffer.handleInput(key);
|
||||
if (key.sequence && isLargePaste(key.sequence)) {
|
||||
appEvents.emit(AppEvent.TransientMessage, {
|
||||
message: 'Press Ctrl+O to expand pasted text',
|
||||
type: TransientMessageType.Hint,
|
||||
});
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -632,6 +701,12 @@ export const InputPrompt: React.FC<InputPromptProps> = ({
|
||||
}
|
||||
}
|
||||
|
||||
// Ctrl+O to expand/collapse paste placeholders
|
||||
if (keyMatchers[Command.EXPAND_PASTE](key)) {
|
||||
const handled = tryTogglePasteExpansion(buffer);
|
||||
if (handled) return true;
|
||||
}
|
||||
|
||||
if (
|
||||
key.sequence === '!' &&
|
||||
buffer.text === '' &&
|
||||
|
||||
Reference in New Issue
Block a user