Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/emmet/src/selectItemStylesheet.ts
4772 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import * as vscode from 'vscode';
7
import { getDeepestFlatNode, findNextWord, findPrevWord, getFlatNode, offsetRangeToSelection } from './util';
8
import { Node, CssNode, Rule, Property } from 'EmmetFlatNode';
9
10
export function nextItemStylesheet(document: vscode.TextDocument, startPosition: vscode.Position, endPosition: vscode.Position, rootNode: Node): vscode.Selection | undefined {
11
const startOffset = document.offsetAt(startPosition);
12
const endOffset = document.offsetAt(endPosition);
13
let currentNode: CssNode | undefined = <CssNode>getFlatNode(rootNode, endOffset, true);
14
if (!currentNode) {
15
currentNode = <CssNode>rootNode;
16
}
17
if (!currentNode) {
18
return;
19
}
20
// Full property is selected, so select full property value next
21
if (currentNode.type === 'property' &&
22
startOffset === currentNode.start &&
23
endOffset === currentNode.end) {
24
return getSelectionFromProperty(document, currentNode, startOffset, endOffset, true, 'next');
25
}
26
27
// Part or whole of propertyValue is selected, so select the next word in the propertyValue
28
if (currentNode.type === 'property' &&
29
startOffset >= (<Property>currentNode).valueToken.start &&
30
endOffset <= (<Property>currentNode).valueToken.end) {
31
const singlePropertyValue = getSelectionFromProperty(document, currentNode, startOffset, endOffset, false, 'next');
32
if (singlePropertyValue) {
33
return singlePropertyValue;
34
}
35
}
36
37
// Cursor is in the selector or in a property
38
if ((currentNode.type === 'rule' && endOffset < (<Rule>currentNode).selectorToken.end)
39
|| (currentNode.type === 'property' && endOffset < (<Property>currentNode).valueToken.end)) {
40
return getSelectionFromNode(document, currentNode);
41
}
42
43
// Get the first child of current node which is right after the cursor
44
let nextNode = currentNode.firstChild;
45
while (nextNode && endOffset >= nextNode.end) {
46
nextNode = nextNode.nextSibling;
47
}
48
49
// Get next sibling of current node or the parent
50
while (!nextNode && currentNode) {
51
nextNode = currentNode.nextSibling;
52
currentNode = currentNode.parent;
53
}
54
55
return nextNode ? getSelectionFromNode(document, nextNode) : undefined;
56
}
57
58
export function prevItemStylesheet(document: vscode.TextDocument, startPosition: vscode.Position, endPosition: vscode.Position, rootNode: CssNode): vscode.Selection | undefined {
59
const startOffset = document.offsetAt(startPosition);
60
const endOffset = document.offsetAt(endPosition);
61
let currentNode = <CssNode>getFlatNode(rootNode, startOffset, false);
62
if (!currentNode) {
63
currentNode = rootNode;
64
}
65
if (!currentNode) {
66
return;
67
}
68
69
// Full property value is selected, so select the whole property next
70
if (currentNode.type === 'property' &&
71
startOffset === (<Property>currentNode).valueToken.start &&
72
endOffset === (<Property>currentNode).valueToken.end) {
73
return getSelectionFromNode(document, currentNode);
74
}
75
76
// Part of propertyValue is selected, so select the prev word in the propertyValue
77
if (currentNode.type === 'property' &&
78
startOffset >= (<Property>currentNode).valueToken.start &&
79
endOffset <= (<Property>currentNode).valueToken.end) {
80
const singlePropertyValue = getSelectionFromProperty(document, currentNode, startOffset, endOffset, false, 'prev');
81
if (singlePropertyValue) {
82
return singlePropertyValue;
83
}
84
}
85
86
if (currentNode.type === 'property' || !currentNode.firstChild ||
87
(currentNode.type === 'rule' && startOffset <= currentNode.firstChild.start)) {
88
return getSelectionFromNode(document, currentNode);
89
}
90
91
// Select the child that appears just before the cursor
92
let prevNode: CssNode | undefined = currentNode.firstChild;
93
while (prevNode.nextSibling && startOffset >= prevNode.nextSibling.end) {
94
prevNode = prevNode.nextSibling;
95
}
96
prevNode = <CssNode | undefined>getDeepestFlatNode(prevNode);
97
98
return getSelectionFromProperty(document, prevNode, startOffset, endOffset, false, 'prev');
99
}
100
101
102
function getSelectionFromNode(document: vscode.TextDocument, node: Node | undefined): vscode.Selection | undefined {
103
if (!node) {
104
return;
105
}
106
107
const nodeToSelect = node.type === 'rule' ? (<Rule>node).selectorToken : node;
108
return offsetRangeToSelection(document, nodeToSelect.start, nodeToSelect.end);
109
}
110
111
112
function getSelectionFromProperty(document: vscode.TextDocument, node: Node | undefined, selectionStart: number, selectionEnd: number, selectFullValue: boolean, direction: string): vscode.Selection | undefined {
113
if (!node || node.type !== 'property') {
114
return;
115
}
116
const propertyNode = <Property>node;
117
118
const propertyValue = propertyNode.valueToken.stream.substring(propertyNode.valueToken.start, propertyNode.valueToken.end);
119
selectFullValue = selectFullValue ||
120
(direction === 'prev' && selectionStart === propertyNode.valueToken.start && selectionEnd < propertyNode.valueToken.end);
121
122
if (selectFullValue) {
123
return offsetRangeToSelection(document, propertyNode.valueToken.start, propertyNode.valueToken.end);
124
}
125
126
let pos: number = -1;
127
if (direction === 'prev') {
128
if (selectionStart === propertyNode.valueToken.start) {
129
return;
130
}
131
const selectionStartChar = document.positionAt(selectionStart).character;
132
const tokenStartChar = document.positionAt(propertyNode.valueToken.start).character;
133
pos = selectionStart > propertyNode.valueToken.end ? propertyValue.length :
134
selectionStartChar - tokenStartChar;
135
} else if (direction === 'next') {
136
if (selectionEnd === propertyNode.valueToken.end &&
137
(selectionStart > propertyNode.valueToken.start || !propertyValue.includes(' '))) {
138
return;
139
}
140
const selectionEndChar = document.positionAt(selectionEnd).character;
141
const tokenStartChar = document.positionAt(propertyNode.valueToken.start).character;
142
pos = selectionEnd === propertyNode.valueToken.end ? -1 :
143
selectionEndChar - tokenStartChar - 1;
144
}
145
146
147
const [newSelectionStartOffset, newSelectionEndOffset] = direction === 'prev' ? findPrevWord(propertyValue, pos) : findNextWord(propertyValue, pos);
148
if (!newSelectionStartOffset && !newSelectionEndOffset) {
149
return;
150
}
151
152
const tokenStart = document.positionAt(propertyNode.valueToken.start);
153
const newSelectionStart = tokenStart.translate(0, newSelectionStartOffset);
154
const newSelectionEnd = tokenStart.translate(0, newSelectionEndOffset);
155
156
return new vscode.Selection(newSelectionStart, newSelectionEnd);
157
}
158
159
160
161
162