diff options
| author | christine betts <[email protected]> | 2025-07-25 17:46:55 +0000 |
|---|---|---|
| committer | GitHub <[email protected]> | 2025-07-25 17:46:55 +0000 |
| commit | 1b8ba5ca6bf739e4100a1d313721988f953acb49 (patch) | |
| tree | 9dea66f108d427edc6284e1ea38b5883d8e82881 /packages/core/src/services | |
| parent | 3c16429fc4b8102b7ea44c5b2842507e3a99ec72 (diff) | |
[ide-mode] Create an IDE manager class to handle connecting to and exposing methods from the IDE server (#4797)
Diffstat (limited to 'packages/core/src/services')
| -rw-r--r-- | packages/core/src/services/ideContext.test.ts | 140 | ||||
| -rw-r--r-- | packages/core/src/services/ideContext.ts | 122 |
2 files changed, 0 insertions, 262 deletions
diff --git a/packages/core/src/services/ideContext.test.ts b/packages/core/src/services/ideContext.test.ts deleted file mode 100644 index 1cb09c53..00000000 --- a/packages/core/src/services/ideContext.test.ts +++ /dev/null @@ -1,140 +0,0 @@ -/** - * @license - * Copyright 2025 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -import { describe, it, expect, beforeEach, vi } from 'vitest'; -import { createIdeContextStore } from './ideContext.js'; - -describe('ideContext - Active File', () => { - let ideContext: ReturnType<typeof createIdeContextStore>; - - beforeEach(() => { - // Create a fresh, isolated instance for each test - ideContext = createIdeContextStore(); - }); - - it('should return undefined initially for active file context', () => { - expect(ideContext.getOpenFilesContext()).toBeUndefined(); - }); - - it('should set and retrieve the active file context', () => { - const testFile = { - activeFile: '/path/to/test/file.ts', - selectedText: '1234', - }; - - ideContext.setOpenFilesContext(testFile); - - const activeFile = ideContext.getOpenFilesContext(); - expect(activeFile).toEqual(testFile); - }); - - it('should update the active file context when called multiple times', () => { - const firstFile = { - activeFile: '/path/to/first.js', - selectedText: '1234', - }; - ideContext.setOpenFilesContext(firstFile); - - const secondFile = { - activeFile: '/path/to/second.py', - cursor: { line: 20, character: 30 }, - }; - ideContext.setOpenFilesContext(secondFile); - - const activeFile = ideContext.getOpenFilesContext(); - expect(activeFile).toEqual(secondFile); - }); - - it('should handle empty string for file path', () => { - const testFile = { - activeFile: '', - selectedText: '1234', - }; - ideContext.setOpenFilesContext(testFile); - expect(ideContext.getOpenFilesContext()).toEqual(testFile); - }); - - it('should notify subscribers when active file context changes', () => { - const subscriber1 = vi.fn(); - const subscriber2 = vi.fn(); - - ideContext.subscribeToOpenFiles(subscriber1); - ideContext.subscribeToOpenFiles(subscriber2); - - const testFile = { - activeFile: '/path/to/subscribed.ts', - cursor: { line: 15, character: 25 }, - }; - ideContext.setOpenFilesContext(testFile); - - expect(subscriber1).toHaveBeenCalledTimes(1); - expect(subscriber1).toHaveBeenCalledWith(testFile); - expect(subscriber2).toHaveBeenCalledTimes(1); - expect(subscriber2).toHaveBeenCalledWith(testFile); - - // Test with another update - const newFile = { - activeFile: '/path/to/new.js', - selectedText: '1234', - }; - ideContext.setOpenFilesContext(newFile); - - expect(subscriber1).toHaveBeenCalledTimes(2); - expect(subscriber1).toHaveBeenCalledWith(newFile); - expect(subscriber2).toHaveBeenCalledTimes(2); - expect(subscriber2).toHaveBeenCalledWith(newFile); - }); - - it('should stop notifying a subscriber after unsubscribe', () => { - const subscriber1 = vi.fn(); - const subscriber2 = vi.fn(); - - const unsubscribe1 = ideContext.subscribeToOpenFiles(subscriber1); - ideContext.subscribeToOpenFiles(subscriber2); - - ideContext.setOpenFilesContext({ - activeFile: '/path/to/file1.txt', - selectedText: '1234', - }); - expect(subscriber1).toHaveBeenCalledTimes(1); - expect(subscriber2).toHaveBeenCalledTimes(1); - - unsubscribe1(); - - ideContext.setOpenFilesContext({ - activeFile: '/path/to/file2.txt', - selectedText: '1234', - }); - expect(subscriber1).toHaveBeenCalledTimes(1); // Should not be called again - expect(subscriber2).toHaveBeenCalledTimes(2); - }); - - it('should allow the cursor to be optional', () => { - const testFile = { - activeFile: '/path/to/test/file.ts', - }; - - ideContext.setOpenFilesContext(testFile); - - const activeFile = ideContext.getOpenFilesContext(); - expect(activeFile).toEqual(testFile); - }); - - it('should clear the active file context', () => { - const testFile = { - activeFile: '/path/to/test/file.ts', - selectedText: '1234', - }; - - ideContext.setOpenFilesContext(testFile); - - expect(ideContext.getOpenFilesContext()).toEqual(testFile); - - ideContext.clearOpenFilesContext(); - - expect(ideContext.getOpenFilesContext()).toBeUndefined(); - }); -}); diff --git a/packages/core/src/services/ideContext.ts b/packages/core/src/services/ideContext.ts deleted file mode 100644 index f8a50f12..00000000 --- a/packages/core/src/services/ideContext.ts +++ /dev/null @@ -1,122 +0,0 @@ -/** - * @license - * Copyright 2025 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -import { z } from 'zod'; - -/** - * The reserved server name for the IDE's MCP server. - */ -export const IDE_SERVER_NAME = '_ide_server'; -/** - * Zod schema for validating a cursor position. - */ -export const CursorSchema = z.object({ - line: z.number(), - character: z.number(), -}); -export type Cursor = z.infer<typeof CursorSchema>; - -/** - * Zod schema for validating an active file context from the IDE. - */ -export const OpenFilesSchema = z.object({ - activeFile: z.string(), - selectedText: z.string().optional(), - cursor: CursorSchema.optional(), - recentOpenFiles: z - .array( - z.object({ - filePath: z.string(), - timestamp: z.number(), - }), - ) - .optional(), -}); -export type OpenFiles = z.infer<typeof OpenFilesSchema>; - -/** - * Zod schema for validating the 'ide/openFilesChanged' notification from the IDE. - */ -export const OpenFilesNotificationSchema = z.object({ - method: z.literal('ide/openFilesChanged'), - params: OpenFilesSchema, -}); - -type OpenFilesSubscriber = (openFiles: OpenFiles | undefined) => void; - -/** - * Creates a new store for managing the IDE's active file context. - * This factory function encapsulates the state and logic, allowing for the creation - * of isolated instances, which is particularly useful for testing. - * - * @returns An object with methods to interact with the active file context. - */ -export function createIdeContextStore() { - let openFilesContext: OpenFiles | undefined = undefined; - const subscribers = new Set<OpenFilesSubscriber>(); - - /** - * Notifies all registered subscribers about the current active file context. - */ - function notifySubscribers(): void { - for (const subscriber of subscribers) { - subscriber(openFilesContext); - } - } - - /** - * Sets the active file context and notifies all registered subscribers of the change. - * @param newOpenFiles The new active file context from the IDE. - */ - function setOpenFilesContext(newOpenFiles: OpenFiles): void { - openFilesContext = newOpenFiles; - notifySubscribers(); - } - - /** - * Clears the active file context and notifies all registered subscribers of the change. - */ - function clearOpenFilesContext(): void { - openFilesContext = undefined; - notifySubscribers(); - } - - /** - * Retrieves the current active file context. - * @returns The `OpenFiles` object if a file is active; otherwise, `undefined`. - */ - function getOpenFilesContext(): OpenFiles | undefined { - return openFilesContext; - } - - /** - * Subscribes to changes in the active file context. - * - * When the active file context changes, the provided `subscriber` function will be called. - * Note: The subscriber is not called with the current value upon subscription. - * - * @param subscriber The function to be called when the active file context changes. - * @returns A function that, when called, will unsubscribe the provided subscriber. - */ - function subscribeToOpenFiles(subscriber: OpenFilesSubscriber): () => void { - subscribers.add(subscriber); - return () => { - subscribers.delete(subscriber); - }; - } - - return { - setOpenFilesContext, - getOpenFilesContext, - subscribeToOpenFiles, - clearOpenFilesContext, - }; -} - -/** - * The default, shared instance of the IDE context store for the application. - */ -export const ideContext = createIdeContextStore(); |
