[ide-mode] Send the cursor and selected text from the IDE server (#4621)

This commit is contained in:
christine betts 2025-07-24 19:54:41 +00:00 committed by GitHub
parent f9930c2d36
commit 4e376c0447
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 27 additions and 3 deletions

View File

@ -15,7 +15,6 @@ let log: (message: string) => void = () => {};
export async function activate(context: vscode.ExtensionContext) { export async function activate(context: vscode.ExtensionContext) {
logger = vscode.window.createOutputChannel('Gemini CLI IDE Companion'); logger = vscode.window.createOutputChannel('Gemini CLI IDE Companion');
log = createLogger(context, logger); log = createLogger(context, logger);
log('Extension activated'); log('Extension activated');
ideServer = new IDEServer(log); ideServer = new IDEServer(log);
try { try {

View File

@ -18,6 +18,7 @@ import { RecentFilesManager } from './recent-files-manager.js';
const MCP_SESSION_ID_HEADER = 'mcp-session-id'; const MCP_SESSION_ID_HEADER = 'mcp-session-id';
const IDE_SERVER_PORT_ENV_VAR = 'GEMINI_CLI_IDE_SERVER_PORT'; const IDE_SERVER_PORT_ENV_VAR = 'GEMINI_CLI_IDE_SERVER_PORT';
const MAX_SELECTED_TEXT_LENGTH = 16384; // 16 KiB limit
function sendOpenFilesChangedNotification( function sendOpenFilesChangedNotification(
transport: StreamableHTTPServerTransport, transport: StreamableHTTPServerTransport,
@ -29,6 +30,19 @@ function sendOpenFilesChangedNotification(
editor && editor.document.uri.scheme === 'file' editor && editor.document.uri.scheme === 'file'
? editor.document.uri.fsPath ? editor.document.uri.fsPath
: ''; : '';
const selection = editor?.selection;
const cursor = selection
? {
// This value is a zero-based index, but the vscode IDE is one-based.
line: selection.active.line + 1,
character: selection.active.character,
}
: undefined;
let selectedText = editor?.document.getText(selection) ?? undefined;
if (selectedText && selectedText.length > MAX_SELECTED_TEXT_LENGTH) {
selectedText =
selectedText.substring(0, MAX_SELECTED_TEXT_LENGTH) + '... [TRUNCATED]';
}
const notification: JSONRPCNotification = { const notification: JSONRPCNotification = {
jsonrpc: '2.0', jsonrpc: '2.0',
method: 'ide/openFilesChanged', method: 'ide/openFilesChanged',
@ -37,6 +51,8 @@ function sendOpenFilesChangedNotification(
recentOpenFiles: recentFilesManager.recentFiles.filter( recentOpenFiles: recentFilesManager.recentFiles.filter(
(file) => file.filePath !== filePath, (file) => file.filePath !== filePath,
), ),
cursor,
selectedText,
}, },
}; };
log( log(
@ -69,7 +85,7 @@ export class IDEServer {
const mcpServer = createMcpServer(); const mcpServer = createMcpServer();
const recentFilesManager = new RecentFilesManager(context); const recentFilesManager = new RecentFilesManager(context);
const disposable = recentFilesManager.onDidChange(() => { const onDidChangeSubscription = recentFilesManager.onDidChange(() => {
for (const transport of Object.values(transports)) { for (const transport of Object.values(transports)) {
sendOpenFilesChangedNotification( sendOpenFilesChangedNotification(
transport, transport,
@ -78,7 +94,7 @@ export class IDEServer {
); );
} }
}); });
context.subscriptions.push(disposable); context.subscriptions.push(onDidChangeSubscription);
app.post('/mcp', async (req: Request, res: Response) => { app.post('/mcp', async (req: Request, res: Response) => {
const sessionId = req.headers[MCP_SESSION_ID_HEADER] as const sessionId = req.headers[MCP_SESSION_ID_HEADER] as

View File

@ -28,6 +28,7 @@ vi.mock('vscode', () => ({
}), }),
window: { window: {
onDidChangeActiveTextEditor: vi.fn(), onDidChangeActiveTextEditor: vi.fn(),
onDidChangeTextEditorSelection: vi.fn(),
}, },
workspace: { workspace: {
onDidDeleteFiles: vi.fn(), onDidDeleteFiles: vi.fn(),

View File

@ -47,11 +47,19 @@ export class RecentFilesManager {
this.add(newUri); this.add(newUri);
} }
}); });
const selectionWatcher = vscode.window.onDidChangeTextEditorSelection(
() => {
this.fireWithDebounce();
},
);
context.subscriptions.push( context.subscriptions.push(
editorWatcher, editorWatcher,
deleteWatcher, deleteWatcher,
closeWatcher, closeWatcher,
renameWatcher, renameWatcher,
selectionWatcher,
); );
} }