function getAttributeLevelRanges()

in src/services/htmlSelectionRange.ts [132:207]


function getAttributeLevelRanges(document: TextDocument, currNode: Node, currOffset: number) : [number, number][] {
	const currNodeRange = Range.create(document.positionAt(currNode.start), document.positionAt(currNode.end));
	const currNodeText = document.getText(currNodeRange);
	const relativeOffset = currOffset - currNode.start;

	/**
	 * Tag level semantic selection
	 */

	const scanner = createScanner(currNodeText);
	let token = scanner.scan();

	/**
	 * For text like
	 * <div class="foo">bar</div>
	 */
	const positionOffset = currNode.start;

	const result = [];

	let isInsideAttribute = false;
	let attrStart = -1;
	while (token !== TokenType.EOS) {
		switch (token) {
			case TokenType.AttributeName: {
				if (relativeOffset < scanner.getTokenOffset()) {
					isInsideAttribute = false;
					break;
				}

				if (relativeOffset <= scanner.getTokenEnd()) {
					// `class`
					result.unshift([scanner.getTokenOffset(), scanner.getTokenEnd()]);
				}

				isInsideAttribute = true;
				attrStart = scanner.getTokenOffset();
				break;
			}
			case TokenType.AttributeValue: {
				if (!isInsideAttribute) {
					break;
				}

				const valueText = scanner.getTokenText();
				if (relativeOffset < scanner.getTokenOffset()) {
					// `class="foo"`
					result.push([attrStart, scanner.getTokenEnd()]);
					break;
				}

				if (relativeOffset >= scanner.getTokenOffset() && relativeOffset <= scanner.getTokenEnd()) {
					// `"foo"`
					result.unshift([scanner.getTokenOffset(), scanner.getTokenEnd()]);

					// `foo`
					if ((valueText[0] === `"` && valueText[valueText.length - 1] === `"`) || (valueText[0] === `'` && valueText[valueText.length - 1] === `'`)) {
						if (relativeOffset >= scanner.getTokenOffset() + 1 && relativeOffset <= scanner.getTokenEnd() - 1) {
							result.unshift([scanner.getTokenOffset() + 1, scanner.getTokenEnd() - 1]);
						}
					}

					// `class="foo"`
					result.push([attrStart, scanner.getTokenEnd()]);
				}

				break;
			}
		}
		token = scanner.scan();
	}

	return result.map(pair => {
		return [pair[0] + positionOffset, pair[1] + positionOffset];
	});
}