<commit_before>/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

import { Emitter, Event } from 'vs/base/common/event';
import * as network from 'vs/base/common/network';
import { basename } from 'vs/base/common/path';
import { isEqual, joinPath, extname } from 'vs/base/common/resources';
import { URI } from 'vs/base/common/uri';
import 'vs/css!./media/searchEditor';
import { Range } from 'vs/editor/common/core/range';
import { DefaultEndOfLine, ITextModel, TrackedRangeStickiness } from 'vs/editor/common/model';
import { IModelService } from 'vs/editor/common/services/modelService';
import { IModeService } from 'vs/editor/common/services/modeService';
import { localize } from 'vs/nls';
import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs';
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { EditorInput, GroupIdentifier, IEditorInput, IRevertOptions, ISaveOptions, IMoveResult } from 'vs/workbench/common/editor';
import { SearchEditorFindMatchClass, SearchEditorScheme, SearchEditorBodyScheme } from 'vs/workbench/contrib/searchEditor/browser/constants';
import { extractSearchQuery, serializeSearchConfiguration } from 'vs/workbench/contrib/searchEditor/browser/searchEditorSerialization';
import { IBackupFileService } from 'vs/workbench/services/backup/common/backup';
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { AutoSaveMode, IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService';
import { IRemotePathService } from 'vs/workbench/services/path/common/remotePathService';
import { ITextFileSaveOptions, ITextFileService, snapshotToString, stringToSnapshot } from 'vs/workbench/services/textfile/common/textfiles';
import { IWorkingCopy, IWorkingCopyBackup, IWorkingCopyService, WorkingCopyCapabilities } from 'vs/workbench/services/workingCopy/common/workingCopyService';


export type SearchConfiguration = {
	query: string,
	includes: string,
	excludes: string
	contextLines: number,
	wholeWord: boolean,
	caseSensitive: boolean,
	regexp: boolean,
	useIgnores: boolean,
	showIncludesExcludes: boolean,
};

const SEARCH_EDITOR_EXT = '.code-search';

export class SearchEditorInput extends EditorInput {
	static readonly ID: string = 'workbench.editorinputs.searchEditorInput';

	private dirty: boolean = false;
	private readonly contentsModel: Promise<ITextModel>;
	private readonly headerModel: Promise<ITextModel>;
	private _cachedContentsModel: ITextModel | undefined;
	private _cachedConfig?: SearchConfiguration;

	private readonly _onDidChangeContent = this._register(new Emitter<void>());
	readonly onDidChangeContent: Event<void> = this._onDidChangeContent.event;

	private oldDecorationsIDs: string[] = [];

	constructor(
		public readonly resource: URI,
		getModel: () => Promise<{ contentsModel: ITextModel, headerModel: ITextModel }>,
		@IModelService private readonly modelService: IModelService,
		@IEditorService protected readonly editorService: IEditorService,
		@IEditorGroupsService protected readonly editorGroupService: IEditorGroupsService,
		@ITextFileService protected readonly textFileService: ITextFileService,
		@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService,
		@IFileDialogService private readonly fileDialogService: IFileDialogService,
		@IInstantiationService private readonly instantiationService: IInstantiationService,
		@IWorkingCopyService private readonly workingCopyService: IWorkingCopyService,
		@IFilesConfigurationService private readonly filesConfigurationService: IFilesConfigurationService,
		@ITelemetryService private readonly telemetryService: ITelemetryService,
		@IModeService readonly modeService: IModeService,
		@IRemotePathService private readonly remotePathService: IRemotePathService
	) {
		super();

		// Dummy model to set file icon
		this._register(modelService.createModel('', modeService.create('search-result'), this.resource));

		const modelLoader = getModel()
			.then(({ contentsModel, headerModel }) => {
				this._register(contentsModel.onDidChangeContent(() => this._onDidChangeContent.fire()));
				this._register(headerModel.onDidChangeContent(() => {
					this._cachedConfig = extractSearchQuery(headerModel);
					this._onDidChangeContent.fire();
					this._onDidChangeLabel.fire();
				}));

				this._cachedConfig = extractSearchQuery(headerModel);
				this._cachedContentsModel = contentsModel;

				this._register(contentsModel);
				this._register(headerModel);
				this._onDidChangeLabel.fire();

				return { contentsModel, headerModel };
			});

		this.contentsModel = modelLoader.then(({ contentsModel }) => contentsModel);
		this.headerModel = modelLoader.then(({ headerModel }) => headerModel);


		const input = this;
		const workingCopyAdapter = new class implements IWorkingCopy {
			readonly resource = input.resource;
			get name() { return input.getName(); }
			readonly capabilities = input.isUntitled() ? WorkingCopyCapabilities.Untitled : 0;
			readonly onDidChangeDirty = input.onDidChangeDirty;
			readonly onDidChangeContent = input.onDidChangeContent;
			isDirty(): boolean { return input.isDirty(); }
			backup(): Promise<IWorkingCopyBackup> { return input.backup(); }
			save(options?: ISaveOptions): Promise<boolean> { return input.save(0, options).then(editor => !!editor); }
			revert(options?: IRevertOptions): Promise<void> { return input.revert(0, options); }
		};

		this.workingCopyService.registerWorkingCopy(workingCopyAdapter);
	}

	async save(group: GroupIdentifier, options?: ITextFileSaveOptions): Promise<IEditorInput | undefined> {
		if ((await this.headerModel).isDisposed() || (await this.contentsModel).isDisposed()) { return; }

		if (this.isUntitled()) {
			return this.saveAs(group, options);
		} else {
			await this.textFileService.write(this.resource, await this.serializeForDisk(), options);
			this.setDirty(false);
			return this;
		}
	}

	private async serializeForDisk() {<commit_msg>Add workaround for #93745<commit_after>/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

import { Emitter, Event } from 'vs/base/common/event';
import * as network from 'vs/base/common/network';
import { basename } from 'vs/base/common/path';
import { isEqual, joinPath, extname } from 'vs/base/common/resources';
import { URI } from 'vs/base/common/uri';
import 'vs/css!./media/searchEditor';
import { Range } from 'vs/editor/common/core/range';
import { DefaultEndOfLine, ITextModel, TrackedRangeStickiness } from 'vs/editor/common/model';
import { IModelService } from 'vs/editor/common/services/modelService';
import { IModeService } from 'vs/editor/common/services/modeService';
import { localize } from 'vs/nls';
import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs';
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { EditorInput, GroupIdentifier, IEditorInput, IRevertOptions, ISaveOptions, IMoveResult } from 'vs/workbench/common/editor';
import { SearchEditorFindMatchClass, SearchEditorScheme, SearchEditorBodyScheme } from 'vs/workbench/contrib/searchEditor/browser/constants';
import { extractSearchQuery, serializeSearchConfiguration } from 'vs/workbench/contrib/searchEditor/browser/searchEditorSerialization';
import { IBackupFileService } from 'vs/workbench/services/backup/common/backup';
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { AutoSaveMode, IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService';
import { IRemotePathService } from 'vs/workbench/services/path/common/remotePathService';
import { ITextFileSaveOptions, ITextFileService, snapshotToString, stringToSnapshot } from 'vs/workbench/services/textfile/common/textfiles';
import { IWorkingCopy, IWorkingCopyBackup, IWorkingCopyService, WorkingCopyCapabilities } from 'vs/workbench/services/workingCopy/common/workingCopyService';


export type SearchConfiguration = {
	query: string,
	includes: string,
	excludes: string
	contextLines: number,
	wholeWord: boolean,
	caseSensitive: boolean,
	regexp: boolean,
	useIgnores: boolean,
	showIncludesExcludes: boolean,
};

const SEARCH_EDITOR_EXT = '.code-search';

export class SearchEditorInput extends EditorInput {
	static readonly ID: string = 'workbench.editorinputs.searchEditorInput';

	private dirty: boolean = false;
	private readonly contentsModel: Promise<ITextModel>;
	private readonly headerModel: Promise<ITextModel>;
	private _cachedContentsModel: ITextModel | undefined;
	private _cachedConfig?: SearchConfiguration;

	private readonly _onDidChangeContent = this._register(new Emitter<void>());
	readonly onDidChangeContent: Event<void> = this._onDidChangeContent.event;

	private oldDecorationsIDs: string[] = [];

	constructor(
		public readonly resource: URI,
		getModel: () => Promise<{ contentsModel: ITextModel, headerModel: ITextModel }>,
		@IModelService private readonly modelService: IModelService,
		@IEditorService protected readonly editorService: IEditorService,
		@IEditorGroupsService protected readonly editorGroupService: IEditorGroupsService,
		@ITextFileService protected readonly textFileService: ITextFileService,
		@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService,
		@IFileDialogService private readonly fileDialogService: IFileDialogService,
		@IInstantiationService private readonly instantiationService: IInstantiationService,
		@IWorkingCopyService private readonly workingCopyService: IWorkingCopyService,
		@IFilesConfigurationService private readonly filesConfigurationService: IFilesConfigurationService,
		@ITelemetryService private readonly telemetryService: ITelemetryService,
		@IModeService readonly modeService: IModeService,
		@IRemotePathService private readonly remotePathService: IRemotePathService
	) {
		super();

		// Dummy model to set file icon
		this._register(modelService.createModel('', modeService.create('search-result'), this.resource));

		const modelLoader = getModel()
			.then(({ contentsModel, headerModel }) => {
				this._register(contentsModel.onDidChangeContent(() => this._onDidChangeContent.fire()));
				this._register(headerModel.onDidChangeContent(() => {
					this._cachedConfig = extractSearchQuery(headerModel);
					this._onDidChangeContent.fire();
					this._onDidChangeLabel.fire();
				}));

				this._cachedConfig = extractSearchQuery(headerModel);
				this._cachedContentsModel = contentsModel;

				this._register(contentsModel);
				this._register(headerModel);
				this._onDidChangeLabel.fire();

				return { contentsModel, headerModel };
			});

		this.contentsModel = modelLoader.then(({ contentsModel }) => contentsModel);
		this.headerModel = modelLoader.then(({ headerModel }) => headerModel);

		const input = this;
		const workingCopyAdapter = new class implements IWorkingCopy {
			readonly resource = input.resource;
			get name() { return input.getName(); }
			readonly capabilities = input.isUntitled() ? WorkingCopyCapabilities.Untitled : 0;
			readonly onDidChangeDirty = input.onDidChangeDirty;
			readonly onDidChangeContent = input.onDidChangeContent;
			isDirty(): boolean { return input.isDirty(); }
			backup(): Promise<IWorkingCopyBackup> { return input.backup(); }
			save(options?: ISaveOptions): Promise<boolean> { return input.save(0, options).then(editor => !!editor); }
			revert(options?: IRevertOptions): Promise<void> { return input.revert(0, options); }
		};

		this._register(this.workingCopyService.registerWorkingCopy(workingCopyAdapter));
	}

	async save(group: GroupIdentifier, options?: ITextFileSaveOptions): Promise<IEditorInput | undefined> {
		if ((await this.headerModel).isDisposed() || (await this.contentsModel).isDisposed()) { return; }

		if (this.isUntitled()) {
			return this.saveAs(group, options);
		} else {
			await this.textFileService.write(this.resource, await this.serializeForDisk(), options);
			this.setDirty(false);
			return this;
		}
	}

	private async serializeForDisk() {
{'fraction_non_alphanumeric': 0.12958930850664463, 'fraction_numerical': 0.0008258878294166229, 'mean_word_length': 5.206896551724138, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 35, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7590149', 'n_tokens_mistral': 3767, 'n_tokens_neox': 3504, 'n_words': 797}