diff --git a/src/vs/platform/terminal/common/terminalProfiles.ts b/src/vs/platform/terminal/common/terminalProfiles.ts new file mode 100644 index 0000000..a86cc93 --- /dev/null +++ b/src/vs/platform/terminal/common/terminalProfiles.ts @@ -0,0 +1,115 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Codicon } from '../../../base/common/codicons.js'; +import { isUriComponents, URI } from '../../../base/common/uri.js'; +import { localize } from '../../../nls.js'; +import { IExtensionTerminalProfile, ITerminalProfile, TerminalIcon } from './terminal.js'; +import { ThemeIcon } from '../../../base/common/themables.js'; +import { isObject, isString, type SingleOrMany } from '../../../base/common/types.js'; + +export function createProfileSchemaEnums(detectedProfiles: ITerminalProfile[], extensionProfiles?: readonly IExtensionTerminalProfile[]): { + values: (string | null)[] | undefined; + markdownDescriptions: string[] | undefined; +} { + const result: { name: string | null; description: string }[] = [{ + name: null, + description: localize('terminalAutomaticProfile', 'Automatically detect the default') + }]; + result.push(...detectedProfiles.map(e => { + return { + name: e.profileName, + description: createProfileDescription(e) + }; + })); + if (extensionProfiles) { + result.push(...extensionProfiles.map(extensionProfile => { + return { + name: extensionProfile.title, + description: createExtensionProfileDescription(extensionProfile) + }; + })); + } + return { + values: result.map(e => e.name), + markdownDescriptions: result.map(e => e.description) + }; +} + +function createProfileDescription(profile: ITerminalProfile): string { + let description = `$(${ThemeIcon.isThemeIcon(profile.icon) ? profile.icon.id : profile.icon ? profile.icon : Codicon.terminal.id}) ${profile.profileName}\n- path: ${profile.path}`; + if (profile.args) { + if (isString(profile.args)) { + description += `\n- args: "${profile.args}"`; + } else { + description += `\n- args: [${profile.args.length === 0 ? '' : `'${profile.args.join(`','`)}'`}]`; + } + } + if (profile.overrideName !== undefined) { + description += `\n- overrideName: ${profile.overrideName}`; + } + if (profile.color) { + description += `\n- color: ${profile.color}`; + } + if (profile.env) { + description += `\n- env: ${JSON.stringify(profile.env)}`; + } + return description; +} + +function createExtensionProfileDescription(profile: IExtensionTerminalProfile): string { + const description = `$(${ThemeIcon.isThemeIcon(profile.icon) ? profile.icon.id : profile.icon ? profile.icon : Codicon.terminal.id}) ${profile.title}\n- extensionIdentifier: ${profile.extensionIdentifier}`; + return description; +} + + +export function terminalProfileArgsMatch(args1: SingleOrMany | undefined, args2: SingleOrMany | undefined): boolean { + if (!args1 && !args2) { + return true; + } else if (isString(args1) && isString(args2)) { + return args1 === args2; + } else if (Array.isArray(args1) && Array.isArray(args2)) { + if (args1.length !== args2.length) { + return false; + } + for (let i = 0; i < args1.length; i++) { + if (args1[i] !== args2[i]) { + return false; + } + } + return true; + } + return false; +} + +export function terminalIconsEqual(a?: TerminalIcon, b?: TerminalIcon): boolean { + if (!a && !b) { + return true; + } else if (!a || !b) { + return false; + } + + if (ThemeIcon.isThemeIcon(a) && ThemeIcon.isThemeIcon(b)) { + return a.id === b.id && a.color === b.color; + } + if ( + isObject(a) && !URI.isUri(a) && !ThemeIcon.isThemeIcon(a) && + isObject(b) && !URI.isUri(b) && !ThemeIcon.isThemeIcon(b) + ) { + const castedA = (a as { light: unknown; dark: unknown }); + const castedB = (b as { light: unknown; dark: unknown }); + if ((URI.isUri(castedA.light) || isUriComponents(castedA.light)) && (URI.isUri(castedA.dark) || isUriComponents(castedA.dark)) + && (URI.isUri(castedB.light) || isUriComponents(castedB.light)) && (URI.isUri(castedB.dark) || isUriComponents(castedB.dark))) { + return castedA.light.path === castedB.light.path && castedA.dark.path === castedB.dark.path; + } + } + if ((URI.isUri(a) && URI.isUri(b)) || (isUriComponents(a) || isUriComponents(b))) { + const castedA = (a as { scheme: unknown; path: unknown }); + const castedB = (b as { scheme: unknown; path: unknown }); + return castedA.path === castedB.path && castedA.scheme === castedB.scheme; + } + + return false; +} diff --git a/src/vs/platform/terminal/test/common/terminalProfiles.test.ts b/src/vs/platform/terminal/test/common/terminalProfiles.test.ts new file mode 100644 index 0000000..036f9fe --- /dev/null +++ b/src/vs/platform/terminal/test/common/terminalProfiles.test.ts @@ -0,0 +1,93 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { deepStrictEqual } from 'assert'; +import { Codicon } from '../../../../base/common/codicons.js'; +import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; +import { ITerminalProfile } from '../../common/terminal.js'; +import { createProfileSchemaEnums } from '../../common/terminalProfiles.js'; + +suite('terminalProfiles', () => { + ensureNoDisposablesAreLeakedInTestSuite(); + + suite('createProfileSchemaEnums', () => { + test('should return an empty array when there are no profiles', () => { + deepStrictEqual(createProfileSchemaEnums([]), { + values: [ + null + ], + markdownDescriptions: [ + 'Automatically detect the default' + ] + }); + }); + test('should return a single entry when there is one profile', () => { + const profile: ITerminalProfile = { + profileName: 'name', + path: 'path', + isDefault: true + }; + deepStrictEqual(createProfileSchemaEnums([profile]), { + values: [ + null, + 'name' + ], + markdownDescriptions: [ + 'Automatically detect the default', + '$(terminal) name\n- path: path' + ] + }); + }); + test('should show all profile information', () => { + const profile: ITerminalProfile = { + profileName: 'name', + path: 'path', + isDefault: true, + args: ['a', 'b'], + color: 'terminal.ansiRed', + env: { + c: 'd', + e: 'f' + }, + icon: Codicon.zap, + overrideName: true + }; + deepStrictEqual(createProfileSchemaEnums([profile]), { + values: [ + null, + 'name' + ], + markdownDescriptions: [ + 'Automatically detect the default', + `$(zap) name\n- path: path\n- args: ['a','b']\n- overrideName: true\n- color: terminal.ansiRed\n- env: {\"c\":\"d\",\"e\":\"f\"}` + ] + }); + }); + test('should return a multiple entries when there are multiple profiles', () => { + const profile1: ITerminalProfile = { + profileName: 'name', + path: 'path', + isDefault: true + }; + const profile2: ITerminalProfile = { + profileName: 'foo', + path: 'bar', + isDefault: false + }; + deepStrictEqual(createProfileSchemaEnums([profile1, profile2]), { + values: [ + null, + 'name', + 'foo' + ], + markdownDescriptions: [ + 'Automatically detect the default', + '$(terminal) name\n- path: path', + '$(terminal) foo\n- path: bar' + ] + }); + }); + }); +});