Path: blob/main/extensions/emmet/src/selectItemStylesheet.ts
4772 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import * as vscode from 'vscode';6import { getDeepestFlatNode, findNextWord, findPrevWord, getFlatNode, offsetRangeToSelection } from './util';7import { Node, CssNode, Rule, Property } from 'EmmetFlatNode';89export function nextItemStylesheet(document: vscode.TextDocument, startPosition: vscode.Position, endPosition: vscode.Position, rootNode: Node): vscode.Selection | undefined {10const startOffset = document.offsetAt(startPosition);11const endOffset = document.offsetAt(endPosition);12let currentNode: CssNode | undefined = <CssNode>getFlatNode(rootNode, endOffset, true);13if (!currentNode) {14currentNode = <CssNode>rootNode;15}16if (!currentNode) {17return;18}19// Full property is selected, so select full property value next20if (currentNode.type === 'property' &&21startOffset === currentNode.start &&22endOffset === currentNode.end) {23return getSelectionFromProperty(document, currentNode, startOffset, endOffset, true, 'next');24}2526// Part or whole of propertyValue is selected, so select the next word in the propertyValue27if (currentNode.type === 'property' &&28startOffset >= (<Property>currentNode).valueToken.start &&29endOffset <= (<Property>currentNode).valueToken.end) {30const singlePropertyValue = getSelectionFromProperty(document, currentNode, startOffset, endOffset, false, 'next');31if (singlePropertyValue) {32return singlePropertyValue;33}34}3536// Cursor is in the selector or in a property37if ((currentNode.type === 'rule' && endOffset < (<Rule>currentNode).selectorToken.end)38|| (currentNode.type === 'property' && endOffset < (<Property>currentNode).valueToken.end)) {39return getSelectionFromNode(document, currentNode);40}4142// Get the first child of current node which is right after the cursor43let nextNode = currentNode.firstChild;44while (nextNode && endOffset >= nextNode.end) {45nextNode = nextNode.nextSibling;46}4748// Get next sibling of current node or the parent49while (!nextNode && currentNode) {50nextNode = currentNode.nextSibling;51currentNode = currentNode.parent;52}5354return nextNode ? getSelectionFromNode(document, nextNode) : undefined;55}5657export function prevItemStylesheet(document: vscode.TextDocument, startPosition: vscode.Position, endPosition: vscode.Position, rootNode: CssNode): vscode.Selection | undefined {58const startOffset = document.offsetAt(startPosition);59const endOffset = document.offsetAt(endPosition);60let currentNode = <CssNode>getFlatNode(rootNode, startOffset, false);61if (!currentNode) {62currentNode = rootNode;63}64if (!currentNode) {65return;66}6768// Full property value is selected, so select the whole property next69if (currentNode.type === 'property' &&70startOffset === (<Property>currentNode).valueToken.start &&71endOffset === (<Property>currentNode).valueToken.end) {72return getSelectionFromNode(document, currentNode);73}7475// Part of propertyValue is selected, so select the prev word in the propertyValue76if (currentNode.type === 'property' &&77startOffset >= (<Property>currentNode).valueToken.start &&78endOffset <= (<Property>currentNode).valueToken.end) {79const singlePropertyValue = getSelectionFromProperty(document, currentNode, startOffset, endOffset, false, 'prev');80if (singlePropertyValue) {81return singlePropertyValue;82}83}8485if (currentNode.type === 'property' || !currentNode.firstChild ||86(currentNode.type === 'rule' && startOffset <= currentNode.firstChild.start)) {87return getSelectionFromNode(document, currentNode);88}8990// Select the child that appears just before the cursor91let prevNode: CssNode | undefined = currentNode.firstChild;92while (prevNode.nextSibling && startOffset >= prevNode.nextSibling.end) {93prevNode = prevNode.nextSibling;94}95prevNode = <CssNode | undefined>getDeepestFlatNode(prevNode);9697return getSelectionFromProperty(document, prevNode, startOffset, endOffset, false, 'prev');98}99100101function getSelectionFromNode(document: vscode.TextDocument, node: Node | undefined): vscode.Selection | undefined {102if (!node) {103return;104}105106const nodeToSelect = node.type === 'rule' ? (<Rule>node).selectorToken : node;107return offsetRangeToSelection(document, nodeToSelect.start, nodeToSelect.end);108}109110111function getSelectionFromProperty(document: vscode.TextDocument, node: Node | undefined, selectionStart: number, selectionEnd: number, selectFullValue: boolean, direction: string): vscode.Selection | undefined {112if (!node || node.type !== 'property') {113return;114}115const propertyNode = <Property>node;116117const propertyValue = propertyNode.valueToken.stream.substring(propertyNode.valueToken.start, propertyNode.valueToken.end);118selectFullValue = selectFullValue ||119(direction === 'prev' && selectionStart === propertyNode.valueToken.start && selectionEnd < propertyNode.valueToken.end);120121if (selectFullValue) {122return offsetRangeToSelection(document, propertyNode.valueToken.start, propertyNode.valueToken.end);123}124125let pos: number = -1;126if (direction === 'prev') {127if (selectionStart === propertyNode.valueToken.start) {128return;129}130const selectionStartChar = document.positionAt(selectionStart).character;131const tokenStartChar = document.positionAt(propertyNode.valueToken.start).character;132pos = selectionStart > propertyNode.valueToken.end ? propertyValue.length :133selectionStartChar - tokenStartChar;134} else if (direction === 'next') {135if (selectionEnd === propertyNode.valueToken.end &&136(selectionStart > propertyNode.valueToken.start || !propertyValue.includes(' '))) {137return;138}139const selectionEndChar = document.positionAt(selectionEnd).character;140const tokenStartChar = document.positionAt(propertyNode.valueToken.start).character;141pos = selectionEnd === propertyNode.valueToken.end ? -1 :142selectionEndChar - tokenStartChar - 1;143}144145146const [newSelectionStartOffset, newSelectionEndOffset] = direction === 'prev' ? findPrevWord(propertyValue, pos) : findNextWord(propertyValue, pos);147if (!newSelectionStartOffset && !newSelectionEndOffset) {148return;149}150151const tokenStart = document.positionAt(propertyNode.valueToken.start);152const newSelectionStart = tokenStart.translate(0, newSelectionStartOffset);153const newSelectionEnd = tokenStart.translate(0, newSelectionEndOffset);154155return new vscode.Selection(newSelectionStart, newSelectionEnd);156}157158159160161162