diff --git a/package-lock.json b/package-lock.json index 8f83e602cd..deab195404 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,13 +12,13 @@ "@azure/arm-authorization": "^9.0.0", "@azure/arm-containerregistry": "^10.1.0", "@azure/storage-blob": "^12.14.0", - "@microsoft/compose-language-service": "^0.2.0", + "@microsoft/compose-language-service": "^0.3.0", "@microsoft/vscode-azext-azureappservice": "~3.3.0", "@microsoft/vscode-azext-azureauth": "^2.4.1", "@microsoft/vscode-azext-azureutils": "^3.0.1", "@microsoft/vscode-azext-utils": "^2.5.1", "@microsoft/vscode-container-client": "^0.1.2", - "@microsoft/vscode-docker-registries": "^0.1.12", + "@microsoft/vscode-docker-registries": "^0.1.13", "dayjs": "^1.11.7", "dockerfile-language-server-nodejs": "^0.13.0", "fs-extra": "^11.1.1", @@ -906,9 +906,9 @@ } }, "node_modules/@microsoft/compose-language-service": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@microsoft/compose-language-service/-/compose-language-service-0.2.0.tgz", - "integrity": "sha512-U49V0aATlwNy3KGU+FKiDOVBRdw6cmxM0AmPxYn1jJmi/l/u2oBCEh0cWmzoqcrySNMwUJVkmHNc1w5bXNon6A==", + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@microsoft/compose-language-service/-/compose-language-service-0.3.0.tgz", + "integrity": "sha512-CrekvXhSCD9AYsK+ycnTsBfK1m6XkUw9xndfCOxt07p2q9L7lr2f5+UAaqImbd83qr52+mk5YL4z1ZVeUX8WrA==", "dependencies": { "vscode-languageserver": "^8.0.2", "vscode-languageserver-textdocument": "^1.0.3", @@ -1056,9 +1056,9 @@ } }, "node_modules/@microsoft/vscode-docker-registries": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/@microsoft/vscode-docker-registries/-/vscode-docker-registries-0.1.12.tgz", - "integrity": "sha512-90w09dvXR0Igto+aYJDmwKoXjyiCN1Bq+B3ztjgrjNYXF58sHsrN+Y5dlIAvSFPa2/Mf7FzSG818ANouw65x5A==", + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/@microsoft/vscode-docker-registries/-/vscode-docker-registries-0.1.13.tgz", + "integrity": "sha512-6HQxBQm8q2Yt7v34EJJp322l0yov0Z46LAIq/G6c2V4pmpx8ngBXd9JVxnoy7BtIV6BLiLHFXzzNaG/w2XwF/w==", "dependencies": { "dayjs": "^1.11.7" } diff --git a/package.json b/package.json index beb5800365..6cf0ecf248 100644 --- a/package.json +++ b/package.json @@ -3008,13 +3008,13 @@ "@azure/arm-authorization": "^9.0.0", "@azure/arm-containerregistry": "^10.1.0", "@azure/storage-blob": "^12.14.0", - "@microsoft/compose-language-service": "^0.2.0", + "@microsoft/compose-language-service": "^0.3.0", "@microsoft/vscode-azext-azureappservice": "~3.3.0", "@microsoft/vscode-azext-azureauth": "^2.4.1", "@microsoft/vscode-azext-azureutils": "^3.0.1", "@microsoft/vscode-azext-utils": "^2.5.1", "@microsoft/vscode-container-client": "^0.1.2", - "@microsoft/vscode-docker-registries": "^0.1.12", + "@microsoft/vscode-docker-registries": "^0.1.13", "dayjs": "^1.11.7", "dockerfile-language-server-nodejs": "^0.13.0", "fs-extra": "^11.1.1", diff --git a/src/commands/compose/compose.ts b/src/commands/compose/compose.ts index e9d8dceb85..72453c2c69 100644 --- a/src/commands/compose/compose.ts +++ b/src/commands/compose/compose.ts @@ -12,11 +12,15 @@ import { quickPickWorkspaceFolder } from '../../utils/quickPickWorkspaceFolder'; import { selectComposeCommand } from '../selectCommandTemplate'; import { getComposeProfileList, getComposeProfilesOrServices, getComposeServiceList } from './getComposeSubsetList'; -async function compose(context: IActionContext, commands: ('up' | 'down' | 'upSubset')[], message: string, dockerComposeFileUri?: vscode.Uri, selectedComposeFileUris?: vscode.Uri[]): Promise { +async function compose(context: IActionContext, commands: ('up' | 'down' | 'upSubset')[], message: string, dockerComposeFileUri?: vscode.Uri | string, selectedComposeFileUris?: vscode.Uri[], preselectedServices?: string[], preselectedProfiles?: string[]): Promise { if (!vscode.workspace.isTrusted) { throw new UserCancelledError('enforceTrust'); } + if (typeof dockerComposeFileUri === 'string') { + dockerComposeFileUri = vscode.Uri.parse(dockerComposeFileUri); + } + // If a file is chosen, get its workspace folder, otherwise, require the user to choose // If a file is chosen that is not in a workspace, it will automatically fall back to quickPickWorkspaceFolder const folder: vscode.WorkspaceFolder = (dockerComposeFileUri ? vscode.workspace.getWorkspaceFolder(dockerComposeFileUri) : undefined) || @@ -59,7 +63,7 @@ async function compose(context: IActionContext, commands: ('up' | 'down' | 'upSu ); // Add the service list if needed - terminalCommand.command = await addServicesOrProfilesIfNeeded(context, folder, terminalCommand.command); + terminalCommand.command = await addServicesOrProfilesIfNeeded(context, folder, terminalCommand.command, preselectedServices, preselectedProfiles); const client = await ext.orchestratorManager.getClient(); const taskCRF = new TaskCommandRunnerFactory({ @@ -72,12 +76,14 @@ async function compose(context: IActionContext, commands: ('up' | 'down' | 'upSu } } -export async function composeUp(context: IActionContext, dockerComposeFileUri?: vscode.Uri, selectedComposeFileUris?: vscode.Uri[]): Promise { +// The parameters of this function should not be changed without updating the compose language service which uses this command +export async function composeUp(context: IActionContext, dockerComposeFileUri?: vscode.Uri | string, selectedComposeFileUris?: vscode.Uri[]): Promise { return await compose(context, ['up'], vscode.l10n.t('Choose Docker Compose file to bring up'), dockerComposeFileUri, selectedComposeFileUris); } -export async function composeUpSubset(context: IActionContext, dockerComposeFileUri?: vscode.Uri, selectedComposeFileUris?: vscode.Uri[]): Promise { - return await compose(context, ['upSubset'], vscode.l10n.t('Choose Docker Compose file to bring up'), dockerComposeFileUri, selectedComposeFileUris); +// The parameters of this function should not be changed without updating the compose language service which uses this command +export async function composeUpSubset(context: IActionContext, dockerComposeFileUri?: vscode.Uri | string, selectedComposeFileUris?: vscode.Uri[], preselectedServices?: string[], preselectedProfiles?: string[]): Promise { + return await compose(context, ['upSubset'], vscode.l10n.t('Choose Docker Compose file to bring up'), dockerComposeFileUri, selectedComposeFileUris, preselectedServices, preselectedProfiles); } export async function composeDown(context: IActionContext, dockerComposeFileUri?: vscode.Uri, selectedComposeFileUris?: vscode.Uri[]): Promise { @@ -90,18 +96,19 @@ export async function composeRestart(context: IActionContext, dockerComposeFileU const serviceListPlaceholder = /\${serviceList}/i; const profileListPlaceholder = /\${profileList}/i; -async function addServicesOrProfilesIfNeeded(context: IActionContext, workspaceFolder: vscode.WorkspaceFolder, command: string): Promise { +async function addServicesOrProfilesIfNeeded(context: IActionContext, workspaceFolder: vscode.WorkspaceFolder, command: string, preselectedServices: string[], preselectedProfiles: string[]): Promise { const commandWithoutPlaceholders = command.replace(serviceListPlaceholder, '').replace(profileListPlaceholder, ''); + if (serviceListPlaceholder.test(command) && profileListPlaceholder.test(command)) { // If both are present, need to ask - const { services, profiles } = await getComposeProfilesOrServices(context, workspaceFolder, commandWithoutPlaceholders); + const { services, profiles } = await getComposeProfilesOrServices(context, workspaceFolder, commandWithoutPlaceholders, preselectedServices, preselectedProfiles); return command .replace(serviceListPlaceholder, services) .replace(profileListPlaceholder, profiles); } else if (serviceListPlaceholder.test(command)) { - return command.replace(serviceListPlaceholder, await getComposeServiceList(context, workspaceFolder, commandWithoutPlaceholders)); + return command.replace(serviceListPlaceholder, await getComposeServiceList(context, workspaceFolder, commandWithoutPlaceholders, preselectedServices)); } else if (profileListPlaceholder.test(command)) { - return command.replace(profileListPlaceholder, await getComposeProfileList(context, workspaceFolder, commandWithoutPlaceholders)); + return command.replace(profileListPlaceholder, await getComposeProfileList(context, workspaceFolder, commandWithoutPlaceholders, preselectedProfiles)); } else { return command; } diff --git a/src/commands/compose/getComposeSubsetList.ts b/src/commands/compose/getComposeSubsetList.ts index 611166c023..04630f0968 100644 --- a/src/commands/compose/getComposeSubsetList.ts +++ b/src/commands/compose/getComposeSubsetList.ts @@ -13,13 +13,21 @@ const composeCommandReplaceRegex = /(\b(up|down)\b).*$/i; type SubsetType = 'services' | 'profiles'; -export async function getComposeProfilesOrServices(context: IActionContext, workspaceFolder: vscode.WorkspaceFolder, composeCommand: string): Promise<{ services: string | undefined, profiles: string | undefined }> { +export async function getComposeProfilesOrServices(context: IActionContext, workspaceFolder: vscode.WorkspaceFolder, composeCommand: string, preselectedServices?: string[], preselectedProfiles?: string[]): Promise<{ services: string | undefined, profiles: string | undefined }> { const profiles = await getServiceSubsets(workspaceFolder, composeCommand, 'profiles'); + if (preselectedServices?.length && preselectedProfiles?.length) { + throw new Error(vscode.l10n.t('Cannot specify both services and profiles to start. Please choose one or the other.')); + } + // If there any profiles, we need to ask the user whether they want profiles or services, since they are mutually exclusive to use // Otherwise, if there are no profiles, we'll automatically assume services let useProfiles = false; - if (profiles?.length) { + if (preselectedProfiles?.length) { + useProfiles = true; + } else if (preselectedServices?.length) { + useProfiles = false; + } else if (profiles?.length) { const profilesOrServices: IAzureQuickPickItem[] = [ { label: vscode.l10n.t('Services'), @@ -35,12 +43,12 @@ export async function getComposeProfilesOrServices(context: IActionContext, work } return { - profiles: useProfiles ? await getComposeProfileList(context, workspaceFolder, composeCommand, profiles) : '', - services: !useProfiles ? await getComposeServiceList(context, workspaceFolder, composeCommand) : '', + profiles: useProfiles ? await getComposeProfileList(context, workspaceFolder, composeCommand, profiles, preselectedProfiles) : '', + services: !useProfiles ? await getComposeServiceList(context, workspaceFolder, composeCommand, preselectedServices) : '', }; } -export async function getComposeProfileList(context: IActionContext, workspaceFolder: vscode.WorkspaceFolder, composeCommand: string, prefetchedProfiles?: string[]): Promise { +export async function getComposeProfileList(context: IActionContext, workspaceFolder: vscode.WorkspaceFolder, composeCommand: string, prefetchedProfiles?: string[], preselectedProfiles?: string[]): Promise { const profiles = prefetchedProfiles ?? await getServiceSubsets(workspaceFolder, composeCommand, 'profiles'); if (!profiles?.length) { @@ -51,7 +59,7 @@ export async function getComposeProfileList(context: IActionContext, workspaceFo // Fetch the previously chosen profiles list. By default, all will be selected. const workspaceProfileListKey = `vscode-docker.composeProfiles.${workspaceFolder.name}`; const previousChoices = ext.context.workspaceState.get(workspaceProfileListKey, profiles); - const result = await pickSubsets(context, 'profiles', profiles, previousChoices); + const result = preselectedProfiles?.length ? preselectedProfiles : await pickSubsets(context, 'profiles', profiles, previousChoices); // Update the cache await ext.context.workspaceState.update(workspaceProfileListKey, result); @@ -59,7 +67,7 @@ export async function getComposeProfileList(context: IActionContext, workspaceFo return result.map(p => `--profile ${p}`).join(' '); } -export async function getComposeServiceList(context: IActionContext, workspaceFolder: vscode.WorkspaceFolder, composeCommand: string): Promise { +export async function getComposeServiceList(context: IActionContext, workspaceFolder: vscode.WorkspaceFolder, composeCommand: string, preselectedServices?: string[]): Promise { const services = await getServiceSubsets(workspaceFolder, composeCommand, 'services'); if (!services?.length) { @@ -70,7 +78,7 @@ export async function getComposeServiceList(context: IActionContext, workspaceFo // Fetch the previously chosen services list. By default, all will be selected. const workspaceServiceListKey = `vscode-docker.composeServices.${workspaceFolder.name}`; const previousChoices = ext.context.workspaceState.get(workspaceServiceListKey, services); - const result = await pickSubsets(context, 'services', services, previousChoices); + const result = preselectedServices?.length ? preselectedServices : await pickSubsets(context, 'services', services, previousChoices); // Update the cache await ext.context.workspaceState.update(workspaceServiceListKey, result); diff --git a/src/utils/AlternateYamlLanguageServiceClientFeature.ts b/src/utils/AlternateYamlLanguageServiceClientFeature.ts index 39946d04d2..a1bcd31c90 100644 --- a/src/utils/AlternateYamlLanguageServiceClientFeature.ts +++ b/src/utils/AlternateYamlLanguageServiceClientFeature.ts @@ -30,6 +30,7 @@ export class AlternateYamlLanguageServiceClientFeature implements StaticFeature, advancedCompletions: false, // YAML extension does not have advanced completions for compose docs hover: false, // YAML extension provides hover, but the compose spec lacks descriptions -- https://github.com/compose-spec/compose-spec/issues/138 imageLinks: false, // YAML extension does not have image hyperlinks for compose docs + serviceStartupCodeLens: false, // YAML extension does not have service startup code lens for compose docs formatting: true, };