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