Example #1
0
	/**
	 * Given an unsuccessful analysis, delegate to the block comment command
	 */
	private _executeBlockComment(model: editorCommon.ITokenizedModel, builder: editorCommon.IEditOperationBuilder, s: Selection): void {
		model.tokenizeIfCheap(s.startLineNumber);
		let languageId = model.getLanguageIdAtPosition(s.startLineNumber, s.startColumn);
		let config = LanguageConfigurationRegistry.getComments(languageId);
		if (!config || !config.blockCommentStartToken || !config.blockCommentEndToken) {
			// Mode does not support block comments
			return;
		}

		var startToken = config.blockCommentStartToken;
		var endToken = config.blockCommentEndToken;

		var ops = this._attemptRemoveBlockComment(model, s, startToken, endToken);
		if (!ops) {
			if (s.isEmpty()) {
				var lineContent = model.getLineContent(s.startLineNumber);
				var firstNonWhitespaceIndex = strings.firstNonWhitespaceIndex(lineContent);
				if (firstNonWhitespaceIndex === -1) {
					// Line is empty or contains only whitespace
					firstNonWhitespaceIndex = lineContent.length;
				}
				ops = BlockCommentCommand._createAddBlockCommentOperations(
					new Range(s.startLineNumber, firstNonWhitespaceIndex + 1, s.startLineNumber, lineContent.length + 1), startToken, endToken
				);
			} else {
				ops = BlockCommentCommand._createAddBlockCommentOperations(
					new Range(s.startLineNumber, model.getLineFirstNonWhitespaceColumn(s.startLineNumber), s.endLineNumber, model.getLineMaxColumn(s.endLineNumber)), startToken, endToken
				);
			}

			if (ops.length === 1) {
				// Leave cursor after token and Space
				this._deltaColumn = startToken.length + 1;
			}
		}
		this._selectionId = builder.trackSelection(s);
		for (var i = 0; i < ops.length; i++) {
			builder.addEditOperation(ops[i].range, ops[i].text);
		}
	}
	/**
	 * Do an initial pass over the lines and gather info about the line comment string.
	 * Returns null if any of the lines doesn't support a line comment string.
	 */
	public static _gatherPreflightCommentStrings(model:editorCommon.ITokenizedModel, startLineNumber: number, endLineNumber: number): ILinePreflightData[] {
		var lines: ILinePreflightData[] = [],
			config:ICommentsConfiguration,
			commentStr:string,
			seenModes: {[modeId:string]:string;} = Object.create(null),
			i:number,
			lineCount:number,
			lineNumber:number,
			mode: IMode,
			modeId: string;

		for (i = 0, lineCount = endLineNumber - startLineNumber + 1; i < lineCount; i++) {
			lineNumber = startLineNumber + i;
			mode = model.getModeAtPosition(lineNumber, 1);
			modeId = mode.getId();

			// Find the commentStr for this line, if none is found then bail out: we cannot do line comments
			if (seenModes[modeId]) {
				commentStr = seenModes[modeId];
			} else {
				config = LanguageConfigurationRegistry.getComments(mode);
				commentStr = (config ? config.lineCommentToken : null);
				if (!commentStr) {
					// Mode does not support line comments
					return null;
				}

				seenModes[modeId] = commentStr;
			}

			lines.push({
				ignore: false,
				commentStr: commentStr,
				commentStrOffset: 0,
				commentStrLength: commentStr.length
			});
		}

		return lines;
	}
