<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 } from 'vs/base/common/event';
import { Disposable, DisposableStore, IDisposable, IReference, MutableDisposable } from 'vs/base/common/lifecycle';
import { LineTokens } from 'vs/editor/common/tokens/lineTokens';
import { IPosition, Position } from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range';
import { BracketPairsTree } from 'vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/bracketPairsTree';
import { BracketInfo, BracketPairInfo, BracketPairWithMinIndentationInfo, IBracketPairsTextModelPart, IFoundBracket } from 'vs/editor/common/textModelBracketPairs';
import { TextModel } from 'vs/editor/common/model/textModel';
import { IModelContentChangedEvent, IModelLanguageChangedEvent, IModelOptionsChangedEvent, IModelTokensChangedEvent } from 'vs/editor/common/textModelEvents';
import { ILanguageConfigurationService } from 'vs/editor/common/languages/languageConfigurationRegistry';
import { ignoreBracketsInToken } from 'vs/editor/common/languages/supports';
import { RichEditBrackets, BracketsUtils, RichEditBracket } from 'vs/editor/common/languages/supports/richEditBrackets';
import { compareBy, findLast, findLastMaxBy } from 'vs/base/common/arrays';
import { LanguageBracketsConfiguration } from 'vs/editor/common/languages/supports/languageBracketsConfiguration';

export class BracketPairsTextModelPart extends Disposable implements IBracketPairsTextModelPart {
	private readonly bracketPairsTree = this._register(new MutableDisposable<IReference<BracketPairsTree>>());

	private readonly onDidChangeEmitter = new Emitter<void>();
	public readonly onDidChange = this.onDidChangeEmitter.event;

	private get canBuildAST() {
		const maxSupportedDocumentLength = /* max lines */ 50_000 * /* average column count */ 100;
		return this.textModel.getValueLength() <= maxSupportedDocumentLength;
	}

	private bracketsRequested = false;

	public constructor(
		private readonly textModel: TextModel,
		private readonly languageConfigurationService: ILanguageConfigurationService
	) {
		super();

		this._register(
			this.languageConfigurationService.onDidChange(e => {
				if (!e.languageId || this.bracketPairsTree.value?.object.didLanguageChange(e.languageId)) {
					this.bracketPairsTree.clear();
					this.updateBracketPairsTree();
				}
			})
		);
	}

	//#region TextModel events

	public handleDidChangeOptions(e: IModelOptionsChangedEvent): void {
		this.bracketPairsTree.clear();
		this.updateBracketPairsTree();
	}

	public handleDidChangeLanguage(e: IModelLanguageChangedEvent): void {
		this.bracketPairsTree.clear();
		this.updateBracketPairsTree();
	}

	public handleDidChangeContent(change: IModelContentChangedEvent) {
		this.bracketPairsTree.value?.object.handleContentChanged(change);
	}

	public handleDidChangeBackgroundTokenizationState(): void {
		this.bracketPairsTree.value?.object.handleDidChangeBackgroundTokenizationState();
	}

	public handleDidChangeTokens(e: IModelTokensChangedEvent): void {
		this.bracketPairsTree.value?.object.handleDidChangeTokens(e);
	}

	//#endregion

	private updateBracketPairsTree() {
		if (this.bracketsRequested && this.canBuildAST) {
			if (!this.bracketPairsTree.value) {
				const store = new DisposableStore();

				this.bracketPairsTree.value = createDisposableRef(
					store.add(
						new BracketPairsTree(this.textModel, (languageId) => {
							return this.languageConfigurationService.getLanguageConfiguration(languageId);
						})
					),
					store
				);
				store.add(this.bracketPairsTree.value.object.onDidChange(e => this.onDidChangeEmitter.fire(e)));
				this.onDidChangeEmitter.fire();
			}
		} else {
			if (this.bracketPairsTree.value) {
				this.bracketPairsTree.clear();
				// Important: Don't call fire if there was no change!
				this.onDidChangeEmitter.fire();
			}
		}
	}

