Add support for VSCode-like editors (#5699)
Co-authored-by: Jacob Richman <jacob314@gmail.com>
This commit is contained in:
parent
4656f17524
commit
0e98641b51
|
@ -939,8 +939,7 @@ const App = ({ config, settings, startupWarnings = [], version }: AppProps) => {
|
||||||
|
|
||||||
{shouldShowIdePrompt ? (
|
{shouldShowIdePrompt ? (
|
||||||
<IdeIntegrationNudge
|
<IdeIntegrationNudge
|
||||||
question="Do you want to connect your VS Code editor to Gemini CLI?"
|
ideName={config.getIdeClient().getDetectedIdeDisplayName()}
|
||||||
description="If you select Yes, we'll install an extension that allows the CLI to access your open files and display diffs directly in VS Code."
|
|
||||||
onComplete={handleIdePromptComplete}
|
onComplete={handleIdePromptComplete}
|
||||||
/>
|
/>
|
||||||
) : isFolderTrustDialogOpen ? (
|
) : isFolderTrustDialogOpen ? (
|
||||||
|
|
|
@ -13,14 +13,12 @@ import {
|
||||||
export type IdeIntegrationNudgeResult = 'yes' | 'no' | 'dismiss';
|
export type IdeIntegrationNudgeResult = 'yes' | 'no' | 'dismiss';
|
||||||
|
|
||||||
interface IdeIntegrationNudgeProps {
|
interface IdeIntegrationNudgeProps {
|
||||||
question: string;
|
ideName?: string;
|
||||||
description?: string;
|
|
||||||
onComplete: (result: IdeIntegrationNudgeResult) => void;
|
onComplete: (result: IdeIntegrationNudgeResult) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function IdeIntegrationNudge({
|
export function IdeIntegrationNudge({
|
||||||
question,
|
ideName,
|
||||||
description,
|
|
||||||
onComplete,
|
onComplete,
|
||||||
}: IdeIntegrationNudgeProps) {
|
}: IdeIntegrationNudgeProps) {
|
||||||
useInput((_input, key) => {
|
useInput((_input, key) => {
|
||||||
|
@ -56,9 +54,11 @@ export function IdeIntegrationNudge({
|
||||||
<Box marginBottom={1} flexDirection="column">
|
<Box marginBottom={1} flexDirection="column">
|
||||||
<Text>
|
<Text>
|
||||||
<Text color="yellow">{'> '}</Text>
|
<Text color="yellow">{'> '}</Text>
|
||||||
{question}
|
{`Do you want to connect your ${ideName ?? 'your'} editor to Gemini CLI?`}
|
||||||
</Text>
|
</Text>
|
||||||
{description && <Text dimColor>{description}</Text>}
|
<Text
|
||||||
|
dimColor
|
||||||
|
>{`If you select Yes, we'll install an extension that allows the CLI to access your open files and display diffs directly in ${ideName ?? 'your editor'}.`}</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<RadioButtonSelect
|
<RadioButtonSelect
|
||||||
items={OPTIONS}
|
items={OPTIONS}
|
||||||
|
|
|
@ -6,12 +6,33 @@
|
||||||
|
|
||||||
export enum DetectedIde {
|
export enum DetectedIde {
|
||||||
VSCode = 'vscode',
|
VSCode = 'vscode',
|
||||||
|
VSCodium = 'vscodium',
|
||||||
|
Cursor = 'cursor',
|
||||||
|
CloudShell = 'cloudshell',
|
||||||
|
Codespaces = 'codespaces',
|
||||||
|
Windsurf = 'windsurf',
|
||||||
|
FirebaseStudio = 'firebasestudio',
|
||||||
|
Trae = 'trae',
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getIdeDisplayName(ide: DetectedIde): string {
|
export function getIdeDisplayName(ide: DetectedIde): string {
|
||||||
switch (ide) {
|
switch (ide) {
|
||||||
case DetectedIde.VSCode:
|
case DetectedIde.VSCode:
|
||||||
return 'VS Code';
|
return 'VS Code';
|
||||||
|
case DetectedIde.VSCodium:
|
||||||
|
return 'VSCodium';
|
||||||
|
case DetectedIde.Cursor:
|
||||||
|
return 'Cursor';
|
||||||
|
case DetectedIde.CloudShell:
|
||||||
|
return 'Cloud Shell';
|
||||||
|
case DetectedIde.Codespaces:
|
||||||
|
return 'GitHub Codespaces';
|
||||||
|
case DetectedIde.Windsurf:
|
||||||
|
return 'Windsurf';
|
||||||
|
case DetectedIde.FirebaseStudio:
|
||||||
|
return 'Firebase Studio';
|
||||||
|
case DetectedIde.Trae:
|
||||||
|
return 'Trae';
|
||||||
default: {
|
default: {
|
||||||
// This ensures that if a new IDE is added to the enum, we get a compile-time error.
|
// This ensures that if a new IDE is added to the enum, we get a compile-time error.
|
||||||
const exhaustiveCheck: never = ide;
|
const exhaustiveCheck: never = ide;
|
||||||
|
@ -21,8 +42,24 @@ export function getIdeDisplayName(ide: DetectedIde): string {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function detectIde(): DetectedIde | undefined {
|
export function detectIde(): DetectedIde | undefined {
|
||||||
if (process.env.TERM_PROGRAM === 'vscode') {
|
// Only VSCode-based integrations are currently supported.
|
||||||
return DetectedIde.VSCode;
|
if (process.env.TERM_PROGRAM !== 'vscode') {
|
||||||
}
|
|
||||||
return undefined;
|
return undefined;
|
||||||
|
}
|
||||||
|
if (process.env.CURSOR_TRACE_ID) {
|
||||||
|
return DetectedIde.Cursor;
|
||||||
|
}
|
||||||
|
if (process.env.CODESPACES) {
|
||||||
|
return DetectedIde.Codespaces;
|
||||||
|
}
|
||||||
|
if (process.env.EDITOR_IN_CLOUD_SHELL) {
|
||||||
|
return DetectedIde.CloudShell;
|
||||||
|
}
|
||||||
|
if (process.env.TERM_PRODUCT === 'Trae') {
|
||||||
|
return DetectedIde.Trae;
|
||||||
|
}
|
||||||
|
if (process.env.FIREBASE_DEPLOY_AGENT) {
|
||||||
|
return DetectedIde.FirebaseStudio;
|
||||||
|
}
|
||||||
|
return DetectedIde.VSCode;
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,9 +24,17 @@ describe('ide-installer', () => {
|
||||||
expect(installer).toBeInstanceOf(Object);
|
expect(installer).toBeInstanceOf(Object);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return null for an unknown IDE', () => {
|
it('should return an OpenVSXInstaller for "vscodium"', () => {
|
||||||
|
const installer = getIdeInstaller(DetectedIde.VSCodium);
|
||||||
|
expect(installer).not.toBeNull();
|
||||||
|
expect(installer).toBeInstanceOf(Object);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return a DefaultIDEInstaller for an unknown IDE', () => {
|
||||||
const installer = getIdeInstaller('unknown' as DetectedIde);
|
const installer = getIdeInstaller('unknown' as DetectedIde);
|
||||||
expect(installer).toBeNull();
|
// Assuming DefaultIDEInstaller is the fallback
|
||||||
|
expect(installer).not.toBeNull();
|
||||||
|
expect(installer).toBeInstanceOf(Object);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -59,4 +67,44 @@ describe('ide-installer', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('OpenVSXInstaller', () => {
|
||||||
|
let installer: IdeInstaller;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
installer = getIdeInstaller(DetectedIde.VSCodium)!;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
vi.restoreAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('install', () => {
|
||||||
|
it('should call execSync with the correct command and return success', async () => {
|
||||||
|
const execSyncSpy = vi
|
||||||
|
.spyOn(child_process, 'execSync')
|
||||||
|
.mockImplementation(() => '');
|
||||||
|
const result = await installer.install();
|
||||||
|
expect(execSyncSpy).toHaveBeenCalledWith(
|
||||||
|
'npx ovsx get google.gemini-cli-vscode-ide-companion',
|
||||||
|
{ stdio: 'pipe' },
|
||||||
|
);
|
||||||
|
expect(result.success).toBe(true);
|
||||||
|
expect(result.message).toContain(
|
||||||
|
'VS Code companion extension was installed successfully from OpenVSX',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return a failure message on failed installation', async () => {
|
||||||
|
vi.spyOn(child_process, 'execSync').mockImplementation(() => {
|
||||||
|
throw new Error('Command failed');
|
||||||
|
});
|
||||||
|
const result = await installer.install();
|
||||||
|
expect(result.success).toBe(false);
|
||||||
|
expect(result.message).toContain(
|
||||||
|
'Failed to install VS Code companion extension from OpenVSX',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -147,11 +147,31 @@ class VsCodeInstaller implements IdeInstaller {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class OpenVSXInstaller implements IdeInstaller {
|
||||||
|
async install(): Promise<InstallResult> {
|
||||||
|
// TODO: Use the correct extension path.
|
||||||
|
const command = `npx ovsx get google.gemini-cli-vscode-ide-companion`;
|
||||||
|
try {
|
||||||
|
child_process.execSync(command, { stdio: 'pipe' });
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
message:
|
||||||
|
'VS Code companion extension was installed successfully from OpenVSX. Please restart your terminal to complete the setup.',
|
||||||
|
};
|
||||||
|
} catch (_error) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: `Failed to install VS Code companion extension from OpenVSX. Please try installing it manually.`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function getIdeInstaller(ide: DetectedIde): IdeInstaller | null {
|
export function getIdeInstaller(ide: DetectedIde): IdeInstaller | null {
|
||||||
switch (ide) {
|
switch (ide) {
|
||||||
case DetectedIde.VSCode:
|
case DetectedIde.VSCode:
|
||||||
return new VsCodeInstaller();
|
return new VsCodeInstaller();
|
||||||
default:
|
default:
|
||||||
return null;
|
return new OpenVSXInstaller();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue