vscode-json-languageservice 4.2.0 → 5.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 +10 -1
- package/SECURITY.md +41 -0
- package/lib/esm/jsonLanguageService.js +22 -22
- package/lib/esm/jsonLanguageTypes.d.ts +3 -1
- package/lib/esm/jsonLanguageTypes.js +3 -2
- package/lib/esm/jsonSchema.d.ts +21 -2
- package/lib/esm/parser/jsonParser.js +488 -488
- package/lib/esm/services/configuration.js +9 -9
- package/lib/esm/services/jsonCompletion.js +280 -290
- package/lib/esm/services/jsonDocumentSymbols.js +88 -99
- package/lib/esm/services/jsonFolding.js +38 -39
- package/lib/esm/services/jsonHover.js +40 -43
- package/lib/esm/services/jsonLinks.js +12 -13
- package/lib/esm/services/jsonSchemaService.js +234 -253
- package/lib/esm/services/jsonSelectionRanges.js +9 -9
- package/lib/esm/services/jsonValidation.js +53 -51
- package/lib/esm/utils/colors.js +7 -8
- package/lib/esm/utils/glob.js +12 -12
- package/lib/esm/utils/json.js +7 -7
- package/lib/esm/utils/objects.js +3 -0
- package/lib/esm/utils/strings.js +3 -3
- package/lib/umd/jsonLanguageService.js +36 -32
- package/lib/umd/jsonLanguageTypes.d.ts +3 -1
- package/lib/umd/jsonLanguageTypes.js +5 -3
- package/lib/umd/jsonSchema.d.ts +21 -2
- package/lib/umd/parser/jsonParser.js +492 -482
- package/lib/umd/services/configuration.js +9 -9
- package/lib/umd/services/jsonCompletion.js +287 -296
- package/lib/umd/services/jsonDocumentSymbols.js +92 -102
- package/lib/umd/services/jsonFolding.js +40 -41
- package/lib/umd/services/jsonHover.js +42 -44
- package/lib/umd/services/jsonLinks.js +13 -14
- package/lib/umd/services/jsonSchemaService.js +241 -257
- package/lib/umd/services/jsonSelectionRanges.js +11 -11
- package/lib/umd/services/jsonValidation.js +56 -53
- package/lib/umd/utils/colors.js +7 -8
- package/lib/umd/utils/glob.js +12 -12
- package/lib/umd/utils/json.js +7 -7
- package/lib/umd/utils/objects.js +5 -1
- package/lib/umd/utils/strings.js +3 -3
- package/package.json +12 -12
|
@@ -2,28 +2,13 @@
|
|
|
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 __extends = (this && this.__extends) || (function () {
|
|
6
|
-
var extendStatics = function (d, b) {
|
|
7
|
-
extendStatics = Object.setPrototypeOf ||
|
|
8
|
-
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
|
9
|
-
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
|
|
10
|
-
return extendStatics(d, b);
|
|
11
|
-
};
|
|
12
|
-
return function (d, b) {
|
|
13
|
-
if (typeof b !== "function" && b !== null)
|
|
14
|
-
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
|
|
15
|
-
extendStatics(d, b);
|
|
16
|
-
function __() { this.constructor = d; }
|
|
17
|
-
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
|
18
|
-
};
|
|
19
|
-
})();
|
|
20
5
|
import * as Json from 'jsonc-parser';
|
|
21
|
-
import { isNumber, equals, isBoolean, isString, isDefined } from '../utils/objects';
|
|
6
|
+
import { isNumber, equals, isBoolean, isString, isDefined, isObject } from '../utils/objects';
|
|
22
7
|
import { extendedRegExp } from '../utils/strings';
|
|
23
8
|
import { ErrorCode, Diagnostic, DiagnosticSeverity, Range } from '../jsonLanguageTypes';
|
|
24
9
|
import * as nls from 'vscode-nls';
|
|
25
|
-
|
|
26
|
-
|
|
10
|
+
const localize = nls.loadMessageBundle();
|
|
11
|
+
const formats = {
|
|
27
12
|
'color-hex': { errorMessage: localize('colorHexFormatWarning', 'Invalid color format. Use #RGB, #RGBA, #RRGGBB or #RRGGBBAA.'), pattern: /^#([0-9A-Fa-f]{3,4}|([0-9A-Fa-f]{2}){3,4})$/ },
|
|
28
13
|
'date-time': { errorMessage: localize('dateTimeFormatWarning', 'String is not a RFC3339 date-time.'), pattern: /^(\d{4})-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])T([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]|60)(\.[0-9]+)?(Z|(\+|-)([01][0-9]|2[0-3]):([0-5][0-9]))$/i },
|
|
29
14
|
'date': { errorMessage: localize('dateFormatWarning', 'String is not a RFC3339 date.'), pattern: /^(\d{4})-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$/i },
|
|
@@ -33,126 +18,79 @@ var formats = {
|
|
|
33
18
|
'ipv4': { errorMessage: localize('ipv4FormatWarning', 'String is not an IPv4 address.'), pattern: /^(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)$/ },
|
|
34
19
|
'ipv6': { errorMessage: localize('ipv6FormatWarning', 'String is not an IPv6 address.'), pattern: /^((([0-9a-f]{1,4}:){7}([0-9a-f]{1,4}|:))|(([0-9a-f]{1,4}:){6}(:[0-9a-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9a-f]{1,4}:){5}(((:[0-9a-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9a-f]{1,4}:){4}(((:[0-9a-f]{1,4}){1,3})|((:[0-9a-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){3}(((:[0-9a-f]{1,4}){1,4})|((:[0-9a-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){2}(((:[0-9a-f]{1,4}){1,5})|((:[0-9a-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){1}(((:[0-9a-f]{1,4}){1,6})|((:[0-9a-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9a-f]{1,4}){1,7})|((:[0-9a-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))$/i },
|
|
35
20
|
};
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
if (length === void 0) { length = 0; }
|
|
21
|
+
export class ASTNodeImpl {
|
|
22
|
+
constructor(parent, offset, length = 0) {
|
|
39
23
|
this.offset = offset;
|
|
40
24
|
this.length = length;
|
|
41
25
|
this.parent = parent;
|
|
42
26
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
enumerable: false,
|
|
48
|
-
configurable: true
|
|
49
|
-
});
|
|
50
|
-
ASTNodeImpl.prototype.toString = function () {
|
|
27
|
+
get children() {
|
|
28
|
+
return [];
|
|
29
|
+
}
|
|
30
|
+
toString() {
|
|
51
31
|
return 'type: ' + this.type + ' (' + this.offset + '/' + this.length + ')' + (this.parent ? ' parent: {' + this.parent.toString() + '}' : '');
|
|
52
|
-
};
|
|
53
|
-
return ASTNodeImpl;
|
|
54
|
-
}());
|
|
55
|
-
export { ASTNodeImpl };
|
|
56
|
-
var NullASTNodeImpl = /** @class */ (function (_super) {
|
|
57
|
-
__extends(NullASTNodeImpl, _super);
|
|
58
|
-
function NullASTNodeImpl(parent, offset) {
|
|
59
|
-
var _this = _super.call(this, parent, offset) || this;
|
|
60
|
-
_this.type = 'null';
|
|
61
|
-
_this.value = null;
|
|
62
|
-
return _this;
|
|
63
32
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
33
|
+
}
|
|
34
|
+
export class NullASTNodeImpl extends ASTNodeImpl {
|
|
35
|
+
constructor(parent, offset) {
|
|
36
|
+
super(parent, offset);
|
|
37
|
+
this.type = 'null';
|
|
38
|
+
this.value = null;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
export class BooleanASTNodeImpl extends ASTNodeImpl {
|
|
42
|
+
constructor(parent, boolValue, offset) {
|
|
43
|
+
super(parent, offset);
|
|
44
|
+
this.type = 'boolean';
|
|
45
|
+
this.value = boolValue;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
export class ArrayASTNodeImpl extends ASTNodeImpl {
|
|
49
|
+
constructor(parent, offset) {
|
|
50
|
+
super(parent, offset);
|
|
51
|
+
this.type = 'array';
|
|
52
|
+
this.items = [];
|
|
53
|
+
}
|
|
54
|
+
get children() {
|
|
55
|
+
return this.items;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
export class NumberASTNodeImpl extends ASTNodeImpl {
|
|
59
|
+
constructor(parent, offset) {
|
|
60
|
+
super(parent, offset);
|
|
61
|
+
this.type = 'number';
|
|
62
|
+
this.isInteger = true;
|
|
63
|
+
this.value = Number.NaN;
|
|
74
64
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
var _this = _super.call(this, parent, offset) || this;
|
|
82
|
-
_this.type = 'array';
|
|
83
|
-
_this.items = [];
|
|
84
|
-
return _this;
|
|
65
|
+
}
|
|
66
|
+
export class StringASTNodeImpl extends ASTNodeImpl {
|
|
67
|
+
constructor(parent, offset, length) {
|
|
68
|
+
super(parent, offset, length);
|
|
69
|
+
this.type = 'string';
|
|
70
|
+
this.value = '';
|
|
85
71
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
return ArrayASTNodeImpl;
|
|
94
|
-
}(ASTNodeImpl));
|
|
95
|
-
export { ArrayASTNodeImpl };
|
|
96
|
-
var NumberASTNodeImpl = /** @class */ (function (_super) {
|
|
97
|
-
__extends(NumberASTNodeImpl, _super);
|
|
98
|
-
function NumberASTNodeImpl(parent, offset) {
|
|
99
|
-
var _this = _super.call(this, parent, offset) || this;
|
|
100
|
-
_this.type = 'number';
|
|
101
|
-
_this.isInteger = true;
|
|
102
|
-
_this.value = Number.NaN;
|
|
103
|
-
return _this;
|
|
72
|
+
}
|
|
73
|
+
export class PropertyASTNodeImpl extends ASTNodeImpl {
|
|
74
|
+
constructor(parent, offset, keyNode) {
|
|
75
|
+
super(parent, offset);
|
|
76
|
+
this.type = 'property';
|
|
77
|
+
this.colonOffset = -1;
|
|
78
|
+
this.keyNode = keyNode;
|
|
104
79
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
export { NumberASTNodeImpl };
|
|
108
|
-
var StringASTNodeImpl = /** @class */ (function (_super) {
|
|
109
|
-
__extends(StringASTNodeImpl, _super);
|
|
110
|
-
function StringASTNodeImpl(parent, offset, length) {
|
|
111
|
-
var _this = _super.call(this, parent, offset, length) || this;
|
|
112
|
-
_this.type = 'string';
|
|
113
|
-
_this.value = '';
|
|
114
|
-
return _this;
|
|
80
|
+
get children() {
|
|
81
|
+
return this.valueNode ? [this.keyNode, this.valueNode] : [this.keyNode];
|
|
115
82
|
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
var _this = _super.call(this, parent, offset) || this;
|
|
123
|
-
_this.type = 'property';
|
|
124
|
-
_this.colonOffset = -1;
|
|
125
|
-
_this.keyNode = keyNode;
|
|
126
|
-
return _this;
|
|
83
|
+
}
|
|
84
|
+
export class ObjectASTNodeImpl extends ASTNodeImpl {
|
|
85
|
+
constructor(parent, offset) {
|
|
86
|
+
super(parent, offset);
|
|
87
|
+
this.type = 'object';
|
|
88
|
+
this.properties = [];
|
|
127
89
|
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
return this.valueNode ? [this.keyNode, this.valueNode] : [this.keyNode];
|
|
131
|
-
},
|
|
132
|
-
enumerable: false,
|
|
133
|
-
configurable: true
|
|
134
|
-
});
|
|
135
|
-
return PropertyASTNodeImpl;
|
|
136
|
-
}(ASTNodeImpl));
|
|
137
|
-
export { PropertyASTNodeImpl };
|
|
138
|
-
var ObjectASTNodeImpl = /** @class */ (function (_super) {
|
|
139
|
-
__extends(ObjectASTNodeImpl, _super);
|
|
140
|
-
function ObjectASTNodeImpl(parent, offset) {
|
|
141
|
-
var _this = _super.call(this, parent, offset) || this;
|
|
142
|
-
_this.type = 'object';
|
|
143
|
-
_this.properties = [];
|
|
144
|
-
return _this;
|
|
90
|
+
get children() {
|
|
91
|
+
return this.properties;
|
|
145
92
|
}
|
|
146
|
-
|
|
147
|
-
get: function () {
|
|
148
|
-
return this.properties;
|
|
149
|
-
},
|
|
150
|
-
enumerable: false,
|
|
151
|
-
configurable: true
|
|
152
|
-
});
|
|
153
|
-
return ObjectASTNodeImpl;
|
|
154
|
-
}(ASTNodeImpl));
|
|
155
|
-
export { ObjectASTNodeImpl };
|
|
93
|
+
}
|
|
156
94
|
export function asSchema(schema) {
|
|
157
95
|
if (isBoolean(schema)) {
|
|
158
96
|
return schema ? {} : { "not": {} };
|
|
@@ -164,75 +102,66 @@ export var EnumMatch;
|
|
|
164
102
|
EnumMatch[EnumMatch["Key"] = 0] = "Key";
|
|
165
103
|
EnumMatch[EnumMatch["Enum"] = 1] = "Enum";
|
|
166
104
|
})(EnumMatch || (EnumMatch = {}));
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
if (focusOffset === void 0) { focusOffset = -1; }
|
|
105
|
+
class SchemaCollector {
|
|
106
|
+
constructor(focusOffset = -1, exclude) {
|
|
170
107
|
this.focusOffset = focusOffset;
|
|
171
108
|
this.exclude = exclude;
|
|
172
109
|
this.schemas = [];
|
|
173
110
|
}
|
|
174
|
-
|
|
111
|
+
add(schema) {
|
|
175
112
|
this.schemas.push(schema);
|
|
176
|
-
}
|
|
177
|
-
|
|
113
|
+
}
|
|
114
|
+
merge(other) {
|
|
178
115
|
Array.prototype.push.apply(this.schemas, other.schemas);
|
|
179
|
-
}
|
|
180
|
-
|
|
116
|
+
}
|
|
117
|
+
include(node) {
|
|
181
118
|
return (this.focusOffset === -1 || contains(node, this.focusOffset)) && (node !== this.exclude);
|
|
182
|
-
}
|
|
183
|
-
|
|
119
|
+
}
|
|
120
|
+
newSub() {
|
|
184
121
|
return new SchemaCollector(-1, this.exclude);
|
|
185
|
-
};
|
|
186
|
-
return SchemaCollector;
|
|
187
|
-
}());
|
|
188
|
-
var NoOpSchemaCollector = /** @class */ (function () {
|
|
189
|
-
function NoOpSchemaCollector() {
|
|
190
122
|
}
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
var ValidationResult = /** @class */ (function () {
|
|
204
|
-
function ValidationResult() {
|
|
123
|
+
}
|
|
124
|
+
class NoOpSchemaCollector {
|
|
125
|
+
constructor() { }
|
|
126
|
+
get schemas() { return []; }
|
|
127
|
+
add(schema) { }
|
|
128
|
+
merge(other) { }
|
|
129
|
+
include(node) { return true; }
|
|
130
|
+
newSub() { return this; }
|
|
131
|
+
}
|
|
132
|
+
NoOpSchemaCollector.instance = new NoOpSchemaCollector();
|
|
133
|
+
export class ValidationResult {
|
|
134
|
+
constructor() {
|
|
205
135
|
this.problems = [];
|
|
206
136
|
this.propertiesMatches = 0;
|
|
137
|
+
this.processedProperties = new Set();
|
|
207
138
|
this.propertiesValueMatches = 0;
|
|
208
139
|
this.primaryValueMatches = 0;
|
|
209
140
|
this.enumValueMatch = false;
|
|
210
141
|
this.enumValues = undefined;
|
|
211
142
|
}
|
|
212
|
-
|
|
143
|
+
hasProblems() {
|
|
213
144
|
return !!this.problems.length;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
for (
|
|
217
|
-
var validationResult = validationResults_1[_i];
|
|
145
|
+
}
|
|
146
|
+
mergeAll(validationResults) {
|
|
147
|
+
for (const validationResult of validationResults) {
|
|
218
148
|
this.merge(validationResult);
|
|
219
149
|
}
|
|
220
|
-
}
|
|
221
|
-
|
|
150
|
+
}
|
|
151
|
+
merge(validationResult) {
|
|
222
152
|
this.problems = this.problems.concat(validationResult.problems);
|
|
223
|
-
}
|
|
224
|
-
|
|
153
|
+
}
|
|
154
|
+
mergeEnumValues(validationResult) {
|
|
225
155
|
if (!this.enumValueMatch && !validationResult.enumValueMatch && this.enumValues && validationResult.enumValues) {
|
|
226
156
|
this.enumValues = this.enumValues.concat(validationResult.enumValues);
|
|
227
|
-
for (
|
|
228
|
-
var error = _a[_i];
|
|
157
|
+
for (const error of this.problems) {
|
|
229
158
|
if (error.code === ErrorCode.EnumValueMismatch) {
|
|
230
|
-
error.message = localize('enumWarning', 'Value is not accepted. Valid values: {0}.', this.enumValues.map(
|
|
159
|
+
error.message = localize('enumWarning', 'Value is not accepted. Valid values: {0}.', this.enumValues.map(v => JSON.stringify(v)).join(', '));
|
|
231
160
|
}
|
|
232
161
|
}
|
|
233
162
|
}
|
|
234
|
-
}
|
|
235
|
-
|
|
163
|
+
}
|
|
164
|
+
mergePropertyMatch(propertyValidationResult) {
|
|
236
165
|
this.merge(propertyValidationResult);
|
|
237
166
|
this.propertiesMatches++;
|
|
238
167
|
if (propertyValidationResult.enumValueMatch || !propertyValidationResult.hasProblems() && propertyValidationResult.propertiesMatches) {
|
|
@@ -241,9 +170,12 @@ var ValidationResult = /** @class */ (function () {
|
|
|
241
170
|
if (propertyValidationResult.enumValueMatch && propertyValidationResult.enumValues && propertyValidationResult.enumValues.length === 1) {
|
|
242
171
|
this.primaryValueMatches++;
|
|
243
172
|
}
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
|
|
173
|
+
}
|
|
174
|
+
mergeProcessedProperties(validationResult) {
|
|
175
|
+
validationResult.processedProperties.forEach(p => this.processedProperties.add(p));
|
|
176
|
+
}
|
|
177
|
+
compare(other) {
|
|
178
|
+
const hasProblems = this.hasProblems();
|
|
247
179
|
if (hasProblems !== other.hasProblems()) {
|
|
248
180
|
return hasProblems ? -1 : 1;
|
|
249
181
|
}
|
|
@@ -257,12 +189,9 @@ var ValidationResult = /** @class */ (function () {
|
|
|
257
189
|
return this.propertiesValueMatches - other.propertiesValueMatches;
|
|
258
190
|
}
|
|
259
191
|
return this.propertiesMatches - other.propertiesMatches;
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
export { ValidationResult };
|
|
264
|
-
export function newJSONDocument(root, diagnostics) {
|
|
265
|
-
if (diagnostics === void 0) { diagnostics = []; }
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
export function newJSONDocument(root, diagnostics = []) {
|
|
266
195
|
return new JSONDocument(root, diagnostics, []);
|
|
267
196
|
}
|
|
268
197
|
export function getNodeValue(node) {
|
|
@@ -271,86 +200,78 @@ export function getNodeValue(node) {
|
|
|
271
200
|
export function getNodePath(node) {
|
|
272
201
|
return Json.getNodePath(node);
|
|
273
202
|
}
|
|
274
|
-
export function contains(node, offset, includeRightBound) {
|
|
275
|
-
if (includeRightBound === void 0) { includeRightBound = false; }
|
|
203
|
+
export function contains(node, offset, includeRightBound = false) {
|
|
276
204
|
return offset >= node.offset && offset < (node.offset + node.length) || includeRightBound && offset === (node.offset + node.length);
|
|
277
205
|
}
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
if (syntaxErrors === void 0) { syntaxErrors = []; }
|
|
281
|
-
if (comments === void 0) { comments = []; }
|
|
206
|
+
export class JSONDocument {
|
|
207
|
+
constructor(root, syntaxErrors = [], comments = []) {
|
|
282
208
|
this.root = root;
|
|
283
209
|
this.syntaxErrors = syntaxErrors;
|
|
284
210
|
this.comments = comments;
|
|
285
211
|
}
|
|
286
|
-
|
|
287
|
-
if (includeRightBound === void 0) { includeRightBound = false; }
|
|
212
|
+
getNodeFromOffset(offset, includeRightBound = false) {
|
|
288
213
|
if (this.root) {
|
|
289
214
|
return Json.findNodeAtOffset(this.root, offset, includeRightBound);
|
|
290
215
|
}
|
|
291
216
|
return undefined;
|
|
292
|
-
}
|
|
293
|
-
|
|
217
|
+
}
|
|
218
|
+
visit(visitor) {
|
|
294
219
|
if (this.root) {
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
220
|
+
const doVisit = (node) => {
|
|
221
|
+
let ctn = visitor(node);
|
|
222
|
+
const children = node.children;
|
|
298
223
|
if (Array.isArray(children)) {
|
|
299
|
-
for (
|
|
300
|
-
ctn =
|
|
224
|
+
for (let i = 0; i < children.length && ctn; i++) {
|
|
225
|
+
ctn = doVisit(children[i]);
|
|
301
226
|
}
|
|
302
227
|
}
|
|
303
228
|
return ctn;
|
|
304
229
|
};
|
|
305
|
-
|
|
230
|
+
doVisit(this.root);
|
|
306
231
|
}
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
if (severity === void 0) { severity = DiagnosticSeverity.Warning; }
|
|
232
|
+
}
|
|
233
|
+
validate(textDocument, schema, severity = DiagnosticSeverity.Warning) {
|
|
310
234
|
if (this.root && schema) {
|
|
311
|
-
|
|
235
|
+
const validationResult = new ValidationResult();
|
|
312
236
|
validate(this.root, schema, validationResult, NoOpSchemaCollector.instance);
|
|
313
|
-
return validationResult.problems.map(
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
return Diagnostic.create(range, p.message, (_a = p.severity) !== null && _a !== void 0 ? _a : severity, p.code);
|
|
237
|
+
return validationResult.problems.map(p => {
|
|
238
|
+
const range = Range.create(textDocument.positionAt(p.location.offset), textDocument.positionAt(p.location.offset + p.location.length));
|
|
239
|
+
return Diagnostic.create(range, p.message, p.severity ?? severity, p.code);
|
|
317
240
|
});
|
|
318
241
|
}
|
|
319
242
|
return undefined;
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
var matchingSchemas = new SchemaCollector(focusOffset, exclude);
|
|
243
|
+
}
|
|
244
|
+
getMatchingSchemas(schema, focusOffset = -1, exclude) {
|
|
245
|
+
const matchingSchemas = new SchemaCollector(focusOffset, exclude);
|
|
324
246
|
if (this.root && schema) {
|
|
325
247
|
validate(this.root, schema, new ValidationResult(), matchingSchemas);
|
|
326
248
|
}
|
|
327
249
|
return matchingSchemas.schemas;
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
}());
|
|
331
|
-
export { JSONDocument };
|
|
250
|
+
}
|
|
251
|
+
}
|
|
332
252
|
function validate(n, schema, validationResult, matchingSchemas) {
|
|
333
253
|
if (!n || !matchingSchemas.include(n)) {
|
|
334
254
|
return;
|
|
335
255
|
}
|
|
336
|
-
|
|
256
|
+
if (n.type === 'property') {
|
|
257
|
+
return validate(n.valueNode, schema, validationResult, matchingSchemas);
|
|
258
|
+
}
|
|
259
|
+
const node = n;
|
|
260
|
+
_validateNode();
|
|
337
261
|
switch (node.type) {
|
|
338
262
|
case 'object':
|
|
339
|
-
_validateObjectNode(node
|
|
263
|
+
_validateObjectNode(node);
|
|
340
264
|
break;
|
|
341
265
|
case 'array':
|
|
342
|
-
_validateArrayNode(node
|
|
266
|
+
_validateArrayNode(node);
|
|
343
267
|
break;
|
|
344
268
|
case 'string':
|
|
345
|
-
_validateStringNode(node
|
|
269
|
+
_validateStringNode(node);
|
|
346
270
|
break;
|
|
347
271
|
case 'number':
|
|
348
|
-
_validateNumberNode(node
|
|
272
|
+
_validateNumberNode(node);
|
|
349
273
|
break;
|
|
350
|
-
case 'property':
|
|
351
|
-
return validate(node.valueNode, schema, validationResult, matchingSchemas);
|
|
352
274
|
}
|
|
353
|
-
_validateNode();
|
|
354
275
|
matchingSchemas.add({ node: node, schema: schema });
|
|
355
276
|
function _validateNode() {
|
|
356
277
|
function matchesType(type) {
|
|
@@ -373,15 +294,14 @@ function validate(n, schema, validationResult, matchingSchemas) {
|
|
|
373
294
|
}
|
|
374
295
|
}
|
|
375
296
|
if (Array.isArray(schema.allOf)) {
|
|
376
|
-
for (
|
|
377
|
-
var subSchemaRef = _a[_i];
|
|
297
|
+
for (const subSchemaRef of schema.allOf) {
|
|
378
298
|
validate(node, asSchema(subSchemaRef), validationResult, matchingSchemas);
|
|
379
299
|
}
|
|
380
300
|
}
|
|
381
|
-
|
|
301
|
+
const notSchema = asSchema(schema.not);
|
|
382
302
|
if (notSchema) {
|
|
383
|
-
|
|
384
|
-
|
|
303
|
+
const subValidationResult = new ValidationResult();
|
|
304
|
+
const subMatchingSchemas = matchingSchemas.newSub();
|
|
385
305
|
validate(node, notSchema, subValidationResult, subMatchingSchemas);
|
|
386
306
|
if (!subValidationResult.hasProblems()) {
|
|
387
307
|
validationResult.problems.push({
|
|
@@ -389,21 +309,19 @@ function validate(n, schema, validationResult, matchingSchemas) {
|
|
|
389
309
|
message: localize('notSchemaWarning', "Matches a schema that is not allowed.")
|
|
390
310
|
});
|
|
391
311
|
}
|
|
392
|
-
for (
|
|
393
|
-
var ms = _c[_b];
|
|
312
|
+
for (const ms of subMatchingSchemas.schemas) {
|
|
394
313
|
ms.inverted = !ms.inverted;
|
|
395
314
|
matchingSchemas.add(ms);
|
|
396
315
|
}
|
|
397
316
|
}
|
|
398
|
-
|
|
399
|
-
|
|
317
|
+
const testAlternatives = (alternatives, maxOneMatch) => {
|
|
318
|
+
const matches = [];
|
|
400
319
|
// remember the best match that is used for error messages
|
|
401
|
-
|
|
402
|
-
for (
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
var subMatchingSchemas = matchingSchemas.newSub();
|
|
320
|
+
let bestMatch = undefined;
|
|
321
|
+
for (const subSchemaRef of alternatives) {
|
|
322
|
+
const subSchema = asSchema(subSchemaRef);
|
|
323
|
+
const subValidationResult = new ValidationResult();
|
|
324
|
+
const subMatchingSchemas = matchingSchemas.newSub();
|
|
407
325
|
validate(node, subSchema, subValidationResult, subMatchingSchemas);
|
|
408
326
|
if (!subValidationResult.hasProblems()) {
|
|
409
327
|
matches.push(subSchema);
|
|
@@ -417,9 +335,10 @@ function validate(n, schema, validationResult, matchingSchemas) {
|
|
|
417
335
|
bestMatch.matchingSchemas.merge(subMatchingSchemas);
|
|
418
336
|
bestMatch.validationResult.propertiesMatches += subValidationResult.propertiesMatches;
|
|
419
337
|
bestMatch.validationResult.propertiesValueMatches += subValidationResult.propertiesValueMatches;
|
|
338
|
+
bestMatch.validationResult.mergeProcessedProperties(subValidationResult);
|
|
420
339
|
}
|
|
421
340
|
else {
|
|
422
|
-
|
|
341
|
+
const compareResult = subValidationResult.compare(bestMatch.validationResult);
|
|
423
342
|
if (compareResult > 0) {
|
|
424
343
|
// our node is the best matching so far
|
|
425
344
|
bestMatch = { schema: subSchema, validationResult: subValidationResult, matchingSchemas: subMatchingSchemas };
|
|
@@ -442,6 +361,7 @@ function validate(n, schema, validationResult, matchingSchemas) {
|
|
|
442
361
|
validationResult.merge(bestMatch.validationResult);
|
|
443
362
|
validationResult.propertiesMatches += bestMatch.validationResult.propertiesMatches;
|
|
444
363
|
validationResult.propertiesValueMatches += bestMatch.validationResult.propertiesValueMatches;
|
|
364
|
+
validationResult.mergeProcessedProperties(bestMatch.validationResult);
|
|
445
365
|
matchingSchemas.merge(bestMatch.matchingSchemas);
|
|
446
366
|
}
|
|
447
367
|
return matches.length;
|
|
@@ -452,21 +372,23 @@ function validate(n, schema, validationResult, matchingSchemas) {
|
|
|
452
372
|
if (Array.isArray(schema.oneOf)) {
|
|
453
373
|
testAlternatives(schema.oneOf, true);
|
|
454
374
|
}
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
375
|
+
const testBranch = (schema) => {
|
|
376
|
+
const subValidationResult = new ValidationResult();
|
|
377
|
+
const subMatchingSchemas = matchingSchemas.newSub();
|
|
458
378
|
validate(node, asSchema(schema), subValidationResult, subMatchingSchemas);
|
|
459
379
|
validationResult.merge(subValidationResult);
|
|
460
380
|
validationResult.propertiesMatches += subValidationResult.propertiesMatches;
|
|
461
381
|
validationResult.propertiesValueMatches += subValidationResult.propertiesValueMatches;
|
|
382
|
+
validationResult.mergeProcessedProperties(subValidationResult);
|
|
462
383
|
matchingSchemas.merge(subMatchingSchemas);
|
|
463
384
|
};
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
385
|
+
const testCondition = (ifSchema, thenSchema, elseSchema) => {
|
|
386
|
+
const subSchema = asSchema(ifSchema);
|
|
387
|
+
const subValidationResult = new ValidationResult();
|
|
388
|
+
const subMatchingSchemas = matchingSchemas.newSub();
|
|
468
389
|
validate(node, subSchema, subValidationResult, subMatchingSchemas);
|
|
469
390
|
matchingSchemas.merge(subMatchingSchemas);
|
|
391
|
+
validationResult.mergeProcessedProperties(subValidationResult);
|
|
470
392
|
if (!subValidationResult.hasProblems()) {
|
|
471
393
|
if (thenSchema) {
|
|
472
394
|
testBranch(thenSchema);
|
|
@@ -476,15 +398,14 @@ function validate(n, schema, validationResult, matchingSchemas) {
|
|
|
476
398
|
testBranch(elseSchema);
|
|
477
399
|
}
|
|
478
400
|
};
|
|
479
|
-
|
|
401
|
+
const ifSchema = asSchema(schema.if);
|
|
480
402
|
if (ifSchema) {
|
|
481
403
|
testCondition(ifSchema, asSchema(schema.then), asSchema(schema.else));
|
|
482
404
|
}
|
|
483
405
|
if (Array.isArray(schema.enum)) {
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
for (
|
|
487
|
-
var e = _e[_d];
|
|
406
|
+
const val = getNodeValue(node);
|
|
407
|
+
let enumValueMatch = false;
|
|
408
|
+
for (const e of schema.enum) {
|
|
488
409
|
if (equals(val, e)) {
|
|
489
410
|
enumValueMatch = true;
|
|
490
411
|
break;
|
|
@@ -496,12 +417,12 @@ function validate(n, schema, validationResult, matchingSchemas) {
|
|
|
496
417
|
validationResult.problems.push({
|
|
497
418
|
location: { offset: node.offset, length: node.length },
|
|
498
419
|
code: ErrorCode.EnumValueMismatch,
|
|
499
|
-
message: schema.errorMessage || localize('enumWarning', 'Value is not accepted. Valid values: {0}.', schema.enum.map(
|
|
420
|
+
message: schema.errorMessage || localize('enumWarning', 'Value is not accepted. Valid values: {0}.', schema.enum.map(v => JSON.stringify(v)).join(', '))
|
|
500
421
|
});
|
|
501
422
|
}
|
|
502
423
|
}
|
|
503
424
|
if (isDefined(schema.const)) {
|
|
504
|
-
|
|
425
|
+
const val = getNodeValue(node);
|
|
505
426
|
if (!equals(val, schema.const)) {
|
|
506
427
|
validationResult.problems.push({
|
|
507
428
|
location: { offset: node.offset, length: node.length },
|
|
@@ -515,36 +436,37 @@ function validate(n, schema, validationResult, matchingSchemas) {
|
|
|
515
436
|
}
|
|
516
437
|
validationResult.enumValues = [schema.const];
|
|
517
438
|
}
|
|
518
|
-
|
|
439
|
+
let deprecationMessage = schema.deprecationMessage;
|
|
440
|
+
if ((deprecationMessage || schema.deprecated) && node.parent) {
|
|
441
|
+
deprecationMessage = deprecationMessage || localize('deprecated', 'Value is deprecated');
|
|
519
442
|
validationResult.problems.push({
|
|
520
443
|
location: { offset: node.parent.offset, length: node.parent.length },
|
|
521
444
|
severity: DiagnosticSeverity.Warning,
|
|
522
|
-
message:
|
|
445
|
+
message: deprecationMessage,
|
|
523
446
|
code: ErrorCode.Deprecated
|
|
524
447
|
});
|
|
525
448
|
}
|
|
526
449
|
}
|
|
527
|
-
function _validateNumberNode(node
|
|
528
|
-
|
|
450
|
+
function _validateNumberNode(node) {
|
|
451
|
+
const val = node.value;
|
|
529
452
|
function normalizeFloats(float) {
|
|
530
|
-
|
|
531
|
-
var parts = /^(-?\d+)(?:\.(\d+))?(?:e([-+]\d+))?$/.exec(float.toString());
|
|
453
|
+
const parts = /^(-?\d+)(?:\.(\d+))?(?:e([-+]\d+))?$/.exec(float.toString());
|
|
532
454
|
return parts && {
|
|
533
455
|
value: Number(parts[1] + (parts[2] || '')),
|
|
534
|
-
multiplier: (
|
|
456
|
+
multiplier: (parts[2]?.length || 0) - (parseInt(parts[3]) || 0)
|
|
535
457
|
};
|
|
536
458
|
}
|
|
537
459
|
;
|
|
538
460
|
if (isNumber(schema.multipleOf)) {
|
|
539
|
-
|
|
461
|
+
let remainder = -1;
|
|
540
462
|
if (Number.isInteger(schema.multipleOf)) {
|
|
541
463
|
remainder = val % schema.multipleOf;
|
|
542
464
|
}
|
|
543
465
|
else {
|
|
544
|
-
|
|
545
|
-
|
|
466
|
+
let normMultipleOf = normalizeFloats(schema.multipleOf);
|
|
467
|
+
let normValue = normalizeFloats(val);
|
|
546
468
|
if (normMultipleOf && normValue) {
|
|
547
|
-
|
|
469
|
+
const multiplier = 10 ** Math.abs(normValue.multiplier - normMultipleOf.multiplier);
|
|
548
470
|
if (normValue.multiplier < normMultipleOf.multiplier) {
|
|
549
471
|
normValue.value *= multiplier;
|
|
550
472
|
}
|
|
@@ -576,28 +498,28 @@ function validate(n, schema, validationResult, matchingSchemas) {
|
|
|
576
498
|
}
|
|
577
499
|
return undefined;
|
|
578
500
|
}
|
|
579
|
-
|
|
501
|
+
const exclusiveMinimum = getExclusiveLimit(schema.minimum, schema.exclusiveMinimum);
|
|
580
502
|
if (isNumber(exclusiveMinimum) && val <= exclusiveMinimum) {
|
|
581
503
|
validationResult.problems.push({
|
|
582
504
|
location: { offset: node.offset, length: node.length },
|
|
583
505
|
message: localize('exclusiveMinimumWarning', 'Value is below the exclusive minimum of {0}.', exclusiveMinimum)
|
|
584
506
|
});
|
|
585
507
|
}
|
|
586
|
-
|
|
508
|
+
const exclusiveMaximum = getExclusiveLimit(schema.maximum, schema.exclusiveMaximum);
|
|
587
509
|
if (isNumber(exclusiveMaximum) && val >= exclusiveMaximum) {
|
|
588
510
|
validationResult.problems.push({
|
|
589
511
|
location: { offset: node.offset, length: node.length },
|
|
590
512
|
message: localize('exclusiveMaximumWarning', 'Value is above the exclusive maximum of {0}.', exclusiveMaximum)
|
|
591
513
|
});
|
|
592
514
|
}
|
|
593
|
-
|
|
515
|
+
const minimum = getLimit(schema.minimum, schema.exclusiveMinimum);
|
|
594
516
|
if (isNumber(minimum) && val < minimum) {
|
|
595
517
|
validationResult.problems.push({
|
|
596
518
|
location: { offset: node.offset, length: node.length },
|
|
597
519
|
message: localize('minimumWarning', 'Value is below the minimum of {0}.', minimum)
|
|
598
520
|
});
|
|
599
521
|
}
|
|
600
|
-
|
|
522
|
+
const maximum = getLimit(schema.maximum, schema.exclusiveMaximum);
|
|
601
523
|
if (isNumber(maximum) && val > maximum) {
|
|
602
524
|
validationResult.problems.push({
|
|
603
525
|
location: { offset: node.offset, length: node.length },
|
|
@@ -605,7 +527,7 @@ function validate(n, schema, validationResult, matchingSchemas) {
|
|
|
605
527
|
});
|
|
606
528
|
}
|
|
607
529
|
}
|
|
608
|
-
function _validateStringNode(node
|
|
530
|
+
function _validateStringNode(node) {
|
|
609
531
|
if (isNumber(schema.minLength) && node.value.length < schema.minLength) {
|
|
610
532
|
validationResult.problems.push({
|
|
611
533
|
location: { offset: node.offset, length: node.length },
|
|
@@ -619,8 +541,8 @@ function validate(n, schema, validationResult, matchingSchemas) {
|
|
|
619
541
|
});
|
|
620
542
|
}
|
|
621
543
|
if (isString(schema.pattern)) {
|
|
622
|
-
|
|
623
|
-
if (!(regex
|
|
544
|
+
const regex = extendedRegExp(schema.pattern);
|
|
545
|
+
if (!(regex?.test(node.value))) {
|
|
624
546
|
validationResult.problems.push({
|
|
625
547
|
location: { offset: node.offset, length: node.length },
|
|
626
548
|
message: schema.patternErrorMessage || schema.errorMessage || localize('patternWarning', 'String does not match the pattern of "{0}".', schema.pattern)
|
|
@@ -632,12 +554,12 @@ function validate(n, schema, validationResult, matchingSchemas) {
|
|
|
632
554
|
case 'uri':
|
|
633
555
|
case 'uri-reference':
|
|
634
556
|
{
|
|
635
|
-
|
|
557
|
+
let errorMessage;
|
|
636
558
|
if (!node.value) {
|
|
637
559
|
errorMessage = localize('uriEmpty', 'URI expected.');
|
|
638
560
|
}
|
|
639
561
|
else {
|
|
640
|
-
|
|
562
|
+
const match = /^(([^:/?#]+?):)?(\/\/([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/.exec(node.value);
|
|
641
563
|
if (!match) {
|
|
642
564
|
errorMessage = localize('uriMissing', 'URI is expected.');
|
|
643
565
|
}
|
|
@@ -661,7 +583,7 @@ function validate(n, schema, validationResult, matchingSchemas) {
|
|
|
661
583
|
case 'hostname':
|
|
662
584
|
case 'ipv4':
|
|
663
585
|
case 'ipv6':
|
|
664
|
-
|
|
586
|
+
const format = formats[schema.format];
|
|
665
587
|
if (!node.value || !format.pattern.exec(node.value)) {
|
|
666
588
|
validationResult.problems.push({
|
|
667
589
|
location: { offset: node.offset, length: node.length },
|
|
@@ -672,62 +594,107 @@ function validate(n, schema, validationResult, matchingSchemas) {
|
|
|
672
594
|
}
|
|
673
595
|
}
|
|
674
596
|
}
|
|
675
|
-
function _validateArrayNode(node
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
597
|
+
function _validateArrayNode(node) {
|
|
598
|
+
let prefixItemsSchemas;
|
|
599
|
+
let additionalItemSchema;
|
|
600
|
+
let isSchema_2020_12 = Array.isArray(schema.prefixItems) || (schema.items !== undefined && !Array.isArray(schema.items) && schema.additionalItems === undefined);
|
|
601
|
+
if (isSchema_2020_12) {
|
|
602
|
+
prefixItemsSchemas = schema.prefixItems;
|
|
603
|
+
additionalItemSchema = !Array.isArray(schema.items) ? schema.items : undefined;
|
|
604
|
+
}
|
|
605
|
+
else {
|
|
606
|
+
prefixItemsSchemas = Array.isArray(schema.items) ? schema.items : undefined;
|
|
607
|
+
additionalItemSchema = !Array.isArray(schema.items) ? schema.items : schema.additionalItems;
|
|
608
|
+
}
|
|
609
|
+
let index = 0;
|
|
610
|
+
if (prefixItemsSchemas !== undefined) {
|
|
611
|
+
const max = Math.min(prefixItemsSchemas.length, node.items.length);
|
|
612
|
+
for (; index < max; index++) {
|
|
613
|
+
const subSchemaRef = prefixItemsSchemas[index];
|
|
614
|
+
const subSchema = asSchema(subSchemaRef);
|
|
615
|
+
const itemValidationResult = new ValidationResult();
|
|
616
|
+
const item = node.items[index];
|
|
683
617
|
if (item) {
|
|
684
618
|
validate(item, subSchema, itemValidationResult, matchingSchemas);
|
|
685
619
|
validationResult.mergePropertyMatch(itemValidationResult);
|
|
686
620
|
}
|
|
687
|
-
|
|
688
|
-
validationResult.propertiesValueMatches++;
|
|
689
|
-
}
|
|
621
|
+
validationResult.processedProperties.add(String(index));
|
|
690
622
|
}
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
validate(node.items[i], schema.additionalItems, itemValidationResult, matchingSchemas);
|
|
696
|
-
validationResult.mergePropertyMatch(itemValidationResult);
|
|
697
|
-
}
|
|
698
|
-
}
|
|
699
|
-
else if (schema.additionalItems === false) {
|
|
623
|
+
}
|
|
624
|
+
if (additionalItemSchema !== undefined && index < node.items.length) {
|
|
625
|
+
if (typeof additionalItemSchema === 'boolean') {
|
|
626
|
+
if (additionalItemSchema === false) {
|
|
700
627
|
validationResult.problems.push({
|
|
701
628
|
location: { offset: node.offset, length: node.length },
|
|
702
|
-
message: localize('additionalItemsWarning', 'Array has too many items according to schema. Expected {0} or fewer.',
|
|
629
|
+
message: localize('additionalItemsWarning', 'Array has too many items according to schema. Expected {0} or fewer.', index)
|
|
703
630
|
});
|
|
704
631
|
}
|
|
632
|
+
for (; index < node.items.length; index++) {
|
|
633
|
+
validationResult.processedProperties.add(String(index));
|
|
634
|
+
validationResult.propertiesValueMatches++;
|
|
635
|
+
}
|
|
705
636
|
}
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
for (var _i = 0, _a = node.items; _i < _a.length; _i++) {
|
|
711
|
-
var item = _a[_i];
|
|
712
|
-
var itemValidationResult = new ValidationResult();
|
|
713
|
-
validate(item, itemSchema, itemValidationResult, matchingSchemas);
|
|
637
|
+
else {
|
|
638
|
+
for (; index < node.items.length; index++) {
|
|
639
|
+
const itemValidationResult = new ValidationResult();
|
|
640
|
+
validate(node.items[index], additionalItemSchema, itemValidationResult, matchingSchemas);
|
|
714
641
|
validationResult.mergePropertyMatch(itemValidationResult);
|
|
642
|
+
validationResult.processedProperties.add(String(index));
|
|
715
643
|
}
|
|
716
644
|
}
|
|
717
645
|
}
|
|
718
|
-
|
|
646
|
+
const containsSchema = asSchema(schema.contains);
|
|
719
647
|
if (containsSchema) {
|
|
720
|
-
|
|
721
|
-
|
|
648
|
+
let containsCount = 0;
|
|
649
|
+
for (let index = 0; index < node.items.length; index++) {
|
|
650
|
+
const item = node.items[index];
|
|
651
|
+
const itemValidationResult = new ValidationResult();
|
|
722
652
|
validate(item, containsSchema, itemValidationResult, NoOpSchemaCollector.instance);
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
653
|
+
if (!itemValidationResult.hasProblems()) {
|
|
654
|
+
containsCount++;
|
|
655
|
+
if (isSchema_2020_12) {
|
|
656
|
+
validationResult.processedProperties.add(String(index));
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
if (containsCount === 0 && !isNumber(schema.minContains)) {
|
|
726
661
|
validationResult.problems.push({
|
|
727
662
|
location: { offset: node.offset, length: node.length },
|
|
728
663
|
message: schema.errorMessage || localize('requiredItemMissingWarning', 'Array does not contain required item.')
|
|
729
664
|
});
|
|
730
665
|
}
|
|
666
|
+
if (isNumber(schema.minContains) && containsCount < schema.minContains) {
|
|
667
|
+
validationResult.problems.push({
|
|
668
|
+
location: { offset: node.offset, length: node.length },
|
|
669
|
+
message: localize('minContainsWarning', 'Array has too few items that match the contains contraint. Expected {0} or more.', schema.minContains)
|
|
670
|
+
});
|
|
671
|
+
}
|
|
672
|
+
if (isNumber(schema.maxContains) && containsCount > schema.maxContains) {
|
|
673
|
+
validationResult.problems.push({
|
|
674
|
+
location: { offset: node.offset, length: node.length },
|
|
675
|
+
message: localize('maxContainsWarning', 'Array has too many items that match the contains contraint. Expected {0} or less.', schema.maxContains)
|
|
676
|
+
});
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
const unevaluatedItems = schema.unevaluatedItems;
|
|
680
|
+
if (unevaluatedItems !== undefined) {
|
|
681
|
+
for (let i = 0; i < node.items.length; i++) {
|
|
682
|
+
if (!validationResult.processedProperties.has(String(i))) {
|
|
683
|
+
if (unevaluatedItems === false) {
|
|
684
|
+
validationResult.problems.push({
|
|
685
|
+
location: { offset: node.offset, length: node.length },
|
|
686
|
+
message: localize('unevaluatedItemsWarning', 'Item does not match any validation rule from the array.')
|
|
687
|
+
});
|
|
688
|
+
}
|
|
689
|
+
else {
|
|
690
|
+
const itemValidationResult = new ValidationResult();
|
|
691
|
+
validate(node.items[i], schema.additionalItems, itemValidationResult, matchingSchemas);
|
|
692
|
+
validationResult.mergePropertyMatch(itemValidationResult);
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
validationResult.processedProperties.add(String(i));
|
|
696
|
+
validationResult.propertiesValueMatches++;
|
|
697
|
+
}
|
|
731
698
|
}
|
|
732
699
|
if (isNumber(schema.minItems) && node.items.length < schema.minItems) {
|
|
733
700
|
validationResult.problems.push({
|
|
@@ -742,9 +709,9 @@ function validate(n, schema, validationResult, matchingSchemas) {
|
|
|
742
709
|
});
|
|
743
710
|
}
|
|
744
711
|
if (schema.uniqueItems === true) {
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
return index !==
|
|
712
|
+
const values = getNodeValue(node);
|
|
713
|
+
const duplicates = values.some((value, index) => {
|
|
714
|
+
return index !== values.lastIndexOf(value);
|
|
748
715
|
});
|
|
749
716
|
if (duplicates) {
|
|
750
717
|
validationResult.problems.push({
|
|
@@ -754,21 +721,19 @@ function validate(n, schema, validationResult, matchingSchemas) {
|
|
|
754
721
|
}
|
|
755
722
|
}
|
|
756
723
|
}
|
|
757
|
-
function _validateObjectNode(node
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
for (
|
|
761
|
-
|
|
762
|
-
var key = propertyNode.keyNode.value;
|
|
724
|
+
function _validateObjectNode(node) {
|
|
725
|
+
const seenKeys = Object.create(null);
|
|
726
|
+
const unprocessedProperties = new Set();
|
|
727
|
+
for (const propertyNode of node.properties) {
|
|
728
|
+
const key = propertyNode.keyNode.value;
|
|
763
729
|
seenKeys[key] = propertyNode.valueNode;
|
|
764
|
-
unprocessedProperties.
|
|
730
|
+
unprocessedProperties.add(key);
|
|
765
731
|
}
|
|
766
732
|
if (Array.isArray(schema.required)) {
|
|
767
|
-
for (
|
|
768
|
-
var propertyName = _c[_b];
|
|
733
|
+
for (const propertyName of schema.required) {
|
|
769
734
|
if (!seenKeys[propertyName]) {
|
|
770
|
-
|
|
771
|
-
|
|
735
|
+
const keyNode = node.parent && node.parent.type === 'property' && node.parent.keyNode;
|
|
736
|
+
const location = keyNode ? { offset: keyNode.offset, length: keyNode.length } : { offset: node.offset, length: 1 };
|
|
772
737
|
validationResult.problems.push({
|
|
773
738
|
location: location,
|
|
774
739
|
message: localize('MissingRequiredPropWarning', 'Missing property "{0}".', propertyName)
|
|
@@ -776,23 +741,19 @@ function validate(n, schema, validationResult, matchingSchemas) {
|
|
|
776
741
|
}
|
|
777
742
|
}
|
|
778
743
|
}
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
unprocessedProperties.splice(index, 1);
|
|
783
|
-
index = unprocessedProperties.indexOf(prop);
|
|
784
|
-
}
|
|
744
|
+
const propertyProcessed = (prop) => {
|
|
745
|
+
unprocessedProperties.delete(prop);
|
|
746
|
+
validationResult.processedProperties.add(prop);
|
|
785
747
|
};
|
|
786
748
|
if (schema.properties) {
|
|
787
|
-
for (
|
|
788
|
-
var propertyName = _e[_d];
|
|
749
|
+
for (const propertyName of Object.keys(schema.properties)) {
|
|
789
750
|
propertyProcessed(propertyName);
|
|
790
|
-
|
|
791
|
-
|
|
751
|
+
const propertySchema = schema.properties[propertyName];
|
|
752
|
+
const child = seenKeys[propertyName];
|
|
792
753
|
if (child) {
|
|
793
754
|
if (isBoolean(propertySchema)) {
|
|
794
755
|
if (!propertySchema) {
|
|
795
|
-
|
|
756
|
+
const propertyNode = child.parent;
|
|
796
757
|
validationResult.problems.push({
|
|
797
758
|
location: { offset: propertyNode.keyNode.offset, length: propertyNode.keyNode.length },
|
|
798
759
|
message: schema.errorMessage || localize('DisallowedExtraPropWarning', 'Property {0} is not allowed.', propertyName)
|
|
@@ -804,7 +765,7 @@ function validate(n, schema, validationResult, matchingSchemas) {
|
|
|
804
765
|
}
|
|
805
766
|
}
|
|
806
767
|
else {
|
|
807
|
-
|
|
768
|
+
const propertyValidationResult = new ValidationResult();
|
|
808
769
|
validate(child, propertySchema, propertyValidationResult, matchingSchemas);
|
|
809
770
|
validationResult.mergePropertyMatch(propertyValidationResult);
|
|
810
771
|
}
|
|
@@ -812,64 +773,86 @@ function validate(n, schema, validationResult, matchingSchemas) {
|
|
|
812
773
|
}
|
|
813
774
|
}
|
|
814
775
|
if (schema.patternProperties) {
|
|
815
|
-
for (
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
776
|
+
for (const propertyPattern of Object.keys(schema.patternProperties)) {
|
|
777
|
+
const regex = extendedRegExp(propertyPattern);
|
|
778
|
+
if (regex) {
|
|
779
|
+
const processed = [];
|
|
780
|
+
for (const propertyName of unprocessedProperties) {
|
|
781
|
+
if (regex.test(propertyName)) {
|
|
782
|
+
processed.push(propertyName);
|
|
783
|
+
const child = seenKeys[propertyName];
|
|
784
|
+
if (child) {
|
|
785
|
+
const propertySchema = schema.patternProperties[propertyPattern];
|
|
786
|
+
if (isBoolean(propertySchema)) {
|
|
787
|
+
if (!propertySchema) {
|
|
788
|
+
const propertyNode = child.parent;
|
|
789
|
+
validationResult.problems.push({
|
|
790
|
+
location: { offset: propertyNode.keyNode.offset, length: propertyNode.keyNode.length },
|
|
791
|
+
message: schema.errorMessage || localize('DisallowedExtraPropWarning', 'Property {0} is not allowed.', propertyName)
|
|
792
|
+
});
|
|
793
|
+
}
|
|
794
|
+
else {
|
|
795
|
+
validationResult.propertiesMatches++;
|
|
796
|
+
validationResult.propertiesValueMatches++;
|
|
797
|
+
}
|
|
832
798
|
}
|
|
833
799
|
else {
|
|
834
|
-
|
|
835
|
-
|
|
800
|
+
const propertyValidationResult = new ValidationResult();
|
|
801
|
+
validate(child, propertySchema, propertyValidationResult, matchingSchemas);
|
|
802
|
+
validationResult.mergePropertyMatch(propertyValidationResult);
|
|
836
803
|
}
|
|
837
804
|
}
|
|
838
|
-
else {
|
|
839
|
-
var propertyValidationResult = new ValidationResult();
|
|
840
|
-
validate(child, propertySchema, propertyValidationResult, matchingSchemas);
|
|
841
|
-
validationResult.mergePropertyMatch(propertyValidationResult);
|
|
842
|
-
}
|
|
843
805
|
}
|
|
844
806
|
}
|
|
807
|
+
processed.forEach(propertyProcessed);
|
|
845
808
|
}
|
|
846
809
|
}
|
|
847
810
|
}
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
811
|
+
const additionalProperties = schema.additionalProperties;
|
|
812
|
+
if (additionalProperties !== undefined && additionalProperties !== true) {
|
|
813
|
+
for (const propertyName of unprocessedProperties) {
|
|
814
|
+
propertyProcessed(propertyName);
|
|
815
|
+
const child = seenKeys[propertyName];
|
|
852
816
|
if (child) {
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
validationResult.mergePropertyMatch(propertyValidationResult);
|
|
856
|
-
}
|
|
857
|
-
}
|
|
858
|
-
}
|
|
859
|
-
else if (schema.additionalProperties === false) {
|
|
860
|
-
if (unprocessedProperties.length > 0) {
|
|
861
|
-
for (var _l = 0, unprocessedProperties_2 = unprocessedProperties; _l < unprocessedProperties_2.length; _l++) {
|
|
862
|
-
var propertyName = unprocessedProperties_2[_l];
|
|
863
|
-
var child = seenKeys[propertyName];
|
|
864
|
-
if (child) {
|
|
865
|
-
var propertyNode = child.parent;
|
|
817
|
+
if (additionalProperties === false) {
|
|
818
|
+
const propertyNode = child.parent;
|
|
866
819
|
validationResult.problems.push({
|
|
867
820
|
location: { offset: propertyNode.keyNode.offset, length: propertyNode.keyNode.length },
|
|
868
821
|
message: schema.errorMessage || localize('DisallowedExtraPropWarning', 'Property {0} is not allowed.', propertyName)
|
|
869
822
|
});
|
|
870
823
|
}
|
|
824
|
+
else {
|
|
825
|
+
const propertyValidationResult = new ValidationResult();
|
|
826
|
+
validate(child, additionalProperties, propertyValidationResult, matchingSchemas);
|
|
827
|
+
validationResult.mergePropertyMatch(propertyValidationResult);
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
const unevaluatedProperties = schema.unevaluatedProperties;
|
|
833
|
+
if (unevaluatedProperties !== undefined && unevaluatedProperties !== true) {
|
|
834
|
+
const processed = [];
|
|
835
|
+
for (const propertyName of unprocessedProperties) {
|
|
836
|
+
if (!validationResult.processedProperties.has(propertyName)) {
|
|
837
|
+
processed.push(propertyName);
|
|
838
|
+
const child = seenKeys[propertyName];
|
|
839
|
+
if (child) {
|
|
840
|
+
if (unevaluatedProperties === false) {
|
|
841
|
+
const propertyNode = child.parent;
|
|
842
|
+
validationResult.problems.push({
|
|
843
|
+
location: { offset: propertyNode.keyNode.offset, length: propertyNode.keyNode.length },
|
|
844
|
+
message: schema.errorMessage || localize('DisallowedExtraPropWarning', 'Property {0} is not allowed.', propertyName)
|
|
845
|
+
});
|
|
846
|
+
}
|
|
847
|
+
else {
|
|
848
|
+
const propertyValidationResult = new ValidationResult();
|
|
849
|
+
validate(child, unevaluatedProperties, propertyValidationResult, matchingSchemas);
|
|
850
|
+
validationResult.mergePropertyMatch(propertyValidationResult);
|
|
851
|
+
}
|
|
852
|
+
}
|
|
871
853
|
}
|
|
872
854
|
}
|
|
855
|
+
processed.forEach(propertyProcessed);
|
|
873
856
|
}
|
|
874
857
|
if (isNumber(schema.maxProperties)) {
|
|
875
858
|
if (node.properties.length > schema.maxProperties) {
|
|
@@ -887,71 +870,88 @@ function validate(n, schema, validationResult, matchingSchemas) {
|
|
|
887
870
|
});
|
|
888
871
|
}
|
|
889
872
|
}
|
|
873
|
+
if (schema.dependentRequired) {
|
|
874
|
+
for (const key in schema.dependentRequired) {
|
|
875
|
+
const prop = seenKeys[key];
|
|
876
|
+
const propertyDeps = schema.dependentRequired[key];
|
|
877
|
+
if (prop && Array.isArray(propertyDeps)) {
|
|
878
|
+
_validatePropertyDependencies(key, propertyDeps);
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
if (schema.dependentSchemas) {
|
|
883
|
+
for (const key in schema.dependentSchemas) {
|
|
884
|
+
const prop = seenKeys[key];
|
|
885
|
+
const propertyDeps = schema.dependentSchemas[key];
|
|
886
|
+
if (prop && isObject(propertyDeps)) {
|
|
887
|
+
_validatePropertyDependencies(key, propertyDeps);
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
}
|
|
890
891
|
if (schema.dependencies) {
|
|
891
|
-
for (
|
|
892
|
-
|
|
893
|
-
var prop = seenKeys[key];
|
|
892
|
+
for (const key in schema.dependencies) {
|
|
893
|
+
const prop = seenKeys[key];
|
|
894
894
|
if (prop) {
|
|
895
|
-
|
|
896
|
-
if (Array.isArray(propertyDep)) {
|
|
897
|
-
for (var _p = 0, propertyDep_1 = propertyDep; _p < propertyDep_1.length; _p++) {
|
|
898
|
-
var requiredProp = propertyDep_1[_p];
|
|
899
|
-
if (!seenKeys[requiredProp]) {
|
|
900
|
-
validationResult.problems.push({
|
|
901
|
-
location: { offset: node.offset, length: node.length },
|
|
902
|
-
message: localize('RequiredDependentPropWarning', 'Object is missing property {0} required by property {1}.', requiredProp, key)
|
|
903
|
-
});
|
|
904
|
-
}
|
|
905
|
-
else {
|
|
906
|
-
validationResult.propertiesValueMatches++;
|
|
907
|
-
}
|
|
908
|
-
}
|
|
909
|
-
}
|
|
910
|
-
else {
|
|
911
|
-
var propertySchema = asSchema(propertyDep);
|
|
912
|
-
if (propertySchema) {
|
|
913
|
-
var propertyValidationResult = new ValidationResult();
|
|
914
|
-
validate(node, propertySchema, propertyValidationResult, matchingSchemas);
|
|
915
|
-
validationResult.mergePropertyMatch(propertyValidationResult);
|
|
916
|
-
}
|
|
917
|
-
}
|
|
895
|
+
_validatePropertyDependencies(key, schema.dependencies[key]);
|
|
918
896
|
}
|
|
919
897
|
}
|
|
920
898
|
}
|
|
921
|
-
|
|
899
|
+
const propertyNames = asSchema(schema.propertyNames);
|
|
922
900
|
if (propertyNames) {
|
|
923
|
-
for (
|
|
924
|
-
|
|
925
|
-
var key = f.keyNode;
|
|
901
|
+
for (const f of node.properties) {
|
|
902
|
+
const key = f.keyNode;
|
|
926
903
|
if (key) {
|
|
927
904
|
validate(key, propertyNames, validationResult, NoOpSchemaCollector.instance);
|
|
928
905
|
}
|
|
929
906
|
}
|
|
930
907
|
}
|
|
908
|
+
function _validatePropertyDependencies(key, propertyDep) {
|
|
909
|
+
if (Array.isArray(propertyDep)) {
|
|
910
|
+
for (const requiredProp of propertyDep) {
|
|
911
|
+
if (!seenKeys[requiredProp]) {
|
|
912
|
+
validationResult.problems.push({
|
|
913
|
+
location: { offset: node.offset, length: node.length },
|
|
914
|
+
message: localize('RequiredDependentPropWarning', 'Object is missing property {0} required by property {1}.', requiredProp, key)
|
|
915
|
+
});
|
|
916
|
+
}
|
|
917
|
+
else {
|
|
918
|
+
validationResult.propertiesValueMatches++;
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
else {
|
|
923
|
+
const propertySchema = asSchema(propertyDep);
|
|
924
|
+
if (propertySchema) {
|
|
925
|
+
const propertyValidationResult = new ValidationResult();
|
|
926
|
+
validate(node, propertySchema, propertyValidationResult, matchingSchemas);
|
|
927
|
+
validationResult.mergePropertyMatch(propertyValidationResult);
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
931
|
}
|
|
932
932
|
}
|
|
933
933
|
export function parse(textDocument, config) {
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
934
|
+
const problems = [];
|
|
935
|
+
let lastProblemOffset = -1;
|
|
936
|
+
const text = textDocument.getText();
|
|
937
|
+
const scanner = Json.createScanner(text, false);
|
|
938
|
+
const commentRanges = config && config.collectComments ? [] : undefined;
|
|
939
939
|
function _scanNext() {
|
|
940
940
|
while (true) {
|
|
941
|
-
|
|
941
|
+
const token = scanner.scan();
|
|
942
942
|
_checkScanError();
|
|
943
|
-
switch (
|
|
944
|
-
case 12 /* LineCommentTrivia */:
|
|
945
|
-
case 13 /* BlockCommentTrivia */:
|
|
943
|
+
switch (token) {
|
|
944
|
+
case 12 /* Json.SyntaxKind.LineCommentTrivia */:
|
|
945
|
+
case 13 /* Json.SyntaxKind.BlockCommentTrivia */:
|
|
946
946
|
if (Array.isArray(commentRanges)) {
|
|
947
947
|
commentRanges.push(Range.create(textDocument.positionAt(scanner.getTokenOffset()), textDocument.positionAt(scanner.getTokenOffset() + scanner.getTokenLength())));
|
|
948
948
|
}
|
|
949
949
|
break;
|
|
950
|
-
case 15 /* Trivia */:
|
|
951
|
-
case 14 /* LineBreakTrivia */:
|
|
950
|
+
case 15 /* Json.SyntaxKind.Trivia */:
|
|
951
|
+
case 14 /* Json.SyntaxKind.LineBreakTrivia */:
|
|
952
952
|
break;
|
|
953
953
|
default:
|
|
954
|
-
return
|
|
954
|
+
return token;
|
|
955
955
|
}
|
|
956
956
|
}
|
|
957
957
|
}
|
|
@@ -962,20 +962,16 @@ export function parse(textDocument, config) {
|
|
|
962
962
|
}
|
|
963
963
|
return false;
|
|
964
964
|
}
|
|
965
|
-
function _errorAtRange(message, code, startOffset, endOffset, severity) {
|
|
966
|
-
if (severity === void 0) { severity = DiagnosticSeverity.Error; }
|
|
965
|
+
function _errorAtRange(message, code, startOffset, endOffset, severity = DiagnosticSeverity.Error) {
|
|
967
966
|
if (problems.length === 0 || startOffset !== lastProblemOffset) {
|
|
968
|
-
|
|
967
|
+
const range = Range.create(textDocument.positionAt(startOffset), textDocument.positionAt(endOffset));
|
|
969
968
|
problems.push(Diagnostic.create(range, message, severity, code, textDocument.languageId));
|
|
970
969
|
lastProblemOffset = startOffset;
|
|
971
970
|
}
|
|
972
971
|
}
|
|
973
|
-
function _error(message, code, node, skipUntilAfter, skipUntil) {
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
if (skipUntil === void 0) { skipUntil = []; }
|
|
977
|
-
var start = scanner.getTokenOffset();
|
|
978
|
-
var end = scanner.getTokenOffset() + scanner.getTokenLength();
|
|
972
|
+
function _error(message, code, node = undefined, skipUntilAfter = [], skipUntil = []) {
|
|
973
|
+
let start = scanner.getTokenOffset();
|
|
974
|
+
let end = scanner.getTokenOffset() + scanner.getTokenLength();
|
|
979
975
|
if (start === end && start > 0) {
|
|
980
976
|
start--;
|
|
981
977
|
while (start > 0 && /\s/.test(text.charAt(start))) {
|
|
@@ -988,38 +984,38 @@ export function parse(textDocument, config) {
|
|
|
988
984
|
_finalize(node, false);
|
|
989
985
|
}
|
|
990
986
|
if (skipUntilAfter.length + skipUntil.length > 0) {
|
|
991
|
-
|
|
992
|
-
while (
|
|
993
|
-
if (skipUntilAfter.indexOf(
|
|
987
|
+
let token = scanner.getToken();
|
|
988
|
+
while (token !== 17 /* Json.SyntaxKind.EOF */) {
|
|
989
|
+
if (skipUntilAfter.indexOf(token) !== -1) {
|
|
994
990
|
_scanNext();
|
|
995
991
|
break;
|
|
996
992
|
}
|
|
997
|
-
else if (skipUntil.indexOf(
|
|
993
|
+
else if (skipUntil.indexOf(token) !== -1) {
|
|
998
994
|
break;
|
|
999
995
|
}
|
|
1000
|
-
|
|
996
|
+
token = _scanNext();
|
|
1001
997
|
}
|
|
1002
998
|
}
|
|
1003
999
|
return node;
|
|
1004
1000
|
}
|
|
1005
1001
|
function _checkScanError() {
|
|
1006
1002
|
switch (scanner.getTokenError()) {
|
|
1007
|
-
case 4 /* InvalidUnicode */:
|
|
1003
|
+
case 4 /* Json.ScanError.InvalidUnicode */:
|
|
1008
1004
|
_error(localize('InvalidUnicode', 'Invalid unicode sequence in string.'), ErrorCode.InvalidUnicode);
|
|
1009
1005
|
return true;
|
|
1010
|
-
case 5 /* InvalidEscapeCharacter */:
|
|
1006
|
+
case 5 /* Json.ScanError.InvalidEscapeCharacter */:
|
|
1011
1007
|
_error(localize('InvalidEscapeCharacter', 'Invalid escape character in string.'), ErrorCode.InvalidEscapeCharacter);
|
|
1012
1008
|
return true;
|
|
1013
|
-
case 3 /* UnexpectedEndOfNumber */:
|
|
1009
|
+
case 3 /* Json.ScanError.UnexpectedEndOfNumber */:
|
|
1014
1010
|
_error(localize('UnexpectedEndOfNumber', 'Unexpected end of number.'), ErrorCode.UnexpectedEndOfNumber);
|
|
1015
1011
|
return true;
|
|
1016
|
-
case 1 /* UnexpectedEndOfComment */:
|
|
1012
|
+
case 1 /* Json.ScanError.UnexpectedEndOfComment */:
|
|
1017
1013
|
_error(localize('UnexpectedEndOfComment', 'Unexpected end of comment.'), ErrorCode.UnexpectedEndOfComment);
|
|
1018
1014
|
return true;
|
|
1019
|
-
case 2 /* UnexpectedEndOfString */:
|
|
1015
|
+
case 2 /* Json.ScanError.UnexpectedEndOfString */:
|
|
1020
1016
|
_error(localize('UnexpectedEndOfString', 'Unexpected end of string.'), ErrorCode.UnexpectedEndOfString);
|
|
1021
1017
|
return true;
|
|
1022
|
-
case 6 /* InvalidCharacter */:
|
|
1018
|
+
case 6 /* Json.ScanError.InvalidCharacter */:
|
|
1023
1019
|
_error(localize('InvalidCharacter', 'Invalid characters in string. Control characters must be escaped.'), ErrorCode.InvalidCharacter);
|
|
1024
1020
|
return true;
|
|
1025
1021
|
}
|
|
@@ -1033,21 +1029,21 @@ export function parse(textDocument, config) {
|
|
|
1033
1029
|
return node;
|
|
1034
1030
|
}
|
|
1035
1031
|
function _parseArray(parent) {
|
|
1036
|
-
if (scanner.getToken() !== 3 /* OpenBracketToken */) {
|
|
1032
|
+
if (scanner.getToken() !== 3 /* Json.SyntaxKind.OpenBracketToken */) {
|
|
1037
1033
|
return undefined;
|
|
1038
1034
|
}
|
|
1039
|
-
|
|
1035
|
+
const node = new ArrayASTNodeImpl(parent, scanner.getTokenOffset());
|
|
1040
1036
|
_scanNext(); // consume OpenBracketToken
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
while (scanner.getToken() !== 4 /* CloseBracketToken */ && scanner.getToken() !== 17 /* EOF */) {
|
|
1044
|
-
if (scanner.getToken() === 5 /* CommaToken */) {
|
|
1037
|
+
const count = 0;
|
|
1038
|
+
let needsComma = false;
|
|
1039
|
+
while (scanner.getToken() !== 4 /* Json.SyntaxKind.CloseBracketToken */ && scanner.getToken() !== 17 /* Json.SyntaxKind.EOF */) {
|
|
1040
|
+
if (scanner.getToken() === 5 /* Json.SyntaxKind.CommaToken */) {
|
|
1045
1041
|
if (!needsComma) {
|
|
1046
1042
|
_error(localize('ValueExpected', 'Value expected'), ErrorCode.ValueExpected);
|
|
1047
1043
|
}
|
|
1048
|
-
|
|
1044
|
+
const commaOffset = scanner.getTokenOffset();
|
|
1049
1045
|
_scanNext(); // consume comma
|
|
1050
|
-
if (scanner.getToken() === 4 /* CloseBracketToken */) {
|
|
1046
|
+
if (scanner.getToken() === 4 /* Json.SyntaxKind.CloseBracketToken */) {
|
|
1051
1047
|
if (needsComma) {
|
|
1052
1048
|
_errorAtRange(localize('TrailingComma', 'Trailing comma'), ErrorCode.TrailingComma, commaOffset, commaOffset + 1);
|
|
1053
1049
|
}
|
|
@@ -1057,29 +1053,29 @@ export function parse(textDocument, config) {
|
|
|
1057
1053
|
else if (needsComma) {
|
|
1058
1054
|
_error(localize('ExpectedComma', 'Expected comma'), ErrorCode.CommaExpected);
|
|
1059
1055
|
}
|
|
1060
|
-
|
|
1056
|
+
const item = _parseValue(node);
|
|
1061
1057
|
if (!item) {
|
|
1062
|
-
_error(localize('PropertyExpected', 'Value expected'), ErrorCode.ValueExpected, undefined, [], [4 /* CloseBracketToken */, 5 /* CommaToken */]);
|
|
1058
|
+
_error(localize('PropertyExpected', 'Value expected'), ErrorCode.ValueExpected, undefined, [], [4 /* Json.SyntaxKind.CloseBracketToken */, 5 /* Json.SyntaxKind.CommaToken */]);
|
|
1063
1059
|
}
|
|
1064
1060
|
else {
|
|
1065
1061
|
node.items.push(item);
|
|
1066
1062
|
}
|
|
1067
1063
|
needsComma = true;
|
|
1068
1064
|
}
|
|
1069
|
-
if (scanner.getToken() !== 4 /* CloseBracketToken */) {
|
|
1065
|
+
if (scanner.getToken() !== 4 /* Json.SyntaxKind.CloseBracketToken */) {
|
|
1070
1066
|
return _error(localize('ExpectedCloseBracket', 'Expected comma or closing bracket'), ErrorCode.CommaOrCloseBacketExpected, node);
|
|
1071
1067
|
}
|
|
1072
1068
|
return _finalize(node, true);
|
|
1073
1069
|
}
|
|
1074
|
-
|
|
1070
|
+
const keyPlaceholder = new StringASTNodeImpl(undefined, 0, 0);
|
|
1075
1071
|
function _parseProperty(parent, keysSeen) {
|
|
1076
|
-
|
|
1077
|
-
|
|
1072
|
+
const node = new PropertyASTNodeImpl(parent, scanner.getTokenOffset(), keyPlaceholder);
|
|
1073
|
+
let key = _parseString(node);
|
|
1078
1074
|
if (!key) {
|
|
1079
|
-
if (scanner.getToken() === 16 /* Unknown */) {
|
|
1075
|
+
if (scanner.getToken() === 16 /* Json.SyntaxKind.Unknown */) {
|
|
1080
1076
|
// give a more helpful error message
|
|
1081
1077
|
_error(localize('DoubleQuotesExpected', 'Property keys must be doublequoted'), ErrorCode.Undefined);
|
|
1082
|
-
|
|
1078
|
+
const keyNode = new StringASTNodeImpl(node, scanner.getTokenOffset(), scanner.getTokenLength());
|
|
1083
1079
|
keyNode.value = scanner.getTokenValue();
|
|
1084
1080
|
key = keyNode;
|
|
1085
1081
|
_scanNext(); // consume Unknown
|
|
@@ -1089,52 +1085,56 @@ export function parse(textDocument, config) {
|
|
|
1089
1085
|
}
|
|
1090
1086
|
}
|
|
1091
1087
|
node.keyNode = key;
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1088
|
+
// For JSON files that forbid code comments, there is a convention to use the key name "//" to add comments.
|
|
1089
|
+
// Multiple instances of "//" are okay.
|
|
1090
|
+
if (key.value !== "//") {
|
|
1091
|
+
const seen = keysSeen[key.value];
|
|
1092
|
+
if (seen) {
|
|
1093
|
+
_errorAtRange(localize('DuplicateKeyWarning', "Duplicate object key"), ErrorCode.DuplicateKey, node.keyNode.offset, node.keyNode.offset + node.keyNode.length, DiagnosticSeverity.Warning);
|
|
1094
|
+
if (isObject(seen)) {
|
|
1095
|
+
_errorAtRange(localize('DuplicateKeyWarning', "Duplicate object key"), ErrorCode.DuplicateKey, seen.keyNode.offset, seen.keyNode.offset + seen.keyNode.length, DiagnosticSeverity.Warning);
|
|
1096
|
+
}
|
|
1097
|
+
keysSeen[key.value] = true; // if the same key is duplicate again, avoid duplicate error reporting
|
|
1098
|
+
}
|
|
1099
|
+
else {
|
|
1100
|
+
keysSeen[key.value] = node;
|
|
1097
1101
|
}
|
|
1098
|
-
keysSeen[key.value] = true; // if the same key is duplicate again, avoid duplicate error reporting
|
|
1099
|
-
}
|
|
1100
|
-
else {
|
|
1101
|
-
keysSeen[key.value] = node;
|
|
1102
1102
|
}
|
|
1103
|
-
if (scanner.getToken() === 6 /* ColonToken */) {
|
|
1103
|
+
if (scanner.getToken() === 6 /* Json.SyntaxKind.ColonToken */) {
|
|
1104
1104
|
node.colonOffset = scanner.getTokenOffset();
|
|
1105
1105
|
_scanNext(); // consume ColonToken
|
|
1106
1106
|
}
|
|
1107
1107
|
else {
|
|
1108
1108
|
_error(localize('ColonExpected', 'Colon expected'), ErrorCode.ColonExpected);
|
|
1109
|
-
if (scanner.getToken() === 10 /* StringLiteral */ && textDocument.positionAt(key.offset + key.length).line < textDocument.positionAt(scanner.getTokenOffset()).line) {
|
|
1109
|
+
if (scanner.getToken() === 10 /* Json.SyntaxKind.StringLiteral */ && textDocument.positionAt(key.offset + key.length).line < textDocument.positionAt(scanner.getTokenOffset()).line) {
|
|
1110
1110
|
node.length = key.length;
|
|
1111
1111
|
return node;
|
|
1112
1112
|
}
|
|
1113
1113
|
}
|
|
1114
|
-
|
|
1114
|
+
const value = _parseValue(node);
|
|
1115
1115
|
if (!value) {
|
|
1116
|
-
return _error(localize('ValueExpected', 'Value expected'), ErrorCode.ValueExpected, node, [], [2 /* CloseBraceToken */, 5 /* CommaToken */]);
|
|
1116
|
+
return _error(localize('ValueExpected', 'Value expected'), ErrorCode.ValueExpected, node, [], [2 /* Json.SyntaxKind.CloseBraceToken */, 5 /* Json.SyntaxKind.CommaToken */]);
|
|
1117
1117
|
}
|
|
1118
1118
|
node.valueNode = value;
|
|
1119
1119
|
node.length = value.offset + value.length - node.offset;
|
|
1120
1120
|
return node;
|
|
1121
1121
|
}
|
|
1122
1122
|
function _parseObject(parent) {
|
|
1123
|
-
if (scanner.getToken() !== 1 /* OpenBraceToken */) {
|
|
1123
|
+
if (scanner.getToken() !== 1 /* Json.SyntaxKind.OpenBraceToken */) {
|
|
1124
1124
|
return undefined;
|
|
1125
1125
|
}
|
|
1126
|
-
|
|
1127
|
-
|
|
1126
|
+
const node = new ObjectASTNodeImpl(parent, scanner.getTokenOffset());
|
|
1127
|
+
const keysSeen = Object.create(null);
|
|
1128
1128
|
_scanNext(); // consume OpenBraceToken
|
|
1129
|
-
|
|
1130
|
-
while (scanner.getToken() !== 2 /* CloseBraceToken */ && scanner.getToken() !== 17 /* EOF */) {
|
|
1131
|
-
if (scanner.getToken() === 5 /* CommaToken */) {
|
|
1129
|
+
let needsComma = false;
|
|
1130
|
+
while (scanner.getToken() !== 2 /* Json.SyntaxKind.CloseBraceToken */ && scanner.getToken() !== 17 /* Json.SyntaxKind.EOF */) {
|
|
1131
|
+
if (scanner.getToken() === 5 /* Json.SyntaxKind.CommaToken */) {
|
|
1132
1132
|
if (!needsComma) {
|
|
1133
1133
|
_error(localize('PropertyExpected', 'Property expected'), ErrorCode.PropertyExpected);
|
|
1134
1134
|
}
|
|
1135
|
-
|
|
1135
|
+
const commaOffset = scanner.getTokenOffset();
|
|
1136
1136
|
_scanNext(); // consume comma
|
|
1137
|
-
if (scanner.getToken() === 2 /* CloseBraceToken */) {
|
|
1137
|
+
if (scanner.getToken() === 2 /* Json.SyntaxKind.CloseBraceToken */) {
|
|
1138
1138
|
if (needsComma) {
|
|
1139
1139
|
_errorAtRange(localize('TrailingComma', 'Trailing comma'), ErrorCode.TrailingComma, commaOffset, commaOffset + 1);
|
|
1140
1140
|
}
|
|
@@ -1144,37 +1144,37 @@ export function parse(textDocument, config) {
|
|
|
1144
1144
|
else if (needsComma) {
|
|
1145
1145
|
_error(localize('ExpectedComma', 'Expected comma'), ErrorCode.CommaExpected);
|
|
1146
1146
|
}
|
|
1147
|
-
|
|
1147
|
+
const property = _parseProperty(node, keysSeen);
|
|
1148
1148
|
if (!property) {
|
|
1149
|
-
_error(localize('PropertyExpected', 'Property expected'), ErrorCode.PropertyExpected, undefined, [], [2 /* CloseBraceToken */, 5 /* CommaToken */]);
|
|
1149
|
+
_error(localize('PropertyExpected', 'Property expected'), ErrorCode.PropertyExpected, undefined, [], [2 /* Json.SyntaxKind.CloseBraceToken */, 5 /* Json.SyntaxKind.CommaToken */]);
|
|
1150
1150
|
}
|
|
1151
1151
|
else {
|
|
1152
1152
|
node.properties.push(property);
|
|
1153
1153
|
}
|
|
1154
1154
|
needsComma = true;
|
|
1155
1155
|
}
|
|
1156
|
-
if (scanner.getToken() !== 2 /* CloseBraceToken */) {
|
|
1156
|
+
if (scanner.getToken() !== 2 /* Json.SyntaxKind.CloseBraceToken */) {
|
|
1157
1157
|
return _error(localize('ExpectedCloseBrace', 'Expected comma or closing brace'), ErrorCode.CommaOrCloseBraceExpected, node);
|
|
1158
1158
|
}
|
|
1159
1159
|
return _finalize(node, true);
|
|
1160
1160
|
}
|
|
1161
1161
|
function _parseString(parent) {
|
|
1162
|
-
if (scanner.getToken() !== 10 /* StringLiteral */) {
|
|
1162
|
+
if (scanner.getToken() !== 10 /* Json.SyntaxKind.StringLiteral */) {
|
|
1163
1163
|
return undefined;
|
|
1164
1164
|
}
|
|
1165
|
-
|
|
1165
|
+
const node = new StringASTNodeImpl(parent, scanner.getTokenOffset());
|
|
1166
1166
|
node.value = scanner.getTokenValue();
|
|
1167
1167
|
return _finalize(node, true);
|
|
1168
1168
|
}
|
|
1169
1169
|
function _parseNumber(parent) {
|
|
1170
|
-
if (scanner.getToken() !== 11 /* NumericLiteral */) {
|
|
1170
|
+
if (scanner.getToken() !== 11 /* Json.SyntaxKind.NumericLiteral */) {
|
|
1171
1171
|
return undefined;
|
|
1172
1172
|
}
|
|
1173
|
-
|
|
1174
|
-
if (scanner.getTokenError() === 0 /* None */) {
|
|
1175
|
-
|
|
1173
|
+
const node = new NumberASTNodeImpl(parent, scanner.getTokenOffset());
|
|
1174
|
+
if (scanner.getTokenError() === 0 /* Json.ScanError.None */) {
|
|
1175
|
+
const tokenValue = scanner.getTokenValue();
|
|
1176
1176
|
try {
|
|
1177
|
-
|
|
1177
|
+
const numberValue = JSON.parse(tokenValue);
|
|
1178
1178
|
if (!isNumber(numberValue)) {
|
|
1179
1179
|
return _error(localize('InvalidNumberFormat', 'Invalid number format.'), ErrorCode.Undefined, node);
|
|
1180
1180
|
}
|
|
@@ -1188,13 +1188,13 @@ export function parse(textDocument, config) {
|
|
|
1188
1188
|
return _finalize(node, true);
|
|
1189
1189
|
}
|
|
1190
1190
|
function _parseLiteral(parent) {
|
|
1191
|
-
|
|
1191
|
+
let node;
|
|
1192
1192
|
switch (scanner.getToken()) {
|
|
1193
|
-
case 7 /* NullKeyword */:
|
|
1193
|
+
case 7 /* Json.SyntaxKind.NullKeyword */:
|
|
1194
1194
|
return _finalize(new NullASTNodeImpl(parent, scanner.getTokenOffset()), true);
|
|
1195
|
-
case 8 /* TrueKeyword */:
|
|
1195
|
+
case 8 /* Json.SyntaxKind.TrueKeyword */:
|
|
1196
1196
|
return _finalize(new BooleanASTNodeImpl(parent, true, scanner.getTokenOffset()), true);
|
|
1197
|
-
case 9 /* FalseKeyword */:
|
|
1197
|
+
case 9 /* Json.SyntaxKind.FalseKeyword */:
|
|
1198
1198
|
return _finalize(new BooleanASTNodeImpl(parent, false, scanner.getTokenOffset()), true);
|
|
1199
1199
|
default:
|
|
1200
1200
|
return undefined;
|
|
@@ -1203,14 +1203,14 @@ export function parse(textDocument, config) {
|
|
|
1203
1203
|
function _parseValue(parent) {
|
|
1204
1204
|
return _parseArray(parent) || _parseObject(parent) || _parseString(parent) || _parseNumber(parent) || _parseLiteral(parent);
|
|
1205
1205
|
}
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
if (token !== 17 /* EOF */) {
|
|
1206
|
+
let _root = undefined;
|
|
1207
|
+
const token = _scanNext();
|
|
1208
|
+
if (token !== 17 /* Json.SyntaxKind.EOF */) {
|
|
1209
1209
|
_root = _parseValue(_root);
|
|
1210
1210
|
if (!_root) {
|
|
1211
1211
|
_error(localize('Invalid symbol', 'Expected a JSON object, array or literal.'), ErrorCode.Undefined);
|
|
1212
1212
|
}
|
|
1213
|
-
else if (scanner.getToken() !== 17 /* EOF */) {
|
|
1213
|
+
else if (scanner.getToken() !== 17 /* Json.SyntaxKind.EOF */) {
|
|
1214
1214
|
_error(localize('End of file expected', 'End of file expected.'), ErrorCode.Undefined);
|
|
1215
1215
|
}
|
|
1216
1216
|
}
|