<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 * as errors from 'vs/base/common/errors';
import Event, { Emitter } from 'vs/base/common/event';
import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle';
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
import { IPartService } from 'vs/workbench/services/part/common/partService';
import { ITerminalService, ITerminalInstance, IShellLaunchConfig, ITerminalConfigHelper, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_VISIBLE, TERMINAL_PANEL_ID, ITerminalTab } from 'vs/workbench/parts/terminal/common/terminal';
import { TPromise } from 'vs/base/common/winjs.base';

export abstract class TerminalService implements ITerminalService {
	public _serviceBrand: any;

	protected _isShuttingDown: boolean;
	protected _terminalFocusContextKey: IContextKey<boolean>;
	protected _findWidgetVisible: IContextKey<boolean>;
	protected _terminalContainer: HTMLElement;
	protected _onInstancesChanged: Emitter<void>;
	protected _onTabDisposed: Emitter<ITerminalTab>;
	protected _onInstanceDisposed: Emitter<ITerminalInstance>;
	protected _onInstanceProcessIdReady: Emitter<ITerminalInstance>;
	protected _onInstanceTitleChanged: Emitter<string>;
	protected _terminalTabs: ITerminalTab[];
	protected abstract _terminalInstances: ITerminalInstance[];

	private _activeTabIndex: number;
	private _onActiveTabChanged: Emitter<void>;

	public get activeTabIndex(): number { return this._activeTabIndex; }
	public get onActiveTabChanged(): Event<void> { return this._onActiveTabChanged.event; }
	public get onTabDisposed(): Event<ITerminalTab> { return this._onTabDisposed.event; }
	public get onInstanceDisposed(): Event<ITerminalInstance> { return this._onInstanceDisposed.event; }
	public get onInstanceProcessIdReady(): Event<ITerminalInstance> { return this._onInstanceProcessIdReady.event; }
	public get onInstanceTitleChanged(): Event<string> { return this._onInstanceTitleChanged.event; }
	public get onInstancesChanged(): Event<void> { return this._onInstancesChanged.event; }
	public get terminalInstances(): ITerminalInstance[] { return this._terminalInstances; }
	public get terminalTabs(): ITerminalTab[] { return this._terminalTabs; }

	public abstract get configHelper(): ITerminalConfigHelper;

	constructor(
		@IContextKeyService private _contextKeyService: IContextKeyService,
		@IPanelService protected _panelService: IPanelService,
		@IPartService private _partService: IPartService,
		@ILifecycleService lifecycleService: ILifecycleService
	) {
		this._activeTabIndex = 0;
		this._isShuttingDown = false;

		this._onActiveTabChanged = new Emitter<void>();
		this._onTabDisposed = new Emitter<ITerminalTab>();
		this._onInstanceDisposed = new Emitter<ITerminalInstance>();
		this._onInstanceProcessIdReady = new Emitter<ITerminalInstance>();
		this._onInstanceTitleChanged = new Emitter<string>();
		this._onInstancesChanged = new Emitter<void>();

		lifecycleService.onWillShutdown(event => event.veto(this._onWillShutdown()));
		lifecycleService.onShutdown(() => this._onShutdown());
		this._terminalFocusContextKey = KEYBINDING_CONTEXT_TERMINAL_FOCUS.bindTo(this._contextKeyService);
		this._findWidgetVisible = KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_VISIBLE.bindTo(this._contextKeyService);
		this.onTabDisposed(tab => this._removeTab(tab));
	}

	protected abstract _showTerminalCloseConfirmation(): TPromise<boolean>;
	public abstract createInstance(shell?: IShellLaunchConfig, wasNewTerminalAction?: boolean): ITerminalInstance;
	public abstract getActiveOrCreateInstance(wasNewTerminalAction?: boolean): ITerminalInstance;
	public abstract selectDefaultWindowsShell(): TPromise<string>;
	public abstract setContainers(panelContainer: HTMLElement, terminalContainer: HTMLElement): void;

