vscode-css-languageservice 5.4.2 → 6.1.0
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 +7 -1
- package/SECURITY.md +41 -0
- package/lib/esm/beautify/beautify-css.js +11 -4
- package/lib/esm/cssLanguageService.d.ts +2 -1
- package/lib/esm/cssLanguageService.js +15 -17
- package/lib/esm/cssLanguageTypes.js +2 -2
- package/lib/esm/data/webCustomData.js +356 -232
- package/lib/esm/languageFacts/builtinData.js +15 -15
- package/lib/esm/languageFacts/colors.js +66 -69
- package/lib/esm/languageFacts/dataManager.js +38 -42
- package/lib/esm/languageFacts/dataProvider.js +17 -23
- package/lib/esm/languageFacts/entry.js +22 -23
- package/lib/esm/parser/cssErrors.js +5 -7
- package/lib/esm/parser/cssNodes.js +869 -1377
- package/lib/esm/parser/cssParser.js +419 -376
- package/lib/esm/parser/cssScanner.js +168 -175
- package/lib/esm/parser/cssSymbolScope.js +107 -137
- package/lib/esm/parser/lessParser.js +177 -202
- package/lib/esm/parser/lessScanner.js +22 -43
- package/lib/esm/parser/scssErrors.js +5 -7
- package/lib/esm/parser/scssParser.js +196 -208
- package/lib/esm/parser/scssScanner.js +33 -54
- package/lib/esm/services/cssCodeActions.js +36 -40
- package/lib/esm/services/cssCompletion.js +300 -395
- package/lib/esm/services/cssFolding.js +32 -35
- package/lib/esm/services/cssFormatter.js +22 -22
- package/lib/esm/services/cssHover.js +30 -33
- package/lib/esm/services/cssNavigation.js +260 -289
- package/lib/esm/services/cssSelectionRange.js +6 -6
- package/lib/esm/services/cssValidation.js +13 -16
- package/lib/esm/services/lessCompletion.js +351 -370
- package/lib/esm/services/lint.js +161 -175
- package/lib/esm/services/lintRules.js +20 -27
- package/lib/esm/services/lintUtil.js +19 -28
- package/lib/esm/services/pathCompletion.js +84 -158
- package/lib/esm/services/scssCompletion.js +283 -307
- package/lib/esm/services/scssNavigation.js +65 -137
- package/lib/esm/services/selectorPrinting.js +131 -175
- package/lib/esm/utils/arrays.js +6 -12
- package/lib/esm/utils/objects.js +1 -1
- package/lib/esm/utils/resources.js +3 -16
- package/lib/esm/utils/strings.js +10 -12
- package/lib/umd/beautify/beautify-css.js +11 -4
- package/lib/umd/cssLanguageService.d.ts +2 -1
- package/lib/umd/cssLanguageService.js +34 -32
- package/lib/umd/cssLanguageTypes.js +4 -3
- package/lib/umd/data/webCustomData.js +355 -231
- package/lib/umd/languageFacts/colors.js +65 -68
- package/lib/umd/languageFacts/dataManager.js +41 -44
- package/lib/umd/languageFacts/dataProvider.js +17 -22
- package/lib/umd/languageFacts/entry.js +22 -23
- package/lib/umd/languageFacts/facts.js +5 -1
- package/lib/umd/parser/cssErrors.js +5 -6
- package/lib/umd/parser/cssNodes.js +870 -1307
- package/lib/umd/parser/cssParser.js +424 -380
- package/lib/umd/parser/cssScanner.js +168 -173
- package/lib/umd/parser/cssSymbolScope.js +109 -134
- package/lib/umd/parser/lessParser.js +182 -206
- package/lib/umd/parser/lessScanner.js +22 -42
- package/lib/umd/parser/scssErrors.js +5 -6
- package/lib/umd/parser/scssParser.js +202 -213
- package/lib/umd/parser/scssScanner.js +25 -45
- package/lib/umd/services/cssCodeActions.js +41 -44
- package/lib/umd/services/cssCompletion.js +308 -402
- package/lib/umd/services/cssFolding.js +35 -38
- package/lib/umd/services/cssFormatter.js +25 -25
- package/lib/umd/services/cssHover.js +36 -38
- package/lib/umd/services/cssNavigation.js +267 -295
- package/lib/umd/services/cssSelectionRange.js +8 -8
- package/lib/umd/services/cssValidation.js +17 -19
- package/lib/umd/services/lessCompletion.js +354 -372
- package/lib/umd/services/lint.js +167 -180
- package/lib/umd/services/lintRules.js +20 -24
- package/lib/umd/services/lintUtil.js +20 -28
- package/lib/umd/services/pathCompletion.js +87 -160
- package/lib/umd/services/scssCompletion.js +287 -310
- package/lib/umd/services/scssNavigation.js +69 -140
- package/lib/umd/services/selectorPrinting.js +134 -174
- package/lib/umd/utils/arrays.js +6 -12
- package/lib/umd/utils/objects.js +1 -1
- package/lib/umd/utils/resources.js +4 -17
- package/lib/umd/utils/strings.js +10 -12
- package/package.json +16 -15
|
@@ -5,31 +5,27 @@
|
|
|
5
5
|
'use strict';
|
|
6
6
|
import * as nodes from '../parser/cssNodes';
|
|
7
7
|
import * as nls from 'vscode-nls';
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
8
|
+
const localize = nls.loadMessageBundle();
|
|
9
|
+
const Warning = nodes.Level.Warning;
|
|
10
|
+
const Error = nodes.Level.Error;
|
|
11
|
+
const Ignore = nodes.Level.Ignore;
|
|
12
|
+
export class Rule {
|
|
13
|
+
constructor(id, message, defaultValue) {
|
|
14
14
|
this.id = id;
|
|
15
15
|
this.message = message;
|
|
16
16
|
this.defaultValue = defaultValue;
|
|
17
17
|
// nothing to do
|
|
18
18
|
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
var Setting = /** @class */ (function () {
|
|
23
|
-
function Setting(id, message, defaultValue) {
|
|
19
|
+
}
|
|
20
|
+
export class Setting {
|
|
21
|
+
constructor(id, message, defaultValue) {
|
|
24
22
|
this.id = id;
|
|
25
23
|
this.message = message;
|
|
26
24
|
this.defaultValue = defaultValue;
|
|
27
25
|
// nothing to do
|
|
28
26
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
export { Setting };
|
|
32
|
-
export var Rules = {
|
|
27
|
+
}
|
|
28
|
+
export const Rules = {
|
|
33
29
|
AllVendorPrefixes: new Rule('compatibleVendorPrefixes', localize('rule.vendorprefixes.all', "When using a vendor-specific prefix make sure to also include all other vendor-specific properties"), Ignore),
|
|
34
30
|
IncludeStandardPropertyWhenUsingVendorPrefix: new Rule('vendorPrefix', localize('rule.standardvendorprefix.all', "When using a vendor-specific prefix also include the standard property"), Warning),
|
|
35
31
|
DuplicateDeclarations: new Rule('duplicateProperties', localize('rule.duplicateDeclarations', "Do not use duplicate style definitions"), Ignore),
|
|
@@ -50,29 +46,26 @@ export var Rules = {
|
|
|
50
46
|
AvoidFloat: new Rule('float', localize('rule.avoidFloat', "Avoid using 'float'. Floats lead to fragile CSS that is easy to break if one aspect of the layout changes."), Ignore),
|
|
51
47
|
AvoidIdSelector: new Rule('idSelector', localize('rule.avoidIdSelector', "Selectors should not contain IDs because these rules are too tightly coupled with the HTML."), Ignore),
|
|
52
48
|
};
|
|
53
|
-
export
|
|
49
|
+
export const Settings = {
|
|
54
50
|
ValidProperties: new Setting('validProperties', localize('rule.validProperties', "A list of properties that are not validated against the `unknownProperties` rule."), [])
|
|
55
51
|
};
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
if (conf === void 0) { conf = {}; }
|
|
52
|
+
export class LintConfigurationSettings {
|
|
53
|
+
constructor(conf = {}) {
|
|
59
54
|
this.conf = conf;
|
|
60
55
|
}
|
|
61
|
-
|
|
56
|
+
getRule(rule) {
|
|
62
57
|
if (this.conf.hasOwnProperty(rule.id)) {
|
|
63
|
-
|
|
58
|
+
const level = toLevel(this.conf[rule.id]);
|
|
64
59
|
if (level) {
|
|
65
60
|
return level;
|
|
66
61
|
}
|
|
67
62
|
}
|
|
68
63
|
return rule.defaultValue;
|
|
69
|
-
}
|
|
70
|
-
|
|
64
|
+
}
|
|
65
|
+
getSetting(setting) {
|
|
71
66
|
return this.conf[setting.id];
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
}());
|
|
75
|
-
export { LintConfigurationSettings };
|
|
67
|
+
}
|
|
68
|
+
}
|
|
76
69
|
function toLevel(level) {
|
|
77
70
|
switch (level) {
|
|
78
71
|
case 'ignore': return nodes.Level.Ignore;
|
|
@@ -4,16 +4,14 @@
|
|
|
4
4
|
*--------------------------------------------------------------------------------------------*/
|
|
5
5
|
'use strict';
|
|
6
6
|
import { includes } from '../utils/arrays';
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
export class Element {
|
|
8
|
+
constructor(decl) {
|
|
9
9
|
this.fullPropertyName = decl.getFullPropertyName().toLowerCase();
|
|
10
10
|
this.node = decl;
|
|
11
11
|
}
|
|
12
|
-
|
|
13
|
-
}());
|
|
14
|
-
export { Element };
|
|
12
|
+
}
|
|
15
13
|
function setSide(model, side, value, property) {
|
|
16
|
-
|
|
14
|
+
const state = model[side];
|
|
17
15
|
state.value = value;
|
|
18
16
|
if (value) {
|
|
19
17
|
if (!includes(state.properties, property)) {
|
|
@@ -62,8 +60,7 @@ function updateModelWithList(model, values, property) {
|
|
|
62
60
|
}
|
|
63
61
|
}
|
|
64
62
|
function matches(value, candidates) {
|
|
65
|
-
for (
|
|
66
|
-
var candidate = candidates_1[_i];
|
|
63
|
+
for (let candidate of candidates) {
|
|
67
64
|
if (value.matches(candidate)) {
|
|
68
65
|
return true;
|
|
69
66
|
}
|
|
@@ -74,8 +71,7 @@ function matches(value, candidates) {
|
|
|
74
71
|
* @param allowsKeywords whether the initial value of property is zero, so keywords `initial` and `unset` count as zero
|
|
75
72
|
* @return `true` if this node represents a non-zero border; otherwise, `false`
|
|
76
73
|
*/
|
|
77
|
-
function checkLineWidth(value, allowsKeywords) {
|
|
78
|
-
if (allowsKeywords === void 0) { allowsKeywords = true; }
|
|
74
|
+
function checkLineWidth(value, allowsKeywords = true) {
|
|
79
75
|
if (allowsKeywords && matches(value, ['initial', 'unset'])) {
|
|
80
76
|
return false;
|
|
81
77
|
}
|
|
@@ -83,16 +79,14 @@ function checkLineWidth(value, allowsKeywords) {
|
|
|
83
79
|
// so use `parseFloat` to strip the unit
|
|
84
80
|
return parseFloat(value.getText()) !== 0;
|
|
85
81
|
}
|
|
86
|
-
function checkLineWidthList(nodes, allowsKeywords) {
|
|
87
|
-
|
|
88
|
-
return nodes.map(function (node) { return checkLineWidth(node, allowsKeywords); });
|
|
82
|
+
function checkLineWidthList(nodes, allowsKeywords = true) {
|
|
83
|
+
return nodes.map(node => checkLineWidth(node, allowsKeywords));
|
|
89
84
|
}
|
|
90
85
|
/**
|
|
91
86
|
* @param allowsKeywords whether keywords `initial` and `unset` count as zero
|
|
92
87
|
* @return `true` if this node represents a non-zero border; otherwise, `false`
|
|
93
88
|
*/
|
|
94
|
-
function checkLineStyle(valueNode, allowsKeywords) {
|
|
95
|
-
if (allowsKeywords === void 0) { allowsKeywords = true; }
|
|
89
|
+
function checkLineStyle(valueNode, allowsKeywords = true) {
|
|
96
90
|
if (matches(valueNode, ['none', 'hidden'])) {
|
|
97
91
|
return false;
|
|
98
92
|
}
|
|
@@ -101,23 +95,21 @@ function checkLineStyle(valueNode, allowsKeywords) {
|
|
|
101
95
|
}
|
|
102
96
|
return true;
|
|
103
97
|
}
|
|
104
|
-
function checkLineStyleList(nodes, allowsKeywords) {
|
|
105
|
-
|
|
106
|
-
return nodes.map(function (node) { return checkLineStyle(node, allowsKeywords); });
|
|
98
|
+
function checkLineStyleList(nodes, allowsKeywords = true) {
|
|
99
|
+
return nodes.map(node => checkLineStyle(node, allowsKeywords));
|
|
107
100
|
}
|
|
108
101
|
function checkBorderShorthand(node) {
|
|
109
|
-
|
|
102
|
+
const children = node.getChildren();
|
|
110
103
|
// the only child can be a keyword, a <line-width>, or a <line-style>
|
|
111
104
|
// if either check returns false, the result is no border
|
|
112
105
|
if (children.length === 1) {
|
|
113
|
-
|
|
106
|
+
const value = children[0];
|
|
114
107
|
return checkLineWidth(value) && checkLineStyle(value);
|
|
115
108
|
}
|
|
116
109
|
// multiple children can't contain keywords
|
|
117
110
|
// if any child means no border, the result is no border
|
|
118
|
-
for (
|
|
119
|
-
|
|
120
|
-
var value = child;
|
|
111
|
+
for (const child of children) {
|
|
112
|
+
const value = child;
|
|
121
113
|
if (!checkLineWidth(value, /* allowsKeywords: */ false) ||
|
|
122
114
|
!checkLineStyle(value, /* allowsKeywords: */ false)) {
|
|
123
115
|
return false;
|
|
@@ -126,15 +118,14 @@ function checkBorderShorthand(node) {
|
|
|
126
118
|
return true;
|
|
127
119
|
}
|
|
128
120
|
export default function calculateBoxModel(propertyTable) {
|
|
129
|
-
|
|
121
|
+
const model = {
|
|
130
122
|
top: { value: false, properties: [] },
|
|
131
123
|
right: { value: false, properties: [] },
|
|
132
124
|
bottom: { value: false, properties: [] },
|
|
133
125
|
left: { value: false, properties: [] },
|
|
134
126
|
};
|
|
135
|
-
for (
|
|
136
|
-
|
|
137
|
-
var value = property.node.value;
|
|
127
|
+
for (const property of propertyTable) {
|
|
128
|
+
const value = property.node.value;
|
|
138
129
|
if (typeof value === 'undefined') {
|
|
139
130
|
continue;
|
|
140
131
|
}
|
|
@@ -154,7 +145,7 @@ export default function calculateBoxModel(propertyTable) {
|
|
|
154
145
|
model.height = property;
|
|
155
146
|
break;
|
|
156
147
|
default:
|
|
157
|
-
|
|
148
|
+
const segments = property.fullPropertyName.split('-');
|
|
158
149
|
switch (segments[0]) {
|
|
159
150
|
case 'border':
|
|
160
151
|
switch (segments[1]) {
|
|
@@ -2,170 +2,96 @@
|
|
|
2
2
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
3
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
4
4
|
*--------------------------------------------------------------------------------------------*/
|
|
5
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
6
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
7
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
8
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
9
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
10
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
11
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
12
|
-
});
|
|
13
|
-
};
|
|
14
|
-
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
15
|
-
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
16
|
-
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
17
|
-
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
18
|
-
function step(op) {
|
|
19
|
-
if (f) throw new TypeError("Generator is already executing.");
|
|
20
|
-
while (_) try {
|
|
21
|
-
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
22
|
-
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
23
|
-
switch (op[0]) {
|
|
24
|
-
case 0: case 1: t = op; break;
|
|
25
|
-
case 4: _.label++; return { value: op[1], done: false };
|
|
26
|
-
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
27
|
-
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
28
|
-
default:
|
|
29
|
-
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
30
|
-
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
31
|
-
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
32
|
-
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
33
|
-
if (t[2]) _.ops.pop();
|
|
34
|
-
_.trys.pop(); continue;
|
|
35
|
-
}
|
|
36
|
-
op = body.call(thisArg, _);
|
|
37
|
-
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
38
|
-
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
39
|
-
}
|
|
40
|
-
};
|
|
41
5
|
import { FileType, CompletionItemKind, TextEdit, Range, Position } from '../cssLanguageTypes';
|
|
42
6
|
import { startsWith, endsWith } from '../utils/strings';
|
|
43
7
|
import { joinPath } from '../utils/resources';
|
|
44
|
-
|
|
45
|
-
|
|
8
|
+
export class PathCompletionParticipant {
|
|
9
|
+
constructor(readDirectory) {
|
|
46
10
|
this.readDirectory = readDirectory;
|
|
47
11
|
this.literalCompletions = [];
|
|
48
12
|
this.importCompletions = [];
|
|
49
13
|
}
|
|
50
|
-
|
|
14
|
+
onCssURILiteralValue(context) {
|
|
51
15
|
this.literalCompletions.push(context);
|
|
52
|
-
}
|
|
53
|
-
|
|
16
|
+
}
|
|
17
|
+
onCssImportPath(context) {
|
|
54
18
|
this.importCompletions.push(context);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
uriValue = literalCompletion.uriValue;
|
|
69
|
-
fullValue = stripQuotes(uriValue);
|
|
70
|
-
if (!(fullValue === '.' || fullValue === '..')) return [3 /*break*/, 2];
|
|
71
|
-
result.isIncomplete = true;
|
|
72
|
-
return [3 /*break*/, 4];
|
|
73
|
-
case 2: return [4 /*yield*/, this.providePathSuggestions(uriValue, literalCompletion.position, literalCompletion.range, document, documentContext)];
|
|
74
|
-
case 3:
|
|
75
|
-
items = _f.sent();
|
|
76
|
-
for (_b = 0, items_1 = items; _b < items_1.length; _b++) {
|
|
77
|
-
item = items_1[_b];
|
|
78
|
-
result.items.push(item);
|
|
79
|
-
}
|
|
80
|
-
_f.label = 4;
|
|
81
|
-
case 4:
|
|
82
|
-
_i++;
|
|
83
|
-
return [3 /*break*/, 1];
|
|
84
|
-
case 5:
|
|
85
|
-
_c = 0, _d = this.importCompletions;
|
|
86
|
-
_f.label = 6;
|
|
87
|
-
case 6:
|
|
88
|
-
if (!(_c < _d.length)) return [3 /*break*/, 10];
|
|
89
|
-
importCompletion = _d[_c];
|
|
90
|
-
pathValue = importCompletion.pathValue;
|
|
91
|
-
fullValue = stripQuotes(pathValue);
|
|
92
|
-
if (!(fullValue === '.' || fullValue === '..')) return [3 /*break*/, 7];
|
|
93
|
-
result.isIncomplete = true;
|
|
94
|
-
return [3 /*break*/, 9];
|
|
95
|
-
case 7: return [4 /*yield*/, this.providePathSuggestions(pathValue, importCompletion.position, importCompletion.range, document, documentContext)];
|
|
96
|
-
case 8:
|
|
97
|
-
suggestions = _f.sent();
|
|
98
|
-
if (document.languageId === 'scss') {
|
|
99
|
-
suggestions.forEach(function (s) {
|
|
100
|
-
if (startsWith(s.label, '_') && endsWith(s.label, '.scss')) {
|
|
101
|
-
if (s.textEdit) {
|
|
102
|
-
s.textEdit.newText = s.label.slice(1, -5);
|
|
103
|
-
}
|
|
104
|
-
else {
|
|
105
|
-
s.label = s.label.slice(1, -5);
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
});
|
|
109
|
-
}
|
|
110
|
-
for (_e = 0, suggestions_1 = suggestions; _e < suggestions_1.length; _e++) {
|
|
111
|
-
item = suggestions_1[_e];
|
|
112
|
-
result.items.push(item);
|
|
113
|
-
}
|
|
114
|
-
_f.label = 9;
|
|
115
|
-
case 9:
|
|
116
|
-
_c++;
|
|
117
|
-
return [3 /*break*/, 6];
|
|
118
|
-
case 10: return [2 /*return*/, result];
|
|
19
|
+
}
|
|
20
|
+
async computeCompletions(document, documentContext) {
|
|
21
|
+
const result = { items: [], isIncomplete: false };
|
|
22
|
+
for (const literalCompletion of this.literalCompletions) {
|
|
23
|
+
const uriValue = literalCompletion.uriValue;
|
|
24
|
+
const fullValue = stripQuotes(uriValue);
|
|
25
|
+
if (fullValue === '.' || fullValue === '..') {
|
|
26
|
+
result.isIncomplete = true;
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
const items = await this.providePathSuggestions(uriValue, literalCompletion.position, literalCompletion.range, document, documentContext);
|
|
30
|
+
for (let item of items) {
|
|
31
|
+
result.items.push(item);
|
|
119
32
|
}
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
parentDir = documentContext.resolveReference(valueBeforeLastSlash || '.', currentDocUri);
|
|
139
|
-
if (!parentDir) return [3 /*break*/, 4];
|
|
140
|
-
_b.label = 1;
|
|
141
|
-
case 1:
|
|
142
|
-
_b.trys.push([1, 3, , 4]);
|
|
143
|
-
result = [];
|
|
144
|
-
return [4 /*yield*/, this.readDirectory(parentDir)];
|
|
145
|
-
case 2:
|
|
146
|
-
infos = _b.sent();
|
|
147
|
-
for (_i = 0, infos_1 = infos; _i < infos_1.length; _i++) {
|
|
148
|
-
_a = infos_1[_i], name = _a[0], type = _a[1];
|
|
149
|
-
// Exclude paths that start with `.`
|
|
150
|
-
if (name.charCodeAt(0) !== CharCode_dot && (type === FileType.Directory || joinPath(parentDir, name) !== currentDocUri)) {
|
|
151
|
-
result.push(createCompletionItem(name, type === FileType.Directory, replaceRange));
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
for (const importCompletion of this.importCompletions) {
|
|
36
|
+
const pathValue = importCompletion.pathValue;
|
|
37
|
+
const fullValue = stripQuotes(pathValue);
|
|
38
|
+
if (fullValue === '.' || fullValue === '..') {
|
|
39
|
+
result.isIncomplete = true;
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
let suggestions = await this.providePathSuggestions(pathValue, importCompletion.position, importCompletion.range, document, documentContext);
|
|
43
|
+
if (document.languageId === 'scss') {
|
|
44
|
+
suggestions.forEach(s => {
|
|
45
|
+
if (startsWith(s.label, '_') && endsWith(s.label, '.scss')) {
|
|
46
|
+
if (s.textEdit) {
|
|
47
|
+
s.textEdit.newText = s.label.slice(1, -5);
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
s.label = s.label.slice(1, -5);
|
|
152
51
|
}
|
|
153
52
|
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
for (let item of suggestions) {
|
|
56
|
+
result.items.push(item);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return result;
|
|
61
|
+
}
|
|
62
|
+
async providePathSuggestions(pathValue, position, range, document, documentContext) {
|
|
63
|
+
const fullValue = stripQuotes(pathValue);
|
|
64
|
+
const isValueQuoted = startsWith(pathValue, `'`) || startsWith(pathValue, `"`);
|
|
65
|
+
const valueBeforeCursor = isValueQuoted
|
|
66
|
+
? fullValue.slice(0, position.character - (range.start.character + 1))
|
|
67
|
+
: fullValue.slice(0, position.character - range.start.character);
|
|
68
|
+
const currentDocUri = document.uri;
|
|
69
|
+
const fullValueRange = isValueQuoted ? shiftRange(range, 1, -1) : range;
|
|
70
|
+
const replaceRange = pathToReplaceRange(valueBeforeCursor, fullValue, fullValueRange);
|
|
71
|
+
const valueBeforeLastSlash = valueBeforeCursor.substring(0, valueBeforeCursor.lastIndexOf('/') + 1); // keep the last slash
|
|
72
|
+
let parentDir = documentContext.resolveReference(valueBeforeLastSlash || '.', currentDocUri);
|
|
73
|
+
if (parentDir) {
|
|
74
|
+
try {
|
|
75
|
+
const result = [];
|
|
76
|
+
const infos = await this.readDirectory(parentDir);
|
|
77
|
+
for (const [name, type] of infos) {
|
|
78
|
+
// Exclude paths that start with `.`
|
|
79
|
+
if (name.charCodeAt(0) !== CharCode_dot && (type === FileType.Directory || joinPath(parentDir, name) !== currentDocUri)) {
|
|
80
|
+
result.push(createCompletionItem(name, type === FileType.Directory, replaceRange));
|
|
81
|
+
}
|
|
159
82
|
}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
|
|
83
|
+
return result;
|
|
84
|
+
}
|
|
85
|
+
catch (e) {
|
|
86
|
+
// ignore
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return [];
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
const CharCode_dot = '.'.charCodeAt(0);
|
|
167
93
|
function stripQuotes(fullValue) {
|
|
168
|
-
if (startsWith(fullValue,
|
|
94
|
+
if (startsWith(fullValue, `'`) || startsWith(fullValue, `"`)) {
|
|
169
95
|
return fullValue.slice(1, -1);
|
|
170
96
|
}
|
|
171
97
|
else {
|
|
@@ -173,19 +99,19 @@ function stripQuotes(fullValue) {
|
|
|
173
99
|
}
|
|
174
100
|
}
|
|
175
101
|
function pathToReplaceRange(valueBeforeCursor, fullValue, fullValueRange) {
|
|
176
|
-
|
|
177
|
-
|
|
102
|
+
let replaceRange;
|
|
103
|
+
const lastIndexOfSlash = valueBeforeCursor.lastIndexOf('/');
|
|
178
104
|
if (lastIndexOfSlash === -1) {
|
|
179
105
|
replaceRange = fullValueRange;
|
|
180
106
|
}
|
|
181
107
|
else {
|
|
182
108
|
// For cases where cursor is in the middle of attribute value, like <script src="./s|rc/test.js">
|
|
183
109
|
// Find the last slash before cursor, and calculate the start of replace range from there
|
|
184
|
-
|
|
185
|
-
|
|
110
|
+
const valueAfterLastSlash = fullValue.slice(lastIndexOfSlash + 1);
|
|
111
|
+
const startPos = shiftPosition(fullValueRange.end, -valueAfterLastSlash.length);
|
|
186
112
|
// If whitespace exists, replace until it
|
|
187
|
-
|
|
188
|
-
|
|
113
|
+
const whitespaceIndex = valueAfterLastSlash.indexOf(' ');
|
|
114
|
+
let endPos;
|
|
189
115
|
if (whitespaceIndex !== -1) {
|
|
190
116
|
endPos = shiftPosition(startPos, whitespaceIndex);
|
|
191
117
|
}
|
|
@@ -225,7 +151,7 @@ function shiftPosition(pos, offset) {
|
|
|
225
151
|
return Position.create(pos.line, pos.character + offset);
|
|
226
152
|
}
|
|
227
153
|
function shiftRange(range, startOffset, endOffset) {
|
|
228
|
-
|
|
229
|
-
|
|
154
|
+
const start = shiftPosition(range.start, startOffset);
|
|
155
|
+
const end = shiftPosition(range.end, endOffset);
|
|
230
156
|
return Range.create(start, end);
|
|
231
157
|
}
|