src/vs/editor/common/cursor/cursorTypeOperations.ts/TypeOperations/_getAutoClosingPairClose
class TypeOperations:	private static _getAutoClosingPairClose(config: CursorConfiguration, model: ITextModel, selections: Selection[], ch: string, chIsAlreadyTyped: boolean): string | null {
		const chIsQuote = isQuote(ch);
		const autoCloseConfig = (chIsQuote ? config.autoClosingQuotes : config.autoClosingBrackets);
		const shouldAutoCloseBefore = (chIsQuote ? config.shouldAutoCloseBefore.quote : config.shouldAutoCloseBefore.bracket);

		if (autoCloseConfig === 'never') {
			return null;
		}

		for (const selection of selections) {
			if (!selection.isEmpty()) {
				return null;
			}
		}

		// This method is called both when typing (regularly) and when composition ends
		// This means that we need to work with a text buffer where sometimes `ch` is not
		// there (it is being typed right now) or with a text buffer where `ch` has already been typed
		//
		// In order to avoid adding checks for `chIsAlreadyTyped` in all places, we will work
		// with two conceptual positions, the position before `ch` and the position after `ch`
		//
		const positions: { lineNumber: number; beforeColumn: number; afterColumn: number }[] = selections.map((s) => {
			const position = s.getPosition();
			if (chIsAlreadyTyped) {
				return { lineNumber: position.lineNumber, beforeColumn: position.column - ch.length, afterColumn: position.column };
			} else {
				return { lineNumber: position.lineNumber, beforeColumn: position.column, afterColumn: position.column };
			}
		});


		// Find the longest auto-closing open pair in case of multiple ending in `ch`
		// e.g. when having [f","] and [","], it picks [f","] if the character before is f
		const pair = this._findAutoClosingPairOpen(config, model, positions.map(p => new Position(p.lineNumber, p.beforeColumn)), ch);
		if (!pair) {
			return null;
		}

		// Sometimes, it is possible to have two auto-closing pairs that have a containment relationship
		// e.g. when having [(,)] and [(*,*)]
		// - when typing (, the resulting state is (|)
		// - when typing *, the desired resulting state is (*|*), not (*|*))
		const containedPair = this._findContainedAutoClosingPair(config, pair);
		const containedPairClose = containedPair ? containedPair.close : '';
		let isContainedPairPresent = true;

		for (const position of positions) {
			const { lineNumber, beforeColumn, afterColumn } = position;
			const lineText = model.getLineContent(lineNumber);
			const lineBefore = lineText.substring(0, beforeColumn - 1);
			const lineAfter = lineText.substring(afterColumn - 1);

			if (!lineAfter.startsWith(containedPairClose)) {
				isContainedPairPresent = false;
			}

			// Only consider auto closing the pair if an allowed character follows or if another autoclosed pair closing brace follows
			if (lineAfter.length > 0) {
				const characterAfter = lineAfter.charAt(0);
				const isBeforeCloseBrace = TypeOperations._isBeforeClosingBrace(config, lineAfter);

				if (!isBeforeCloseBrace && !shouldAutoCloseBefore(characterAfter)) {
					return null;
				}
			}

			// Do not auto-close ' or " after a word character
			if (pair.open.length === 1 && (ch === '\'' || ch === '"') && autoCloseConfig !== 'always') {
				const wordSeparators = getMapForWordSeparators(config.wordSeparators);
				if (lineBefore.length > 0) {
					const characterBefore = lineBefore.charCodeAt(lineBefore.length - 1);
					if (wordSeparators.get(characterBefore) === WordCharacterClass.Regular) {
						return null;
					}
				}
			}

			if (!model.tokenization.isCheapToTokenize(lineNumber)) {
				// Do not force tokenization
				return null;
			}

			model.tokenization.forceTokenization(lineNumber);
			const lineTokens = model.tokenization.getLineTokens(lineNumber);
			const scopedLineTokens = createScopedLineTokens(lineTokens, beforeColumn - 1);
			if (!pair.shouldAutoClose(scopedLineTokens, beforeColumn - scopedLineTokens.firstCharOffset)) {
				return null;
			}

			// Typing for example a quote could either start a new string, in which case auto-closing is desirable
			// or it could end a previously started string, in which case auto-closing is not desirable
			//
			// In certain cases, it is really not possible to look at the previous token to determine
			// what would happen. That's why we do something really unusual, we pretend to type a different
			// character and ask the tokenizer what the outcome of doing that is: after typing a neutral
			// character, are we in a string (i.e. the quote would most likely end a string) or not?
			//
			const neutralCharacter = pair.findNeutralCharacter();
			if (neutralCharacter) {
				const tokenType = model.tokenization.getTokenTypeIfInsertingCharacter(lineNumber, beforeColumn, neutralCharacter);
				if (!pair.isOK(tokenType)) {
					return null;
				}
			}
		}

		if (isContainedPairPresent) {
			return pair.close.substring(0, pair.close.length - containedPairClose.length);
		} else {
			return pair.close;
		}
	}