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

import { alert } from 'vs/base/browser/ui/aria/aria';
import { isFalsyOrEmpty } from 'vs/base/common/arrays';
import { onUnexpectedError } from 'vs/base/common/errors';
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { EditorAction, EditorCommand, registerEditorAction, registerEditorCommand, registerEditorContribution, ServicesAccessor } from 'vs/editor/browser/editorExtensions';
import { EditOperation } from 'vs/editor/common/core/editOperation';
import { Range } from 'vs/editor/common/core/range';
import { IEditorContribution, ScrollType } from 'vs/editor/common/editorCommon';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { ISuggestSupport, ISuggestion } from 'vs/editor/common/modes';
import { SnippetController2 } from 'vs/editor/contrib/snippet/snippetController2';
import { SnippetParser } from 'vs/editor/contrib/snippet/snippetParser';
import { SuggestMemories } from 'vs/editor/contrib/suggest/suggestMemory';
import * as nls from 'vs/nls';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { ContextKeyExpr, IContextKeyService, IContextKey, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { ICompletionItem, CompletionModel } from './completionModel';
import { Context as SuggestContext } from './suggest';
import { State, SuggestModel } from './suggestModel';
import { ISelectedSuggestion, SuggestWidget } from './suggestWidget';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';

class AcceptOnCharacterOracle {

	private _disposables: IDisposable[] = [];

	private _activeAcceptCharacters = new Set<string>();
	private _activeItem: ISelectedSuggestion;

	constructor(editor: ICodeEditor, widget: SuggestWidget, accept: (selected: ISelectedSuggestion) => any) {

		this._disposables.push(widget.onDidShow(() => this._onItem(widget.getFocusedItem())));
		this._disposables.push(widget.onDidFocus(this._onItem, this));
		this._disposables.push(widget.onDidHide(this.reset, this));

		this._disposables.push(editor.onWillType(text => {
			if (this._activeItem) {
				const ch = text[text.length - 1];
				if (this._activeAcceptCharacters.has(ch) && editor.getConfiguration().contribInfo.acceptSuggestionOnCommitCharacter) {
					accept(this._activeItem);
				}
			}
		}));
	}

	private _onItem(selected: ISelectedSuggestion): void {
		if (!selected || isFalsyOrEmpty(selected.item.suggestion.commitCharacters)) {
			this.reset();
			return;
		}
		this._activeItem = selected;
		this._activeAcceptCharacters.clear();
		for (const ch of selected.item.suggestion.commitCharacters) {
			if (ch.length > 0) {
				this._activeAcceptCharacters.add(ch[0]);
			}
		}
	}

	reset(): void {
		this._activeItem = undefined;
	}

	dispose() {
		dispose(this._disposables);
	}
}

class SuggestAlternatives {

	static OtherSuggestions = new RawContextKey<boolean>('hasOtherSuggestions', false);

	private readonly _ckOtherSuggestions: IContextKey<boolean>;

	private _index: number;
	private _model: CompletionModel;
	private _listener: IDisposable;
	private _ignore: boolean;

	constructor(
		private readonly _editor: ICodeEditor,
		private readonly _accept: (selected: ISelectedSuggestion) => any,
		@IContextKeyService contextKeyService: IContextKeyService,
	) {
		this._ckOtherSuggestions = SuggestAlternatives.OtherSuggestions.bindTo(contextKeyService);
	}

	reset(): void {
		this._ckOtherSuggestions.set(false);
		dispose(this._listener);
		this._model = undefined;
		this._ignore = false;
	}

	set(event: ISelectedSuggestion): void {
		// todo@joh filter snippets and so
		if (event.model.items.length > 1) {
			this._model = event.model;
			this._index = event.index;
			this._listener = this._editor.onDidChangeCursorPosition(() => this._onCursorMove());
			this._ckOtherSuggestions.set(true);
		} else {
			this.reset();
		}
	}

	private _onCursorMove(): void {
		if (!this._ignore) {
			this.reset();
		}
	}

	next(): void {
		this._move(true);
	}

	prev(): void {
		this._move(false);
	}

	private _move(fwd: boolean): void {
		if (this._model) {
			try {
				this._ignore = true;
				this._index = (this._index + this._model.items.length + (fwd ? 1 : -1)) % this._model.items.length;
				this._accept({ index: this._index, item: this._model.items[this._index], model: this._model });
			} finally {
				this._ignore = false;
			}
		}
	}

}

export class SuggestController implements IEditorContribution {

	private static readonly ID: string = 'editor.contrib.suggestController';

	public static get(editor: ICodeEditor): SuggestController {
		return editor.getContribution<SuggestController>(SuggestController.ID);
	}
<commit_msg>skip snippets when cycling through other suggestions<commit_after>/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/
'use strict';

import { alert } from 'vs/base/browser/ui/aria/aria';
import { isFalsyOrEmpty } from 'vs/base/common/arrays';
import { onUnexpectedError } from 'vs/base/common/errors';
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { EditorAction, EditorCommand, registerEditorAction, registerEditorCommand, registerEditorContribution, ServicesAccessor } from 'vs/editor/browser/editorExtensions';
import { EditOperation } from 'vs/editor/common/core/editOperation';
import { Range } from 'vs/editor/common/core/range';
import { IEditorContribution, ScrollType } from 'vs/editor/common/editorCommon';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { ISuggestSupport, ISuggestion } from 'vs/editor/common/modes';
import { SnippetController2 } from 'vs/editor/contrib/snippet/snippetController2';
import { SnippetParser } from 'vs/editor/contrib/snippet/snippetParser';
import { SuggestMemories } from 'vs/editor/contrib/suggest/suggestMemory';
import * as nls from 'vs/nls';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { ContextKeyExpr, IContextKeyService, IContextKey, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { ICompletionItem, CompletionModel } from './completionModel';
import { Context as SuggestContext } from './suggest';
import { State, SuggestModel } from './suggestModel';
import { ISelectedSuggestion, SuggestWidget } from './suggestWidget';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';

class AcceptOnCharacterOracle {

	private _disposables: IDisposable[] = [];

	private _activeAcceptCharacters = new Set<string>();
	private _activeItem: ISelectedSuggestion;

	constructor(editor: ICodeEditor, widget: SuggestWidget, accept: (selected: ISelectedSuggestion) => any) {

		this._disposables.push(widget.onDidShow(() => this._onItem(widget.getFocusedItem())));
		this._disposables.push(widget.onDidFocus(this._onItem, this));
		this._disposables.push(widget.onDidHide(this.reset, this));

		this._disposables.push(editor.onWillType(text => {
			if (this._activeItem) {
				const ch = text[text.length - 1];
				if (this._activeAcceptCharacters.has(ch) && editor.getConfiguration().contribInfo.acceptSuggestionOnCommitCharacter) {
					accept(this._activeItem);
				}
			}
		}));
	}

	private _onItem(selected: ISelectedSuggestion): void {
		if (!selected || isFalsyOrEmpty(selected.item.suggestion.commitCharacters)) {
			this.reset();
			return;
		}
		this._activeItem = selected;
		this._activeAcceptCharacters.clear();
		for (const ch of selected.item.suggestion.commitCharacters) {
			if (ch.length > 0) {
				this._activeAcceptCharacters.add(ch[0]);
			}
		}
	}

	reset(): void {
		this._activeItem = undefined;
	}

	dispose() {
		dispose(this._disposables);
	}
}

class SuggestAlternatives {

	static OtherSuggestions = new RawContextKey<boolean>('hasOtherSuggestions', false);

	private readonly _ckOtherSuggestions: IContextKey<boolean>;

	private _index: number;
	private _model: CompletionModel;
	private _listener: IDisposable;
	private _ignore: boolean;

	constructor(
		private readonly _editor: ICodeEditor,
		private readonly _accept: (selected: ISelectedSuggestion) => any,
		@IContextKeyService contextKeyService: IContextKeyService,
	) {
		this._ckOtherSuggestions = SuggestAlternatives.OtherSuggestions.bindTo(contextKeyService);
	}

	reset(): void {
		this._ckOtherSuggestions.reset();
		dispose(this._listener);
		this._model = undefined;
		this._ignore = false;
	}

	set({ model, index }: ISelectedSuggestion): void {
		// no suggestions -> nothing to do
		if (model.items.length === 0) {
			this.reset();
			return;
		}

		// no alternative suggestions -> nothing to do
		let nextIndex = SuggestAlternatives._moveIndex(true, model, index);
		if (nextIndex === index) {
			this.reset();
			return;
		}

		this._model = model;
		this._index = index;
		this._listener = this._editor.onDidChangeCursorPosition(() => {
			if (!this._ignore) {
				this.reset();
			}
		});
		this._ckOtherSuggestions.set(true);
	}

	private static _moveIndex(fwd: boolean, model: CompletionModel, index: number): number {
		let newIndex = index;
		while (true) {
			newIndex = (newIndex + model.items.length + (fwd ? +1 : -1)) % model.items.length;
			if (newIndex === index) {
				break;
			}
			if (!model.items[newIndex].suggestion.insertTextIsSnippet) {
				break;
			}
		}
		return newIndex;
	}

	next(): void {
		this._move(true);
	}

	prev(): void {
		this._move(false);
	}

	private _move(fwd: boolean): void {
		if (!this._model) {
			// nothing to reason about
			return;
		}
		try {
			this._ignore = true;
			this._index = SuggestAlternatives._moveIndex(fwd, this._model, this._index);
			this._accept({ index: this._index, item: this._model.items[this._index], model: this._model });
		} finally {
			this._ignore = false;
		}
	}
}

export class SuggestController implements IEditorContribution {

	private static readonly ID: string = 'editor.contrib.suggestController';

	public static get(editor: ICodeEditor): SuggestController {
		return editor.getContribution<SuggestController>(SuggestController.ID);
	}

{'fraction_non_alphanumeric': 0.15364120781527532, 'fraction_numerical': 0.0014209591474245115, 'mean_word_length': 4.0885675553547225, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 26, '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': '3515563', 'n_tokens_mistral': 3631, 'n_tokens_neox': 3295, 'n_words': 712}