private async saveProfile()

in patched-vscode/src/vs/workbench/services/userDataProfile/browser/userDataProfileImportExportService.ts [247:543]


	private async saveProfile(profile?: IUserDataProfile, source?: IUserDataProfile | URI | Mutable<IUserDataProfileTemplate>): Promise<void> {

		type SaveProfileInfoClassification = {
			owner: 'sandy081';
			comment: 'Report when profile is about to be saved';
		};
		type CreateProfileInfoClassification = {
			owner: 'sandy081';
			comment: 'Report when profile is about to be created';
			source: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Type of profile source' };
		};
		type CreateProfileInfoEvent = {
			source: string | undefined;
		};
		const createProfileTelemetryData: CreateProfileInfoEvent = { source: source instanceof URI ? 'template' : isUserDataProfile(source) ? 'profile' : source ? 'external' : undefined };

		if (profile) {
			this.telemetryService.publicLog2<{}, SaveProfileInfoClassification>('userDataProfile.startEdit');
		} else {
			this.telemetryService.publicLog2<CreateProfileInfoEvent, CreateProfileInfoClassification>('userDataProfile.startCreate', createProfileTelemetryData);
		}

		const disposables = new DisposableStore();
		const title = profile ? localize('save profile', "Edit {0} Profile...", profile.name) : localize('create new profle', "Create New Profile...");

		const settings: IQuickPickItem & { id: ProfileResourceType } = { id: ProfileResourceType.Settings, label: localize('settings', "Settings"), picked: !profile?.useDefaultFlags?.settings };
		const keybindings: IQuickPickItem & { id: ProfileResourceType } = { id: ProfileResourceType.Keybindings, label: localize('keybindings', "Keyboard Shortcuts"), picked: !profile?.useDefaultFlags?.keybindings };
		const snippets: IQuickPickItem & { id: ProfileResourceType } = { id: ProfileResourceType.Snippets, label: localize('snippets', "User Snippets"), picked: !profile?.useDefaultFlags?.snippets };
		const tasks: IQuickPickItem & { id: ProfileResourceType } = { id: ProfileResourceType.Tasks, label: localize('tasks', "User Tasks"), picked: !profile?.useDefaultFlags?.tasks };
		const extensions: IQuickPickItem & { id: ProfileResourceType } = { id: ProfileResourceType.Extensions, label: localize('extensions', "Extensions"), picked: !profile?.useDefaultFlags?.extensions };
		const resources = [settings, keybindings, snippets, tasks, extensions];

		const quickPick = this.quickInputService.createQuickPick();
		quickPick.title = title;
		quickPick.placeholder = localize('name placeholder', "Profile name");
		quickPick.value = profile?.name ?? (isUserDataProfileTemplate(source) ? this.generateProfileName(source.name) : '');
		quickPick.canSelectMany = true;
		quickPick.matchOnDescription = false;
		quickPick.matchOnDetail = false;
		quickPick.matchOnLabel = false;
		quickPick.sortByLabel = false;
		quickPick.hideCountBadge = true;
		quickPick.ok = false;
		quickPick.customButton = true;
		quickPick.hideCheckAll = true;
		quickPick.ignoreFocusOut = true;
		quickPick.customLabel = profile ? localize('save', "Save") : localize('create', "Create");
		quickPick.description = localize('customise the profile', "Choose what to configure in your Profile:");
		quickPick.items = [...resources];

		const update = () => {
			quickPick.items = resources;
			quickPick.selectedItems = resources.filter(item => item.picked);
		};
		update();

		const validate = () => {
			if (!profile && this.userDataProfilesService.profiles.some(p => p.name === quickPick.value)) {
				quickPick.validationMessage = localize('profileExists', "Profile with name {0} already exists.", quickPick.value);
				quickPick.severity = Severity.Warning;
				return;
			}
			if (resources.every(resource => !resource.picked)) {
				quickPick.validationMessage = localize('invalid configurations', "The profile should contain at least one configuration.");
				quickPick.severity = Severity.Warning;
				return;
			}
			quickPick.severity = Severity.Ignore;
			quickPick.validationMessage = undefined;
		};

		disposables.add(quickPick.onDidChangeSelection(items => {
			let needUpdate = false;
			for (const resource of resources) {
				resource.picked = items.includes(resource);
				const description = resource.picked ? undefined : localize('use default profile', "Using Default Profile");
				if (resource.description !== description) {
					resource.description = description;
					needUpdate = true;
				}
			}
			if (needUpdate) {
				update();
			}
			validate();
		}));

		disposables.add(quickPick.onDidChangeValue(validate));

		let icon = DEFAULT_ICON;
		if (profile?.icon) {
			icon = ThemeIcon.fromId(profile.icon);
		}
		if (isUserDataProfileTemplate(source) && source.icon) {
			icon = ThemeIcon.fromId(source.icon);
		}
		if (icon.id !== DEFAULT_ICON.id && !ICONS.some(({ id }) => id === icon.id) && !getAllCodicons().some(({ id }) => id === icon.id)) {
			icon = DEFAULT_ICON;
		}
		let result: { name: string; items: ReadonlyArray<IQuickPickItem>; icon?: string | null } | undefined;
		disposables.add(Event.any(quickPick.onDidCustom, quickPick.onDidAccept)(() => {
			const name = quickPick.value.trim();
			if (!name) {
				quickPick.validationMessage = localize('name required', "Profile name is required and must be a non-empty value.");
				quickPick.severity = Severity.Error;
			}
			if (quickPick.validationMessage) {
				return;
			}
			result = { name, items: quickPick.selectedItems, icon: icon.id === DEFAULT_ICON.id ? null : icon.id };
			quickPick.hide();
			quickPick.severity = Severity.Ignore;
			quickPick.validationMessage = undefined;
		}));

		const domNode = DOM.$('.profile-edit-widget');

		const profileIconContainer = DOM.$('.profile-icon-container');
		DOM.append(profileIconContainer, DOM.$('.profile-icon-label', undefined, localize('icon', "Icon:")));
		const profileIconElement = DOM.append(profileIconContainer, DOM.$(`.profile-icon${ThemeIcon.asCSSSelector(icon)}`));
		profileIconElement.tabIndex = 0;
		profileIconElement.role = 'button';
		profileIconElement.ariaLabel = localize('select icon', "Icon: {0}", icon.id);
		const iconSelectBox = disposables.add(this.instantiationService.createInstance(WorkbenchIconSelectBox, { icons: ICONS, inputBoxStyles: defaultInputBoxStyles }));
		const dimension = new DOM.Dimension(486, 260);
		iconSelectBox.layout(dimension);
		let hoverWidget: IHoverWidget | undefined;

		const updateIcon = (updated: ThemeIcon | undefined) => {
			icon = updated ?? DEFAULT_ICON;
			profileIconElement.className = `profile-icon ${ThemeIcon.asClassName(icon)}`;
			profileIconElement.ariaLabel = localize('select icon', "Icon: {0}", icon.id);
		};
		disposables.add(iconSelectBox.onDidSelect(selectedIcon => {
			if (icon.id !== selectedIcon.id) {
				updateIcon(selectedIcon);
			}
			hoverWidget?.dispose();
			profileIconElement.focus();
		}));
		const showIconSelectBox = () => {
			iconSelectBox.clearInput();
			hoverWidget = this.hoverService.showHover({
				content: iconSelectBox.domNode,
				target: profileIconElement,
				position: {
					hoverPosition: HoverPosition.BELOW,
				},
				persistence: {
					sticky: true,
				},
				appearance: {
					showPointer: true,
				},
			}, true);
			if (hoverWidget) {
				iconSelectBox.layout(dimension);
				disposables.add(hoverWidget);
			}
			iconSelectBox.focus();
		};
		disposables.add(DOM.addDisposableListener(profileIconElement, DOM.EventType.CLICK, (e: MouseEvent) => {
			DOM.EventHelper.stop(e, true);
			showIconSelectBox();
		}));
		disposables.add(DOM.addDisposableListener(profileIconElement, DOM.EventType.KEY_DOWN, e => {
			const event = new StandardKeyboardEvent(e);
			if (event.equals(KeyCode.Enter) || event.equals(KeyCode.Space)) {
				DOM.EventHelper.stop(event, true);
				showIconSelectBox();
			}
		}));
		disposables.add(DOM.addDisposableListener(iconSelectBox.domNode, DOM.EventType.KEY_DOWN, e => {
			const event = new StandardKeyboardEvent(e);
			if (event.equals(KeyCode.Escape)) {
				DOM.EventHelper.stop(event, true);
				hoverWidget?.dispose();
				profileIconElement.focus();
			}
		}));

		if (!profile && !isUserDataProfileTemplate(source)) {
			const profileTypeContainer = DOM.append(domNode, DOM.$('.profile-type-container'));
			DOM.append(profileTypeContainer, DOM.$('.profile-type-create-label', undefined, localize('create from', "Copy from:")));
			const separator = { text: '\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500', isDisabled: true };
			const profileOptions: (ISelectOptionItem & { id?: string; source?: IUserDataProfile | URI })[] = [];
			profileOptions.push({ text: localize('empty profile', "None") });
			const templates = await this.userDataProfileManagementService.getBuiltinProfileTemplates();
			if (templates.length) {
				profileOptions.push({ ...separator, decoratorRight: localize('from templates', "Profile Templates") });
				for (const template of templates) {
					profileOptions.push({ text: template.name, id: template.url, source: URI.parse(template.url) });
				}
			}
			profileOptions.push({ ...separator, decoratorRight: localize('from existing profiles', "Existing Profiles") });
			for (const profile of this.userDataProfilesService.profiles) {
				profileOptions.push({ text: profile.name, id: profile.id, source: profile });
			}

			const findOptionIndex = () => {
				const index = profileOptions.findIndex(option => {
					if (source instanceof URI) {
						return option.source instanceof URI && this.uriIdentityService.extUri.isEqual(option.source, source);
					} else if (isUserDataProfile(source)) {
						return option.id === source.id;
					}
					return false;
				});
				return index > -1 ? index : 0;
			};

			const initialIndex = findOptionIndex();
			const selectBox = disposables.add(this.instantiationService.createInstance(SelectBox,
				profileOptions,
				initialIndex,
				this.contextViewService,
				defaultSelectBoxStyles,
				{
					useCustomDrawn: true,
					ariaLabel: localize('copy profile from', "Copy profile from"),
				}
			));
			selectBox.render(DOM.append(profileTypeContainer, DOM.$('.profile-type-select-container')));

			if (profileOptions[initialIndex].source) {
				quickPick.value = this.generateProfileName(profileOptions[initialIndex].text);
			}

			const updateOptions = () => {
				const option = profileOptions[findOptionIndex()];
				for (const resource of resources) {
					resource.picked = option.source && !(option.source instanceof URI) ? !option.source?.useDefaultFlags?.[resource.id] : true;
				}
				updateIcon(!(option.source instanceof URI) && option.source?.icon ? ThemeIcon.fromId(option.source.icon) : undefined);
				update();
			};

			updateOptions();
			disposables.add(selectBox.onDidSelect(({ index }) => {
				source = profileOptions[index].source;
				updateOptions();
			}));
		}

		DOM.append(domNode, profileIconContainer);

		quickPick.widget = domNode;
		quickPick.show();

		await new Promise<void>((c, e) => {
			disposables.add(quickPick.onDidHide(() => {
				disposables.dispose();
				c();
			}));
		});

		if (!result) {
			if (profile) {
				this.telemetryService.publicLog2<{}, SaveProfileInfoClassification>('userDataProfile.cancelEdit');
			} else {
				this.telemetryService.publicLog2<CreateProfileInfoEvent, CreateProfileInfoClassification>('userDataProfile.cancelCreate', createProfileTelemetryData);
			}
			return;
		}

		try {
			const useDefaultFlags: UseDefaultProfileFlags | undefined = result.items.length === resources.length
				? undefined
				: {
					settings: !result.items.includes(settings),
					keybindings: !result.items.includes(keybindings),
					snippets: !result.items.includes(snippets),
					tasks: !result.items.includes(tasks),
					extensions: !result.items.includes(extensions)
				};
			if (profile) {
				await this.userDataProfileManagementService.updateProfile(profile, { name: result.name, icon: result.icon, useDefaultFlags: profile.useDefaultFlags && !useDefaultFlags ? {} : useDefaultFlags });
			} else {
				if (source instanceof URI) {
					this.telemetryService.publicLog2<CreateProfileInfoEvent, CreateProfileInfoClassification>('userDataProfile.createFromTemplate', createProfileTelemetryData);
					await this.importProfile(source, { mode: 'apply', name: result.name, useDefaultFlags, icon: result.icon ? result.icon : undefined });
				} else if (isUserDataProfile(source)) {
					this.telemetryService.publicLog2<CreateProfileInfoEvent, CreateProfileInfoClassification>('userDataProfile.createFromProfile', createProfileTelemetryData);
					await this.createFromProfile(source, result.name, { useDefaultFlags, icon: result.icon ? result.icon : undefined });
				} else if (isUserDataProfileTemplate(source)) {
					source.name = result.name;
					this.telemetryService.publicLog2<CreateProfileInfoEvent, CreateProfileInfoClassification>('userDataProfile.createFromExternalTemplate', createProfileTelemetryData);
					await this.createAndSwitch(source, false, true, { useDefaultFlags, icon: result.icon ? result.icon : undefined }, localize('create profile', "Create Profile"));
				} else {
					this.telemetryService.publicLog2<CreateProfileInfoEvent, CreateProfileInfoClassification>('userDataProfile.createEmptyProfile', createProfileTelemetryData);
					await this.userDataProfileManagementService.createAndEnterProfile(result.name, { useDefaultFlags, icon: result.icon ? result.icon : undefined });
				}
			}
		} catch (error) {
			this.notificationService.error(error);
		}
	}