extensions/emmet/src/selectItemHTML.ts/prevItemHTML
export function prevItemHTML(document: vscode.TextDocument, selectionStart: vscode.Position, selectionEnd: vscode.Position, rootNode: HtmlNode): vscode.Selection | undefined {
	const selectionStartOffset = document.offsetAt(selectionStart);
	let currentNode = getHtmlFlatNode(document.getText(), rootNode, selectionStartOffset, false);
	let prevNode: HtmlNode | undefined = undefined;

	if (!currentNode) {
		return;
	}

	const selectionEndOffset = document.offsetAt(selectionEnd);
	if (currentNode.open &&
		currentNode.type !== 'comment' &&
		selectionStartOffset - 1 > currentNode.open.start) {
		if (selectionStartOffset < currentNode.open.end || !currentNode.firstChild || selectionEndOffset <= currentNode.firstChild.start) {
			prevNode = currentNode;
		} else {
			// Select the child that appears just before the cursor and is not a comment
			prevNode = currentNode.firstChild;
			let oldOption: HtmlNode | undefined = undefined;
			while (prevNode.nextSibling && selectionStartOffset >= prevNode.nextSibling.end) {
				if (prevNode && prevNode.type !== 'comment') {
					oldOption = prevNode;
				}
				prevNode = prevNode.nextSibling;
			}

			prevNode = <HtmlNode>getDeepestFlatNode((prevNode && prevNode.type !== 'comment') ? prevNode : oldOption);
		}
	}

	// Select previous sibling which is not a comment. If none found, then select parent
	while (!prevNode && currentNode) {
		if (currentNode.previousSibling) {
			if (currentNode.previousSibling.type !== 'comment') {
				prevNode = <HtmlNode>getDeepestFlatNode(currentNode.previousSibling);
			} else {
				currentNode = currentNode.previousSibling;
			}
		} else {
			prevNode = currentNode.parent;
		}

	}

	if (!prevNode) {
		return undefined;
	}

	const attrSelection = getPrevAttribute(document, selectionStartOffset, selectionEndOffset, prevNode);
	return attrSelection ? attrSelection : getSelectionFromNode(document, prevNode);
}