All files / src/services jsonSelectionRanges.ts

97.22% Statements 35/36
95.65% Branches 22/23
100% Functions 4/4
97.06% Lines 33/34

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78          1x     1x   1x     29x 29x   29x   29x 46x         27x 27x 18x   27x 27x         19x 19x   46x 10x 10x 7x     46x   29x 29x 71x   29x     29x       71x     29x     10x 10x 10x 7x   3x     29x              
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/
 
import { Range, Position, SelectionRange, TextDocument } from '../jsonLanguageTypes';
 
import { JSONDocument } from '../parser/jsonParser';
import { SyntaxKind, createScanner } from 'jsonc-parser';
 
export function getSelectionRanges(document: TextDocument, positions: Position[], doc: JSONDocument): SelectionRange[] {
 
	function getSelectionRange(position: Position): SelectionRange {
		let offset = document.offsetAt(position);
		let node = doc.getNodeFromOffset(offset, true);
 
		const result: Range[] = [];
 
		while (node) {
			switch (node.type) {
				case 'string':
				case 'object':
				case 'array':
					// range without ", [ or {
					const cStart = node.offset + 1, cEnd = node.offset + node.length - 1;
					if (cStart < cEnd && offset >= cStart && offset <= cEnd) {
						result.push(newRange(cStart, cEnd));
					}
					result.push(newRange(node.offset, node.offset + node.length));
					break;
				case 'number':
				case 'boolean':
				case 'null':
				case 'property':
					result.push(newRange(node.offset, node.offset + node.length));
					break;
			}
			if (node.type === 'property' || node.parent && node.parent.type === 'array') {
				const afterCommaOffset = getOffsetAfterNextToken(node.offset + node.length, SyntaxKind.CommaToken);
				if (afterCommaOffset !== -1) {
					result.push(newRange(node.offset, afterCommaOffset));
				}
			}
			node = node.parent;
		}
		let current: SelectionRange | undefined = undefined;
		for (let index = result.length - 1; index >= 0; index--) {
			current = SelectionRange.create(result[index], current);
		}
		Iif (!current) {
			current = SelectionRange.create(Range.create(position, position));
		}
		return current;
	}
 
	function newRange(start: number, end: number): Range {
		return Range.create(document.positionAt(start), document.positionAt(end));
	}
 
	const scanner = createScanner(document.getText(), true);
 
	function getOffsetAfterNextToken(offset: number, expectedToken: SyntaxKind): number {
		scanner.setPosition(offset);
		let token = scanner.scan();
		if (token === expectedToken) {
			return scanner.getTokenOffset() + scanner.getTokenLength();
		}
		return -1;
	}
 
	return positions.map(getSelectionRange);
}