Refactor Emmet merge lines and select item commands (#113516)
This commit is contained in:
parent
c7dbab59ff
commit
c44b7d25d9
|
@ -4,8 +4,9 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { Node } from 'EmmetNode';
|
||||
import { getNode, parseDocument, validate } from './util';
|
||||
import { Node } from 'EmmetFlatNode';
|
||||
import { getFlatNode, offsetRangeToVsRange, validate } from './util';
|
||||
import { getRootNode } from './parseDocument';
|
||||
|
||||
export function mergeLines() {
|
||||
if (!validate(false) || !vscode.window.activeTextEditor) {
|
||||
|
@ -14,14 +15,14 @@ export function mergeLines() {
|
|||
|
||||
const editor = vscode.window.activeTextEditor;
|
||||
|
||||
let rootNode = parseDocument(editor.document);
|
||||
const rootNode = getRootNode(editor.document, true);
|
||||
if (!rootNode) {
|
||||
return;
|
||||
}
|
||||
|
||||
return editor.edit(editBuilder => {
|
||||
editor.selections.reverse().forEach(selection => {
|
||||
let textEdit = getRangesToReplace(editor.document, selection, rootNode!);
|
||||
const textEdit = getRangesToReplace(editor.document, selection, rootNode);
|
||||
if (textEdit) {
|
||||
editBuilder.replace(textEdit.range, textEdit.newText);
|
||||
}
|
||||
|
@ -30,25 +31,36 @@ export function mergeLines() {
|
|||
}
|
||||
|
||||
function getRangesToReplace(document: vscode.TextDocument, selection: vscode.Selection, rootNode: Node): vscode.TextEdit | undefined {
|
||||
let startNodeToUpdate: Node | null;
|
||||
let endNodeToUpdate: Node | null;
|
||||
let startNodeToUpdate: Node | undefined;
|
||||
let endNodeToUpdate: Node | undefined;
|
||||
|
||||
const selectionStart = document.offsetAt(selection.start);
|
||||
const selectionEnd = document.offsetAt(selection.end);
|
||||
if (selection.isEmpty) {
|
||||
startNodeToUpdate = endNodeToUpdate = getNode(rootNode, selection.start, true);
|
||||
startNodeToUpdate = endNodeToUpdate = getFlatNode(rootNode, selectionStart, true);
|
||||
} else {
|
||||
startNodeToUpdate = getNode(rootNode, selection.start, true);
|
||||
endNodeToUpdate = getNode(rootNode, selection.end, true);
|
||||
startNodeToUpdate = getFlatNode(rootNode, selectionStart, true);
|
||||
endNodeToUpdate = getFlatNode(rootNode, selectionEnd, true);
|
||||
}
|
||||
|
||||
if (!startNodeToUpdate || !endNodeToUpdate || startNodeToUpdate.start.line === endNodeToUpdate.end.line) {
|
||||
if (!startNodeToUpdate || !endNodeToUpdate) {
|
||||
return;
|
||||
}
|
||||
|
||||
let rangeToReplace = new vscode.Range(startNodeToUpdate.start, endNodeToUpdate.end);
|
||||
let textToReplaceWith = document.lineAt(startNodeToUpdate.start.line).text.substr(startNodeToUpdate.start.character);
|
||||
for (let i = startNodeToUpdate.start.line + 1; i <= endNodeToUpdate.end.line; i++) {
|
||||
const startPos = document.positionAt(startNodeToUpdate.start);
|
||||
const startLine = startPos.line;
|
||||
const startChar = startPos.character;
|
||||
const endPos = document.positionAt(endNodeToUpdate.end);
|
||||
const endLine = endPos.line;
|
||||
if (startLine === endLine) {
|
||||
return;
|
||||
}
|
||||
|
||||
const rangeToReplace = offsetRangeToVsRange(document, startNodeToUpdate.start, endNodeToUpdate.end);
|
||||
let textToReplaceWith = document.lineAt(startLine).text.substr(startChar);
|
||||
for (let i = startLine + 1; i <= endLine; i++) {
|
||||
textToReplaceWith += document.lineAt(i).text.trim();
|
||||
}
|
||||
|
||||
return new vscode.TextEdit(rangeToReplace, textToReplaceWith);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,17 +4,19 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { validate, parseDocument, isStyleSheet } from './util';
|
||||
import { validate, isStyleSheet } from './util';
|
||||
import { nextItemHTML, prevItemHTML } from './selectItemHTML';
|
||||
import { nextItemStylesheet, prevItemStylesheet } from './selectItemStylesheet';
|
||||
import { HtmlNode, CssNode } from 'EmmetNode';
|
||||
import { HtmlNode, CssNode } from 'EmmetFlatNode';
|
||||
import { getRootNode } from './parseDocument';
|
||||
|
||||
export function fetchSelectItem(direction: string): void {
|
||||
if (!validate() || !vscode.window.activeTextEditor) {
|
||||
return;
|
||||
}
|
||||
const editor = vscode.window.activeTextEditor;
|
||||
let rootNode = parseDocument(editor.document);
|
||||
const document = editor.document;
|
||||
const rootNode = getRootNode(document, true);
|
||||
if (!rootNode) {
|
||||
return;
|
||||
}
|
||||
|
@ -26,12 +28,16 @@ export function fetchSelectItem(direction: string): void {
|
|||
|
||||
let updatedSelection;
|
||||
if (isStyleSheet(editor.document.languageId)) {
|
||||
updatedSelection = direction === 'next' ? nextItemStylesheet(selectionStart, selectionEnd, <CssNode>rootNode!) : prevItemStylesheet(selectionStart, selectionEnd, <CssNode>rootNode!);
|
||||
updatedSelection = direction === 'next' ?
|
||||
nextItemStylesheet(document, selectionStart, selectionEnd, <CssNode>rootNode) :
|
||||
prevItemStylesheet(document, selectionStart, selectionEnd, <CssNode>rootNode);
|
||||
} else {
|
||||
updatedSelection = direction === 'next' ? nextItemHTML(selectionStart, selectionEnd, editor, <HtmlNode>rootNode!) : prevItemHTML(selectionStart, selectionEnd, editor, <HtmlNode>rootNode!);
|
||||
updatedSelection = direction === 'next' ?
|
||||
nextItemHTML(document, selectionStart, selectionEnd, <HtmlNode>rootNode) :
|
||||
prevItemHTML(document, selectionStart, selectionEnd, <HtmlNode>rootNode);
|
||||
}
|
||||
newSelections.push(updatedSelection ? updatedSelection : selection);
|
||||
});
|
||||
editor.selections = newSelections;
|
||||
editor.revealRange(editor.selections[editor.selections.length - 1]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,11 +4,12 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { getDeepestNode, findNextWord, findPrevWord, getHtmlNode, isNumber } from './util';
|
||||
import { HtmlNode } from 'EmmetNode';
|
||||
import { getDeepestFlatNode, findNextWord, findPrevWord, getHtmlFlatNode, offsetRangeToSelection } from './util';
|
||||
import { HtmlNode } from 'EmmetFlatNode';
|
||||
|
||||
export function nextItemHTML(selectionStart: vscode.Position, selectionEnd: vscode.Position, editor: vscode.TextEditor, rootNode: HtmlNode): vscode.Selection | undefined {
|
||||
let currentNode = getHtmlNode(editor.document, rootNode, selectionEnd, false);
|
||||
export function nextItemHTML(document: vscode.TextDocument, selectionStart: vscode.Position, selectionEnd: vscode.Position, rootNode: HtmlNode): vscode.Selection | undefined {
|
||||
const selectionEndOffset = document.offsetAt(selectionEnd);
|
||||
let currentNode = getHtmlFlatNode(document.getText(), rootNode, selectionEndOffset, false);
|
||||
let nextNode: HtmlNode | undefined = undefined;
|
||||
|
||||
if (!currentNode) {
|
||||
|
@ -17,13 +18,16 @@ export function nextItemHTML(selectionStart: vscode.Position, selectionEnd: vsco
|
|||
|
||||
if (currentNode.type !== 'comment') {
|
||||
// If cursor is in the tag name, select tag
|
||||
if (selectionEnd.isBefore(currentNode.open.start.translate(0, currentNode.name.length))) {
|
||||
return getSelectionFromNode(currentNode);
|
||||
if (currentNode.open &&
|
||||
selectionEndOffset < currentNode.open.start + currentNode.name.length) {
|
||||
return getSelectionFromNode(document, currentNode);
|
||||
}
|
||||
|
||||
// If cursor is in the open tag, look for attributes
|
||||
if (selectionEnd.isBefore(currentNode.open.end)) {
|
||||
let attrSelection = getNextAttribute(selectionStart, selectionEnd, currentNode);
|
||||
if (currentNode.open &&
|
||||
selectionEndOffset < currentNode.open.end) {
|
||||
const selectionStartOffset = document.offsetAt(selectionStart);
|
||||
const attrSelection = getNextAttribute(document, selectionStartOffset, selectionEndOffset, currentNode);
|
||||
if (attrSelection) {
|
||||
return attrSelection;
|
||||
}
|
||||
|
@ -31,12 +35,11 @@ export function nextItemHTML(selectionStart: vscode.Position, selectionEnd: vsco
|
|||
|
||||
// Get the first child of current node which is right after the cursor and is not a comment
|
||||
nextNode = currentNode.firstChild;
|
||||
while (nextNode && (selectionEnd.isAfterOrEqual(nextNode.end) || nextNode.type === 'comment')) {
|
||||
while (nextNode && (selectionEndOffset >= nextNode.end || nextNode.type === 'comment')) {
|
||||
nextNode = nextNode.nextSibling;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Get next sibling of current node which is not a comment. If none is found try the same on the parent
|
||||
while (!nextNode && currentNode) {
|
||||
if (currentNode.nextSibling) {
|
||||
|
@ -50,33 +53,36 @@ export function nextItemHTML(selectionStart: vscode.Position, selectionEnd: vsco
|
|||
}
|
||||
}
|
||||
|
||||
return nextNode && getSelectionFromNode(nextNode);
|
||||
return nextNode && getSelectionFromNode(document, nextNode);
|
||||
}
|
||||
|
||||
export function prevItemHTML(selectionStart: vscode.Position, selectionEnd: vscode.Position, editor: vscode.TextEditor, rootNode: HtmlNode): vscode.Selection | undefined {
|
||||
let currentNode = getHtmlNode(editor.document, rootNode, selectionStart, false);
|
||||
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;
|
||||
}
|
||||
|
||||
if (currentNode.type !== 'comment' && selectionStart.translate(0, -1).isAfter(currentNode.open.start)) {
|
||||
|
||||
if (selectionStart.isBefore(currentNode.open.end) || !currentNode.firstChild || selectionEnd.isBeforeOrEqual(currentNode.firstChild.start)) {
|
||||
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 && selectionStart.isAfterOrEqual(prevNode.nextSibling.end)) {
|
||||
while (prevNode.nextSibling && selectionStartOffset >= prevNode.nextSibling.end) {
|
||||
if (prevNode && prevNode.type !== 'comment') {
|
||||
oldOption = prevNode;
|
||||
}
|
||||
prevNode = prevNode.nextSibling;
|
||||
}
|
||||
|
||||
prevNode = <HtmlNode>getDeepestNode((prevNode && prevNode.type !== 'comment') ? prevNode : oldOption);
|
||||
prevNode = <HtmlNode>getDeepestFlatNode((prevNode && prevNode.type !== 'comment') ? prevNode : oldOption);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -84,7 +90,7 @@ export function prevItemHTML(selectionStart: vscode.Position, selectionEnd: vsco
|
|||
while (!prevNode && currentNode) {
|
||||
if (currentNode.previousSibling) {
|
||||
if (currentNode.previousSibling.type !== 'comment') {
|
||||
prevNode = <HtmlNode>getDeepestNode(currentNode.previousSibling);
|
||||
prevNode = <HtmlNode>getDeepestFlatNode(currentNode.previousSibling);
|
||||
} else {
|
||||
currentNode = currentNode.previousSibling;
|
||||
}
|
||||
|
@ -98,66 +104,66 @@ export function prevItemHTML(selectionStart: vscode.Position, selectionEnd: vsco
|
|||
return undefined;
|
||||
}
|
||||
|
||||
let attrSelection = getPrevAttribute(selectionStart, selectionEnd, prevNode);
|
||||
return attrSelection ? attrSelection : getSelectionFromNode(prevNode);
|
||||
const attrSelection = getPrevAttribute(document, selectionStartOffset, selectionEndOffset, prevNode);
|
||||
return attrSelection ? attrSelection : getSelectionFromNode(document, prevNode);
|
||||
}
|
||||
|
||||
function getSelectionFromNode(node: HtmlNode): vscode.Selection | undefined {
|
||||
function getSelectionFromNode(document: vscode.TextDocument, node: HtmlNode): vscode.Selection | undefined {
|
||||
if (node && node.open) {
|
||||
let selectionStart = (<vscode.Position>node.open.start).translate(0, 1);
|
||||
let selectionEnd = selectionStart.translate(0, node.name.length);
|
||||
|
||||
return new vscode.Selection(selectionStart, selectionEnd);
|
||||
const selectionStart = node.open.start + 1;
|
||||
const selectionEnd = selectionStart + node.name.length;
|
||||
return offsetRangeToSelection(document, selectionStart, selectionEnd);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function getNextAttribute(selectionStart: vscode.Position, selectionEnd: vscode.Position, node: HtmlNode): vscode.Selection | undefined {
|
||||
|
||||
function getNextAttribute(document: vscode.TextDocument, selectionStart: number, selectionEnd: number, node: HtmlNode): vscode.Selection | undefined {
|
||||
if (!node.attributes || node.attributes.length === 0 || node.type === 'comment') {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const attr of node.attributes) {
|
||||
if (selectionEnd.isBefore(attr.start)) {
|
||||
if (selectionEnd < attr.start) {
|
||||
// select full attr
|
||||
return new vscode.Selection(attr.start, attr.end);
|
||||
return offsetRangeToSelection(document, attr.start, attr.end);
|
||||
}
|
||||
|
||||
if (!attr.value || (<vscode.Position>attr.value.start).isEqual(attr.value.end)) {
|
||||
if (!attr.value || attr.value.start === attr.value.end) {
|
||||
// No attr value to select
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((selectionStart.isEqual(attr.start) && selectionEnd.isEqual(attr.end)) || selectionEnd.isBefore(attr.value.start)) {
|
||||
if ((selectionStart === attr.start && selectionEnd === attr.end) ||
|
||||
selectionEnd < attr.value.start) {
|
||||
// cursor is in attr name, so select full attr value
|
||||
return new vscode.Selection(attr.value.start, attr.value.end);
|
||||
return offsetRangeToSelection(document, attr.value.start, attr.value.end);
|
||||
}
|
||||
|
||||
// Fetch the next word in the attr value
|
||||
|
||||
if (attr.value.toString().indexOf(' ') === -1) {
|
||||
// attr value does not have space, so no next word to find
|
||||
continue;
|
||||
}
|
||||
|
||||
let pos: number | undefined = undefined;
|
||||
if (selectionStart.isEqual(attr.value.start) && selectionEnd.isEqual(attr.value.end)) {
|
||||
if (selectionStart === attr.value.start && selectionEnd === attr.value.end) {
|
||||
pos = -1;
|
||||
}
|
||||
if (pos === undefined && selectionEnd.isBefore(attr.end)) {
|
||||
pos = selectionEnd.character - attr.value.start.character - 1;
|
||||
if (pos === undefined && selectionEnd < attr.end) {
|
||||
const selectionEndCharacter = document.positionAt(selectionEnd).character;
|
||||
const attrValueStartCharacter = document.positionAt(attr.value.start).character;
|
||||
pos = selectionEndCharacter - attrValueStartCharacter - 1;
|
||||
}
|
||||
|
||||
if (pos !== undefined) {
|
||||
let [newSelectionStartOffset, newSelectionEndOffset] = findNextWord(attr.value.toString(), pos);
|
||||
if (!isNumber(newSelectionStartOffset) || !isNumber(newSelectionEndOffset)) {
|
||||
const [newSelectionStartOffset, newSelectionEndOffset] = findNextWord(attr.value.toString(), pos);
|
||||
if (newSelectionStartOffset === undefined || newSelectionEndOffset === undefined) {
|
||||
return;
|
||||
}
|
||||
if (newSelectionStartOffset >= 0 && newSelectionEndOffset >= 0) {
|
||||
const newSelectionStart = (<vscode.Position>attr.value.start).translate(0, newSelectionStartOffset);
|
||||
const newSelectionEnd = (<vscode.Position>attr.value.start).translate(0, newSelectionEndOffset);
|
||||
return new vscode.Selection(newSelectionStart, newSelectionEnd);
|
||||
const newSelectionStart = attr.value.start + newSelectionStartOffset;
|
||||
const newSelectionEnd = attr.value.start + newSelectionEndOffset;
|
||||
return offsetRangeToSelection(document, newSelectionStart, newSelectionEnd);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -166,44 +172,44 @@ function getNextAttribute(selectionStart: vscode.Position, selectionEnd: vscode.
|
|||
return;
|
||||
}
|
||||
|
||||
function getPrevAttribute(selectionStart: vscode.Position, selectionEnd: vscode.Position, node: HtmlNode): vscode.Selection | undefined {
|
||||
|
||||
function getPrevAttribute(document: vscode.TextDocument, selectionStart: number, selectionEnd: number, node: HtmlNode): vscode.Selection | undefined {
|
||||
if (!node.attributes || node.attributes.length === 0 || node.type === 'comment') {
|
||||
return;
|
||||
}
|
||||
|
||||
for (let i = node.attributes.length - 1; i >= 0; i--) {
|
||||
let attr = node.attributes[i];
|
||||
|
||||
if (selectionStart.isBeforeOrEqual(attr.start)) {
|
||||
const attr = node.attributes[i];
|
||||
if (selectionStart <= attr.start) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!attr.value || (<vscode.Position>attr.value.start).isEqual(attr.value.end) || selectionStart.isBefore(attr.value.start)) {
|
||||
if (!attr.value || attr.value.start === attr.value.end || selectionStart < attr.value.start) {
|
||||
// select full attr
|
||||
return new vscode.Selection(attr.start, attr.end);
|
||||
return offsetRangeToSelection(document, attr.start, attr.end);
|
||||
}
|
||||
|
||||
if (selectionStart.isEqual(attr.value.start)) {
|
||||
if (selectionEnd.isAfterOrEqual(attr.value.end)) {
|
||||
if (selectionStart === attr.value.start) {
|
||||
if (selectionEnd >= attr.value.end) {
|
||||
// select full attr
|
||||
return new vscode.Selection(attr.start, attr.end);
|
||||
return offsetRangeToSelection(document, attr.start, attr.end);
|
||||
}
|
||||
// select attr value
|
||||
return new vscode.Selection(attr.value.start, attr.value.end);
|
||||
return offsetRangeToSelection(document, attr.value.start, attr.value.end);
|
||||
}
|
||||
|
||||
// Fetch the prev word in the attr value
|
||||
|
||||
let pos = selectionStart.isAfter(attr.value.end) ? attr.value.toString().length : selectionStart.character - attr.value.start.character;
|
||||
let [newSelectionStartOffset, newSelectionEndOffset] = findPrevWord(attr.value.toString(), pos);
|
||||
if (!isNumber(newSelectionStartOffset) || !isNumber(newSelectionEndOffset)) {
|
||||
const selectionStartCharacter = document.positionAt(selectionStart).character;
|
||||
const attrValueStartCharacter = document.positionAt(attr.value.start).character;
|
||||
const pos = selectionStart > attr.value.end ? attr.value.toString().length :
|
||||
selectionStartCharacter - attrValueStartCharacter;
|
||||
const [newSelectionStartOffset, newSelectionEndOffset] = findPrevWord(attr.value.toString(), pos);
|
||||
if (newSelectionStartOffset === undefined || newSelectionEndOffset === undefined) {
|
||||
return;
|
||||
}
|
||||
if (newSelectionStartOffset >= 0 && newSelectionEndOffset >= 0) {
|
||||
const newSelectionStart = (<vscode.Position>attr.value.start).translate(0, newSelectionStartOffset);
|
||||
const newSelectionEnd = (<vscode.Position>attr.value.start).translate(0, newSelectionEndOffset);
|
||||
return new vscode.Selection(newSelectionStart, newSelectionEnd);
|
||||
const newSelectionStart = attr.value.start + newSelectionStartOffset;
|
||||
const newSelectionEnd = attr.value.start + newSelectionEndOffset;
|
||||
return offsetRangeToSelection(document, newSelectionStart, newSelectionEnd);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,11 +4,13 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { getDeepestNode, findNextWord, findPrevWord, getNode } from './util';
|
||||
import { Node, CssNode, Rule, Property } from 'EmmetNode';
|
||||
import { getDeepestFlatNode, findNextWord, findPrevWord, getFlatNode, offsetRangeToSelection } from './util';
|
||||
import { Node, CssNode, Rule, Property } from 'EmmetFlatNode';
|
||||
|
||||
export function nextItemStylesheet(startOffset: vscode.Position, endOffset: vscode.Position, rootNode: Node): vscode.Selection | undefined {
|
||||
let currentNode = <CssNode>getNode(rootNode, endOffset, true);
|
||||
export function nextItemStylesheet(document: vscode.TextDocument, startPosition: vscode.Position, endPosition: vscode.Position, rootNode: Node): vscode.Selection | undefined {
|
||||
const startOffset = document.offsetAt(startPosition);
|
||||
const endOffset = document.offsetAt(endPosition);
|
||||
let currentNode: CssNode | undefined = <CssNode>getFlatNode(rootNode, endOffset, true);
|
||||
if (!currentNode) {
|
||||
currentNode = <CssNode>rootNode;
|
||||
}
|
||||
|
@ -16,27 +18,31 @@ export function nextItemStylesheet(startOffset: vscode.Position, endOffset: vsco
|
|||
return;
|
||||
}
|
||||
// Full property is selected, so select full property value next
|
||||
if (currentNode.type === 'property' && startOffset.isEqual(currentNode.start) && endOffset.isEqual(currentNode.end)) {
|
||||
return getSelectionFromProperty(currentNode, startOffset, endOffset, true, 'next');
|
||||
if (currentNode.type === 'property' &&
|
||||
startOffset === currentNode.start &&
|
||||
endOffset === currentNode.end) {
|
||||
return getSelectionFromProperty(document, currentNode, startOffset, endOffset, true, 'next');
|
||||
}
|
||||
|
||||
// Part or whole of propertyValue is selected, so select the next word in the propertyValue
|
||||
if (currentNode.type === 'property' && startOffset.isAfterOrEqual((<Property>currentNode).valueToken.start) && endOffset.isBeforeOrEqual((<Property>currentNode).valueToken.end)) {
|
||||
let singlePropertyValue = getSelectionFromProperty(currentNode, startOffset, endOffset, false, 'next');
|
||||
if (currentNode.type === 'property' &&
|
||||
startOffset >= (<Property>currentNode).valueToken.start &&
|
||||
endOffset <= (<Property>currentNode).valueToken.end) {
|
||||
let singlePropertyValue = getSelectionFromProperty(document, currentNode, startOffset, endOffset, false, 'next');
|
||||
if (singlePropertyValue) {
|
||||
return singlePropertyValue;
|
||||
}
|
||||
}
|
||||
|
||||
// Cursor is in the selector or in a property
|
||||
if ((currentNode.type === 'rule' && endOffset.isBefore((<Rule>currentNode).selectorToken.end))
|
||||
|| (currentNode.type === 'property' && endOffset.isBefore((<Property>currentNode).valueToken.end))) {
|
||||
return getSelectionFromNode(currentNode);
|
||||
if ((currentNode.type === 'rule' && endOffset < (<Rule>currentNode).selectorToken.end)
|
||||
|| (currentNode.type === 'property' && endOffset < (<Property>currentNode).valueToken.end)) {
|
||||
return getSelectionFromNode(document, currentNode);
|
||||
}
|
||||
|
||||
// Get the first child of current node which is right after the cursor
|
||||
let nextNode = currentNode.firstChild;
|
||||
while (nextNode && endOffset.isAfterOrEqual(nextNode.end)) {
|
||||
while (nextNode && endOffset >= nextNode.end) {
|
||||
nextNode = nextNode.nextSibling;
|
||||
}
|
||||
|
||||
|
@ -46,12 +52,13 @@ export function nextItemStylesheet(startOffset: vscode.Position, endOffset: vsco
|
|||
currentNode = currentNode.parent;
|
||||
}
|
||||
|
||||
return getSelectionFromNode(nextNode);
|
||||
|
||||
return nextNode ? getSelectionFromNode(document, nextNode) : undefined;
|
||||
}
|
||||
|
||||
export function prevItemStylesheet(startOffset: vscode.Position, endOffset: vscode.Position, rootNode: CssNode): vscode.Selection | undefined {
|
||||
let currentNode = <CssNode>getNode(rootNode, startOffset, false);
|
||||
export function prevItemStylesheet(document: vscode.TextDocument, startPosition: vscode.Position, endPosition: vscode.Position, rootNode: CssNode): vscode.Selection | undefined {
|
||||
const startOffset = document.offsetAt(startPosition);
|
||||
const endOffset = document.offsetAt(endPosition);
|
||||
let currentNode = <CssNode>getFlatNode(rootNode, startOffset, false);
|
||||
if (!currentNode) {
|
||||
currentNode = rootNode;
|
||||
}
|
||||
|
@ -60,70 +67,80 @@ export function prevItemStylesheet(startOffset: vscode.Position, endOffset: vsco
|
|||
}
|
||||
|
||||
// Full property value is selected, so select the whole property next
|
||||
if (currentNode.type === 'property' && startOffset.isEqual((<Property>currentNode).valueToken.start) && endOffset.isEqual((<Property>currentNode).valueToken.end)) {
|
||||
return getSelectionFromNode(currentNode);
|
||||
if (currentNode.type === 'property' &&
|
||||
startOffset === (<Property>currentNode).valueToken.start &&
|
||||
endOffset === (<Property>currentNode).valueToken.end) {
|
||||
return getSelectionFromNode(document, currentNode);
|
||||
}
|
||||
|
||||
// Part of propertyValue is selected, so select the prev word in the propertyValue
|
||||
if (currentNode.type === 'property' && startOffset.isAfterOrEqual((<Property>currentNode).valueToken.start) && endOffset.isBeforeOrEqual((<Property>currentNode).valueToken.end)) {
|
||||
let singlePropertyValue = getSelectionFromProperty(currentNode, startOffset, endOffset, false, 'prev');
|
||||
if (currentNode.type === 'property' &&
|
||||
startOffset >= (<Property>currentNode).valueToken.start &&
|
||||
endOffset <= (<Property>currentNode).valueToken.end) {
|
||||
let singlePropertyValue = getSelectionFromProperty(document, currentNode, startOffset, endOffset, false, 'prev');
|
||||
if (singlePropertyValue) {
|
||||
return singlePropertyValue;
|
||||
}
|
||||
}
|
||||
|
||||
if (currentNode.type === 'property' || !currentNode.firstChild || (currentNode.type === 'rule' && startOffset.isBeforeOrEqual(currentNode.firstChild.start))) {
|
||||
return getSelectionFromNode(currentNode);
|
||||
if (currentNode.type === 'property' || !currentNode.firstChild ||
|
||||
(currentNode.type === 'rule' && startOffset <= currentNode.firstChild.start)) {
|
||||
return getSelectionFromNode(document, currentNode);
|
||||
}
|
||||
|
||||
// Select the child that appears just before the cursor
|
||||
let prevNode = currentNode.firstChild;
|
||||
while (prevNode.nextSibling && startOffset.isAfterOrEqual(prevNode.nextSibling.end)) {
|
||||
let prevNode: CssNode | undefined = currentNode.firstChild;
|
||||
while (prevNode.nextSibling && startOffset >= prevNode.nextSibling.end) {
|
||||
prevNode = prevNode.nextSibling;
|
||||
}
|
||||
prevNode = <CssNode>getDeepestNode(prevNode);
|
||||
|
||||
return getSelectionFromProperty(prevNode, startOffset, endOffset, false, 'prev');
|
||||
prevNode = <CssNode | undefined>getDeepestFlatNode(prevNode);
|
||||
|
||||
return getSelectionFromProperty(document, prevNode, startOffset, endOffset, false, 'prev');
|
||||
}
|
||||
|
||||
|
||||
function getSelectionFromNode(node: Node): vscode.Selection | undefined {
|
||||
function getSelectionFromNode(document: vscode.TextDocument, node: Node | undefined): vscode.Selection | undefined {
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
|
||||
let nodeToSelect = node.type === 'rule' ? (<Rule>node).selectorToken : node;
|
||||
return new vscode.Selection(nodeToSelect.start, nodeToSelect.end);
|
||||
const nodeToSelect = node.type === 'rule' ? (<Rule>node).selectorToken : node;
|
||||
return offsetRangeToSelection(document, nodeToSelect.start, nodeToSelect.end);
|
||||
}
|
||||
|
||||
|
||||
function getSelectionFromProperty(node: Node, selectionStart: vscode.Position, selectionEnd: vscode.Position, selectFullValue: boolean, direction: string): vscode.Selection | undefined {
|
||||
function getSelectionFromProperty(document: vscode.TextDocument, node: Node | undefined, selectionStart: number, selectionEnd: number, selectFullValue: boolean, direction: string): vscode.Selection | undefined {
|
||||
if (!node || node.type !== 'property') {
|
||||
return;
|
||||
}
|
||||
const propertyNode = <Property>node;
|
||||
|
||||
let propertyValue = propertyNode.valueToken.stream.substring(propertyNode.valueToken.start, propertyNode.valueToken.end);
|
||||
selectFullValue = selectFullValue || (direction === 'prev' && selectionStart.isEqual(propertyNode.valueToken.start) && selectionEnd.isBefore(propertyNode.valueToken.end));
|
||||
selectFullValue = selectFullValue ||
|
||||
(direction === 'prev' && selectionStart === propertyNode.valueToken.start && selectionEnd < propertyNode.valueToken.end);
|
||||
|
||||
if (selectFullValue) {
|
||||
return new vscode.Selection(propertyNode.valueToken.start, propertyNode.valueToken.end);
|
||||
return offsetRangeToSelection(document, propertyNode.valueToken.start, propertyNode.valueToken.end);
|
||||
}
|
||||
|
||||
let pos: number = -1;
|
||||
if (direction === 'prev') {
|
||||
if (selectionStart.isEqual(propertyNode.valueToken.start)) {
|
||||
if (selectionStart === propertyNode.valueToken.start) {
|
||||
return;
|
||||
}
|
||||
pos = selectionStart.isAfter(propertyNode.valueToken.end) ? propertyValue.length : selectionStart.character - propertyNode.valueToken.start.character;
|
||||
}
|
||||
|
||||
if (direction === 'next') {
|
||||
if (selectionEnd.isEqual(propertyNode.valueToken.end) && (selectionStart.isAfter(propertyNode.valueToken.start) || propertyValue.indexOf(' ') === -1)) {
|
||||
const selectionStartChar = document.positionAt(selectionStart).character;
|
||||
const tokenStartChar = document.positionAt(propertyNode.valueToken.start).character;
|
||||
pos = selectionStart > propertyNode.valueToken.end ? propertyValue.length :
|
||||
selectionStartChar - tokenStartChar;
|
||||
} else if (direction === 'next') {
|
||||
if (selectionEnd === propertyNode.valueToken.end &&
|
||||
(selectionStart > propertyNode.valueToken.start || !propertyValue.includes(' '))) {
|
||||
return;
|
||||
}
|
||||
pos = selectionEnd.isEqual(propertyNode.valueToken.end) ? -1 : selectionEnd.character - propertyNode.valueToken.start.character - 1;
|
||||
const selectionEndChar = document.positionAt(selectionEnd).character;
|
||||
const tokenStartChar = document.positionAt(propertyNode.valueToken.start).character;
|
||||
pos = selectionEnd === propertyNode.valueToken.end ? -1 :
|
||||
selectionEndChar - tokenStartChar - 1;
|
||||
}
|
||||
|
||||
|
||||
|
@ -132,8 +149,9 @@ function getSelectionFromProperty(node: Node, selectionStart: vscode.Position, s
|
|||
return;
|
||||
}
|
||||
|
||||
const newSelectionStart = (<vscode.Position>propertyNode.valueToken.start).translate(0, newSelectionStartOffset);
|
||||
const newSelectionEnd = (<vscode.Position>propertyNode.valueToken.start).translate(0, newSelectionEndOffset);
|
||||
const tokenStart = document.positionAt(propertyNode.valueToken.start);
|
||||
const newSelectionStart = tokenStart.translate(0, newSelectionStartOffset);
|
||||
const newSelectionEnd = tokenStart.translate(0, newSelectionEndOffset);
|
||||
|
||||
return new vscode.Selection(newSelectionStart, newSelectionEnd);
|
||||
}
|
||||
|
|
|
@ -473,6 +473,21 @@ export function getDeepestNode(node: Node | undefined): Node | undefined {
|
|||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the deepest non comment node under given node
|
||||
*/
|
||||
export function getDeepestFlatNode(node: FlatNode | undefined): FlatNode | undefined {
|
||||
if (!node || !node.children || node.children.length === 0 || !node.children.find(x => x.type !== 'comment')) {
|
||||
return node;
|
||||
}
|
||||
for (let i = node.children.length - 1; i >= 0; i--) {
|
||||
if (node.children[i].type !== 'comment') {
|
||||
return getDeepestFlatNode(node.children[i]);
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function findNextWord(propertyValue: string, pos: number): [number | undefined, number | undefined] {
|
||||
|
||||
let foundSpace = pos === -1;
|
||||
|
|
Loading…
Reference in a new issue