	/**
	 * Returns all bracket pairs that intersect the given range.
	 * The result is sorted by the start position.
	*/
	public getBracketPairsInRange(range: Range): BracketPairInfo[] {
		this.bracketsRequested = true;
		this.updateBracketPairsTree();
		return this.bracketPairsTree.value?.object.getBracketPairsInRange(range, false) || [];
	}

	public getBracketPairsInRangeWithMinIndentation(range: Range): BracketPairWithMinIndentationInfo[] {
		this.bracketsRequested = true;
		this.updateBracketPairsTree();
		return this.bracketPairsTree.value?.object.getBracketPairsInRange(range, true) || [];
	}

	public getBracketsInRange(range: Range): BracketInfo[] {
		this.bracketsRequested = true;
		this.updateBracketPairsTree();
		return this.bracketPairsTree.value?.object.getBracketsInRange(range) || [];
	}

	public findMatchingBracketUp(_bracket: string, _position: IPosition, maxDuration?: number): Range | null {
		const position = this.textModel.validatePosition(_position);
		const languageId = this.textModel.getLanguageIdAtPosition(position.lineNumber, position.column);

		if (this.canBuildAST) {
			const closingBracketInfo = this.languageConfigurationService
				.getLanguageConfiguration(languageId)
				.bracketsNew.getClosingBracketInfo(_bracket)!;

			const bracketPair = findLast(this.getBracketPairsInRange(Range.fromPositions(_position, _position)) || [], (b) =>
				closingBracketInfo.closes(b.openingBracketInfo)
			);

			if (bracketPair) {
				return bracketPair.openingBracketRange;
			}
			return null;
		} else {
			// Fallback to old bracket matching code:
			const bracket = _bracket.toLowerCase();

			const bracketsSupport = this.languageConfigurationService.getLanguageConfiguration(languageId).brackets;

			if (!bracketsSupport) {
				return null;
			}

			const data = bracketsSupport.textIsBracket[bracket];
<commit_msg>Fixes #149460 by returning null for unconfigured brackets.<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 } from 'vs/base/common/event';
import { Disposable, DisposableStore, IDisposable, IReference, MutableDisposable } from 'vs/base/common/lifecycle';
import { LineTokens } from 'vs/editor/common/tokens/lineTokens';
import { IPosition, Position } from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range';
import { BracketPairsTree } from 'vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/bracketPairsTree';
import { BracketInfo, BracketPairInfo, BracketPairWithMinIndentationInfo, IBracketPairsTextModelPart, IFoundBracket } from 'vs/editor/common/textModelBracketPairs';
import { TextModel } from 'vs/editor/common/model/textModel';
import { IModelContentChangedEvent, IModelLanguageChangedEvent, IModelOptionsChangedEvent, IModelTokensChangedEvent } from 'vs/editor/common/textModelEvents';
import { ILanguageConfigurationService } from 'vs/editor/common/languages/languageConfigurationRegistry';
import { ignoreBracketsInToken } from 'vs/editor/common/languages/supports';
import { RichEditBrackets, BracketsUtils, RichEditBracket } from 'vs/editor/common/languages/supports/richEditBrackets';
import { compareBy, findLast, findLastMaxBy } from 'vs/base/common/arrays';
import { LanguageBracketsConfiguration } from 'vs/editor/common/languages/supports/languageBracketsConfiguration';

export class BracketPairsTextModelPart extends Disposable implements IBracketPairsTextModelPart {
	private readonly bracketPairsTree = this._register(new MutableDisposable<IReference<BracketPairsTree>>());

	private readonly onDidChangeEmitter = new Emitter<void>();
	public readonly onDidChange = this.onDidChangeEmitter.event;

	private get canBuildAST() {
		const maxSupportedDocumentLength = /* max lines */ 50_000 * /* average column count */ 100;
		return this.textModel.getValueLength() <= maxSupportedDocumentLength;
	}

	private bracketsRequested = false;

	public constructor(
		private readonly textModel: TextModel,
		private readonly languageConfigurationService: ILanguageConfigurationService
	) {
		super();

		this._register(
			this.languageConfigurationService.onDidChange(e => {
				if (!e.languageId || this.bracketPairsTree.value?.object.didLanguageChange(e.languageId)) {
					this.bracketPairsTree.clear();
					this.updateBracketPairsTree();
				}
			})
		);
	}

	//#region TextModel events

	public handleDidChangeOptions(e: IModelOptionsChangedEvent): void {
		this.bracketPairsTree.clear();
		this.updateBracketPairsTree();
	}

	public handleDidChangeLanguage(e: IModelLanguageChangedEvent): void {
		this.bracketPairsTree.clear();
		this.updateBracketPairsTree();
	}

	public handleDidChangeContent(change: IModelContentChangedEvent) {
		this.bracketPairsTree.value?.object.handleContentChanged(change);
	}

	public handleDidChangeBackgroundTokenizationState(): void {
		this.bracketPairsTree.value?.object.handleDidChangeBackgroundTokenizationState();
	}

	public handleDidChangeTokens(e: IModelTokensChangedEvent): void {
		this.bracketPairsTree.value?.object.handleDidChangeTokens(e);
	}

	//#endregion

	private updateBracketPairsTree() {
		if (this.bracketsRequested && this.canBuildAST) {
			if (!this.bracketPairsTree.value) {
				const store = new DisposableStore();

				this.bracketPairsTree.value = createDisposableRef(
					store.add(
						new BracketPairsTree(this.textModel, (languageId) => {
							return this.languageConfigurationService.getLanguageConfiguration(languageId);
						})
					),
					store
				);
				store.add(this.bracketPairsTree.value.object.onDidChange(e => this.onDidChangeEmitter.fire(e)));
				this.onDidChangeEmitter.fire();
			}
		} else {
			if (this.bracketPairsTree.value) {
				this.bracketPairsTree.clear();
				// Important: Don't call fire if there was no change!
				this.onDidChangeEmitter.fire();
			}
		}
	}

	/**
	 * Returns all bracket pairs that intersect the given range.
	 * The result is sorted by the start position.
	*/
	public getBracketPairsInRange(range: Range): BracketPairInfo[] {
		this.bracketsRequested = true;
		this.updateBracketPairsTree();
		return this.bracketPairsTree.value?.object.getBracketPairsInRange(range, false) || [];
	}

	public getBracketPairsInRangeWithMinIndentation(range: Range): BracketPairWithMinIndentationInfo[] {
		this.bracketsRequested = true;
		this.updateBracketPairsTree();
		return this.bracketPairsTree.value?.object.getBracketPairsInRange(range, true) || [];
	}

	public getBracketsInRange(range: Range): BracketInfo[] {
		this.bracketsRequested = true;
		this.updateBracketPairsTree();
		return this.bracketPairsTree.value?.object.getBracketsInRange(range) || [];
	}

	public findMatchingBracketUp(_bracket: string, _position: IPosition, maxDuration?: number): Range | null {
		const position = this.textModel.validatePosition(_position);
		const languageId = this.textModel.getLanguageIdAtPosition(position.lineNumber, position.column);

		if (this.canBuildAST) {
			const closingBracketInfo = this.languageConfigurationService
				.getLanguageConfiguration(languageId)
				.bracketsNew.getClosingBracketInfo(_bracket);

			if (!closingBracketInfo) {
				return null;
			}

			const bracketPair = findLast(this.getBracketPairsInRange(Range.fromPositions(_position, _position)) || [], (b) =>
				closingBracketInfo.closes(b.openingBracketInfo)
			);

			if (bracketPair) {
				return bracketPair.openingBracketRange;
			}
			return null;
		} else {
			// Fallback to old bracket matching code:
			const bracket = _bracket.toLowerCase();

			const bracketsSupport = this.languageConfigurationService.getLanguageConfiguration(languageId).brackets;

			if (!bracketsSupport) {
				return null;
			}

			const data = bracketsSupport.textIsBracket[bracket];

{'fraction_non_alphanumeric': 0.1307547632676425, 'fraction_numerical': 0.0017990023714122168, 'mean_word_length': 4.543970988213962, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 17, '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': '3022259', 'n_tokens_mistral': 3920, 'n_tokens_neox': 3440, 'n_words': 647}