Example #1
0
	private static _isAutoClosingOpenCharType(config: CursorConfiguration, model: ITextModel, selections: Selection[], ch: string): boolean {
		if (!config.autoClosingBrackets || !config.autoClosingPairsOpen.hasOwnProperty(ch)) {
			return false;
		}

		for (let i = 0, len = selections.length; i < len; i++) {
			const selection = selections[i];
			if (!selection.isEmpty()) {
				return false;
			}

			const position = selection.getPosition();
			const lineText = model.getLineContent(position.lineNumber);

			// Do not auto-close ' or " after a word character
			if ((ch === '\'' || ch === '"') && position.column > 1) {
				const wordSeparators = getMapForWordSeparators(config.wordSeparators);
				const characterBeforeCode = lineText.charCodeAt(position.column - 2);
				const characterBeforeType = wordSeparators.get(characterBeforeCode);
				if (characterBeforeType === WordCharacterClass.Regular) {
					return false;
				}
			}

			// Only consider auto closing the pair if a space follows or if another autoclosed pair follows
			const characterAfter = lineText.charAt(position.column - 1);
			if (characterAfter) {
				let isBeforeCloseBrace = TypeOperations._isBeforeClosingBrace(config, ch, characterAfter);
				if (!isBeforeCloseBrace && !/\s/.test(characterAfter)) {
					return false;
				}
			}

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

			model.forceTokenization(position.lineNumber);
			const lineTokens = model.getLineTokens(position.lineNumber);

			let shouldAutoClosePair = false;
			try {
				shouldAutoClosePair = LanguageConfigurationRegistry.shouldAutoClosePair(ch, lineTokens, position.column);
			} catch (e) {
				onUnexpectedError(e);
			}

			if (!shouldAutoClosePair) {
				return false;
			}
		}

		return true;
	}
Example #2
0
	private static _typeInterceptorAutoClosingOpenChar(config: CursorConfiguration, model: ITokenizedModel, cursor: SingleCursorState, ch: string): EditOperationResult {
		if (!config.autoClosingBrackets) {
			return null;
		}

		let selection = cursor.selection;

		if (!selection.isEmpty() || !config.autoClosingPairsOpen.hasOwnProperty(ch)) {
			return null;
		}

		let position = cursor.position;
		let lineText = model.getLineContent(position.lineNumber);
		let beforeCharacter = lineText.charAt(position.column - 1);

		// Only consider auto closing the pair if a space follows or if another autoclosed pair follows
		if (beforeCharacter) {
			let thisBraceIsSymmetric = (config.autoClosingPairsOpen[ch] === ch);

			let isBeforeCloseBrace = false;
			for (let otherCloseBrace in config.autoClosingPairsClose) {
				let otherBraceIsSymmetric = (config.autoClosingPairsOpen[otherCloseBrace] === otherCloseBrace);
				if (!thisBraceIsSymmetric && otherBraceIsSymmetric) {
					continue;
				}
				if (beforeCharacter === otherCloseBrace) {
					isBeforeCloseBrace = true;
					break;
				}
			}
			if (!isBeforeCloseBrace && !/\s/.test(beforeCharacter)) {
				return null;
			}
		}

		let lineTokens = model.getLineTokens(position.lineNumber, false);

		let shouldAutoClosePair = false;
		try {
			shouldAutoClosePair = LanguageConfigurationRegistry.shouldAutoClosePair(ch, lineTokens, position.column);
		} catch (e) {
			onUnexpectedError(e);
		}

		if (!shouldAutoClosePair) {
			return null;
		}

		let closeCharacter = config.autoClosingPairsOpen[ch];
		return new EditOperationResult(new ReplaceCommandWithOffsetCursorState(selection, ch + closeCharacter, 0, -closeCharacter.length), {
			shouldPushStackElementBefore: true,
			shouldPushStackElementAfter: false
		});
	}
	private static _isAutoClosingOpenCharType(config: CursorConfiguration, model: ITokenizedModel, cursors: SingleCursorState[], ch: string): boolean {
		if (!config.autoClosingBrackets || !config.autoClosingPairsOpen.hasOwnProperty(ch)) {
			return false;
		}

		for (let i = 0, len = cursors.length; i < len; i++) {
			const cursor = cursors[i];
			const selection = cursor.selection;
			if (!selection.isEmpty()) {
				return false;
			}

			const position = cursor.position;
			const lineText = model.getLineContent(position.lineNumber);
			const afterCharacter = lineText.charAt(position.column - 1);

			// Only consider auto closing the pair if a space follows or if another autoclosed pair follows
			if (afterCharacter) {
				const thisBraceIsSymmetric = (config.autoClosingPairsOpen[ch] === ch);

				let isBeforeCloseBrace = false;
				for (let otherCloseBrace in config.autoClosingPairsClose) {
					const otherBraceIsSymmetric = (config.autoClosingPairsOpen[otherCloseBrace] === otherCloseBrace);
					if (!thisBraceIsSymmetric && otherBraceIsSymmetric) {
						continue;
					}
					if (afterCharacter === otherCloseBrace) {
						isBeforeCloseBrace = true;
						break;
					}
				}
				if (!isBeforeCloseBrace && !/\s/.test(afterCharacter)) {
					return false;
				}
			}

			model.forceTokenization(position.lineNumber);
			const lineTokens = model.getLineTokens(position.lineNumber);

			let shouldAutoClosePair = false;
			try {
				shouldAutoClosePair = LanguageConfigurationRegistry.shouldAutoClosePair(ch, lineTokens, position.column);
			} catch (e) {
				onUnexpectedError(e);
			}

			if (!shouldAutoClosePair) {
				return false;
			}
		}

		return true;
	}