	private _onWillShutdown(): boolean | TPromise<boolean> {
		if (this.terminalInstances.length === 0) {
			// No terminal instances, don't veto
			return false;
		}

		if (this.configHelper.config.confirmOnExit) {
			// veto if configured to show confirmation and the user choosed not to exit
			return this._showTerminalCloseConfirmation().then(veto => {
				if (!veto) {
					this._isShuttingDown = true;
				}
				return veto;
			});
		}

		this._isShuttingDown = true;

		return false;
	}

	private _onShutdown(): void {
		this.terminalInstances.forEach(instance => instance.dispose());
	}

	public getTabLabels(): string[] {
		console.log('tabs', this._terminalTabs);
		return this._terminalTabs.map((tab, index) => `${index + 1}: ${tab.title}`);
	}

	private _removeTab(tab: ITerminalTab): void {
		console.log('_removeTab');
		// Get the index of the tab and remove it from the list
		const index = this._terminalTabs.indexOf(tab);
		const wasActiveTab = tab === this.getActiveTab();
		if (index !== -1) {
			this._terminalTabs.splice(index, 1);
		}

		// Adjust focus if the tab was active
		if (wasActiveTab && this._terminalTabs.length > 0) {
			let newIndex = index < this._terminalTabs.length ? index : this._terminalTabs.length - 1;
			this.setActiveInstanceByIndex(newIndex);
			// TODO: Needs to be made to work with multiple instances in a tab
			if (tab.terminalInstances[0].hadFocusOnExit) {
				this.getActiveInstance().focus(true);
			}
		}
		// Hide the panel if there are no more instances, provided that VS Code is not shutting
		// down. When shutting down the panel is locked in place so that it is restored upon next
		// launch.
		if (this._terminalTabs.length === 0 && !this._isShuttingDown) {<commit_msg>Only refocus after removing tab if the tab had focus<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 * as errors from 'vs/base/common/errors';
import Event, { Emitter } from 'vs/base/common/event';
import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle';
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
import { IPartService } from 'vs/workbench/services/part/common/partService';
import { ITerminalService, ITerminalInstance, IShellLaunchConfig, ITerminalConfigHelper, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_VISIBLE, TERMINAL_PANEL_ID, ITerminalTab } from 'vs/workbench/parts/terminal/common/terminal';
import { TPromise } from 'vs/base/common/winjs.base';

export abstract class TerminalService implements ITerminalService {
	public _serviceBrand: any;

	protected _isShuttingDown: boolean;
	protected _terminalFocusContextKey: IContextKey<boolean>;
	protected _findWidgetVisible: IContextKey<boolean>;
	protected _terminalContainer: HTMLElement;
	protected _onInstancesChanged: Emitter<void>;
	protected _onTabDisposed: Emitter<ITerminalTab>;
	protected _onInstanceDisposed: Emitter<ITerminalInstance>;
	protected _onInstanceProcessIdReady: Emitter<ITerminalInstance>;
	protected _onInstanceTitleChanged: Emitter<string>;
	protected _terminalTabs: ITerminalTab[];
	protected abstract _terminalInstances: ITerminalInstance[];

	private _activeTabIndex: number;
	private _onActiveTabChanged: Emitter<void>;

	public get activeTabIndex(): number { return this._activeTabIndex; }
	public get onActiveTabChanged(): Event<void> { return this._onActiveTabChanged.event; }
	public get onTabDisposed(): Event<ITerminalTab> { return this._onTabDisposed.event; }
	public get onInstanceDisposed(): Event<ITerminalInstance> { return this._onInstanceDisposed.event; }
	public get onInstanceProcessIdReady(): Event<ITerminalInstance> { return this._onInstanceProcessIdReady.event; }
	public get onInstanceTitleChanged(): Event<string> { return this._onInstanceTitleChanged.event; }
	public get onInstancesChanged(): Event<void> { return this._onInstancesChanged.event; }
	public get terminalInstances(): ITerminalInstance[] { return this._terminalInstances; }
	public get terminalTabs(): ITerminalTab[] { return this._terminalTabs; }

	public abstract get configHelper(): ITerminalConfigHelper;

	constructor(
		@IContextKeyService private _contextKeyService: IContextKeyService,
		@IPanelService protected _panelService: IPanelService,
		@IPartService private _partService: IPartService,
		@ILifecycleService lifecycleService: ILifecycleService
	) {
		this._activeTabIndex = 0;
		this._isShuttingDown = false;

		this._onActiveTabChanged = new Emitter<void>();
		this._onTabDisposed = new Emitter<ITerminalTab>();
		this._onInstanceDisposed = new Emitter<ITerminalInstance>();
		this._onInstanceProcessIdReady = new Emitter<ITerminalInstance>();
		this._onInstanceTitleChanged = new Emitter<string>();
		this._onInstancesChanged = new Emitter<void>();

		lifecycleService.onWillShutdown(event => event.veto(this._onWillShutdown()));
		lifecycleService.onShutdown(() => this._onShutdown());
		this._terminalFocusContextKey = KEYBINDING_CONTEXT_TERMINAL_FOCUS.bindTo(this._contextKeyService);
		this._findWidgetVisible = KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_VISIBLE.bindTo(this._contextKeyService);
		this.onTabDisposed(tab => this._removeTab(tab));
	}

	protected abstract _showTerminalCloseConfirmation(): TPromise<boolean>;
	public abstract createInstance(shell?: IShellLaunchConfig, wasNewTerminalAction?: boolean): ITerminalInstance;
	public abstract getActiveOrCreateInstance(wasNewTerminalAction?: boolean): ITerminalInstance;
	public abstract selectDefaultWindowsShell(): TPromise<string>;
	public abstract setContainers(panelContainer: HTMLElement, terminalContainer: HTMLElement): void;

	private _onWillShutdown(): boolean | TPromise<boolean> {
		if (this.terminalInstances.length === 0) {
			// No terminal instances, don't veto
			return false;
		}

		if (this.configHelper.config.confirmOnExit) {
			// veto if configured to show confirmation and the user choosed not to exit
			return this._showTerminalCloseConfirmation().then(veto => {
				if (!veto) {
					this._isShuttingDown = true;
				}
				return veto;
			});
		}

		this._isShuttingDown = true;

		return false;
	}

	private _onShutdown(): void {
		this.terminalInstances.forEach(instance => instance.dispose());
	}

	public getTabLabels(): string[] {
		console.log('tabs', this._terminalTabs);
		return this._terminalTabs.map((tab, index) => `${index + 1}: ${tab.title}`);
	}

	private _removeTab(tab: ITerminalTab): void {
		console.log('_removeTab');
		// Get the index of the tab and remove it from the list
		const index = this._terminalTabs.indexOf(tab);
		const wasActiveTab = tab === this.getActiveTab();
		if (index !== -1) {
			this._terminalTabs.splice(index, 1);
		}

		// Adjust focus if the tab was active
		if (wasActiveTab && this._terminalTabs.length > 0) {
			const hasFocusOnExit = tab.activeInstance.hadFocusOnExit;
			let newIndex = index < this._terminalTabs.length ? index : this._terminalTabs.length - 1;
			this.setActiveInstanceByIndex(newIndex);
			if (hasFocusOnExit) {
				this.getActiveInstance().focus(true);
			}
		}
		// Hide the panel if there are no more instances, provided that VS Code is not shutting
		// down. When shutting down the panel is locked in place so that it is restored upon next
		// launch.
		if (this._terminalTabs.length === 0 && !this._isShuttingDown) {
{'fraction_non_alphanumeric': 0.12942357567137958, 'fraction_numerical': 0.0014222370952898854, 'mean_word_length': 4.859803921568627, 'pattern_counts': {'":': 0, '<': 51, '<?xml version=': 0, '>': 63, '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': '5009356', 'n_tokens_mistral': 3668, 'n_tokens_neox': 3406, 'n_words': 811}