From 01e289eac392c7a2e45da386e3ce3333db327916 Mon Sep 17 00:00:00 2001 From: Ramya Achutha Rao Date: Thu, 25 May 2017 23:50:49 -0700 Subject: [PATCH] Select single words in property value --- extensions/emmet/src/selectItemHTML.ts | 65 +++++++++++++++-- extensions/emmet/src/selectItemStylesheet.ts | 75 ++++++++++++++++--- extensions/emmet/src/util.ts | 76 ++++++++++++++++++++ 3 files changed, 200 insertions(+), 16 deletions(-) diff --git a/extensions/emmet/src/selectItemHTML.ts b/extensions/emmet/src/selectItemHTML.ts index 79d0a585e0a..2c0be704ead 100644 --- a/extensions/emmet/src/selectItemHTML.ts +++ b/extensions/emmet/src/selectItemHTML.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import { getNode, getDeepestNode } from './util'; +import { getNode, getDeepestNode, findNextWord, findPrevWord } from './util'; import Node from '@emmetio/node'; export function nextItemHTML(selection: vscode.Selection, editor: vscode.TextEditor, rootNode: Node): vscode.Selection { @@ -99,10 +99,40 @@ function getNextAttribute(selection: vscode.Selection, document: vscode.TextDocu return new vscode.Selection(document.positionAt(attr.start), document.positionAt(attr.end)); } - if ((attr.value.start !== attr.value.end) && ((selectionStart === attr.start && selectionEnd === attr.end) || selectionEnd < attr.end - 1)) { - // select attr value + if (attr.value.start === attr.value.end) { + // No attr value to select + continue; + } + + 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(document.positionAt(attr.value.start), document.positionAt(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 = undefined; + if (selectionStart === attr.value.start && selectionEnd === attr.value.end) { + pos = -1; + } + if (pos === undefined && selectionEnd < attr.end) { + pos = selectionEnd - attr.value.start - 1; + } + + if (pos !== undefined) { + let [newSelectionStart, newSelectionEnd] = findNextWord(attr.value.toString(), pos); + if (newSelectionStart >= 0 && newSelectionEnd >= 0) { + newSelectionStart += attr.value.start; + newSelectionEnd += attr.value.start; + return new vscode.Selection(document.positionAt(newSelectionStart), document.positionAt(newSelectionEnd)); + } + } + } } @@ -113,18 +143,39 @@ function getPrevAttribute(selection: vscode.Selection, document: vscode.TextDocu } let selectionStart = document.offsetAt(selection.anchor); + let selectionEnd = document.offsetAt(selection.active); for (let i = node.attributes.length - 1; i >= 0; i--) { let attr = node.attributes[i]; - if (selectionStart > attr.value.start) { - // select attr value - return new vscode.Selection(document.positionAt(attr.value.start), document.positionAt(attr.value.end)); + if (selectionStart <= attr.start) { + continue; } - if (selectionStart > attr.start) { + if (attr.value.start === attr.value.end || selectionStart < attr.value.start) { // select full attr return new vscode.Selection(document.positionAt(attr.start), document.positionAt(attr.end)); } + + if (selectionStart === attr.value.start) { + if (selectionEnd >= attr.value.end) { + // select full attr + return new vscode.Selection(document.positionAt(attr.start), document.positionAt(attr.end)); + } + // select attr value + return new vscode.Selection(document.positionAt(attr.value.start), document.positionAt(attr.value.end)); + } + + // Fetch the prev word in the attr value + + let pos = selectionStart > attr.value.end ? attr.value.toString().length : selectionStart - attr.value.start; + let [newSelectionStart, newSelectionEnd] = findPrevWord(attr.value.toString(), pos); + if (newSelectionStart >= 0 && newSelectionEnd >= 0) { + newSelectionStart += attr.value.start; + newSelectionEnd += attr.value.start; + return new vscode.Selection(document.positionAt(newSelectionStart), document.positionAt(newSelectionEnd)); + } + + } } \ No newline at end of file diff --git a/extensions/emmet/src/selectItemStylesheet.ts b/extensions/emmet/src/selectItemStylesheet.ts index d0167a2d8b9..e3144cbf666 100644 --- a/extensions/emmet/src/selectItemStylesheet.ts +++ b/extensions/emmet/src/selectItemStylesheet.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import { getNode, getDeepestNode } from './util'; +import { getNode, getDeepestNode, findNextWord, findPrevWord } from './util'; import Node from '@emmetio/node'; export function nextItemStylesheet(selection: vscode.Selection, editor: vscode.TextEditor, rootNode: Node): vscode.Selection { @@ -12,9 +12,17 @@ export function nextItemStylesheet(selection: vscode.Selection, editor: vscode.T let endOffset = editor.document.offsetAt(selection.active); let currentNode = getNode(rootNode, endOffset, true); - // Full property is selected, so select property value next + // Full property is selected, so select full property value next if (currentNode.type === 'property' && startOffset === currentNode.start && endOffset === currentNode.end) { - return getSelectionFromNode(currentNode, editor.document, true); + return getSelectionFromProperty(currentNode, editor.document, startOffset, endOffset, true, 'next'); + } + + // Part or whole of propertyValue is selected, so select the next word in the propertyValue + if (currentNode.type === 'property' && startOffset >= currentNode.valueToken.start && endOffset <= currentNode.valueToken.end) { + let singlePropertyValue = getSelectionFromProperty(currentNode, editor.document, startOffset, endOffset, false, 'next'); + if (singlePropertyValue) { + return singlePropertyValue; + } } // Cursor is in the selector or in a property @@ -41,13 +49,27 @@ export function nextItemStylesheet(selection: vscode.Selection, editor: vscode.T export function prevItemStylesheet(selection: vscode.Selection, editor: vscode.TextEditor, rootNode: Node): vscode.Selection { let startOffset = editor.document.offsetAt(selection.anchor); + let endOffset = editor.document.offsetAt(selection.active); let currentNode = getNode(rootNode, startOffset); if (!currentNode) { currentNode = rootNode; } + // Full property value is selected, so select the whole property next + if (currentNode.type === 'property' && startOffset === currentNode.valueToken.start && endOffset === currentNode.valueToken.end) { + return getSelectionFromNode(currentNode, editor.document); + } + + // Part of propertyValue is selected, so select the prev word in the propertyValue + if (currentNode.type === 'property' && startOffset >= currentNode.valueToken.start && endOffset <= currentNode.valueToken.end) { + let singlePropertyValue = getSelectionFromProperty(currentNode, editor.document, startOffset, endOffset, false, 'prev'); + if (singlePropertyValue) { + return singlePropertyValue; + } + } + if (currentNode.type === 'property' || !currentNode.firstChild || (currentNode.type === 'rule' && startOffset <= currentNode.firstChild.start)) { - return getSelectionFromNode(currentNode, editor.document);; + return getSelectionFromNode(currentNode, editor.document); } // Select the child that appears just before the cursor @@ -57,23 +79,58 @@ export function prevItemStylesheet(selection: vscode.Selection, editor: vscode.T } prevNode = getDeepestNode(prevNode); - return getSelectionFromNode(prevNode, editor.document, true); + return getSelectionFromProperty(prevNode, editor.document, startOffset, endOffset, false, 'prev'); } -function getSelectionFromNode(node: Node, document: vscode.TextDocument, selectPropertyValue: boolean = false): vscode.Selection { +function getSelectionFromNode(node: Node, document: vscode.TextDocument): vscode.Selection { if (!node) { return; } let nodeToSelect = node.type === 'rule' ? node.selectorToken : node; + return new vscode.Selection(document.positionAt(nodeToSelect.start), document.positionAt(nodeToSelect.end)); +} + + +function getSelectionFromProperty(node: Node, document: vscode.TextDocument, selectionStart: number, selectionEnd: number, selectFullValue: boolean, direction: string): vscode.Selection { + if (!node || node.type !== 'property') { + return; + } + + let propertyValue = node.valueToken.stream.substring(node.valueToken.start, node.valueToken.end); + selectFullValue = selectFullValue || (direction === 'prev' && selectionStart === node.valueToken.start && selectionEnd < node.valueToken.end); + + if (selectFullValue) { + return new vscode.Selection(document.positionAt(node.valueToken.start), document.positionAt(node.valueToken.end)); + } + + let pos; + if (direction === 'prev') { + if (selectionStart === node.valueToken.start) { + return; + } + pos = selectionStart > node.valueToken.end ? propertyValue.length : selectionStart - node.valueToken.start; + } + + if (direction === 'next') { + if (selectionEnd === node.valueToken.end && (selectionStart > node.valueToken.start || propertyValue.indexOf(' ') === -1)) { + return; + } + pos = selectionEnd === node.valueToken.end ? -1 : selectionEnd - node.valueToken.start - 1; + } - let selectionStart = (node.type === 'property' && selectPropertyValue) ? node.valueToken.start : nodeToSelect.start; - let selectionEnd = (node.type === 'property' && selectPropertyValue) ? node.valueToken.end : nodeToSelect.end; - return new vscode.Selection(document.positionAt(selectionStart), document.positionAt(selectionEnd)); + let [newSelectionStart, newSelectionEnd] = direction === 'prev' ? findPrevWord(propertyValue, pos) : findNextWord(propertyValue, pos); + if (!newSelectionStart && !newSelectionEnd) { + return; + } + + newSelectionStart += node.valueToken.start; + newSelectionEnd += node.valueToken.start; + return new vscode.Selection(document.positionAt(newSelectionStart), document.positionAt(newSelectionEnd)); } diff --git a/extensions/emmet/src/util.ts b/extensions/emmet/src/util.ts index 3b495069d1e..81085e5b6d6 100644 --- a/extensions/emmet/src/util.ts +++ b/extensions/emmet/src/util.ts @@ -136,4 +136,80 @@ export function getDeepestNode(node: Node): Node { } return getDeepestNode(node.children[node.children.length - 1]); +} + +export function findNextWord(propertyValue: string, pos: number): [number, number] { + + let foundSpace = pos === -1; + let foundStart = false; + let foundEnd = false; + + let newSelectionStart; + let newSelectionEnd; + while (pos < propertyValue.length - 1) { + pos++; + if (!foundSpace) { + if (propertyValue[pos] === ' ') { + foundSpace = true; + } + continue; + } + if (foundSpace && !foundStart && propertyValue[pos] === ' ') { + continue; + } + if (!foundStart) { + newSelectionStart = pos; + foundStart = true; + continue; + } + if (propertyValue[pos] === ' ') { + newSelectionEnd = pos; + foundEnd = true; + break; + } + } + + if (foundStart && !foundEnd) { + newSelectionEnd = propertyValue.length; + } + + return [newSelectionStart, newSelectionEnd]; +} + +export function findPrevWord(propertyValue: string, pos: number): [number, number] { + + let foundSpace = pos === propertyValue.length; + let foundStart = false; + let foundEnd = false; + + let newSelectionStart; + let newSelectionEnd; + while (pos > -1) { + pos--; + if (!foundSpace) { + if (propertyValue[pos] === ' ') { + foundSpace = true; + } + continue; + } + if (foundSpace && !foundEnd && propertyValue[pos] === ' ') { + continue; + } + if (!foundEnd) { + newSelectionEnd = pos + 1; + foundEnd = true; + continue; + } + if (propertyValue[pos] === ' ') { + newSelectionStart = pos + 1; + foundStart = true; + break; + } + } + + if (foundEnd && !foundStart) { + newSelectionStart = 0; + } + + return [newSelectionStart, newSelectionEnd]; } \ No newline at end of file -- GitLab