Example #4
0
	public static compositionEndWithInterceptors(prevEditOperationType: EditOperationType, config: CursorConfiguration, model: ITextModel, selections: Selection[]): EditOperationResult {
		if (config.autoClosingQuotes === 'never') {
			return null;
		}

		let commands: ICommand[] = [];

		for (let i = 0; i < selections.length; i++) {
			if (!selections[i].isEmpty()) {
				continue;
			}
			const position = selections[i].getPosition();
			const lineText = model.getLineContent(position.lineNumber);
			const ch = lineText.charAt(position.column - 2);

			if (config.autoClosingPairsClose.hasOwnProperty(ch)) { // first of all, it's a closing tag
				if (ch === config.autoClosingPairsClose[ch] /** isEqualPair */) {
					const lineTextBeforeCursor = lineText.substr(0, position.column - 2);
					const chCntBefore = this._countNeedlesInHaystack(lineTextBeforeCursor, ch);

					if (chCntBefore % 2 === 1) {
						continue; // it pairs with the opening tag.
					}
				}
			}

			// As we are not typing in a new character, so we don't need to run `_runAutoClosingCloseCharType`
			// Next step, let's try to check if it's an open char.
			if (config.autoClosingPairsOpen.hasOwnProperty(ch)) {
				if (isQuote(ch) && position.column > 2) {
					const wordSeparators = getMapForWordSeparators(config.wordSeparators);
					const characterBeforeCode = lineText.charCodeAt(position.column - 3);
					const characterBeforeType = wordSeparators.get(characterBeforeCode);
					if (characterBeforeType === WordCharacterClass.Regular) {
						continue;
					}
				}

				const characterAfter = lineText.charAt(position.column - 1);

				if (characterAfter) {
					let isBeforeCloseBrace = TypeOperations._isBeforeClosingBrace(config, ch, characterAfter);
					let shouldAutoCloseBefore = isQuote(ch) ? config.shouldAutoCloseBefore.quote : config.shouldAutoCloseBefore.bracket;
					if (isBeforeCloseBrace) {
						// In normal auto closing logic, we will auto close if the cursor is even before a closing brace intentionally.
						// However for composition mode, we do nothing here as users might clear all the characters for composition and we don't want to do a unnecessary auto close.
						// Related: microsoft/vscode#57250.
						continue;
					}
					if (!shouldAutoCloseBefore(characterAfter)) {
						continue;
					}
				}

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

				model.forceTokenization(position.lineNumber);
				const lineTokens = model.getLineTokens(position.lineNumber);

				let shouldAutoClosePair = false;

				try {
					shouldAutoClosePair = LanguageConfigurationRegistry.shouldAutoClosePair(ch, lineTokens, position.column - 1);
				} catch (e) {
					onUnexpectedError(e);
				}

				if (shouldAutoClosePair) {
					const closeCharacter = config.autoClosingPairsOpen[ch];
					commands[i] = new ReplaceCommandWithOffsetCursorState(selections[i], closeCharacter, 0, -closeCharacter.length);
				}
			}
		}

		return new EditOperationResult(EditOperationType.Typing, commands, {
			shouldPushStackElementBefore: true,
			shouldPushStackElementAfter: false
		});
	}