diff --git a/extensions/emmet/src/selectItemHTML.ts b/extensions/emmet/src/selectItemHTML.ts
index 79d0a585e0ae084ad0a4bc89335fba6d721eeddc..2c0be704eadf323d3e42f7e5f4f4a18276ac40de 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 d0167a2d8b93d3fab3532fecf920dfa3f4db47a4..e3144cbf6662a2e66bba2c73b56ac2cec61e9af9 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 3b495069d1e53df9ec6485c5c58349e58ba86496..81085e5b6d6dcc444b4ba4803b30ba13f3416dc8 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