vscode-css-languageservice 5.4.1 → 6.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +5 -1
- package/README.md +1 -0
- package/lib/esm/beautify/beautify-css.js +50 -8
- package/lib/esm/cssLanguageService.d.ts +37 -37
- package/lib/esm/cssLanguageService.js +72 -75
- package/lib/esm/cssLanguageTypes.d.ts +238 -238
- package/lib/esm/cssLanguageTypes.js +42 -42
- package/lib/esm/data/webCustomData.js +21959 -21965
- package/lib/esm/languageFacts/builtinData.js +142 -142
- package/lib/esm/languageFacts/colors.js +469 -472
- package/lib/esm/languageFacts/dataManager.js +88 -92
- package/lib/esm/languageFacts/dataProvider.js +73 -79
- package/lib/esm/languageFacts/entry.js +137 -138
- package/lib/esm/languageFacts/facts.js +8 -8
- package/lib/esm/parser/cssErrors.js +48 -50
- package/lib/esm/parser/cssNodes.js +1502 -2019
- package/lib/esm/parser/cssParser.js +1534 -1566
- package/lib/esm/parser/cssScanner.js +592 -599
- package/lib/esm/parser/cssSymbolScope.js +311 -341
- package/lib/esm/parser/lessParser.js +714 -740
- package/lib/esm/parser/lessScanner.js +57 -78
- package/lib/esm/parser/scssErrors.js +18 -20
- package/lib/esm/parser/scssParser.js +796 -818
- package/lib/esm/parser/scssScanner.js +95 -116
- package/lib/esm/services/cssCodeActions.js +77 -81
- package/lib/esm/services/cssCompletion.js +1054 -1149
- package/lib/esm/services/cssFolding.js +190 -193
- package/lib/esm/services/cssFormatter.js +136 -136
- package/lib/esm/services/cssHover.js +148 -151
- package/lib/esm/services/cssNavigation.js +378 -470
- package/lib/esm/services/cssSelectionRange.js +47 -47
- package/lib/esm/services/cssValidation.js +41 -44
- package/lib/esm/services/lessCompletion.js +378 -397
- package/lib/esm/services/lint.js +518 -532
- package/lib/esm/services/lintRules.js +76 -83
- package/lib/esm/services/lintUtil.js +196 -205
- package/lib/esm/services/pathCompletion.js +157 -231
- package/lib/esm/services/scssCompletion.js +354 -378
- package/lib/esm/services/scssNavigation.js +82 -154
- package/lib/esm/services/selectorPrinting.js +492 -536
- package/lib/esm/utils/arrays.js +40 -46
- package/lib/esm/utils/objects.js +11 -11
- package/lib/esm/utils/resources.js +11 -24
- package/lib/esm/utils/strings.js +102 -104
- package/lib/umd/beautify/beautify-css.js +50 -8
- package/lib/umd/cssLanguageService.d.ts +37 -37
- package/lib/umd/cssLanguageService.js +99 -102
- package/lib/umd/cssLanguageTypes.d.ts +238 -238
- package/lib/umd/cssLanguageTypes.js +89 -88
- package/lib/umd/data/webCustomData.js +21972 -21978
- package/lib/umd/languageFacts/builtinData.js +154 -154
- package/lib/umd/languageFacts/colors.js +492 -495
- package/lib/umd/languageFacts/dataManager.js +101 -104
- package/lib/umd/languageFacts/dataProvider.js +86 -91
- package/lib/umd/languageFacts/entry.js +152 -153
- package/lib/umd/languageFacts/facts.js +29 -29
- package/lib/umd/parser/cssErrors.js +61 -62
- package/lib/umd/parser/cssNodes.js +1587 -2034
- package/lib/umd/parser/cssParser.js +1547 -1578
- package/lib/umd/parser/cssScanner.js +606 -611
- package/lib/umd/parser/cssSymbolScope.js +328 -353
- package/lib/umd/parser/lessParser.js +727 -752
- package/lib/umd/parser/lessScanner.js +70 -90
- package/lib/umd/parser/scssErrors.js +31 -32
- package/lib/umd/parser/scssParser.js +809 -830
- package/lib/umd/parser/scssScanner.js +108 -128
- package/lib/umd/services/cssCodeActions.js +90 -93
- package/lib/umd/services/cssCompletion.js +1067 -1161
- package/lib/umd/services/cssFolding.js +203 -206
- package/lib/umd/services/cssFormatter.js +150 -150
- package/lib/umd/services/cssHover.js +161 -163
- package/lib/umd/services/cssNavigation.js +391 -482
- package/lib/umd/services/cssSelectionRange.js +60 -60
- package/lib/umd/services/cssValidation.js +54 -56
- package/lib/umd/services/lessCompletion.js +391 -409
- package/lib/umd/services/lint.js +531 -544
- package/lib/umd/services/lintRules.js +91 -95
- package/lib/umd/services/lintUtil.js +210 -218
- package/lib/umd/services/pathCompletion.js +171 -244
- package/lib/umd/services/scssCompletion.js +367 -390
- package/lib/umd/services/scssNavigation.js +95 -166
- package/lib/umd/services/selectorPrinting.js +510 -550
- package/lib/umd/utils/arrays.js +55 -61
- package/lib/umd/utils/objects.js +25 -25
- package/lib/umd/utils/resources.js +26 -39
- package/lib/umd/utils/strings.js +120 -122
- package/package.json +11 -11
|
@@ -1,193 +1,190 @@
|
|
|
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
|
-
'use strict';
|
|
6
|
-
import { TokenType, Scanner } from '../parser/cssScanner';
|
|
7
|
-
import { SCSSScanner, InterpolationFunction } from '../parser/scssScanner';
|
|
8
|
-
import { LESSScanner } from '../parser/lessScanner';
|
|
9
|
-
export function getFoldingRanges(document, context) {
|
|
10
|
-
|
|
11
|
-
return limitFoldingRanges(ranges, context);
|
|
12
|
-
}
|
|
13
|
-
function computeFoldingRanges(document) {
|
|
14
|
-
function getStartLine(t) {
|
|
15
|
-
return document.positionAt(t.offset).line;
|
|
16
|
-
}
|
|
17
|
-
function getEndLine(t) {
|
|
18
|
-
return document.positionAt(t.offset + t.len).line;
|
|
19
|
-
}
|
|
20
|
-
function getScanner() {
|
|
21
|
-
switch (document.languageId) {
|
|
22
|
-
case 'scss':
|
|
23
|
-
return new SCSSScanner();
|
|
24
|
-
case 'less':
|
|
25
|
-
return new LESSScanner();
|
|
26
|
-
default:
|
|
27
|
-
return new Scanner();
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
function tokenToRange(t, kind) {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
if (startLine !== endLine) {
|
|
34
|
-
return {
|
|
35
|
-
startLine
|
|
36
|
-
endLine
|
|
37
|
-
kind
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
else {
|
|
41
|
-
return null;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
scanner.ignoreComment = false;
|
|
48
|
-
scanner.setSource(document.getText());
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
switch (token.type) {
|
|
53
|
-
case TokenType.CurlyL:
|
|
54
|
-
case InterpolationFunction:
|
|
55
|
-
{
|
|
56
|
-
delimiterStack.push({ line: getStartLine(token), type: 'brace', isStart: true });
|
|
57
|
-
break;
|
|
58
|
-
}
|
|
59
|
-
case TokenType.CurlyR: {
|
|
60
|
-
if (delimiterStack.length !== 0) {
|
|
61
|
-
|
|
62
|
-
if (!prevDelimiter) {
|
|
63
|
-
break;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
if (prevDelimiter.type === 'brace') {
|
|
67
|
-
/**
|
|
68
|
-
* Other than the case when curly brace is not on a new line by itself, for example
|
|
69
|
-
* .foo {
|
|
70
|
-
* color: red; }
|
|
71
|
-
* Use endLine minus one to show ending curly brace
|
|
72
|
-
*/
|
|
73
|
-
if (prevToken && getEndLine(prevToken) !== endLine) {
|
|
74
|
-
endLine--;
|
|
75
|
-
}
|
|
76
|
-
if (prevDelimiter.line !== endLine) {
|
|
77
|
-
ranges.push({
|
|
78
|
-
startLine: prevDelimiter.line,
|
|
79
|
-
endLine
|
|
80
|
-
kind: undefined
|
|
81
|
-
});
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
break;
|
|
86
|
-
}
|
|
87
|
-
/**
|
|
88
|
-
* In CSS, there is no single line comment prefixed with //
|
|
89
|
-
* All comments are marked as `Comment`
|
|
90
|
-
*/
|
|
91
|
-
case TokenType.Comment: {
|
|
92
|
-
|
|
93
|
-
if (marker === '#region') {
|
|
94
|
-
return { line: getStartLine(token), type: 'comment', isStart: true };
|
|
95
|
-
}
|
|
96
|
-
else {
|
|
97
|
-
return { line: getEndLine(token), type: 'comment', isStart: false };
|
|
98
|
-
}
|
|
99
|
-
};
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
if (matches) {
|
|
103
|
-
return
|
|
104
|
-
}
|
|
105
|
-
else if (document.languageId === 'scss' || document.languageId === 'less') {
|
|
106
|
-
|
|
107
|
-
if (
|
|
108
|
-
return
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
return null;
|
|
112
|
-
};
|
|
113
|
-
|
|
114
|
-
// /* */ comment region folding
|
|
115
|
-
// All #region and #endregion cases
|
|
116
|
-
if (currDelimiter) {
|
|
117
|
-
if (currDelimiter.isStart) {
|
|
118
|
-
delimiterStack.push(currDelimiter);
|
|
119
|
-
}
|
|
120
|
-
else {
|
|
121
|
-
|
|
122
|
-
if (!prevDelimiter) {
|
|
123
|
-
break;
|
|
124
|
-
}
|
|
125
|
-
if (prevDelimiter.type === 'comment') {
|
|
126
|
-
if (prevDelimiter.line !== currDelimiter.line) {
|
|
127
|
-
ranges.push({
|
|
128
|
-
startLine: prevDelimiter.line,
|
|
129
|
-
endLine: currDelimiter.line,
|
|
130
|
-
kind: 'region'
|
|
131
|
-
});
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
// Multiline comment case
|
|
137
|
-
else {
|
|
138
|
-
|
|
139
|
-
if (range) {
|
|
140
|
-
ranges.push(range);
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
break;
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
prevToken = token;
|
|
147
|
-
token = scanner.scan();
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
return validRanges;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
return validRanges.slice(0, maxRanges);
|
|
192
|
-
}
|
|
193
|
-
}
|
|
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
|
+
'use strict';
|
|
6
|
+
import { TokenType, Scanner } from '../parser/cssScanner';
|
|
7
|
+
import { SCSSScanner, InterpolationFunction } from '../parser/scssScanner';
|
|
8
|
+
import { LESSScanner } from '../parser/lessScanner';
|
|
9
|
+
export function getFoldingRanges(document, context) {
|
|
10
|
+
const ranges = computeFoldingRanges(document);
|
|
11
|
+
return limitFoldingRanges(ranges, context);
|
|
12
|
+
}
|
|
13
|
+
function computeFoldingRanges(document) {
|
|
14
|
+
function getStartLine(t) {
|
|
15
|
+
return document.positionAt(t.offset).line;
|
|
16
|
+
}
|
|
17
|
+
function getEndLine(t) {
|
|
18
|
+
return document.positionAt(t.offset + t.len).line;
|
|
19
|
+
}
|
|
20
|
+
function getScanner() {
|
|
21
|
+
switch (document.languageId) {
|
|
22
|
+
case 'scss':
|
|
23
|
+
return new SCSSScanner();
|
|
24
|
+
case 'less':
|
|
25
|
+
return new LESSScanner();
|
|
26
|
+
default:
|
|
27
|
+
return new Scanner();
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function tokenToRange(t, kind) {
|
|
31
|
+
const startLine = getStartLine(t);
|
|
32
|
+
const endLine = getEndLine(t);
|
|
33
|
+
if (startLine !== endLine) {
|
|
34
|
+
return {
|
|
35
|
+
startLine,
|
|
36
|
+
endLine,
|
|
37
|
+
kind
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
const ranges = [];
|
|
45
|
+
const delimiterStack = [];
|
|
46
|
+
const scanner = getScanner();
|
|
47
|
+
scanner.ignoreComment = false;
|
|
48
|
+
scanner.setSource(document.getText());
|
|
49
|
+
let token = scanner.scan();
|
|
50
|
+
let prevToken = null;
|
|
51
|
+
while (token.type !== TokenType.EOF) {
|
|
52
|
+
switch (token.type) {
|
|
53
|
+
case TokenType.CurlyL:
|
|
54
|
+
case InterpolationFunction:
|
|
55
|
+
{
|
|
56
|
+
delimiterStack.push({ line: getStartLine(token), type: 'brace', isStart: true });
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
case TokenType.CurlyR: {
|
|
60
|
+
if (delimiterStack.length !== 0) {
|
|
61
|
+
const prevDelimiter = popPrevStartDelimiterOfType(delimiterStack, 'brace');
|
|
62
|
+
if (!prevDelimiter) {
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
let endLine = getEndLine(token);
|
|
66
|
+
if (prevDelimiter.type === 'brace') {
|
|
67
|
+
/**
|
|
68
|
+
* Other than the case when curly brace is not on a new line by itself, for example
|
|
69
|
+
* .foo {
|
|
70
|
+
* color: red; }
|
|
71
|
+
* Use endLine minus one to show ending curly brace
|
|
72
|
+
*/
|
|
73
|
+
if (prevToken && getEndLine(prevToken) !== endLine) {
|
|
74
|
+
endLine--;
|
|
75
|
+
}
|
|
76
|
+
if (prevDelimiter.line !== endLine) {
|
|
77
|
+
ranges.push({
|
|
78
|
+
startLine: prevDelimiter.line,
|
|
79
|
+
endLine,
|
|
80
|
+
kind: undefined
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* In CSS, there is no single line comment prefixed with //
|
|
89
|
+
* All comments are marked as `Comment`
|
|
90
|
+
*/
|
|
91
|
+
case TokenType.Comment: {
|
|
92
|
+
const commentRegionMarkerToDelimiter = (marker) => {
|
|
93
|
+
if (marker === '#region') {
|
|
94
|
+
return { line: getStartLine(token), type: 'comment', isStart: true };
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
return { line: getEndLine(token), type: 'comment', isStart: false };
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
const getCurrDelimiter = (token) => {
|
|
101
|
+
const matches = token.text.match(/^\s*\/\*\s*(#region|#endregion)\b\s*(.*?)\s*\*\//);
|
|
102
|
+
if (matches) {
|
|
103
|
+
return commentRegionMarkerToDelimiter(matches[1]);
|
|
104
|
+
}
|
|
105
|
+
else if (document.languageId === 'scss' || document.languageId === 'less') {
|
|
106
|
+
const matches = token.text.match(/^\s*\/\/\s*(#region|#endregion)\b\s*(.*?)\s*/);
|
|
107
|
+
if (matches) {
|
|
108
|
+
return commentRegionMarkerToDelimiter(matches[1]);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return null;
|
|
112
|
+
};
|
|
113
|
+
const currDelimiter = getCurrDelimiter(token);
|
|
114
|
+
// /* */ comment region folding
|
|
115
|
+
// All #region and #endregion cases
|
|
116
|
+
if (currDelimiter) {
|
|
117
|
+
if (currDelimiter.isStart) {
|
|
118
|
+
delimiterStack.push(currDelimiter);
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
const prevDelimiter = popPrevStartDelimiterOfType(delimiterStack, 'comment');
|
|
122
|
+
if (!prevDelimiter) {
|
|
123
|
+
break;
|
|
124
|
+
}
|
|
125
|
+
if (prevDelimiter.type === 'comment') {
|
|
126
|
+
if (prevDelimiter.line !== currDelimiter.line) {
|
|
127
|
+
ranges.push({
|
|
128
|
+
startLine: prevDelimiter.line,
|
|
129
|
+
endLine: currDelimiter.line,
|
|
130
|
+
kind: 'region'
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
// Multiline comment case
|
|
137
|
+
else {
|
|
138
|
+
const range = tokenToRange(token, 'comment');
|
|
139
|
+
if (range) {
|
|
140
|
+
ranges.push(range);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
break;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
prevToken = token;
|
|
147
|
+
token = scanner.scan();
|
|
148
|
+
}
|
|
149
|
+
return ranges;
|
|
150
|
+
}
|
|
151
|
+
function popPrevStartDelimiterOfType(stack, type) {
|
|
152
|
+
if (stack.length === 0) {
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
for (let i = stack.length - 1; i >= 0; i--) {
|
|
156
|
+
if (stack[i].type === type && stack[i].isStart) {
|
|
157
|
+
return stack.splice(i, 1)[0];
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* - Sort regions
|
|
164
|
+
* - Remove invalid regions (intersections)
|
|
165
|
+
* - If limit exceeds, only return `rangeLimit` amount of ranges
|
|
166
|
+
*/
|
|
167
|
+
function limitFoldingRanges(ranges, context) {
|
|
168
|
+
const maxRanges = context && context.rangeLimit || Number.MAX_VALUE;
|
|
169
|
+
const sortedRanges = ranges.sort((r1, r2) => {
|
|
170
|
+
let diff = r1.startLine - r2.startLine;
|
|
171
|
+
if (diff === 0) {
|
|
172
|
+
diff = r1.endLine - r2.endLine;
|
|
173
|
+
}
|
|
174
|
+
return diff;
|
|
175
|
+
});
|
|
176
|
+
const validRanges = [];
|
|
177
|
+
let prevEndLine = -1;
|
|
178
|
+
sortedRanges.forEach(r => {
|
|
179
|
+
if (!(r.startLine < prevEndLine && prevEndLine < r.endLine)) {
|
|
180
|
+
validRanges.push(r);
|
|
181
|
+
prevEndLine = r.endLine;
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
if (validRanges.length < maxRanges) {
|
|
185
|
+
return validRanges;
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
return validRanges.slice(0, maxRanges);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
@@ -1,136 +1,136 @@
|
|
|
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
|
-
import { Range, Position } from '../cssLanguageTypes';
|
|
6
|
-
import { css_beautify } from '../beautify/beautify-css';
|
|
7
|
-
import { repeat } from '../utils/strings';
|
|
8
|
-
export function format(document, range, options) {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
if (range) {
|
|
15
|
-
|
|
16
|
-
// include all leading whitespace iff at the beginning of the line
|
|
17
|
-
|
|
18
|
-
while (extendedStart > 0 && isWhitespace(value, extendedStart - 1)) {
|
|
19
|
-
extendedStart--;
|
|
20
|
-
}
|
|
21
|
-
if (extendedStart === 0 || isEOL(value, extendedStart - 1)) {
|
|
22
|
-
startOffset = extendedStart;
|
|
23
|
-
}
|
|
24
|
-
else {
|
|
25
|
-
// else keep at least one whitespace
|
|
26
|
-
if (extendedStart < startOffset) {
|
|
27
|
-
startOffset = extendedStart + 1;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
// include all following whitespace until the end of the line
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
while (extendedEnd < value.length && isWhitespace(value, extendedEnd)) {
|
|
34
|
-
extendedEnd++;
|
|
35
|
-
}
|
|
36
|
-
if (extendedEnd === value.length || isEOL(value, extendedEnd)) {
|
|
37
|
-
endOffset = extendedEnd;
|
|
38
|
-
}
|
|
39
|
-
range = Range.create(document.positionAt(startOffset), document.positionAt(endOffset));
|
|
40
|
-
// Test if inside a rule
|
|
41
|
-
inRule = isInRule(value, startOffset);
|
|
42
|
-
includesEnd = endOffset === value.length;
|
|
43
|
-
value = value.substring(startOffset, endOffset);
|
|
44
|
-
if (startOffset !== 0) {
|
|
45
|
-
|
|
46
|
-
initialIndentLevel = computeIndentLevel(document.getText(), startOfLineOffset, options);
|
|
47
|
-
}
|
|
48
|
-
if (inRule) {
|
|
49
|
-
value =
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
else {
|
|
53
|
-
range = Range.create(Position.create(0, 0), document.positionAt(value.length));
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
indent_size: tabSize,
|
|
57
|
-
indent_char: options.insertSpaces ? ' ' : '\t',
|
|
58
|
-
end_with_newline: includesEnd && getFormatOption(options, 'insertFinalNewline', false),
|
|
59
|
-
selector_separator_newline: getFormatOption(options, 'newlineBetweenSelectors', true),
|
|
60
|
-
newline_between_rules: getFormatOption(options, 'newlineBetweenRules', true),
|
|
61
|
-
space_around_selector_separator: getFormatOption(options, 'spaceAroundSelectorSeparator', false),
|
|
62
|
-
brace_style: getFormatOption(options, 'braceStyle', 'collapse'),
|
|
63
|
-
indent_empty_lines: getFormatOption(options, 'indentEmptyLines', false),
|
|
64
|
-
max_preserve_newlines: getFormatOption(options, 'maxPreserveNewLines', undefined),
|
|
65
|
-
preserve_newlines: getFormatOption(options, 'preserveNewLines', true),
|
|
66
|
-
wrap_line_length: getFormatOption(options, 'wrapLineLength', undefined),
|
|
67
|
-
eol: '\n'
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
if (inRule) {
|
|
71
|
-
result = trimLeft(result.substring(2));
|
|
72
|
-
}
|
|
73
|
-
if (initialIndentLevel > 0) {
|
|
74
|
-
|
|
75
|
-
result = result.split('\n').join('\n' + indent);
|
|
76
|
-
if (range.start.character === 0) {
|
|
77
|
-
result = indent + result; // keep the indent
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
return [{
|
|
81
|
-
range: range,
|
|
82
|
-
newText: result
|
|
83
|
-
}];
|
|
84
|
-
}
|
|
85
|
-
function trimLeft(str) {
|
|
86
|
-
return str.replace(/^\s+/, '');
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
function isInRule(str, offset) {
|
|
91
|
-
while (offset >= 0) {
|
|
92
|
-
|
|
93
|
-
if (ch === _CUL) {
|
|
94
|
-
return true;
|
|
95
|
-
}
|
|
96
|
-
else if (ch === _CUR) {
|
|
97
|
-
return false;
|
|
98
|
-
}
|
|
99
|
-
offset--;
|
|
100
|
-
}
|
|
101
|
-
return false;
|
|
102
|
-
}
|
|
103
|
-
function getFormatOption(options, key, dflt) {
|
|
104
|
-
if (options && options.hasOwnProperty(key)) {
|
|
105
|
-
|
|
106
|
-
if (value !== null) {
|
|
107
|
-
return value;
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
return dflt;
|
|
111
|
-
}
|
|
112
|
-
function computeIndentLevel(content, offset, options) {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
while (i < content.length) {
|
|
117
|
-
|
|
118
|
-
if (ch === ' ') {
|
|
119
|
-
nChars++;
|
|
120
|
-
}
|
|
121
|
-
else if (ch === '\t') {
|
|
122
|
-
nChars += tabSize;
|
|
123
|
-
}
|
|
124
|
-
else {
|
|
125
|
-
break;
|
|
126
|
-
}
|
|
127
|
-
i++;
|
|
128
|
-
}
|
|
129
|
-
return Math.floor(nChars / tabSize);
|
|
130
|
-
}
|
|
131
|
-
function isEOL(text, offset) {
|
|
132
|
-
return '\r\n'.indexOf(text.charAt(offset)) !== -1;
|
|
133
|
-
}
|
|
134
|
-
function isWhitespace(text, offset) {
|
|
135
|
-
return ' \t'.indexOf(text.charAt(offset)) !== -1;
|
|
136
|
-
}
|
|
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
|
+
import { Range, Position } from '../cssLanguageTypes';
|
|
6
|
+
import { css_beautify } from '../beautify/beautify-css';
|
|
7
|
+
import { repeat } from '../utils/strings';
|
|
8
|
+
export function format(document, range, options) {
|
|
9
|
+
let value = document.getText();
|
|
10
|
+
let includesEnd = true;
|
|
11
|
+
let initialIndentLevel = 0;
|
|
12
|
+
let inRule = false;
|
|
13
|
+
const tabSize = options.tabSize || 4;
|
|
14
|
+
if (range) {
|
|
15
|
+
let startOffset = document.offsetAt(range.start);
|
|
16
|
+
// include all leading whitespace iff at the beginning of the line
|
|
17
|
+
let extendedStart = startOffset;
|
|
18
|
+
while (extendedStart > 0 && isWhitespace(value, extendedStart - 1)) {
|
|
19
|
+
extendedStart--;
|
|
20
|
+
}
|
|
21
|
+
if (extendedStart === 0 || isEOL(value, extendedStart - 1)) {
|
|
22
|
+
startOffset = extendedStart;
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
// else keep at least one whitespace
|
|
26
|
+
if (extendedStart < startOffset) {
|
|
27
|
+
startOffset = extendedStart + 1;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
// include all following whitespace until the end of the line
|
|
31
|
+
let endOffset = document.offsetAt(range.end);
|
|
32
|
+
let extendedEnd = endOffset;
|
|
33
|
+
while (extendedEnd < value.length && isWhitespace(value, extendedEnd)) {
|
|
34
|
+
extendedEnd++;
|
|
35
|
+
}
|
|
36
|
+
if (extendedEnd === value.length || isEOL(value, extendedEnd)) {
|
|
37
|
+
endOffset = extendedEnd;
|
|
38
|
+
}
|
|
39
|
+
range = Range.create(document.positionAt(startOffset), document.positionAt(endOffset));
|
|
40
|
+
// Test if inside a rule
|
|
41
|
+
inRule = isInRule(value, startOffset);
|
|
42
|
+
includesEnd = endOffset === value.length;
|
|
43
|
+
value = value.substring(startOffset, endOffset);
|
|
44
|
+
if (startOffset !== 0) {
|
|
45
|
+
const startOfLineOffset = document.offsetAt(Position.create(range.start.line, 0));
|
|
46
|
+
initialIndentLevel = computeIndentLevel(document.getText(), startOfLineOffset, options);
|
|
47
|
+
}
|
|
48
|
+
if (inRule) {
|
|
49
|
+
value = `{\n${trimLeft(value)}`;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
range = Range.create(Position.create(0, 0), document.positionAt(value.length));
|
|
54
|
+
}
|
|
55
|
+
const cssOptions = {
|
|
56
|
+
indent_size: tabSize,
|
|
57
|
+
indent_char: options.insertSpaces ? ' ' : '\t',
|
|
58
|
+
end_with_newline: includesEnd && getFormatOption(options, 'insertFinalNewline', false),
|
|
59
|
+
selector_separator_newline: getFormatOption(options, 'newlineBetweenSelectors', true),
|
|
60
|
+
newline_between_rules: getFormatOption(options, 'newlineBetweenRules', true),
|
|
61
|
+
space_around_selector_separator: getFormatOption(options, 'spaceAroundSelectorSeparator', false),
|
|
62
|
+
brace_style: getFormatOption(options, 'braceStyle', 'collapse'),
|
|
63
|
+
indent_empty_lines: getFormatOption(options, 'indentEmptyLines', false),
|
|
64
|
+
max_preserve_newlines: getFormatOption(options, 'maxPreserveNewLines', undefined),
|
|
65
|
+
preserve_newlines: getFormatOption(options, 'preserveNewLines', true),
|
|
66
|
+
wrap_line_length: getFormatOption(options, 'wrapLineLength', undefined),
|
|
67
|
+
eol: '\n'
|
|
68
|
+
};
|
|
69
|
+
let result = css_beautify(value, cssOptions);
|
|
70
|
+
if (inRule) {
|
|
71
|
+
result = trimLeft(result.substring(2));
|
|
72
|
+
}
|
|
73
|
+
if (initialIndentLevel > 0) {
|
|
74
|
+
const indent = options.insertSpaces ? repeat(' ', tabSize * initialIndentLevel) : repeat('\t', initialIndentLevel);
|
|
75
|
+
result = result.split('\n').join('\n' + indent);
|
|
76
|
+
if (range.start.character === 0) {
|
|
77
|
+
result = indent + result; // keep the indent
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return [{
|
|
81
|
+
range: range,
|
|
82
|
+
newText: result
|
|
83
|
+
}];
|
|
84
|
+
}
|
|
85
|
+
function trimLeft(str) {
|
|
86
|
+
return str.replace(/^\s+/, '');
|
|
87
|
+
}
|
|
88
|
+
const _CUL = '{'.charCodeAt(0);
|
|
89
|
+
const _CUR = '}'.charCodeAt(0);
|
|
90
|
+
function isInRule(str, offset) {
|
|
91
|
+
while (offset >= 0) {
|
|
92
|
+
const ch = str.charCodeAt(offset);
|
|
93
|
+
if (ch === _CUL) {
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
else if (ch === _CUR) {
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
offset--;
|
|
100
|
+
}
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
function getFormatOption(options, key, dflt) {
|
|
104
|
+
if (options && options.hasOwnProperty(key)) {
|
|
105
|
+
const value = options[key];
|
|
106
|
+
if (value !== null) {
|
|
107
|
+
return value;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return dflt;
|
|
111
|
+
}
|
|
112
|
+
function computeIndentLevel(content, offset, options) {
|
|
113
|
+
let i = offset;
|
|
114
|
+
let nChars = 0;
|
|
115
|
+
const tabSize = options.tabSize || 4;
|
|
116
|
+
while (i < content.length) {
|
|
117
|
+
const ch = content.charAt(i);
|
|
118
|
+
if (ch === ' ') {
|
|
119
|
+
nChars++;
|
|
120
|
+
}
|
|
121
|
+
else if (ch === '\t') {
|
|
122
|
+
nChars += tabSize;
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
break;
|
|
126
|
+
}
|
|
127
|
+
i++;
|
|
128
|
+
}
|
|
129
|
+
return Math.floor(nChars / tabSize);
|
|
130
|
+
}
|
|
131
|
+
function isEOL(text, offset) {
|
|
132
|
+
return '\r\n'.indexOf(text.charAt(offset)) !== -1;
|
|
133
|
+
}
|
|
134
|
+
function isWhitespace(text, offset) {
|
|
135
|
+
return ' \t'.indexOf(text.charAt(offset)) !== -1;
|
|
136
|
+
}
|