Example #3
0
	constructor() {
		super();
		LanguageConfigurationRegistry.register(this.getId(), {
			brackets: [
				['(', ')'],
				['{', '}'],
				['[', ']']
			],

			onEnterRules: [
				{
					// e.g. /** | */
					beforeText: /^\s*\/\*\*(?!\/)([^\*]|\*(?!\/))*$/,
					afterText: /^\s*\*\/$/,
					action: { indentAction: IndentAction.IndentOutdent, appendText: ' * ' }
				},
				{
					// e.g. /** ...|
					beforeText: /^\s*\/\*\*(?!\/)([^\*]|\*(?!\/))*$/,
					action: { indentAction: IndentAction.None, appendText: ' * ' }
				},
				{
					// e.g.  * ...|
					beforeText: /^(\t|(\ \ ))*\ \*(\ ([^\*]|\*(?!\/))*)?$/,
					action: { indentAction: IndentAction.None, appendText: '* ' }
				},
				{
					// e.g.  */|
					beforeText: /^(\t|(\ \ ))*\ \*\/\s*$/,
					action: { indentAction: IndentAction.None, removeText: 1 }
				},
				{
					// e.g.  *-----*/|
					beforeText: /^(\t|(\ \ ))*\ \*[^/]*\*\/\s*$/,
					action: { indentAction: IndentAction.None, removeText: 1 }
				}
			]
		});
	}
		constructor(commentsConfig: CommentRule) {
			super(OUTER_LANGUAGE_ID);
			this._register(LanguageConfigurationRegistry.register(this.getLanguageIdentifier(), {
				comments: commentsConfig
			}));

			this._register(modes.TokenizationRegistry.register(this.getLanguageIdentifier().language, {
				getInitialState: (): modes.IState => NULL_STATE,
				tokenize: undefined,
				tokenize2: (line: string, state: modes.IState): TokenizationResult2 => {
					let languageId = (/^  /.test(line) ? INNER_LANGUAGE_ID : OUTER_LANGUAGE_ID);

					let tokens = new Uint32Array(1 << 1);
					tokens[(0 << 1)] = 0;
					tokens[(0 << 1) + 1] = (
						(modes.ColorId.DefaultForeground << modes.MetadataConsts.FOREGROUND_OFFSET)
						| (languageId.id << modes.MetadataConsts.LANGUAGEID_OFFSET)
					);
					return new TokenizationResult2(tokens, state);
				}
			}));
		}
	test('issue #61296: VS code freezes when editing CSS file with emoji', async function () {
		let toDispose = LanguageConfigurationRegistry.register(modeService.getLanguageIdentifier('fooLang')!, {
			wordPattern: /(#?-?\d*\.\d\w*%?)|(::?[\w-]*(?=[^,{;]*[,{]))|(([@#.!])?[\w-?]+%?|[@#!.])/g
		});
		snippetService = new SimpleSnippetService([new Snippet(
			['fooLang'],
			'bug',
			'-a-bug',
			'',
			'second',
			'',
			SnippetSource.User
		)]);

		const provider = new SnippetCompletionProvider(modeService, snippetService);

		let model = TextModel.createFromString('.🐷-a-b', undefined, modeService.getLanguageIdentifier('fooLang'));
		let result = await provider.provideCompletionItems(model, new Position(1, 8))!;

		assert.equal(result.suggestions.length, 1);

		toDispose.dispose();
	});
	private static _goodIndentForLine(config: CursorConfiguration, model: ITokenizedModel, lineNumber: number): string {
		let lastLineNumber = lineNumber - 1;

		for (lastLineNumber = lineNumber - 1; lastLineNumber >= 1; lastLineNumber--) {
			let lineText = model.getLineContent(lastLineNumber);
			let nonWhitespaceIdx = strings.lastNonWhitespaceIndex(lineText);
			if (nonWhitespaceIdx >= 0) {
				break;
			}
		}

		if (lastLineNumber < 1) {
			// No previous line with content found
			return '\t';
		}

		let r = LanguageConfigurationRegistry.getEnterActionAtPosition(model, lastLineNumber, model.getLineMaxColumn(lastLineNumber));

		let indentation: string;
		if (r.enterAction.indentAction === IndentAction.Outdent) {
			let desiredIndentCount = ShiftCommand.unshiftIndentCount(r.indentation, r.indentation.length, config.tabSize);
			indentation = '';
			for (let i = 0; i < desiredIndentCount; i++) {
				indentation += '\t';
			}
			indentation = config.normalizeIndentation(indentation);
		} else {
			indentation = r.indentation;
		}

		let result = indentation + r.enterAction.appendText;
		if (result.length === 0) {
			// good position is at column 1, but we gotta do something...
			return '\t';
		}
		return result;
	}
Example #7
0
	/**
	 * Do an initial pass over the lines and gather info about the line comment string.
	 * Returns null if any of the lines doesn't support a line comment string.
	 */
	public static _gatherPreflightCommentStrings(model: editorCommon.ITokenizedModel, startLineNumber: number, endLineNumber: number): ILinePreflightData[] {

		model.forceTokenization(startLineNumber);
		const languageId = model.getLanguageIdAtPosition(startLineNumber, 1);

		const config = LanguageConfigurationRegistry.getComments(languageId);
		const commentStr = (config ? config.lineCommentToken : null);
		if (!commentStr) {
			// Mode does not support line comments
			return null;
		}

		let lines: ILinePreflightData[] = [];
		for (let i = 0, lineCount = endLineNumber - startLineNumber + 1; i < lineCount; i++) {
			lines[i] = {
				ignore: false,
				commentStr: commentStr,
				commentStrOffset: 0,
				commentStrLength: commentStr.length
			};
		}

		return lines;
	}
Example #8
0
	public getEditOperations(model: ITokenizedModel, builder: IEditOperationBuilder): void {
		const startLine = this._selection.startLineNumber;

		let endLine = this._selection.endLineNumber;
		if (this._selection.endColumn === 1 && startLine !== endLine) {
			endLine = endLine - 1;
		}

		const tabSize = this._opts.tabSize;
		const oneIndent = this._opts.oneIndent;
		const shouldIndentEmptyLines = (startLine === endLine);

		// if indenting or outdenting on a whitespace only line
		if (this._selection.isEmpty()) {
			if (/^\s*$/.test(model.getLineContent(startLine))) {
				this._useLastEditRangeForCursorEndPosition = true;
			}
		}

		if (this._opts.useTabStops) {
			// indents[i] represents i * oneIndent
			let indents: string[] = ['', oneIndent];

			// keep track of previous line's "miss-alignment"
			let previousLineExtraSpaces = 0, extraSpaces = 0;
			for (let lineNumber = startLine; lineNumber <= endLine; lineNumber++ , previousLineExtraSpaces = extraSpaces) {
				extraSpaces = 0;
				let lineText = model.getLineContent(lineNumber);
				let indentationEndIndex = strings.firstNonWhitespaceIndex(lineText);

				if (this._opts.isUnshift && (lineText.length === 0 || indentationEndIndex === 0)) {
					// empty line or line with no leading whitespace => nothing to do
					continue;
				}

				if (!shouldIndentEmptyLines && !this._opts.isUnshift && lineText.length === 0) {
					// do not indent empty lines => nothing to do
					continue;
				}

				if (indentationEndIndex === -1) {
					// the entire line is whitespace
					indentationEndIndex = lineText.length;
				}

				if (lineNumber > 1) {
					let contentStartVisibleColumn = CursorColumns.visibleColumnFromColumn(lineText, indentationEndIndex + 1, tabSize);
					if (contentStartVisibleColumn % tabSize !== 0) {
						// The current line is "miss-aligned", so let's see if this is expected...
						// This can only happen when it has trailing commas in the indent
						if (model.isCheapToTokenize(lineNumber - 1)) {
							let enterAction = LanguageConfigurationRegistry.getRawEnterActionAtPosition(model, lineNumber - 1, model.getLineMaxColumn(lineNumber - 1));
							if (enterAction) {
								extraSpaces = previousLineExtraSpaces;
								if (enterAction.appendText) {
									for (let j = 0, lenJ = enterAction.appendText.length; j < lenJ && extraSpaces < tabSize; j++) {
										if (enterAction.appendText.charCodeAt(j) === CharCode.Space) {
											extraSpaces++;
										} else {
											break;
										}
									}
								}
								if (enterAction.removeText) {
									extraSpaces = Math.max(0, extraSpaces - enterAction.removeText);
								}

								// Act as if `prefixSpaces` is not part of the indentation
								for (let j = 0; j < extraSpaces; j++) {
									if (indentationEndIndex === 0 || lineText.charCodeAt(indentationEndIndex - 1) !== CharCode.Space) {
										break;
									}
									indentationEndIndex--;
								}
							}
						}
					}
				}


				if (this._opts.isUnshift && indentationEndIndex === 0) {
					// line with no leading whitespace => nothing to do
					continue;
				}

				let desiredIndentCount: number;
				if (this._opts.isUnshift) {
					desiredIndentCount = ShiftCommand.unshiftIndentCount(lineText, indentationEndIndex + 1, tabSize);
				} else {
					desiredIndentCount = ShiftCommand.shiftIndentCount(lineText, indentationEndIndex + 1, tabSize);
				}

				// Fill `indents`, as needed
				for (let j = indents.length; j <= desiredIndentCount; j++) {
					indents[j] = indents[j - 1] + oneIndent;
				}

				this._addEditOperation(builder, new Range(lineNumber, 1, lineNumber, indentationEndIndex + 1), indents[desiredIndentCount]);
				if (lineNumber === startLine) {
					// Force the startColumn to stay put because we're inserting after it
					this._selectionStartColumnStaysPut = (this._selection.startColumn <= indentationEndIndex + 1);
				}
			}
		} else {

			for (let lineNumber = startLine; lineNumber <= endLine; lineNumber++) {
				const lineText = model.getLineContent(lineNumber);
				let indentationEndIndex = strings.firstNonWhitespaceIndex(lineText);

				if (this._opts.isUnshift && (lineText.length === 0 || indentationEndIndex === 0)) {
					// empty line or line with no leading whitespace => nothing to do
					continue;
				}

				if (!shouldIndentEmptyLines && !this._opts.isUnshift && lineText.length === 0) {
					// do not indent empty lines => nothing to do
					continue;
				}

				if (indentationEndIndex === -1) {
					// the entire line is whitespace
					indentationEndIndex = lineText.length;
				}

				if (this._opts.isUnshift && indentationEndIndex === 0) {
					// line with no leading whitespace => nothing to do
					continue;
				}

				if (this._opts.isUnshift) {

					indentationEndIndex = Math.min(indentationEndIndex, tabSize);
					for (let i = 0; i < indentationEndIndex; i++) {
						const chr = lineText.charCodeAt(i);
						if (chr === CharCode.Tab) {
							indentationEndIndex = i + 1;
							break;
						}
					}

					this._addEditOperation(builder, new Range(lineNumber, 1, lineNumber, indentationEndIndex + 1), '');
				} else {
					this._addEditOperation(builder, new Range(lineNumber, 1, lineNumber, 1), oneIndent);
					if (lineNumber === startLine) {
						// Force the startColumn to stay put because we're inserting after it
						this._selectionStartColumnStaysPut = (this._selection.startColumn === 1);
					}
				}
			}
		}

		this._selectionId = builder.trackSelection(this._selection);
	}
Example #9
0
	private static _enter(config: CursorConfiguration, model: ITokenizedModel, keepPosition: boolean, range: Range): EditOperationResult {
		let r = LanguageConfigurationRegistry.getEnterAction(model, range);
		let enterAction = r.enterAction;
		let indentation = r.indentation;

		let beforeText = '';

		if (!r.ignoreCurrentLine) {
			// textBeforeEnter doesn't match unIndentPattern.
			let goodIndent = this._goodIndentForLine(config, model, range.startLineNumber);

			if (goodIndent !== null && goodIndent === r.indentation) {
				if (enterAction.outdentCurrentLine) {
					goodIndent = TypeOperations.unshiftIndent(config, goodIndent);
				}

				let lineText = model.getLineContent(range.startLineNumber);
				if (config.normalizeIndentation(goodIndent) !== config.normalizeIndentation(indentation)) {
					beforeText = config.normalizeIndentation(goodIndent) + lineText.substring(indentation.length, range.startColumn - 1);
					indentation = goodIndent;
					range = new Range(range.startLineNumber, 1, range.endLineNumber, range.endColumn);
				}
			}
		}

		if (enterAction.removeText) {
			indentation = indentation.substring(0, indentation.length - enterAction.removeText);
		}

		let executeCommand: ICommand;
		if (enterAction.indentAction === IndentAction.None) {
			// Nothing special
			executeCommand = TypeOperations.typeCommand(range, beforeText + '\n' + config.normalizeIndentation(indentation + enterAction.appendText), keepPosition);

		} else if (enterAction.indentAction === IndentAction.Indent) {
			// Indent once
			executeCommand = TypeOperations.typeCommand(range, beforeText + '\n' + config.normalizeIndentation(indentation + enterAction.appendText), keepPosition);

		} else if (enterAction.indentAction === IndentAction.IndentOutdent) {
			// Ultra special
			let normalIndent = config.normalizeIndentation(indentation);
			let increasedIndent = config.normalizeIndentation(indentation + enterAction.appendText);

			let typeText = beforeText + '\n' + increasedIndent + '\n' + normalIndent;

			if (keepPosition) {
				executeCommand = new ReplaceCommandWithoutChangingPosition(range, typeText);
			} else {
				executeCommand = new ReplaceCommandWithOffsetCursorState(range, typeText, -1, increasedIndent.length - normalIndent.length);
			}
		} else if (enterAction.indentAction === IndentAction.Outdent) {
			let actualIndentation = TypeOperations.unshiftIndent(config, indentation);
			executeCommand = TypeOperations.typeCommand(range, beforeText + '\n' + config.normalizeIndentation(actualIndentation + enterAction.appendText), keepPosition);
		}

		return new EditOperationResult(executeCommand, {
			shouldPushStackElementBefore: true,
			shouldPushStackElementAfter: false,
			isAutoWhitespaceCommand: true
		});
	}
Example #10
0
	constructor(commentsConfig: CommentRule) {
		super();
		LanguageConfigurationRegistry.register(this.getId(), {
			comments: commentsConfig
		});
	}
Example #11
0
			constructor() {
				super();
				LanguageConfigurationRegistry.register(this.getId(), {
					brackets: brackets
				});
			}
	public static massageWordDefinitionOf(modeId: string): RegExp {
		let wordDefinition = LanguageConfigurationRegistry.getWordDefinition(modeId);
		return ensureValidWordDefinition(wordDefinition);
	}
	function testBrackets(contents: string[], brackets: CharacterPair[]): void {
		function toRelaxedFoundBracket(a: IFoundBracket) {
			if (!a) {
				return null;
			}
			return {
				range: a.range.toString(),
				open: a.open,
				close: a.close,
				isOpen: a.isOpen
			};
		}

		let charIsBracket: { [char: string]: boolean } = {};
		let charIsOpenBracket: { [char: string]: boolean } = {};
		let openForChar: { [char: string]: string } = {};
		let closeForChar: { [char: string]: string } = {};
		brackets.forEach((b) => {
			charIsBracket[b[0]] = true;
			charIsBracket[b[1]] = true;

			charIsOpenBracket[b[0]] = true;
			charIsOpenBracket[b[1]] = false;

			openForChar[b[0]] = b[0];
			closeForChar[b[0]] = b[1];

			openForChar[b[1]] = b[0];
			closeForChar[b[1]] = b[1];
		});

		let expectedBrackets: IFoundBracket[] = [];
		for (let lineIndex = 0; lineIndex < contents.length; lineIndex++) {
			let lineText = contents[lineIndex];

			for (let charIndex = 0; charIndex < lineText.length; charIndex++) {
				let ch = lineText.charAt(charIndex);
				if (charIsBracket[ch]) {
					expectedBrackets.push({
						open: openForChar[ch],
						close: closeForChar[ch],
						isOpen: charIsOpenBracket[ch],
						range: new Range(lineIndex + 1, charIndex + 1, lineIndex + 1, charIndex + 2)
					});
				}
			}
		}

		const languageIdentifier = new LanguageIdentifier('testMode', LanguageId.PlainText);

		let registration = LanguageConfigurationRegistry.register(languageIdentifier, {
			brackets: brackets
		});

		let model = new TextModelWithTokens(
			[],
			RawTextSource.fromString(contents.join('\n')),
			TextModel.DEFAULT_CREATION_OPTIONS,
			languageIdentifier
		);

		// findPrevBracket
		{
			let expectedBracketIndex = expectedBrackets.length - 1;
			let currentExpectedBracket = expectedBracketIndex >= 0 ? expectedBrackets[expectedBracketIndex] : null;
			for (let lineNumber = contents.length; lineNumber >= 1; lineNumber--) {
				let lineText = contents[lineNumber - 1];

				for (let column = lineText.length + 1; column >= 1; column--) {

					if (currentExpectedBracket) {
						if (lineNumber === currentExpectedBracket.range.startLineNumber && column < currentExpectedBracket.range.endColumn) {
							expectedBracketIndex--;
							currentExpectedBracket = expectedBracketIndex >= 0 ? expectedBrackets[expectedBracketIndex] : null;
						}
					}

					let actual = model.findPrevBracket({
						lineNumber: lineNumber,
						column: column
					});

					assert.deepEqual(toRelaxedFoundBracket(actual), toRelaxedFoundBracket(currentExpectedBracket), 'findPrevBracket of ' + lineNumber + ', ' + column);
				}
			}
		}

		// findNextBracket
		{
			let expectedBracketIndex = 0;
			let currentExpectedBracket = expectedBracketIndex < expectedBrackets.length ? expectedBrackets[expectedBracketIndex] : null;
			for (let lineNumber = 1; lineNumber <= contents.length; lineNumber++) {
				let lineText = contents[lineNumber - 1];

				for (let column = 1; column <= lineText.length + 1; column++) {

					if (currentExpectedBracket) {
						if (lineNumber === currentExpectedBracket.range.startLineNumber && column > currentExpectedBracket.range.startColumn) {
							expectedBracketIndex++;
							currentExpectedBracket = expectedBracketIndex < expectedBrackets.length ? expectedBrackets[expectedBracketIndex] : null;
						}
					}

					let actual = model.findNextBracket({
						lineNumber: lineNumber,
						column: column
					});

					assert.deepEqual(toRelaxedFoundBracket(actual), toRelaxedFoundBracket(currentExpectedBracket), 'findNextBracket of ' + lineNumber + ', ' + column);
				}
			}
		}

		model.dispose();
		registration.dispose();
	}
	private static _safeGetWordDefinition(modeId:string): RegExp {
		return LanguageConfigurationRegistry.getWordDefinition(modeId);
	}
	private static _enter(config: CursorConfiguration, model: ITextModel, keepPosition: boolean, range: Range): ICommand {
		if (!model.isCheapToTokenize(range.getStartPosition().lineNumber)) {
			let lineText = model.getLineContent(range.startLineNumber);
			let indentation = strings.getLeadingWhitespace(lineText).substring(0, range.startColumn - 1);
			return TypeOperations._typeCommand(range, '\n' + config.normalizeIndentation(indentation), keepPosition);
		}

		let r = LanguageConfigurationRegistry.getEnterAction(model, range);
		if (r) {
			let enterAction = r.enterAction;
			let indentation = r.indentation;

			if (enterAction.indentAction === IndentAction.None) {
				// Nothing special
				return TypeOperations._typeCommand(range, '\n' + config.normalizeIndentation(indentation + enterAction.appendText), keepPosition);

			} else if (enterAction.indentAction === IndentAction.Indent) {
				// Indent once
				return TypeOperations._typeCommand(range, '\n' + config.normalizeIndentation(indentation + enterAction.appendText), keepPosition);

			} else if (enterAction.indentAction === IndentAction.IndentOutdent) {
				// Ultra special
				let normalIndent = config.normalizeIndentation(indentation);
				let increasedIndent = config.normalizeIndentation(indentation + enterAction.appendText);

				let typeText = '\n' + increasedIndent + '\n' + normalIndent;

				if (keepPosition) {
					return new ReplaceCommandWithoutChangingPosition(range, typeText, true);
				} else {
					return new ReplaceCommandWithOffsetCursorState(range, typeText, -1, increasedIndent.length - normalIndent.length, true);
				}
			} else if (enterAction.indentAction === IndentAction.Outdent) {
				let actualIndentation = TypeOperations.unshiftIndent(config, indentation);
				return TypeOperations._typeCommand(range, '\n' + config.normalizeIndentation(actualIndentation + enterAction.appendText), keepPosition);
			}
		}

		// no enter rules applied, we should check indentation rules then.
		if (!config.autoIndent) {
			// Nothing special
			let lineText = model.getLineContent(range.startLineNumber);
			let indentation = strings.getLeadingWhitespace(lineText).substring(0, range.startColumn - 1);
			return TypeOperations._typeCommand(range, '\n' + config.normalizeIndentation(indentation), keepPosition);
		}

		let ir = LanguageConfigurationRegistry.getIndentForEnter(model, range, {
			unshiftIndent: (indent) => {
				return TypeOperations.unshiftIndent(config, indent);
			},
			shiftIndent: (indent) => {
				return TypeOperations.shiftIndent(config, indent);
			},
			normalizeIndentation: (indent) => {
				return config.normalizeIndentation(indent);
			}
		}, config.autoIndent);

		let lineText = model.getLineContent(range.startLineNumber);
		let indentation = strings.getLeadingWhitespace(lineText).substring(0, range.startColumn - 1);

		if (ir) {
			let oldEndViewColumn = CursorColumns.visibleColumnFromColumn2(config, model, range.getEndPosition());
			let oldEndColumn = range.endColumn;

			let beforeText = '\n';
			if (indentation !== config.normalizeIndentation(ir.beforeEnter)) {
				beforeText = config.normalizeIndentation(ir.beforeEnter) + lineText.substring(indentation.length, range.startColumn - 1) + '\n';
				range = new Range(range.startLineNumber, 1, range.endLineNumber, range.endColumn);
			}

			let newLineContent = model.getLineContent(range.endLineNumber);
			let firstNonWhitespace = strings.firstNonWhitespaceIndex(newLineContent);
			if (firstNonWhitespace >= 0) {
				range = range.setEndPosition(range.endLineNumber, Math.max(range.endColumn, firstNonWhitespace + 1));
			} else {
				range = range.setEndPosition(range.endLineNumber, model.getLineMaxColumn(range.endLineNumber));
			}

			if (keepPosition) {
				return new ReplaceCommandWithoutChangingPosition(range, beforeText + config.normalizeIndentation(ir.afterEnter), true);
			} else {
				let offset = 0;
				if (oldEndColumn <= firstNonWhitespace + 1) {
					if (!config.insertSpaces) {
						oldEndViewColumn = Math.ceil(oldEndViewColumn / config.tabSize);
					}
					offset = Math.min(oldEndViewColumn + 1 - config.normalizeIndentation(ir.afterEnter).length - 1, 0);
				}
				return new ReplaceCommandWithOffsetCursorState(range, beforeText + config.normalizeIndentation(ir.afterEnter), 0, offset, true);
			}

		} else {
			return TypeOperations._typeCommand(range, '\n' + config.normalizeIndentation(indentation), keepPosition);
		}
	}
	private static _typeInterceptorElectricChar(prevEditOperationType: EditOperationType, config: CursorConfiguration, model: ITextModel, selection: Selection, ch: string): EditOperationResult {
		if (!config.electricChars.hasOwnProperty(ch) || !selection.isEmpty()) {
			return null;
		}

		let position = selection.getPosition();
		model.forceTokenization(position.lineNumber);
		let lineTokens = model.getLineTokens(position.lineNumber);

		let electricAction: IElectricAction;
		try {
			electricAction = LanguageConfigurationRegistry.onElectricCharacter(ch, lineTokens, position.column);
		} catch (e) {
			onUnexpectedError(e);
		}

		if (!electricAction) {
			return null;
		}

		if (electricAction.appendText) {
			const command = new ReplaceCommandWithOffsetCursorState(selection, ch + electricAction.appendText, 0, -electricAction.appendText.length);
			return new EditOperationResult(EditOperationType.Typing, [command], {
				shouldPushStackElementBefore: false,
				shouldPushStackElementAfter: true
			});
		}

		if (electricAction.matchOpenBracket) {
			let endColumn = (lineTokens.getLineContent() + ch).lastIndexOf(electricAction.matchOpenBracket) + 1;
			let match = model.findMatchingBracketUp(electricAction.matchOpenBracket, {
				lineNumber: position.lineNumber,
				column: endColumn
			});

			if (match) {
				if (match.startLineNumber === position.lineNumber) {
					// matched something on the same line => no change in indentation
					return null;
				}
				let matchLine = model.getLineContent(match.startLineNumber);
				let matchLineIndentation = strings.getLeadingWhitespace(matchLine);
				let newIndentation = config.normalizeIndentation(matchLineIndentation);

				let lineText = model.getLineContent(position.lineNumber);
				let lineFirstNonBlankColumn = model.getLineFirstNonWhitespaceColumn(position.lineNumber) || position.column;

				let prefix = lineText.substring(lineFirstNonBlankColumn - 1, position.column - 1);
				let typeText = newIndentation + prefix + ch;

				let typeSelection = new Range(position.lineNumber, 1, position.lineNumber, position.column);

				const command = new ReplaceCommand(typeSelection, typeText);
				return new EditOperationResult(EditOperationType.Typing, [command], {
					shouldPushStackElementBefore: false,
					shouldPushStackElementAfter: true
				});
			}
		}

		return null;
	}
Example #17
0
	constructor() {
		super('css-mock-mode-id');
		LanguageConfigurationRegistry.register(this.getId(), {
			wordPattern: /(#?-?\d*\.\d\w*%?)|([@#.:!]?[\w-?]+%?)|[@#.!]/g
		});
	}
	compute(editorModel: ITextModel): TPromise<FoldingRegions> {
		let foldingRules = LanguageConfigurationRegistry.getFoldingRules(editorModel.getLanguageIdentifier().id);
		let offSide = foldingRules && foldingRules.offSide;
		let markers = foldingRules && foldingRules.markers;
		return TPromise.as(computeRanges(editorModel, offSide, markers));
	}
Example #19
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
		});
	}
