<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 { localize } from 'vs/nls';
import { basename } from 'vs/base/common/resources';
import { IDisposable, dispose, Disposable, DisposableStore, combinedDisposable, MutableDisposable } from 'vs/base/common/lifecycle';
import { Event } from 'vs/base/common/event';
import { VIEWLET_ID, ISCMService, ISCMRepository } from 'vs/workbench/contrib/scm/common/scm';
import { IActivityService, NumberBadge } from 'vs/workbench/services/activity/common/activity';
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
import { IStatusbarService, StatusbarAlignment as MainThreadStatusBarAlignment } from 'vs/platform/statusbar/common/statusbar';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { commonPrefixLength } from 'vs/base/common/strings';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';

function getCount(repository: ISCMRepository): number {
	if (typeof repository.provider.count === 'number') {
		return repository.provider.count;
	} else {
		return repository.provider.groups.elements.reduce<number>((r, g) => r + g.elements.length, 0);
	}
}

export class SCMStatusController implements IWorkbenchContribution {

	private statusBarDisposable: IDisposable = Disposable.None;
	private focusDisposable: IDisposable = Disposable.None;
	private focusedRepository: ISCMRepository | undefined = undefined;
	private focusedProviderContextKey: IContextKey<string | undefined>;
	private readonly badgeDisposable = new MutableDisposable<IDisposable>();
	private disposables: IDisposable[] = [];

	constructor(
		@ISCMService private readonly scmService: ISCMService,
		@IStatusbarService private readonly statusbarService: IStatusbarService,
		@IContextKeyService readonly contextKeyService: IContextKeyService,
		@IActivityService private readonly activityService: IActivityService,
		@IEditorService private readonly editorService: IEditorService,
		@IConfigurationService private readonly configurationService: IConfigurationService
	) {
		this.focusedProviderContextKey = contextKeyService.createKey<string | undefined>('scmProvider', undefined);
		this.scmService.onDidAddRepository(this.onDidAddRepository, this, this.disposables);

		const onDidChangeSCMCountBadge = Event.filter(configurationService.onDidChangeConfiguration, e => e.affectsConfiguration('scm.countBadge'));
		onDidChangeSCMCountBadge(this.renderActivityCount, this, this.disposables);

		for (const repository of this.scmService.repositories) {
			this.onDidAddRepository(repository);
		}

		editorService.onDidActiveEditorChange(this.onDidActiveEditorChange, this, this.disposables);
		this.renderActivityCount();
	}

	private onDidActiveEditorChange(): void {
		if (!this.editorService.activeEditor) {
			return;
		}

		const resource = this.editorService.activeEditor.getResource();

		if (!resource || resource.scheme !== 'file') {
			return;
		}

		let bestRepository: ISCMRepository | null = null;
		let bestMatchLength = Number.NEGATIVE_INFINITY;

		for (const repository of this.scmService.repositories) {
			const root = repository.provider.rootUri;

			if (!root) {
				continue;
			}

			const rootFSPath = root.fsPath;
			const prefixLength = commonPrefixLength(rootFSPath, resource.fsPath);

			if (prefixLength === rootFSPath.length && prefixLength > bestMatchLength) {
				bestRepository = repository;
				bestMatchLength = prefixLength;
			}
		}

		if (bestRepository) {
			this.onDidFocusRepository(bestRepository);
		}
	}

	private onDidAddRepository(repository: ISCMRepository): void {
		const focusDisposable = repository.onDidFocus(() => this.onDidFocusRepository(repository));

		const onDidChange = Event.any(repository.provider.onDidChange, repository.provider.onDidChangeResources);
		const changeDisposable = onDidChange(() => this.renderActivityCount());

		const onDidRemove = Event.filter(this.scmService.onDidRemoveRepository, e => e === repository);
		const removeDisposable = onDidRemove(() => {
			disposable.dispose();
			this.disposables = this.disposables.filter(d => d !== removeDisposable);

			if (this.scmService.repositories.length === 0) {
				this.onDidFocusRepository(undefined);
			} else if (this.focusedRepository === repository) {
				this.scmService.repositories[0].focus();
			}

			this.renderActivityCount();
		});

		const disposable = combinedDisposable(focusDisposable, changeDisposable, removeDisposable);
		this.disposables.push(disposable);

		if (!this.focusedRepository) {
			this.onDidFocusRepository(repository);
		}
	}

	private onDidFocusRepository(repository: ISCMRepository | undefined): void {
		if (this.focusedRepository === repository) {
			return;
		}

		this.focusedRepository = repository;
		this.focusedProviderContextKey.set(repository && repository.provider.id);
		this.focusDisposable.dispose();

		if (repository && repository.provider.onDidChangeStatusBarCommands) {
			this.focusDisposable = repository.provider.onDidChangeStatusBarCommands(() => this.renderStatusBar(repository));
		}

		this.renderStatusBar(repository);
		this.renderActivityCount();
	}

