feat: Show /ide subcommands based on connection status instead of ideMode boolean (#6496)
This commit is contained in:
parent
fde5511c27
commit
9588aa6ef9
|
@ -162,6 +162,9 @@ vi.mock('@google/gemini-cli-core', async (importOriginal) => {
|
|||
getIdeClient: vi.fn(() => ({
|
||||
getCurrentIde: vi.fn(() => 'vscode'),
|
||||
getDetectedIdeDisplayName: vi.fn(() => 'VSCode'),
|
||||
addStatusChangeListener: vi.fn(),
|
||||
removeStatusChangeListener: vi.fn(),
|
||||
getConnectionStatus: vi.fn(() => 'connected'),
|
||||
})),
|
||||
isTrustedFolder: vi.fn(() => true),
|
||||
};
|
||||
|
|
|
@ -69,16 +69,35 @@ describe('ideCommand', () => {
|
|||
vi.mocked(mockConfig.getIdeClient).mockReturnValue({
|
||||
getCurrentIde: () => DetectedIde.VSCode,
|
||||
getDetectedIdeDisplayName: () => 'VS Code',
|
||||
getConnectionStatus: () => ({
|
||||
status: core.IDEConnectionStatus.Disconnected,
|
||||
}),
|
||||
} as ReturnType<Config['getIdeClient']>);
|
||||
const command = ideCommand(mockConfig);
|
||||
expect(command).not.toBeNull();
|
||||
expect(command?.name).toBe('ide');
|
||||
expect(command?.subCommands).toHaveLength(3);
|
||||
expect(command?.subCommands?.[0].name).toBe('disable');
|
||||
expect(command?.subCommands?.[0].name).toBe('enable');
|
||||
expect(command?.subCommands?.[1].name).toBe('status');
|
||||
expect(command?.subCommands?.[2].name).toBe('install');
|
||||
});
|
||||
|
||||
it('should show disable command when connected', () => {
|
||||
vi.mocked(mockConfig.getIdeMode).mockReturnValue(true);
|
||||
vi.mocked(mockConfig.getIdeClient).mockReturnValue({
|
||||
getCurrentIde: () => DetectedIde.VSCode,
|
||||
getDetectedIdeDisplayName: () => 'VS Code',
|
||||
getConnectionStatus: () => ({
|
||||
status: core.IDEConnectionStatus.Connected,
|
||||
}),
|
||||
} as ReturnType<Config['getIdeClient']>);
|
||||
const command = ideCommand(mockConfig);
|
||||
expect(command).not.toBeNull();
|
||||
const subCommandNames = command?.subCommands?.map((cmd) => cmd.name);
|
||||
expect(subCommandNames).toContain('disable');
|
||||
expect(subCommandNames).not.toContain('enable');
|
||||
});
|
||||
|
||||
describe('status subcommand', () => {
|
||||
const mockGetConnectionStatus = vi.fn();
|
||||
beforeEach(() => {
|
||||
|
@ -161,7 +180,9 @@ describe('ideCommand', () => {
|
|||
vi.mocked(mockConfig.getIdeMode).mockReturnValue(true);
|
||||
vi.mocked(mockConfig.getIdeClient).mockReturnValue({
|
||||
getCurrentIde: () => DetectedIde.VSCode,
|
||||
getConnectionStatus: vi.fn(),
|
||||
getConnectionStatus: () => ({
|
||||
status: core.IDEConnectionStatus.Disconnected,
|
||||
}),
|
||||
getDetectedIdeDisplayName: () => 'VS Code',
|
||||
} as unknown as ReturnType<Config['getIdeClient']>);
|
||||
vi.mocked(core.getIdeInstaller).mockReturnValue({
|
||||
|
|
|
@ -237,13 +237,11 @@ export const ideCommand = (config: Config | null): SlashCommand | null => {
|
|||
},
|
||||
};
|
||||
|
||||
const ideModeEnabled = config.getIdeMode();
|
||||
if (ideModeEnabled) {
|
||||
ideSlashCommand.subCommands = [
|
||||
disableCommand,
|
||||
statusCommand,
|
||||
installCommand,
|
||||
];
|
||||
const { status } = ideClient.getConnectionStatus();
|
||||
const isConnected = status === IDEConnectionStatus.Connected;
|
||||
|
||||
if (isConnected) {
|
||||
ideSlashCommand.subCommands = [statusCommand, disableCommand];
|
||||
} else {
|
||||
ideSlashCommand.subCommands = [
|
||||
enableCommand,
|
||||
|
|
|
@ -206,7 +206,22 @@ export const useSlashCommandProcessor = (
|
|||
],
|
||||
);
|
||||
|
||||
const ideMode = config?.getIdeMode();
|
||||
useEffect(() => {
|
||||
if (!config) {
|
||||
return;
|
||||
}
|
||||
|
||||
const ideClient = config.getIdeClient();
|
||||
const listener = () => {
|
||||
reloadCommands();
|
||||
};
|
||||
|
||||
ideClient.addStatusChangeListener(listener);
|
||||
|
||||
return () => {
|
||||
ideClient.removeStatusChangeListener(listener);
|
||||
};
|
||||
}, [config, reloadCommands]);
|
||||
|
||||
useEffect(() => {
|
||||
const controller = new AbortController();
|
||||
|
@ -228,7 +243,7 @@ export const useSlashCommandProcessor = (
|
|||
return () => {
|
||||
controller.abort();
|
||||
};
|
||||
}, [config, ideMode, reloadTrigger]);
|
||||
}, [config, reloadTrigger]);
|
||||
|
||||
const handleSlashCommand = useCallback(
|
||||
async (
|
||||
|
|
|
@ -63,6 +63,7 @@ export class IdeClient {
|
|||
private readonly currentIde: DetectedIde | undefined;
|
||||
private readonly currentIdeDisplayName: string | undefined;
|
||||
private diffResponses = new Map<string, (result: DiffUpdateResult) => void>();
|
||||
private statusListeners = new Set<(state: IDEConnectionState) => void>();
|
||||
|
||||
private constructor() {
|
||||
this.currentIde = detectIde();
|
||||
|
@ -78,6 +79,14 @@ export class IdeClient {
|
|||
return IdeClient.instance;
|
||||
}
|
||||
|
||||
addStatusChangeListener(listener: (state: IDEConnectionState) => void) {
|
||||
this.statusListeners.add(listener);
|
||||
}
|
||||
|
||||
removeStatusChangeListener(listener: (state: IDEConnectionState) => void) {
|
||||
this.statusListeners.delete(listener);
|
||||
}
|
||||
|
||||
async connect(): Promise<void> {
|
||||
if (!this.currentIde || !this.currentIdeDisplayName) {
|
||||
this.setState(
|
||||
|
@ -237,6 +246,9 @@ export class IdeClient {
|
|||
// disconnected, so that the first detail message is preserved.
|
||||
if (!isAlreadyDisconnected) {
|
||||
this.state = { status, details };
|
||||
for (const listener of this.statusListeners) {
|
||||
listener(this.state);
|
||||
}
|
||||
if (details) {
|
||||
if (logToConsole) {
|
||||
logger.error(details);
|
||||
|
@ -390,7 +402,6 @@ export class IdeClient {
|
|||
logger.debug('Failed to close transport:', closeError);
|
||||
}
|
||||
}
|
||||
logger.error(`Failed to connect: ${_error}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue