<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 'vs/css!./parameterHints';
import * as nls from 'vs/nls';
import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle';
import * as dom from 'vs/base/browser/dom';
import * as aria from 'vs/base/browser/ui/aria/aria';
import * as modes from 'vs/editor/common/modes';
import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IContentWidgetPosition } from 'vs/editor/browser/editorBrowser';
import { RunOnceScheduler, createCancelablePromise, CancelablePromise } from 'vs/base/common/async';
import { onUnexpectedError } from 'vs/base/common/errors';
import { Event, Emitter, chain } from 'vs/base/common/event';
import { domEvent, stop } from 'vs/base/browser/event';
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { Context, provideSignatureHelp } from 'vs/editor/contrib/parameterHints/provideSignatureHelp';
import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
import { CharacterSet } from 'vs/editor/common/core/characterClassifier';
import { IConfigurationChangedEvent } from 'vs/editor/common/config/editorOptions';
import { ICursorSelectionChangedEvent } from 'vs/editor/common/controller/cursorEvents';
import { registerThemingParticipant, HIGH_CONTRAST } from 'vs/platform/theme/common/themeService';
import { editorHoverBackground, editorHoverBorder, textLinkForeground, textCodeBlockBackground } from 'vs/platform/theme/common/colorRegistry';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { IModeService } from 'vs/editor/common/services/modeService';
import { MarkdownRenderer } from 'vs/editor/contrib/markdown/markdownRenderer';

const $ = dom.$;

export interface TriggerContext {
	readonly triggerReason: modes.SignatureHelpTriggerReason;
	readonly triggerCharacter?: string;
}

export interface IHintEvent {
	hints: modes.SignatureHelp;
}

export class ParameterHintsModel extends Disposable {

	private static readonly DEFAULT_DELAY = 120; // ms

	private readonly _onHint = this._register(new Emitter<IHintEvent>());
	public readonly onHint: Event<IHintEvent> = this._onHint.event;

	private readonly _onCancel = this._register(new Emitter<void>());
	public readonly onCancel: Event<void> = this._onCancel.event;

	private editor: ICodeEditor;
	private enabled: boolean;
	private triggerCharactersListeners: IDisposable[];
	private active: boolean = false;
	private pending: boolean = false;
	private triggerChars = new CharacterSet();
	private retriggerChars = new CharacterSet();

	private triggerContext: modes.SignatureHelpContext | undefined;
	private throttledDelayer: RunOnceScheduler;
	private provideSignatureHelpRequest?: CancelablePromise<modes.SignatureHelp | null | undefined>;

	constructor(
		editor: ICodeEditor,
		delay: number = ParameterHintsModel.DEFAULT_DELAY
	) {
		super();

		this.editor = editor;
		this.enabled = false;
		this.triggerCharactersListeners = [];

		this.throttledDelayer = new RunOnceScheduler(() => this.doTrigger(), delay);

		this._register(this.editor.onDidChangeConfiguration(() => this.onEditorConfigurationChange()));
		this._register(this.editor.onDidChangeModel(e => this.onModelChanged()));
		this._register(this.editor.onDidChangeModelLanguage(_ => this.onModelChanged()));
		this._register(this.editor.onDidChangeCursorSelection(e => this.onCursorChange(e)));
		this._register(this.editor.onDidChangeModelContent(e => this.onModelContentChange()));
		this._register(modes.SignatureHelpProviderRegistry.onDidChange(this.onModelChanged, this));
		this._register(this.editor.onDidType(text => this.onDidType(text)));

		this.onEditorConfigurationChange();
		this.onModelChanged();
	}

	cancel(silent: boolean = false): void {
		this.active = false;
		this.pending = false;
		this.triggerContext = undefined;

		this.throttledDelayer.cancel();

		if (!silent) {
			this._onCancel.fire(void 0);
		}

		if (this.provideSignatureHelpRequest) {
			this.provideSignatureHelpRequest.cancel();
			this.provideSignatureHelpRequest = undefined;
		}
	}

	trigger(context: TriggerContext, delay?: number): void {
		let model = this.editor.getModel();
		if (model === null || !modes.SignatureHelpProviderRegistry.has(model)) {
			return;
		}

		const wasTriggered = this.isTriggered;
		this.cancel(true);

		this.triggerContext = {
			triggerReason: context.triggerReason,
			triggerCharacter: context.triggerCharacter,
			isRetrigger: wasTriggered
		};

		return this.throttledDelayer.schedule(delay);
	}

	private doTrigger(): void {
		if (this.provideSignatureHelpRequest) {
			this.provideSignatureHelpRequest.cancel();
		}

		if (!this.editor.hasModel()) {
			return;
		}

		const model = this.editor.getModel();
		const position = this.editor.getPosition();

		this.pending = true;

		const triggerContext = this.triggerContext || { triggerReason: modes.SignatureHelpTriggerReason.Invoke, isRetrigger: false };
		this.triggerContext = undefined;

		this.provideSignatureHelpRequest = createCancelablePromise(token =>
			provideSignatureHelp(model!, position!, triggerContext, token));

		this.provideSignatureHelpRequest.then(result => {
			this.pending = false;

			if (!result || !result.signatures || result.signatures.length === 0) {
				this.cancel();
				this._onCancel.fire(void 0);
				return false;
			}

			this.active = true;
			const event: IHintEvent = { hints: result };
			this._onHint.fire(event);
			return true;

		}).catch(error => {
			this.pending = false;
			onUnexpectedError(error);
		});
	}

	private get isTriggered(): boolean {
		return this.active || this.pending || this.throttledDelayer.isScheduled();
	}

	private onModelChanged(): void {
		this.cancel();<commit_msg>Use delayer for paramter hints trigger<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 'vs/css!./parameterHints';
import * as nls from 'vs/nls';
import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle';
import * as dom from 'vs/base/browser/dom';
import * as aria from 'vs/base/browser/ui/aria/aria';
import * as modes from 'vs/editor/common/modes';
import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IContentWidgetPosition } from 'vs/editor/browser/editorBrowser';
import { createCancelablePromise, CancelablePromise, Delayer } from 'vs/base/common/async';
import { onUnexpectedError } from 'vs/base/common/errors';
import { Event, Emitter, chain } from 'vs/base/common/event';
import { domEvent, stop } from 'vs/base/browser/event';
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { Context, provideSignatureHelp } from 'vs/editor/contrib/parameterHints/provideSignatureHelp';
import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
import { CharacterSet } from 'vs/editor/common/core/characterClassifier';
import { IConfigurationChangedEvent } from 'vs/editor/common/config/editorOptions';
import { ICursorSelectionChangedEvent } from 'vs/editor/common/controller/cursorEvents';
import { registerThemingParticipant, HIGH_CONTRAST } from 'vs/platform/theme/common/themeService';
import { editorHoverBackground, editorHoverBorder, textLinkForeground, textCodeBlockBackground } from 'vs/platform/theme/common/colorRegistry';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { IModeService } from 'vs/editor/common/services/modeService';
import { MarkdownRenderer } from 'vs/editor/contrib/markdown/markdownRenderer';

const $ = dom.$;

export interface TriggerContext {
	readonly triggerReason: modes.SignatureHelpTriggerReason;
	readonly triggerCharacter?: string;
}

export interface IHintEvent {
	hints: modes.SignatureHelp;
}

export class ParameterHintsModel extends Disposable {

	private static readonly DEFAULT_DELAY = 120; // ms

	private readonly _onHint = this._register(new Emitter<IHintEvent>());
	public readonly onHint: Event<IHintEvent> = this._onHint.event;

	private readonly _onCancel = this._register(new Emitter<void>());
	public readonly onCancel: Event<void> = this._onCancel.event;

	private editor: ICodeEditor;
	private enabled: boolean;
	private triggerCharactersListeners: IDisposable[];
	private active: boolean = false;
	private pending: boolean = false;
	private triggerChars = new CharacterSet();
	private retriggerChars = new CharacterSet();

	private throttledDelayer: Delayer<boolean>;
	private provideSignatureHelpRequest?: CancelablePromise<modes.SignatureHelp | null | undefined>;

	constructor(
		editor: ICodeEditor,
		delay: number = ParameterHintsModel.DEFAULT_DELAY
	) {
		super();

		this.editor = editor;
		this.enabled = false;
		this.triggerCharactersListeners = [];

		this.throttledDelayer = new Delayer(delay);

		this._register(this.editor.onDidChangeConfiguration(() => this.onEditorConfigurationChange()));
		this._register(this.editor.onDidChangeModel(e => this.onModelChanged()));
		this._register(this.editor.onDidChangeModelLanguage(_ => this.onModelChanged()));
		this._register(this.editor.onDidChangeCursorSelection(e => this.onCursorChange(e)));
		this._register(this.editor.onDidChangeModelContent(e => this.onModelContentChange()));
		this._register(modes.SignatureHelpProviderRegistry.onDidChange(this.onModelChanged, this));
		this._register(this.editor.onDidType(text => this.onDidType(text)));

		this.onEditorConfigurationChange();
		this.onModelChanged();
	}

	cancel(silent: boolean = false): void {
		this.active = false;
		this.pending = false;

		this.throttledDelayer.cancel();

		if (!silent) {
			this._onCancel.fire(void 0);
		}

		if (this.provideSignatureHelpRequest) {
			this.provideSignatureHelpRequest.cancel();
			this.provideSignatureHelpRequest = undefined;
		}
	}

	trigger(context: TriggerContext, delay?: number): void {
		let model = this.editor.getModel();
		if (model === null || !modes.SignatureHelpProviderRegistry.has(model)) {
			return;
		}

		const wasTriggered = this.isTriggered;
		this.cancel(true);

		this.throttledDelayer.trigger(
			() => this.doTrigger({
				triggerReason: context.triggerReason,
				triggerCharacter: context.triggerCharacter,
				isRetrigger: wasTriggered
			}), delay).then(undefined, onUnexpectedError);
	}

	private doTrigger(triggerContext: modes.SignatureHelpContext): Promise<boolean> {
		if (this.provideSignatureHelpRequest) {
			this.provideSignatureHelpRequest.cancel();
		}

		if (!this.editor.hasModel()) {
			return Promise.resolve(false);
		}

		const model = this.editor.getModel();
		const position = this.editor.getPosition();

		this.pending = true;

		this.provideSignatureHelpRequest = createCancelablePromise(token =>
			provideSignatureHelp(model!, position!, triggerContext, token));

		return this.provideSignatureHelpRequest.then(result => {
			this.pending = false;

			if (!result || !result.signatures || result.signatures.length === 0) {
				this.cancel();
				this._onCancel.fire(void 0);
				return false;
			}

			this.active = true;
			const event: IHintEvent = { hints: result };
			this._onHint.fire(event);
			return true;

		}).catch(error => {
			this.pending = false;
			onUnexpectedError(error);
			return false;
		});
	}

	private get isTriggered(): boolean {
		return this.active || this.pending || this.throttledDelayer.isTriggered();
	}

	private onModelChanged(): void {
		this.cancel();
{'fraction_non_alphanumeric': 0.14620126624458513, 'fraction_numerical': 0.000999666777740753, 'mean_word_length': 4.476733576642336, 'pattern_counts': {'":': 0, '<': 15, '<?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': '2559098', 'n_tokens_mistral': 3649, 'n_tokens_neox': 3338, 'n_words': 717}