	private renderStatusBar(repository: ISCMRepository | undefined): void {
		this.statusBarDisposable.dispose();

		if (!repository) {
			return;
		}

		const commands = repository.provider.statusBarCommands || [];
		const label = repository.provider.rootUri<commit_msg>Focus correct SCM when restoring editors<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 { localize } from 'vs/nls';
import { basename } from 'vs/base/common/resources';
import { IDisposable, dispose, Disposable, DisposableStore, combinedDisposable, MutableDisposable } from 'vs/base/common/lifecycle';
import { Event } from 'vs/base/common/event';
import { VIEWLET_ID, ISCMService, ISCMRepository } from 'vs/workbench/contrib/scm/common/scm';
import { IActivityService, NumberBadge } from 'vs/workbench/services/activity/common/activity';
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
import { IStatusbarService, StatusbarAlignment as MainThreadStatusBarAlignment } from 'vs/platform/statusbar/common/statusbar';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { commonPrefixLength } from 'vs/base/common/strings';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';

function getCount(repository: ISCMRepository): number {
	if (typeof repository.provider.count === 'number') {
		return repository.provider.count;
	} else {
		return repository.provider.groups.elements.reduce<number>((r, g) => r + g.elements.length, 0);
	}
}

export class SCMStatusController implements IWorkbenchContribution {

	private statusBarDisposable: IDisposable = Disposable.None;
	private focusDisposable: IDisposable = Disposable.None;
	private focusedRepository: ISCMRepository | undefined = undefined;
	private focusedProviderContextKey: IContextKey<string | undefined>;
	private readonly badgeDisposable = new MutableDisposable<IDisposable>();
	private disposables: IDisposable[] = [];

	constructor(
		@ISCMService private readonly scmService: ISCMService,
		@IStatusbarService private readonly statusbarService: IStatusbarService,
		@IContextKeyService readonly contextKeyService: IContextKeyService,
		@IActivityService private readonly activityService: IActivityService,
		@IEditorService private readonly editorService: IEditorService,
		@IConfigurationService private readonly configurationService: IConfigurationService
	) {
		this.focusedProviderContextKey = contextKeyService.createKey<string | undefined>('scmProvider', undefined);
		this.scmService.onDidAddRepository(this.onDidAddRepository, this, this.disposables);

		const onDidChangeSCMCountBadge = Event.filter(configurationService.onDidChangeConfiguration, e => e.affectsConfiguration('scm.countBadge'));
		onDidChangeSCMCountBadge(this.renderActivityCount, this, this.disposables);

		for (const repository of this.scmService.repositories) {
			this.onDidAddRepository(repository);
		}

		editorService.onDidActiveEditorChange(this.onDidActiveEditorChange, this, this.disposables);
		this.renderActivityCount();
	}

	private onDidActiveEditorChange(): void {
		if (!this.editorService.activeEditor) {
			return;
		}

		const resource = this.editorService.activeEditor.getResource();

		if (!resource || resource.scheme !== 'file') {
			return;
		}

		let bestRepository: ISCMRepository | null = null;
		let bestMatchLength = Number.NEGATIVE_INFINITY;

		for (const repository of this.scmService.repositories) {
			const root = repository.provider.rootUri;

			if (!root) {
				continue;
			}

			const rootFSPath = root.fsPath;
			const prefixLength = commonPrefixLength(rootFSPath, resource.fsPath);

			if (prefixLength === rootFSPath.length && prefixLength > bestMatchLength) {
				bestRepository = repository;
				bestMatchLength = prefixLength;
			}
		}

		if (bestRepository) {
			this.onDidFocusRepository(bestRepository);
		}
	}

	private onDidAddRepository(repository: ISCMRepository): void {
		const focusDisposable = repository.onDidFocus(() => this.onDidFocusRepository(repository));

		const onDidChange = Event.any(repository.provider.onDidChange, repository.provider.onDidChangeResources);
		const changeDisposable = onDidChange(() => this.renderActivityCount());

		const onDidRemove = Event.filter(this.scmService.onDidRemoveRepository, e => e === repository);
		const removeDisposable = onDidRemove(() => {
			disposable.dispose();
			this.disposables = this.disposables.filter(d => d !== removeDisposable);

			if (this.scmService.repositories.length === 0) {
				this.onDidFocusRepository(undefined);
			} else if (this.focusedRepository === repository) {
				this.scmService.repositories[0].focus();
			}

			this.renderActivityCount();
		});

		const disposable = combinedDisposable(focusDisposable, changeDisposable, removeDisposable);
		this.disposables.push(disposable);

		if (!this.focusedRepository) {
			if (this.editorService.activeEditor) {
				this.onDidActiveEditorChange();
			} else {
				this.onDidFocusRepository(repository);
			}
		}
	}

	private onDidFocusRepository(repository: ISCMRepository | undefined): void {
		if (this.focusedRepository === repository) {
			return;
		}

		this.focusedRepository = repository;
		this.focusedProviderContextKey.set(repository && repository.provider.id);
		this.focusDisposable.dispose();

		if (repository && repository.provider.onDidChangeStatusBarCommands) {
			this.focusDisposable = repository.provider.onDidChangeStatusBarCommands(() => this.renderStatusBar(repository));
		}

		this.renderStatusBar(repository);
		this.renderActivityCount();
	}

	private renderStatusBar(repository: ISCMRepository | undefined): void {
		this.statusBarDisposable.dispose();

		if (!repository) {
			return;
		}

		const commands = repository.provider.statusBarCommands || [];
		const label = repository.provider.rootUri
{'fraction_non_alphanumeric': 0.13401101228293097, 'fraction_numerical': 0.0005082592121982211, 'mean_word_length': 4.6951278340569225, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 29, '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': '410406', 'n_tokens_mistral': 3542, 'n_tokens_neox': 3193, 'n_words': 652}