Example #20
0
	private static _isAutoClosingOpenCharType(config: CursorConfiguration, model: ITextModel, selections: Selection[], ch: string): boolean {
		const chIsQuote = isQuote(ch);
		const autoCloseConfig = chIsQuote ? config.autoClosingQuotes : config.autoClosingBrackets;

		if (autoCloseConfig === 'never' || !config.autoClosingPairsOpen.hasOwnProperty(ch)) {
			return false;
		}

		let shouldAutoCloseBefore = chIsQuote ? config.shouldAutoCloseBefore.quote : config.shouldAutoCloseBefore.bracket;

		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 (chIsQuote && 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 && !shouldAutoCloseBefore(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;
	}
	private static _safeGetWordDefinition(mode:IMode): RegExp {
		return LanguageConfigurationRegistry.getWordDefinition(mode.getId());
	}
Example #22
0
	constructor() {
		super('less-mock-mode-id');
		LanguageConfigurationRegistry.register(this.getId(), {
			wordPattern: /(-?\d*\.\d+)|([\w-]+)/g
		});
	}
Example #23
0
		constructor() {
			super(INNER_LANGUAGE_ID);
			this._register(LanguageConfigurationRegistry.register(this.getLanguageIdentifier(), {}));
		}
	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) {
				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 (characterAfter === otherCloseBrace) {
						isBeforeCloseBrace = true;
						break;
					}
				}
				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 #25
0
			constructor() {
				super(MODE_ID);
				this._register(LanguageConfigurationRegistry.register(this.getLanguageIdentifier(), {
					wordPattern: /(#?-?\d*\.\d\w*%?)|(::?[\w-]*(?=[^,{;]*[,{]))|(([@#.!])?[\w-?]+%?|[@#!.])/g
				}));
			}
Example #26
0
	public getEditOperations(model: ITokenizedModel, builder: IEditOperationBuilder): void {

		var modelLineCount = model.getLineCount();

		if (this._isMovingDown && this._selection.endLineNumber === modelLineCount) {
			return;
		}
		if (!this._isMovingDown && this._selection.startLineNumber === 1) {
			return;
		}

		this._moveEndPositionDown = false;
		var s = this._selection;

		if (s.startLineNumber < s.endLineNumber && s.endColumn === 1) {
			this._moveEndPositionDown = true;
			s = s.setEndPosition(s.endLineNumber - 1, model.getLineMaxColumn(s.endLineNumber - 1));
		}

		let tabSize = model.getOptions().tabSize;
		let insertSpaces = model.getOptions().insertSpaces;
		let indentConverter = this.buildIndentConverter(tabSize);
		let virtualModel = {
			getLineTokens: (lineNumber: number) => {
				return model.getLineTokens(lineNumber);
			},
			getLanguageIdentifier: () => {
				return model.getLanguageIdentifier();
			},
			getLanguageIdAtPosition: (lineNumber: number, column: number) => {
				return model.getLanguageIdAtPosition(lineNumber, column);
			},
			getLineContent: null
		};

		if (s.startLineNumber === s.endLineNumber && model.getLineMaxColumn(s.startLineNumber) === 1) {
			// Current line is empty
			var lineNumber = s.startLineNumber;
			var otherLineNumber = (this._isMovingDown ? lineNumber + 1 : lineNumber - 1);

			if (model.getLineMaxColumn(otherLineNumber) === 1) {
				// Other line number is empty too, so no editing is needed
				// Add a no-op to force running by the model
				builder.addEditOperation(new Range(1, 1, 1, 1), null);
			} else {
				// Type content from other line number on line number
				builder.addEditOperation(new Range(lineNumber, 1, lineNumber, 1), model.getLineContent(otherLineNumber));

				// Remove content from other line number
				builder.addEditOperation(new Range(otherLineNumber, 1, otherLineNumber, model.getLineMaxColumn(otherLineNumber)), null);
			}
			// Track selection at the other line number
			s = new Selection(otherLineNumber, 1, otherLineNumber, 1);

		} else {

			var movingLineNumber: number,
				movingLineText: string;

			if (this._isMovingDown) {
				movingLineNumber = s.endLineNumber + 1;
				movingLineText = model.getLineContent(movingLineNumber);
				// Delete line that needs to be moved
				builder.addEditOperation(new Range(movingLineNumber - 1, model.getLineMaxColumn(movingLineNumber - 1), movingLineNumber, model.getLineMaxColumn(movingLineNumber)), null);

				let insertingText = movingLineText;

				if (this.shouldAutoIndent(model, s)) {
					let movingLineMatchResult = this.matchEnterRule(model, indentConverter, tabSize, movingLineNumber, s.startLineNumber - 1);
					// if s.startLineNumber - 1 matches onEnter rule, we still honor that.
					if (movingLineMatchResult !== null) {
						let oldIndentation = strings.getLeadingWhitespace(model.getLineContent(movingLineNumber));
						let newSpaceCnt = movingLineMatchResult + IndentUtil.getSpaceCnt(oldIndentation, tabSize);
						let newIndentation = IndentUtil.generateIndent(newSpaceCnt, tabSize, insertSpaces);
						insertingText = newIndentation + this.trimLeft(movingLineText);
					} else {
						// no enter rule matches, let's check indentatin rules then.
						virtualModel.getLineContent = (lineNumber: number) => {
							if (lineNumber === s.startLineNumber) {
								return model.getLineContent(movingLineNumber);
							} else {
								return model.getLineContent(lineNumber);
							}
						};
						let indentOfMovingLine = LanguageConfigurationRegistry.getGoodIndentForLine(virtualModel, model.getLanguageIdAtPosition(
							movingLineNumber, 1), s.startLineNumber, indentConverter);
						if (indentOfMovingLine !== null) {
							let oldIndentation = strings.getLeadingWhitespace(model.getLineContent(movingLineNumber));
							let newSpaceCnt = IndentUtil.getSpaceCnt(indentOfMovingLine, tabSize);
							let oldSpaceCnt = IndentUtil.getSpaceCnt(oldIndentation, tabSize);
							if (newSpaceCnt !== oldSpaceCnt) {
								let newIndentation = IndentUtil.generateIndent(newSpaceCnt, tabSize, insertSpaces);
								insertingText = newIndentation + this.trimLeft(movingLineText);
							}
						}
					}

					// add edit operations for moving line first to make sure it's executed after we make indentation change
					// to s.startLineNumber
					builder.addEditOperation(new Range(s.startLineNumber, 1, s.startLineNumber, 1), insertingText + '\n');

					let ret = this.matchEnterRule(model, indentConverter, tabSize, s.startLineNumber, s.startLineNumber, insertingText);
					// check if the line being moved before matches onEnter rules, if so let's adjust the indentation by onEnter rules.
					if (ret !== null) {
						if (ret !== 0) {
							this.getIndentEditsOfMovingBlock(model, builder, s, tabSize, insertSpaces, ret);
						}
					} else {
						// it doesn't match onEnter rules, let's check indentation rules then.
						virtualModel.getLineContent = (lineNumber: number) => {
							if (lineNumber === s.startLineNumber) {
								return insertingText;
							} else if (lineNumber >= s.startLineNumber + 1 && lineNumber <= s.endLineNumber + 1) {
								return model.getLineContent(lineNumber - 1);
							} else {
								return model.getLineContent(lineNumber);
							}
						};

						let newIndentatOfMovingBlock = LanguageConfigurationRegistry.getGoodIndentForLine(virtualModel, model.getLanguageIdAtPosition(
							movingLineNumber, 1), s.startLineNumber + 1, indentConverter);

						if (newIndentatOfMovingBlock !== null) {
							const oldIndentation = strings.getLeadingWhitespace(model.getLineContent(s.startLineNumber));
							const newSpaceCnt = IndentUtil.getSpaceCnt(newIndentatOfMovingBlock, tabSize);
							const oldSpaceCnt = IndentUtil.getSpaceCnt(oldIndentation, tabSize);
							if (newSpaceCnt !== oldSpaceCnt) {
								const spaceCntOffset = newSpaceCnt - oldSpaceCnt;

								this.getIndentEditsOfMovingBlock(model, builder, s, tabSize, insertSpaces, spaceCntOffset);
							}
						}
					}
				} else {
					// Insert line that needs to be moved before
					builder.addEditOperation(new Range(s.startLineNumber, 1, s.startLineNumber, 1), insertingText + '\n');
				}
			} else {
				movingLineNumber = s.startLineNumber - 1;
				movingLineText = model.getLineContent(movingLineNumber);

				// Delete line that needs to be moved
				builder.addEditOperation(new Range(movingLineNumber, 1, movingLineNumber + 1, 1), null);

				// Insert line that needs to be moved after
				builder.addEditOperation(new Range(s.endLineNumber, model.getLineMaxColumn(s.endLineNumber), s.endLineNumber, model.getLineMaxColumn(s.endLineNumber)), '\n' + movingLineText);

				if (this.shouldAutoIndent(model, s)) {
					virtualModel.getLineContent = (lineNumber: number) => {
						if (lineNumber === movingLineNumber) {
							return model.getLineContent(s.startLineNumber);
						} else {
							return model.getLineContent(lineNumber);
						}
					};

					let ret = this.matchEnterRule(model, indentConverter, tabSize, s.startLineNumber, s.startLineNumber - 2);
					// check if s.startLineNumber - 2 matches onEnter rules, if so adjust the moving block by onEnter rules.
					if (ret !== null) {
						if (ret !== 0) {
							this.getIndentEditsOfMovingBlock(model, builder, s, tabSize, insertSpaces, ret);
						}
					} else {
						// it doesn't match any onEnter rule, let's check indentation rules then.
						let indentOfFirstLine = LanguageConfigurationRegistry.getGoodIndentForLine(virtualModel, model.getLanguageIdAtPosition(s.startLineNumber, 1), movingLineNumber, indentConverter);
						if (indentOfFirstLine !== null) {
							// adjust the indentation of the moving block
							let oldIndent = strings.getLeadingWhitespace(model.getLineContent(s.startLineNumber));
							let newSpaceCnt = IndentUtil.getSpaceCnt(indentOfFirstLine, tabSize);
							let oldSpaceCnt = IndentUtil.getSpaceCnt(oldIndent, tabSize);
							if (newSpaceCnt !== oldSpaceCnt) {
								let spaceCntOffset = newSpaceCnt - oldSpaceCnt;

								this.getIndentEditsOfMovingBlock(model, builder, s, tabSize, insertSpaces, spaceCntOffset);
							}
						}
					}
				}
			}
		}

		this._selectionId = builder.trackSelection(s);
	}
Example #27
0
		function onEnter(line:string, offset:number): Modes.EnterAction {
			let model = new TextModelWithTokens([], TextModel.toRawText(line, TextModel.DEFAULT_CREATION_OPTIONS), false, _mode);
			let result = LanguageConfigurationRegistry.getRawEnterActionAtPosition(model, 1, offset + 1);
			model.dispose();
			return result;
		}
		constructor(commentsConfig: CommentRule) {
			super(INNER_LANGUAGE_ID);
			this._register(LanguageConfigurationRegistry.register(this.getLanguageIdentifier(), {
				comments: commentsConfig
			}));
		}
Example #29
0
suite('TextModelWithTokens - bracket matching', () => {

	function isNotABracket(model: Model, lineNumber: number, column: number) {
		let match = model.matchBracket(new Position(lineNumber, column));
		assert.equal(match, null, 'is not matching brackets at ' + lineNumber + ', ' + column);
	}

	function isBracket2(model: Model, testPosition: Position, expected: [Range, Range]): void {
		let actual = model.matchBracket(testPosition);
		assert.deepEqual(actual, expected, 'matches brackets at ' + testPosition);
	}

	const LANGUAGE_ID = 'bracketMode1';

	LanguageConfigurationRegistry.register(LANGUAGE_ID, {
		brackets: [
			['{', '}'],
			['[', ']'],
			['(', ')'],
		]
	});

	test('bracket matching 1', () => {
		let text =
			')]}{[(' + '\n' +
			')]}{[(';
		let model = Model.createFromString(text, undefined, LANGUAGE_ID);

		isNotABracket(model, 1, 1);
		isNotABracket(model, 1, 2);
		isNotABracket(model, 1, 3);
		isBracket2(model, new Position(1, 4), [new Range(1, 4, 1, 5), new Range(2, 3, 2, 4)]);
		isBracket2(model, new Position(1, 5), [new Range(1, 5, 1, 6), new Range(2, 2, 2, 3)]);
		isBracket2(model, new Position(1, 6), [new Range(1, 6, 1, 7), new Range(2, 1, 2, 2)]);
		isBracket2(model, new Position(1, 7), [new Range(1, 6, 1, 7), new Range(2, 1, 2, 2)]);

		isBracket2(model, new Position(2, 1), [new Range(2, 1, 2, 2), new Range(1, 6, 1, 7)]);
		isBracket2(model, new Position(2, 2), [new Range(2, 2, 2, 3), new Range(1, 5, 1, 6)]);
		isBracket2(model, new Position(2, 3), [new Range(2, 3, 2, 4), new Range(1, 4, 1, 5)]);
		isBracket2(model, new Position(2, 4), [new Range(2, 3, 2, 4), new Range(1, 4, 1, 5)]);
		isNotABracket(model, 2, 5);
		isNotABracket(model, 2, 6);
		isNotABracket(model, 2, 7);

		model.dispose();
	});

	test('bracket matching 2', () => {
		let text =
			'var bar = {' + '\n' +
			'foo: {' + '\n' +
			'}, bar: {hallo: [{' + '\n' +
			'}, {' + '\n' +
			'}]}}';
		let model = Model.createFromString(text, undefined, LANGUAGE_ID);

		let brackets: [Position, Range, Range][] = [
			[new Position(1, 11), new Range(1, 11, 1, 12), new Range(5, 4, 5, 5)],
			[new Position(1, 12), new Range(1, 11, 1, 12), new Range(5, 4, 5, 5)],

			[new Position(2, 6), new Range(2, 6, 2, 7), new Range(3, 1, 3, 2)],
			[new Position(2, 7), new Range(2, 6, 2, 7), new Range(3, 1, 3, 2)],

			[new Position(3, 1), new Range(3, 1, 3, 2), new Range(2, 6, 2, 7)],
			[new Position(3, 2), new Range(3, 1, 3, 2), new Range(2, 6, 2, 7)],
			[new Position(3, 9), new Range(3, 9, 3, 10), new Range(5, 3, 5, 4)],
			[new Position(3, 10), new Range(3, 9, 3, 10), new Range(5, 3, 5, 4)],
			[new Position(3, 17), new Range(3, 17, 3, 18), new Range(5, 2, 5, 3)],
			[new Position(3, 18), new Range(3, 18, 3, 19), new Range(4, 1, 4, 2)],
			[new Position(3, 19), new Range(3, 18, 3, 19), new Range(4, 1, 4, 2)],

			[new Position(4, 1), new Range(4, 1, 4, 2), new Range(3, 18, 3, 19)],
			[new Position(4, 2), new Range(4, 1, 4, 2), new Range(3, 18, 3, 19)],
			[new Position(4, 4), new Range(4, 4, 4, 5), new Range(5, 1, 5, 2)],
			[new Position(4, 5), new Range(4, 4, 4, 5), new Range(5, 1, 5, 2)],

			[new Position(5, 1), new Range(5, 1, 5, 2), new Range(4, 4, 4, 5)],
			[new Position(5, 2), new Range(5, 2, 5, 3), new Range(3, 17, 3, 18)],
			[new Position(5, 3), new Range(5, 3, 5, 4), new Range(3, 9, 3, 10)],
			[new Position(5, 4), new Range(5, 4, 5, 5), new Range(1, 11, 1, 12)],
			[new Position(5, 5), new Range(5, 4, 5, 5), new Range(1, 11, 1, 12)],
		];

		let isABracket = { 1: {}, 2: {}, 3: {}, 4: {}, 5: {} };
		for (let i = 0, len = brackets.length; i < len; i++) {
			let [testPos, b1, b2] = brackets[i];
			isBracket2(model, testPos, [b1, b2]);
			isABracket[testPos.lineNumber][testPos.column] = true;
		}

		for (let i = 1, len = model.getLineCount(); i <= len; i++) {
			let line = model.getLineContent(i);
			for (let j = 1, lenJ = line.length + 1; j <= lenJ; j++) {
				if (!isABracket[i].hasOwnProperty(j)) {
					isNotABracket(model, i, j);
				}
			}
		}

		model.dispose();
	});
});