Skip to content

Commit fe442f9

Browse files
authored
Telemetry to capture the type of terminal being used (microsoft#1650)
* 🔨 split out different types of bash shells * Capture type of terminal on startup
1 parent 5171927 commit fe442f9

File tree

9 files changed

+68
-26
lines changed

9 files changed

+68
-26
lines changed

src/client/common/enumUtils.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
1+
// tslint:disable:no-any no-unnecessary-class
12
export class EnumEx {
2-
static getNamesAndValues<T extends number>(e: any) {
3-
return EnumEx.getNames(e).map(n => ({ name: n, value: e[n] as T }));
3+
public static getNamesAndValues<T>(e: any): { name: string; value: T }[] {
4+
return EnumEx.getNames(e).map(n => ({ name: n, value: e[n] }));
45
}
56

6-
static getNames(e: any) {
7-
return EnumEx.getObjValues(e).filter(v => typeof v === "string") as string[];
7+
public static getNames(e: any) {
8+
return EnumEx.getObjValues(e).filter(v => typeof v === 'string') as string[];
89
}
910

10-
static getValues<T extends number>(e: any) {
11-
return EnumEx.getObjValues(e).filter(v => typeof v === "number") as T[];
11+
public static getValues<T>(e: any) {
12+
return EnumEx.getObjValues(e).filter(v => typeof v === 'number') as any as T[];
1213
}
1314

1415
private static getObjValues(e: any): (number | string)[] {

src/client/common/terminal/environmentActivationProviders/bash.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,17 @@ import { BaseActivationCommandProvider } from './baseActivationProvider';
1010

1111
@injectable()
1212
export class Bash extends BaseActivationCommandProvider {
13-
constructor( @inject(IServiceContainer) serviceContainer: IServiceContainer) {
13+
constructor(@inject(IServiceContainer) serviceContainer: IServiceContainer) {
1414
super(serviceContainer);
1515
}
1616
public isShellSupported(targetShell: TerminalShellType): boolean {
1717
return targetShell === TerminalShellType.bash ||
18+
targetShell === TerminalShellType.gitbash ||
19+
targetShell === TerminalShellType.wsl ||
20+
targetShell === TerminalShellType.ksh ||
21+
targetShell === TerminalShellType.zsh ||
1822
targetShell === TerminalShellType.cshell ||
23+
targetShell === TerminalShellType.tcshell ||
1924
targetShell === TerminalShellType.fish;
2025
}
2126
public async getActivationCommands(resource: Uri | undefined, targetShell: TerminalShellType): Promise<string[] | undefined> {
@@ -28,9 +33,14 @@ export class Bash extends BaseActivationCommandProvider {
2833

2934
private getScriptsInOrderOfPreference(targetShell: TerminalShellType): string[] {
3035
switch (targetShell) {
36+
case TerminalShellType.wsl:
37+
case TerminalShellType.ksh:
38+
case TerminalShellType.zsh:
39+
case TerminalShellType.gitbash:
3140
case TerminalShellType.bash: {
3241
return ['activate.sh', 'activate'];
3342
}
43+
case TerminalShellType.tcshell:
3444
case TerminalShellType.cshell: {
3545
return ['activate.csh'];
3646
}

src/client/common/terminal/helper.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,23 +14,33 @@ import { ITerminalActivationCommandProvider, ITerminalHelper, TerminalShellType
1414

1515
// Types of shells can be found here:
1616
// 1. https://wiki.ubuntu.com/ChangingShells
17-
const IS_BASH = /(bash.exe$|wsl.exe$|bash$|zsh$|ksh$)/i;
17+
const IS_GITBASH = /(gitbash.exe$)/i;
18+
const IS_BASH = /(bash.exe$|bash$)/i;
19+
const IS_WSL = /(wsl.exe$)/i;
20+
const IS_ZSH = /(zsh$)/i;
21+
const IS_KSH = /(ksh$)/i;
1822
const IS_COMMAND = /cmd.exe$/i;
1923
const IS_POWERSHELL = /(powershell.exe$|powershell$)/i;
2024
const IS_POWERSHELL_CORE = /(pwsh.exe$|pwsh$)/i;
2125
const IS_FISH = /(fish$)/i;
2226
const IS_CSHELL = /(csh$)/i;
27+
const IS_TCSHELL = /(tcsh$)/i;
2328

2429
@injectable()
2530
export class TerminalHelper implements ITerminalHelper {
2631
private readonly detectableShells: Map<TerminalShellType, RegExp>;
27-
constructor( @inject(IServiceContainer) private serviceContainer: IServiceContainer) {
32+
constructor(@inject(IServiceContainer) private serviceContainer: IServiceContainer) {
2833

2934
this.detectableShells = new Map<TerminalShellType, RegExp>();
3035
this.detectableShells.set(TerminalShellType.powershell, IS_POWERSHELL);
36+
this.detectableShells.set(TerminalShellType.gitbash, IS_GITBASH);
3137
this.detectableShells.set(TerminalShellType.bash, IS_BASH);
38+
this.detectableShells.set(TerminalShellType.wsl, IS_WSL);
39+
this.detectableShells.set(TerminalShellType.zsh, IS_ZSH);
40+
this.detectableShells.set(TerminalShellType.ksh, IS_KSH);
3241
this.detectableShells.set(TerminalShellType.commandPrompt, IS_COMMAND);
3342
this.detectableShells.set(TerminalShellType.fish, IS_FISH);
43+
this.detectableShells.set(TerminalShellType.tcshell, IS_TCSHELL);
3444
this.detectableShells.set(TerminalShellType.cshell, IS_CSHELL);
3545
this.detectableShells.set(TerminalShellType.powershellCore, IS_POWERSHELL_CORE);
3646
}

src/client/common/terminal/types.ts

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,18 @@
55
import { Event, Terminal, Uri } from 'vscode';
66

77
export enum TerminalShellType {
8-
powershell = 1,
9-
powershellCore = 2,
10-
commandPrompt = 3,
11-
bash = 4,
12-
fish = 5,
13-
cshell = 6,
14-
other = 7
8+
powershell = 'powershell',
9+
powershellCore = 'powershellCore',
10+
commandPrompt = 'commandPrompt',
11+
gitbash = 'gitbash',
12+
bash = 'bash',
13+
zsh = 'zsh',
14+
ksh = 'ksh',
15+
fish = 'fish',
16+
cshell = 'cshell',
17+
tcshell = 'tshell',
18+
wsl = 'wsl',
19+
other = 'other'
1520
}
1621

1722
export interface ITerminalService {

src/client/extension.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import { registerTypes as platformRegisterTypes } from './common/platform/servic
2424
import { registerTypes as processRegisterTypes } from './common/process/serviceRegistry';
2525
import { registerTypes as commonRegisterTypes } from './common/serviceRegistry';
2626
import { StopWatch } from './common/stopWatch';
27+
import { ITerminalHelper } from './common/terminal/types';
2728
import { GLOBAL_MEMENTO, IConfigurationService, IDisposableRegistry, ILogger, IMemento, IOutputChannel, IPersistentStateFactory, WORKSPACE_MEMENTO } from './common/types';
2829
import { registerTypes as variableRegisterTypes } from './common/variables/serviceRegistry';
2930
import { AttachRequestArguments, LaunchRequestArguments } from './debugger/Common/Contracts';
@@ -186,10 +187,12 @@ async function sendStartupTelemetry(activatedPromise: Promise<void>, serviceCont
186187
const logger = serviceContainer.get<ILogger>(ILogger);
187188
try {
188189
await activatedPromise;
190+
const terminalHelper = serviceContainer.get<ITerminalHelper>(ITerminalHelper);
191+
const terminalShellType = terminalHelper.identifyTerminalShell(terminalHelper.getTerminalShellPath());
189192
const duration = stopWatch.elapsedTime;
190193
const condaLocator = serviceContainer.get<ICondaService>(ICondaService);
191194
const condaVersion = await condaLocator.getCondaVersion().catch(() => undefined);
192-
const props = condaVersion ? { condaVersion } : undefined;
195+
const props = { condaVersion, terminal: terminalShellType };
193196
sendTelemetryEvent(EDITOR_LOAD, duration, props);
194197
} catch (ex) {
195198
logger.logError('sendStartupTelemetry failed.', ex);

src/client/telemetry/types.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1-
import { LinterId } from '../linters/types';
2-
31
// Copyright (c) Microsoft Corporation. All rights reserved.
42
// Licensed under the MIT License.
53

4+
import { TerminalShellType } from '../common/terminal/types';
5+
import { LinterId } from '../linters/types';
6+
67
export type EditorLoadTelemetry = {
7-
condaVersion: string;
8+
condaVersion: string | undefined;
9+
terminal: TerminalShellType;
810
};
911
export type FormatTelemetry = {
1012
tool: 'autopep8' | 'black' | 'yapf';

src/test/common/terminals/activation.bash.test.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ suite('Terminal Environment Activation (bash)', () => {
3737
EnumEx.getNamesAndValues<TerminalShellType>(TerminalShellType).forEach(shellType => {
3838
let isScriptFileSupported = false;
3939
switch (shellType.value) {
40+
case TerminalShellType.zsh:
41+
case TerminalShellType.ksh:
42+
case TerminalShellType.wsl:
43+
case TerminalShellType.gitbash:
4044
case TerminalShellType.bash: {
4145
isScriptFileSupported = ['activate', 'activate.sh'].indexOf(scriptFileName) >= 0;
4246
break;
@@ -45,6 +49,7 @@ suite('Terminal Environment Activation (bash)', () => {
4549
isScriptFileSupported = ['activate.fish'].indexOf(scriptFileName) >= 0;
4650
break;
4751
}
52+
case TerminalShellType.tcshell:
4853
case TerminalShellType.cshell: {
4954
isScriptFileSupported = ['activate.csh'].indexOf(scriptFileName) >= 0;
5055
break;
@@ -61,7 +66,12 @@ suite('Terminal Environment Activation (bash)', () => {
6166

6267
const supported = bash.isShellSupported(shellType.value);
6368
switch (shellType.value) {
69+
case TerminalShellType.wsl:
70+
case TerminalShellType.zsh:
71+
case TerminalShellType.ksh:
6472
case TerminalShellType.bash:
73+
case TerminalShellType.gitbash:
74+
case TerminalShellType.tcshell:
6575
case TerminalShellType.cshell:
6676
case TerminalShellType.fish: {
6777
expect(supported).to.be.equal(true, `${shellType.name} shell not supported (it should be)`);

src/test/common/terminals/activation.conda.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ suite('Terminal Environment Activation conda', () => {
104104
}
105105
expect(activationCommands).to.deep.equal(expectedActivationCommamnd, 'Incorrect Activation command');
106106
}
107-
EnumEx.getNamesAndValues(TerminalShellType).forEach(shellType => {
107+
EnumEx.getNamesAndValues<TerminalShellType>(TerminalShellType).forEach(shellType => {
108108
test(`Conda activation command for shell ${shellType.name} on (windows)`, async () => {
109109
const pythonPath = path.join('c', 'users', 'xyz', '.conda', 'envs', 'enva', 'python.exe');
110110
await expectNoCondaActivationCommandForPowershell(true, false, false, pythonPath, shellType.value);
@@ -120,7 +120,7 @@ suite('Terminal Environment Activation conda', () => {
120120
await expectNoCondaActivationCommandForPowershell(false, true, false, pythonPath, shellType.value);
121121
});
122122
});
123-
EnumEx.getNamesAndValues(TerminalShellType).forEach(shellType => {
123+
EnumEx.getNamesAndValues<TerminalShellType>(TerminalShellType).forEach(shellType => {
124124
test(`Conda activation command for shell ${shellType.name} on (windows), containing spaces in environment name`, async () => {
125125
const pythonPath = path.join('c', 'users', 'xyz', '.conda', 'envs', 'enva', 'python.exe');
126126
await expectNoCondaActivationCommandForPowershell(true, false, false, pythonPath, shellType.value, true);

src/test/common/terminals/helper.test.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,11 @@ suite('Terminal Service helpers', () => {
4848
shellPathsAndIdentification.set('c:\\windows\\system32\\cmd.exe', TerminalShellType.commandPrompt);
4949

5050
shellPathsAndIdentification.set('c:\\windows\\system32\\bash.exe', TerminalShellType.bash);
51-
shellPathsAndIdentification.set('c:\\windows\\system32\\wsl.exe', TerminalShellType.bash);
52-
shellPathsAndIdentification.set('c:\\windows\\system32\\gitbash.exe', TerminalShellType.bash);
51+
shellPathsAndIdentification.set('c:\\windows\\system32\\wsl.exe', TerminalShellType.wsl);
52+
shellPathsAndIdentification.set('c:\\windows\\system32\\gitbash.exe', TerminalShellType.gitbash);
5353
shellPathsAndIdentification.set('/usr/bin/bash', TerminalShellType.bash);
54-
shellPathsAndIdentification.set('/usr/bin/zsh', TerminalShellType.bash);
55-
shellPathsAndIdentification.set('/usr/bin/ksh', TerminalShellType.bash);
54+
shellPathsAndIdentification.set('/usr/bin/zsh', TerminalShellType.zsh);
55+
shellPathsAndIdentification.set('/usr/bin/ksh', TerminalShellType.ksh);
5656

5757
shellPathsAndIdentification.set('c:\\windows\\system32\\powershell.exe', TerminalShellType.powershell);
5858
shellPathsAndIdentification.set('c:\\windows\\system32\\pwsh.exe', TerminalShellType.powershellCore);
@@ -65,6 +65,7 @@ suite('Terminal Service helpers', () => {
6565
shellPathsAndIdentification.set('/usr/bin/shell', TerminalShellType.other);
6666

6767
shellPathsAndIdentification.set('/usr/bin/csh', TerminalShellType.cshell);
68+
shellPathsAndIdentification.set('/usr/bin/tcsh', TerminalShellType.tcshell);
6869

6970
shellPathsAndIdentification.forEach((shellType, shellPath) => {
7071
expect(helper.identifyTerminalShell(shellPath)).to.equal(shellType, `Incorrect Shell Type for path '${shellPath}'`);

0 commit comments

Comments
 (0)