From ff9c77925e07929f670f4c8727570604e74cab20 Mon Sep 17 00:00:00 2001 From: Chuck Date: Thu, 22 Jan 2026 23:21:05 +0800 Subject: [PATCH] Feature/jetbrains ide detection (#16243) Co-authored-by: Adib234 <30782825+Adib234@users.noreply.github.com> --- packages/core/src/ide/detect-ide.test.ts | 51 ++++++++++++++++++++ packages/core/src/ide/detect-ide.ts | 61 ++++++++++++++++++++++-- 2 files changed, 109 insertions(+), 3 deletions(-) diff --git a/packages/core/src/ide/detect-ide.test.ts b/packages/core/src/ide/detect-ide.test.ts index 6cab76e07d..1f717cec56 100644 --- a/packages/core/src/ide/detect-ide.test.ts +++ b/packages/core/src/ide/detect-ide.test.ts @@ -29,6 +29,7 @@ describe('detectIde', () => { vi.stubEnv('MONOSPACE_ENV', ''); vi.stubEnv('REPLIT_USER', ''); vi.stubEnv('__COG_BASHRC_SOURCED', ''); + vi.stubEnv('TERMINAL_EMULATOR', ''); }); afterEach(() => { @@ -126,6 +127,55 @@ describe('detectIde', () => { vi.stubEnv('ANTIGRAVITY_CLI_ALIAS', 'agy'); expect(detectIde(ideProcessInfo)).toBe(IDE_DEFINITIONS.antigravity); }); + + it('should detect JetBrains IDE via TERMINAL_EMULATOR', () => { + vi.stubEnv('TERMINAL_EMULATOR', 'JetBrains-JediTerm'); + expect(detectIde(ideProcessInfo)).toBe(IDE_DEFINITIONS.jetbrains); + }); + + describe('JetBrains IDE detection via command', () => { + beforeEach(() => { + vi.stubEnv('TERMINAL_EMULATOR', 'JetBrains-JediTerm'); + }); + + it.each([ + [ + 'IntelliJ IDEA', + '/Applications/IntelliJ IDEA.app', + IDE_DEFINITIONS.intellijidea, + ], + ['WebStorm', '/Applications/WebStorm.app', IDE_DEFINITIONS.webstorm], + ['PyCharm', '/Applications/PyCharm.app', IDE_DEFINITIONS.pycharm], + ['GoLand', '/Applications/GoLand.app', IDE_DEFINITIONS.goland], + [ + 'Android Studio', + '/Applications/Android Studio.app', + IDE_DEFINITIONS.androidstudio, + ], + ['CLion', '/Applications/CLion.app', IDE_DEFINITIONS.clion], + ['RustRover', '/Applications/RustRover.app', IDE_DEFINITIONS.rustrover], + ['DataGrip', '/Applications/DataGrip.app', IDE_DEFINITIONS.datagrip], + ['PhpStorm', '/Applications/PhpStorm.app', IDE_DEFINITIONS.phpstorm], + ])('should detect %s via command', (_name, command, expectedIde) => { + const processInfo = { pid: 123, command }; + expect(detectIde(processInfo)).toBe(expectedIde); + }); + }); + + it('should return generic JetBrains when command does not match specific IDE', () => { + vi.stubEnv('TERMINAL_EMULATOR', 'JetBrains-JediTerm'); + const genericProcessInfo = { + pid: 123, + command: '/Applications/SomeJetBrainsApp.app', + }; + expect(detectIde(genericProcessInfo)).toBe(IDE_DEFINITIONS.jetbrains); + }); + + it('should prioritize JetBrains detection over VS Code when TERMINAL_EMULATOR is set', () => { + vi.stubEnv('TERM_PROGRAM', 'vscode'); + vi.stubEnv('TERMINAL_EMULATOR', 'JetBrains-JediTerm'); + expect(detectIde(ideProcessInfo)).toBe(IDE_DEFINITIONS.jetbrains); + }); }); describe('detectIde with ideInfoFromFile', () => { @@ -147,6 +197,7 @@ describe('detectIde with ideInfoFromFile', () => { vi.stubEnv('MONOSPACE_ENV', ''); vi.stubEnv('REPLIT_USER', ''); vi.stubEnv('__COG_BASHRC_SOURCED', ''); + vi.stubEnv('TERMINAL_EMULATOR', ''); }); it('should use the name and displayName from the file', () => { diff --git a/packages/core/src/ide/detect-ide.ts b/packages/core/src/ide/detect-ide.ts index 0939af6b79..6c1f0b458b 100644 --- a/packages/core/src/ide/detect-ide.ts +++ b/packages/core/src/ide/detect-ide.ts @@ -16,6 +16,16 @@ export const IDE_DEFINITIONS = { vscodefork: { name: 'vscodefork', displayName: 'IDE' }, antigravity: { name: 'antigravity', displayName: 'Antigravity' }, sublimetext: { name: 'sublimetext', displayName: 'Sublime Text' }, + jetbrains: { name: 'jetbrains', displayName: 'JetBrains IDE' }, + intellijidea: { name: 'intellijidea', displayName: 'IntelliJ IDEA' }, + webstorm: { name: 'webstorm', displayName: 'WebStorm' }, + pycharm: { name: 'pycharm', displayName: 'PyCharm' }, + goland: { name: 'goland', displayName: 'GoLand' }, + androidstudio: { name: 'androidstudio', displayName: 'Android Studio' }, + clion: { name: 'clion', displayName: 'CLion' }, + rustrover: { name: 'rustrover', displayName: 'RustRover' }, + datagrip: { name: 'datagrip', displayName: 'DataGrip' }, + phpstorm: { name: 'phpstorm', displayName: 'PhpStorm' }, } as const; export interface IdeInfo { @@ -27,6 +37,12 @@ export function isCloudShell(): boolean { return !!(process.env['EDITOR_IN_CLOUD_SHELL'] || process.env['CLOUD_SHELL']); } +export function isJetBrains(): boolean { + return !!process.env['TERMINAL_EMULATOR'] + ?.toLowerCase() + .includes('jetbrains'); +} + export function detectIdeFromEnv(): IdeInfo { if (process.env['ANTIGRAVITY_CLI_ALIAS']) { return IDE_DEFINITIONS.antigravity; @@ -55,6 +71,9 @@ export function detectIdeFromEnv(): IdeInfo { if (process.env['TERM_PROGRAM'] === 'sublime') { return IDE_DEFINITIONS.sublimetext; } + if (isJetBrains()) { + return IDE_DEFINITIONS.jetbrains; + } return IDE_DEFINITIONS.vscode; } @@ -77,6 +96,39 @@ function verifyVSCode( return IDE_DEFINITIONS.vscodefork; } +function verifyJetBrains( + ide: IdeInfo, + ideProcessInfo: { + pid: number; + command: string; + }, +): IdeInfo { + if (ide.name !== IDE_DEFINITIONS.jetbrains.name || !ideProcessInfo.command) { + return ide; + } + + const command = ideProcessInfo.command.toLowerCase(); + const jetbrainsProducts: Array<[string, IdeInfo]> = [ + ['idea', IDE_DEFINITIONS.intellijidea], + ['webstorm', IDE_DEFINITIONS.webstorm], + ['pycharm', IDE_DEFINITIONS.pycharm], + ['goland', IDE_DEFINITIONS.goland], + ['studio', IDE_DEFINITIONS.androidstudio], + ['clion', IDE_DEFINITIONS.clion], + ['rustrover', IDE_DEFINITIONS.rustrover], + ['datagrip', IDE_DEFINITIONS.datagrip], + ['phpstorm', IDE_DEFINITIONS.phpstorm], + ]; + + for (const [product, ideInfo] of jetbrainsProducts) { + if (command.includes(product)) { + return ideInfo; + } + } + + return ide; +} + export function detectIde( ideProcessInfo: { pid: number; @@ -91,14 +143,17 @@ export function detectIde( }; } - // Only VS Code and Sublime Text integrations are currently supported. + // Only VS Code, Sublime Text and JetBrains integrations are currently supported. if ( process.env['TERM_PROGRAM'] !== 'vscode' && - process.env['TERM_PROGRAM'] !== 'sublime' + process.env['TERM_PROGRAM'] !== 'sublime' && + !isJetBrains() ) { return undefined; } const ide = detectIdeFromEnv(); - return verifyVSCode(ide, ideProcessInfo); + return isJetBrains() + ? verifyJetBrains(ide, ideProcessInfo) + : verifyVSCode(ide, ideProcessInfo); }