private shouldRunExperiment()

in src/vs/workbench/contrib/experiments/common/experimentService.ts [443:590]


	private shouldRunExperiment(experiment: IRawExperiment, processedExperiment: IExperiment): Promise<ExperimentState> {
		if (processedExperiment.state !== ExperimentState.Evaluating) {
			return Promise.resolve(processedExperiment.state);
		}

		if (!experiment.enabled) {
			return Promise.resolve(ExperimentState.NoRun);
		}

		const condition = experiment.condition;
		if (!condition) {
			return Promise.resolve(ExperimentState.Run);
		}

		if (experiment.condition?.os && !experiment.condition.os.includes(OS)) {
			return Promise.resolve(ExperimentState.NoRun);
		}

		if (!this.checkExperimentDependencies(experiment)) {
			return Promise.resolve(ExperimentState.NoRun);
		}

		for (const [key, value] of Object.entries(experiment.condition?.userSetting || {})) {
			if (!equals(this.configurationService.getValue(key), value)) {
				return Promise.resolve(ExperimentState.NoRun);
			}
		}

		if (!this.checkActivationEventFrequency(experiment)) {
			return Promise.resolve(ExperimentState.Evaluating);
		}

		if (this.productService.quality === 'stable' && condition.insidersOnly === true) {
			return Promise.resolve(ExperimentState.NoRun);
		}

		const isNewUser = !this.storageService.get(lastSessionDateStorageKey, StorageScope.GLOBAL);
		if ((condition.newUser === true && !isNewUser)
			|| (condition.newUser === false && isNewUser)) {
			return Promise.resolve(ExperimentState.NoRun);
		}

		if (typeof condition.displayLanguage === 'string') {
			let localeToCheck = condition.displayLanguage.toLowerCase();
			let displayLanguage = language!.toLowerCase();

			if (localeToCheck !== displayLanguage) {
				const a = displayLanguage.indexOf('-');
				const b = localeToCheck.indexOf('-');
				if (a > -1) {
					displayLanguage = displayLanguage.substr(0, a);
				}
				if (b > -1) {
					localeToCheck = localeToCheck.substr(0, b);
				}
				if (displayLanguage !== localeToCheck) {
					return Promise.resolve(ExperimentState.NoRun);
				}
			}
		}

		if (!condition.userProbability) {
			condition.userProbability = 1;
		}

		let extensionsCheckPromise = Promise.resolve(true);
		const installedExtensions = condition.installedExtensions;
		if (installedExtensions) {
			extensionsCheckPromise = this.extensionManagementService.getInstalled(ExtensionType.User).then(locals => {
				let includesCheck = true;
				let excludesCheck = true;
				const localExtensions = locals.map(local => `${local.manifest.publisher.toLowerCase()}.${local.manifest.name.toLowerCase()}`);
				if (Array.isArray(installedExtensions.includes) && installedExtensions.includes.length) {
					const extensionIncludes = installedExtensions.includes.map(e => e.toLowerCase());
					includesCheck = localExtensions.some(e => extensionIncludes.indexOf(e) > -1);
				}
				if (Array.isArray(installedExtensions.excludes) && installedExtensions.excludes.length) {
					const extensionExcludes = installedExtensions.excludes.map(e => e.toLowerCase());
					excludesCheck = !localExtensions.some(e => extensionExcludes.indexOf(e) > -1);
				}
				return includesCheck && excludesCheck;
			});
		}

		const storageKey = 'experiments.' + experiment.id;
		const experimentState: IExperimentStorageState = safeParse(this.storageService.get(storageKey, StorageScope.GLOBAL), {});

		return extensionsCheckPromise.then(success => {
			const fileEdits = condition.fileEdits;
			if (!success || !fileEdits || typeof fileEdits.minEditCount !== 'number') {
				const runExperiment = success && typeof condition.userProbability === 'number' && Math.random() < condition.userProbability;
				return runExperiment ? ExperimentState.Run : ExperimentState.NoRun;
			}

			experimentState.editCount = experimentState.editCount || 0;
			if (experimentState.editCount >= fileEdits.minEditCount) {
				return ExperimentState.Run;
			}

			// Process model-save event every 250ms to reduce load
			const onModelsSavedWorker = this._register(new RunOnceWorker<ITextFileEditorModel>(models => {
				const date = new Date().toDateString();
				const latestExperimentState: IExperimentStorageState = safeParse(this.storageService.get(storageKey, StorageScope.GLOBAL), {});
				if (latestExperimentState.state !== ExperimentState.Evaluating) {
					onSaveHandler.dispose();
					onModelsSavedWorker.dispose();
					return;
				}
				models.forEach(async model => {
					if (latestExperimentState.state !== ExperimentState.Evaluating
						|| date === latestExperimentState.lastEditedDate
						|| (typeof latestExperimentState.editCount === 'number' && latestExperimentState.editCount >= fileEdits.minEditCount)
					) {
						return;
					}
					let filePathCheck = true;
					let workspaceCheck = true;

					if (typeof fileEdits.filePathPattern === 'string') {
						filePathCheck = match(fileEdits.filePathPattern, model.resource.fsPath);
					}
					if (Array.isArray(fileEdits.workspaceIncludes) && fileEdits.workspaceIncludes.length) {
						const tags = await this.workspaceTagsService.getTags();
						workspaceCheck = !!tags && fileEdits.workspaceIncludes.some(x => !!tags[x]);
					}
					if (workspaceCheck && Array.isArray(fileEdits.workspaceExcludes) && fileEdits.workspaceExcludes.length) {
						const tags = await this.workspaceTagsService.getTags();
						workspaceCheck = !!tags && !fileEdits.workspaceExcludes.some(x => !!tags[x]);
					}
					if (filePathCheck && workspaceCheck) {
						latestExperimentState.editCount = (latestExperimentState.editCount || 0) + 1;
						latestExperimentState.lastEditedDate = date;
						this.storageService.store(storageKey, JSON.stringify(latestExperimentState), StorageScope.GLOBAL, StorageTarget.MACHINE);
					}
				});
				if (typeof latestExperimentState.editCount === 'number' && latestExperimentState.editCount >= fileEdits.minEditCount) {
					processedExperiment.state = latestExperimentState.state = (typeof condition.userProbability === 'number' && Math.random() < condition.userProbability && this.checkExperimentDependencies(experiment)) ? ExperimentState.Run : ExperimentState.NoRun;
					this.storageService.store(storageKey, JSON.stringify(latestExperimentState), StorageScope.GLOBAL, StorageTarget.MACHINE);
					if (latestExperimentState.state === ExperimentState.Run && processedExperiment.action && ExperimentActionType[processedExperiment.action.type] === ExperimentActionType.Prompt) {
						this.fireRunExperiment(processedExperiment);
					}
				}
			}, 250));

			const onSaveHandler = this._register(this.textFileService.files.onDidSave(e => onModelsSavedWorker.work(e.model)));
			return ExperimentState.Evaluating;
		});
	}