Feature/jetbrains ide detection (#16243)

Co-authored-by: Adib234 <30782825+Adib234@users.noreply.github.com>
This commit is contained in:
Chuck
2026-01-22 23:21:05 +08:00
committed by GitHub
parent 56f9ab025a
commit ff9c77925e
2 changed files with 109 additions and 3 deletions

View File

@@ -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', () => {

View File

@@ -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);
}