vscode-json-languageservice 5.1.4 → 5.3.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.
@@ -0,0 +1,355 @@
1
+ /*---------------------------------------------------------------------------------------------
2
+ * Copyright (c) Microsoft Corporation. All rights reserved.
3
+ * Licensed under the MIT License. See License.txt in the project root for license information.
4
+ *--------------------------------------------------------------------------------------------*/
5
+ // import { TextEdit} from 'vscode-languageserver-textdocument';
6
+ import { createScanner } from 'jsonc-parser';
7
+ import { TextDocument, TextEdit, Position, Range } from '../jsonLanguageTypes';
8
+ import { format } from './format';
9
+ import { PropertyTree, Container } from './propertyTree';
10
+ export function sort(documentToSort, formattingOptions) {
11
+ const options = {
12
+ ...formattingOptions,
13
+ keepLines: false, // keepLines must be false so that the properties are on separate lines for the sorting
14
+ };
15
+ const formattedJsonString = TextDocument.applyEdits(documentToSort, format(documentToSort, options, undefined));
16
+ const formattedJsonDocument = TextDocument.create('test://test.json', 'json', 0, formattedJsonString);
17
+ const jsonPropertyTree = findJsoncPropertyTree(formattedJsonDocument);
18
+ const sortedJsonDocument = sortJsoncDocument(formattedJsonDocument, jsonPropertyTree);
19
+ const edits = format(sortedJsonDocument, options, undefined);
20
+ const sortedAndFormattedJsonDocument = TextDocument.applyEdits(sortedJsonDocument, edits);
21
+ return [TextEdit.replace(Range.create(Position.create(0, 0), documentToSort.positionAt(documentToSort.getText().length)), sortedAndFormattedJsonDocument)];
22
+ }
23
+ function findJsoncPropertyTree(formattedDocument) {
24
+ const formattedString = formattedDocument.getText();
25
+ const scanner = createScanner(formattedString, false);
26
+ // The tree that will be returned
27
+ let rootTree = new PropertyTree();
28
+ // The tree where the current properties can be added as children
29
+ let currentTree = rootTree;
30
+ // The tree representing the current property analyzed
31
+ let currentProperty = rootTree;
32
+ // The tree representing the previous property analyzed
33
+ let lastProperty = rootTree;
34
+ // The current scanned token
35
+ let token = undefined;
36
+ // Line number of the last token found
37
+ let lastTokenLine = 0;
38
+ // Total number of characters on the lines prior to current line
39
+ let numberOfCharactersOnPreviousLines = 0;
40
+ // The last token scanned that is not trivial, nor a comment
41
+ let lastNonTriviaNonCommentToken = undefined;
42
+ // The second to last token scanned that is not trivial, nor a comment
43
+ let secondToLastNonTriviaNonCommentToken = undefined;
44
+ // Line number of last token that is not trivial, nor a comment
45
+ let lineOfLastNonTriviaNonCommentToken = -1;
46
+ // End index on its line of last token that is not trivial, nor a comment
47
+ let endIndexOfLastNonTriviaNonCommentToken = -1;
48
+ // Line number of the start of the range of current/next property
49
+ let beginningLineNumber = 0;
50
+ // Line number of the end of the range of current/next property
51
+ let endLineNumber = 0;
52
+ // Stack indicating whether we are inside of an object or an array
53
+ let currentContainerStack = [];
54
+ // Boolean indicating that the current property end line number needs to be updated. Used only when block comments are encountered.
55
+ let updateLastPropertyEndLineNumber = false;
56
+ // Boolean indicating that the beginning line number should be updated. Used only when block comments are encountered.
57
+ let updateBeginningLineNumber = false;
58
+ while ((token = scanner.scan()) !== 17 /* SyntaxKind.EOF */) {
59
+ // In the case when a block comment has been encountered that starts on the same line as the comma ending a property, update the end line of that
60
+ // property so that it covers the block comment. For example, if we have:
61
+ // 1. "key" : {}, /* some block
62
+ // 2. comment */
63
+ // Then, the end line of the property "key" should be line 2 not line 1
64
+ if (updateLastPropertyEndLineNumber === true
65
+ && token !== 14 /* SyntaxKind.LineBreakTrivia */
66
+ && token !== 15 /* SyntaxKind.Trivia */
67
+ && token !== 12 /* SyntaxKind.LineCommentTrivia */
68
+ && token !== 13 /* SyntaxKind.BlockCommentTrivia */
69
+ && currentProperty.endLineNumber === undefined) {
70
+ let endLineNumber = scanner.getTokenStartLine();
71
+ // Update the end line number in the case when the last property visited is a container (object or array)
72
+ if (secondToLastNonTriviaNonCommentToken === 2 /* SyntaxKind.CloseBraceToken */
73
+ || secondToLastNonTriviaNonCommentToken === 4 /* SyntaxKind.CloseBracketToken */) {
74
+ lastProperty.endLineNumber = endLineNumber - 1;
75
+ }
76
+ // Update the end line number in the case when the last property visited is a simple property
77
+ else {
78
+ currentProperty.endLineNumber = endLineNumber - 1;
79
+ }
80
+ beginningLineNumber = endLineNumber;
81
+ updateLastPropertyEndLineNumber = false;
82
+ }
83
+ // When a block comment follows an open brace or an open bracket, that block comment should be associated to that brace or bracket, not the property below it. For example, for:
84
+ // 1. { /*
85
+ // 2. ... */
86
+ // 3. "key" : {}
87
+ // 4. }
88
+ // Instead of associating the block comment to the property on line 3, it is associate to the property on line 1
89
+ if (updateBeginningLineNumber === true
90
+ && token !== 14 /* SyntaxKind.LineBreakTrivia */
91
+ && token !== 15 /* SyntaxKind.Trivia */
92
+ && token !== 12 /* SyntaxKind.LineCommentTrivia */
93
+ && token !== 13 /* SyntaxKind.BlockCommentTrivia */) {
94
+ beginningLineNumber = scanner.getTokenStartLine();
95
+ updateBeginningLineNumber = false;
96
+ }
97
+ // Update the number of characters on all the previous lines each time the new token is on a different line to the previous token
98
+ if (scanner.getTokenStartLine() !== lastTokenLine) {
99
+ for (let i = lastTokenLine; i < scanner.getTokenStartLine(); i++) {
100
+ const lengthOfLine = formattedDocument.getText(Range.create(Position.create(i, 0), Position.create(i + 1, 0))).length;
101
+ numberOfCharactersOnPreviousLines = numberOfCharactersOnPreviousLines + lengthOfLine;
102
+ }
103
+ lastTokenLine = scanner.getTokenStartLine();
104
+ }
105
+ switch (token) {
106
+ // When a string is found, if it follows an open brace or a comma token and it is within an object, then it corresponds to a key name, not a simple string
107
+ case 10 /* SyntaxKind.StringLiteral */: {
108
+ if ((lastNonTriviaNonCommentToken === undefined
109
+ || lastNonTriviaNonCommentToken === 1 /* SyntaxKind.OpenBraceToken */
110
+ || (lastNonTriviaNonCommentToken === 5 /* SyntaxKind.CommaToken */
111
+ && currentContainerStack[currentContainerStack.length - 1] === Container.Object))) {
112
+ // In that case create the child property which starts at beginningLineNumber, add it to the current tree
113
+ const childProperty = new PropertyTree(scanner.getTokenValue(), beginningLineNumber);
114
+ lastProperty = currentProperty;
115
+ currentProperty = currentTree.addChildProperty(childProperty);
116
+ }
117
+ break;
118
+ }
119
+ // When the token is an open bracket, then we enter into an array
120
+ case 3 /* SyntaxKind.OpenBracketToken */: {
121
+ // If the root tree beginning line number is not defined, then this open bracket is the first open bracket in the document
122
+ if (rootTree.beginningLineNumber === undefined) {
123
+ rootTree.beginningLineNumber = scanner.getTokenStartLine();
124
+ }
125
+ // Suppose we are inside of an object, then the current array is associated to a key, and has already been created
126
+ // We have the following configuration: {"a": "val", "array": [...], "b": "val"}
127
+ // In that case navigate down to the child property
128
+ if (currentContainerStack[currentContainerStack.length - 1] === Container.Object) {
129
+ currentTree = currentProperty;
130
+ }
131
+ // Suppose we are inside of an array, then since the current array is not associated to a key, it has not been created yet
132
+ // We have the following configuration: ["a", [...], "b"]
133
+ // In that case create the property and navigate down
134
+ else if (currentContainerStack[currentContainerStack.length - 1] === Container.Array) {
135
+ const childProperty = new PropertyTree(scanner.getTokenValue(), beginningLineNumber);
136
+ childProperty.noKeyName = true;
137
+ lastProperty = currentProperty;
138
+ currentProperty = currentTree.addChildProperty(childProperty);
139
+ }
140
+ currentContainerStack.push(Container.Array);
141
+ currentProperty.type = Container.Array;
142
+ beginningLineNumber = scanner.getTokenStartLine();
143
+ beginningLineNumber++;
144
+ break;
145
+ }
146
+ // When the token is an open brace, then we enter into an object
147
+ case 1 /* SyntaxKind.OpenBraceToken */: {
148
+ // If the root tree beginning line number is not defined, then this open brace is the first open brace in the document
149
+ if (rootTree.beginningLineNumber === undefined) {
150
+ rootTree.beginningLineNumber = scanner.getTokenStartLine();
151
+ }
152
+ // 1. If we are inside of an objet, the current object is associated to a key and has already been created
153
+ // We have the following configuration: {"a": "val", "object": {...}, "b": "val"}
154
+ // 2. Otherwise the current object property is inside of an array, not associated to a key name and the property has not yet been created
155
+ // We have the following configuration: ["a", {...}, "b"]
156
+ else if (currentContainerStack[currentContainerStack.length - 1] === Container.Array) {
157
+ const childProperty = new PropertyTree(scanner.getTokenValue(), beginningLineNumber);
158
+ childProperty.noKeyName = true;
159
+ lastProperty = currentProperty;
160
+ currentProperty = currentTree.addChildProperty(childProperty);
161
+ }
162
+ currentProperty.type = Container.Object;
163
+ currentContainerStack.push(Container.Object);
164
+ currentTree = currentProperty;
165
+ beginningLineNumber = scanner.getTokenStartLine();
166
+ beginningLineNumber++;
167
+ break;
168
+ }
169
+ case 4 /* SyntaxKind.CloseBracketToken */: {
170
+ endLineNumber = scanner.getTokenStartLine();
171
+ currentContainerStack.pop();
172
+ // If the last non-trivial non-comment token is a closing brace or bracket, then the currentProperty end line number has not been set yet so set it
173
+ // The configuration considered is: [..., {}] or [..., []]
174
+ if (currentProperty.endLineNumber === undefined
175
+ && (lastNonTriviaNonCommentToken === 2 /* SyntaxKind.CloseBraceToken */
176
+ || lastNonTriviaNonCommentToken === 4 /* SyntaxKind.CloseBracketToken */)) {
177
+ currentProperty.endLineNumber = endLineNumber - 1;
178
+ currentProperty.lastProperty = true;
179
+ currentProperty.lineWhereToAddComma = lineOfLastNonTriviaNonCommentToken;
180
+ currentProperty.indexWhereToAddComa = endIndexOfLastNonTriviaNonCommentToken;
181
+ lastProperty = currentProperty;
182
+ currentProperty = currentProperty ? currentProperty.parent : undefined;
183
+ currentTree = currentProperty;
184
+ }
185
+ rootTree.endLineNumber = endLineNumber;
186
+ beginningLineNumber = endLineNumber + 1;
187
+ break;
188
+ }
189
+ case 2 /* SyntaxKind.CloseBraceToken */: {
190
+ endLineNumber = scanner.getTokenStartLine();
191
+ currentContainerStack.pop();
192
+ // If we are not inside of an empty object and current property end line number has not yet been defined, define it
193
+ if (lastNonTriviaNonCommentToken !== 1 /* SyntaxKind.OpenBraceToken */
194
+ && currentProperty.endLineNumber === undefined) {
195
+ currentProperty.endLineNumber = endLineNumber - 1;
196
+ // The current property is also the last property
197
+ currentProperty.lastProperty = true;
198
+ // The last property of an object is associated with the line and index of where to add the comma, in case after sorting, it is no longer the last property
199
+ currentProperty.lineWhereToAddComma = lineOfLastNonTriviaNonCommentToken;
200
+ currentProperty.indexWhereToAddComa = endIndexOfLastNonTriviaNonCommentToken;
201
+ lastProperty = currentProperty;
202
+ currentProperty = currentProperty ? currentProperty.parent : undefined;
203
+ currentTree = currentProperty;
204
+ }
205
+ rootTree.endLineNumber = scanner.getTokenStartLine();
206
+ beginningLineNumber = endLineNumber + 1;
207
+ break;
208
+ }
209
+ case 5 /* SyntaxKind.CommaToken */: {
210
+ endLineNumber = scanner.getTokenStartLine();
211
+ // If the current container is an object or the current container is an array and the last non-trivia non-comment token is a closing brace or a closing bracket
212
+ // Then update the end line number of the current property
213
+ if (currentProperty.endLineNumber === undefined
214
+ && (currentContainerStack[currentContainerStack.length - 1] === Container.Object
215
+ || (currentContainerStack[currentContainerStack.length - 1] === Container.Array
216
+ && (lastNonTriviaNonCommentToken === 2 /* SyntaxKind.CloseBraceToken */
217
+ || lastNonTriviaNonCommentToken === 4 /* SyntaxKind.CloseBracketToken */)))) {
218
+ currentProperty.endLineNumber = endLineNumber;
219
+ // Store the line and the index of the comma in case it needs to be removed during the sorting
220
+ currentProperty.commaIndex = scanner.getTokenOffset() - numberOfCharactersOnPreviousLines;
221
+ currentProperty.commaLine = endLineNumber;
222
+ }
223
+ if (lastNonTriviaNonCommentToken === 2 /* SyntaxKind.CloseBraceToken */
224
+ || lastNonTriviaNonCommentToken === 4 /* SyntaxKind.CloseBracketToken */) {
225
+ lastProperty = currentProperty;
226
+ currentProperty = currentProperty ? currentProperty.parent : undefined;
227
+ currentTree = currentProperty;
228
+ }
229
+ beginningLineNumber = endLineNumber + 1;
230
+ break;
231
+ }
232
+ case 13 /* SyntaxKind.BlockCommentTrivia */: {
233
+ // If the last non trivia non-comment token is a comma and the block comment starts on the same line as the comma, then update the end line number of the current property. For example if:
234
+ // 1. {}, /* ...
235
+ // 2. ..*/
236
+ // The the property on line 1 shoud end on line 2, not line 1
237
+ // In the case we are in an array we update the end line number only if the second to last non-trivia non-comment token is a closing brace or bracket
238
+ if (lastNonTriviaNonCommentToken === 5 /* SyntaxKind.CommaToken */
239
+ && lineOfLastNonTriviaNonCommentToken === scanner.getTokenStartLine()
240
+ && (currentContainerStack[currentContainerStack.length - 1] === Container.Array
241
+ && (secondToLastNonTriviaNonCommentToken === 2 /* SyntaxKind.CloseBraceToken */
242
+ || secondToLastNonTriviaNonCommentToken === 4 /* SyntaxKind.CloseBracketToken */)
243
+ || currentContainerStack[currentContainerStack.length - 1] === Container.Object)) {
244
+ if (currentContainerStack[currentContainerStack.length - 1] === Container.Array && (secondToLastNonTriviaNonCommentToken === 2 /* SyntaxKind.CloseBraceToken */ || secondToLastNonTriviaNonCommentToken === 4 /* SyntaxKind.CloseBracketToken */) || currentContainerStack[currentContainerStack.length - 1] === Container.Object) {
245
+ currentProperty.endLineNumber = undefined;
246
+ updateLastPropertyEndLineNumber = true;
247
+ }
248
+ }
249
+ // When the block comment follows an open brace or an open token, we have the following scenario:
250
+ // { /**
251
+ // ../
252
+ // }
253
+ // The block comment should be assigned to the open brace not the first property below it
254
+ if ((lastNonTriviaNonCommentToken === 1 /* SyntaxKind.OpenBraceToken */
255
+ || lastNonTriviaNonCommentToken === 3 /* SyntaxKind.OpenBracketToken */)
256
+ && lineOfLastNonTriviaNonCommentToken === scanner.getTokenStartLine()) {
257
+ updateBeginningLineNumber = true;
258
+ }
259
+ break;
260
+ }
261
+ }
262
+ // Update the last and second to last non-trivia non-comment tokens
263
+ if (token !== 14 /* SyntaxKind.LineBreakTrivia */
264
+ && token !== 13 /* SyntaxKind.BlockCommentTrivia */
265
+ && token !== 12 /* SyntaxKind.LineCommentTrivia */
266
+ && token !== 15 /* SyntaxKind.Trivia */) {
267
+ secondToLastNonTriviaNonCommentToken = lastNonTriviaNonCommentToken;
268
+ lastNonTriviaNonCommentToken = token;
269
+ lineOfLastNonTriviaNonCommentToken = scanner.getTokenStartLine();
270
+ endIndexOfLastNonTriviaNonCommentToken = scanner.getTokenOffset() + scanner.getTokenLength() - numberOfCharactersOnPreviousLines;
271
+ }
272
+ }
273
+ return rootTree;
274
+ }
275
+ function sortJsoncDocument(jsonDocument, propertyTree) {
276
+ if (propertyTree.childrenProperties.length === 0) {
277
+ return jsonDocument;
278
+ }
279
+ const sortedJsonDocument = TextDocument.create('test://test.json', 'json', 0, jsonDocument.getText());
280
+ const queueToSort = [];
281
+ updateSortingQueue(queueToSort, propertyTree, propertyTree.beginningLineNumber);
282
+ while (queueToSort.length > 0) {
283
+ const dataToSort = queueToSort.shift();
284
+ const propertyTreeArray = dataToSort.propertyTreeArray;
285
+ let beginningLineNumber = dataToSort.beginningLineNumber;
286
+ for (let i = 0; i < propertyTreeArray.length; i++) {
287
+ const propertyTree = propertyTreeArray[i];
288
+ const range = Range.create(Position.create(propertyTree.beginningLineNumber, 0), Position.create(propertyTree.endLineNumber + 1, 0));
289
+ const jsonContentToReplace = jsonDocument.getText(range);
290
+ const jsonDocumentToReplace = TextDocument.create('test://test.json', 'json', 0, jsonContentToReplace);
291
+ if (propertyTree.lastProperty === true && i !== propertyTreeArray.length - 1) {
292
+ const lineWhereToAddComma = propertyTree.lineWhereToAddComma - propertyTree.beginningLineNumber;
293
+ const indexWhereToAddComma = propertyTree.indexWhereToAddComa;
294
+ const edit = {
295
+ range: Range.create(Position.create(lineWhereToAddComma, indexWhereToAddComma), Position.create(lineWhereToAddComma, indexWhereToAddComma)),
296
+ text: ','
297
+ };
298
+ TextDocument.update(jsonDocumentToReplace, [edit], 1);
299
+ }
300
+ else if (propertyTree.lastProperty === false && i === propertyTreeArray.length - 1) {
301
+ const commaIndex = propertyTree.commaIndex;
302
+ const commaLine = propertyTree.commaLine;
303
+ const lineWhereToRemoveComma = commaLine - propertyTree.beginningLineNumber;
304
+ const edit = {
305
+ range: Range.create(Position.create(lineWhereToRemoveComma, commaIndex), Position.create(lineWhereToRemoveComma, commaIndex + 1)),
306
+ text: ''
307
+ };
308
+ TextDocument.update(jsonDocumentToReplace, [edit], 1);
309
+ }
310
+ const length = propertyTree.endLineNumber - propertyTree.beginningLineNumber + 1;
311
+ const edit = {
312
+ range: Range.create(Position.create(beginningLineNumber, 0), Position.create(beginningLineNumber + length, 0)),
313
+ text: jsonDocumentToReplace.getText()
314
+ };
315
+ TextDocument.update(sortedJsonDocument, [edit], 1);
316
+ updateSortingQueue(queueToSort, propertyTree, beginningLineNumber);
317
+ beginningLineNumber = beginningLineNumber + length;
318
+ }
319
+ }
320
+ return sortedJsonDocument;
321
+ }
322
+ function updateSortingQueue(queue, propertyTree, beginningLineNumber) {
323
+ if (propertyTree.childrenProperties.length === 0) {
324
+ return;
325
+ }
326
+ if (propertyTree.type === Container.Object) {
327
+ let minimumBeginningLineNumber = Infinity;
328
+ for (const childProperty of propertyTree.childrenProperties) {
329
+ if (childProperty.beginningLineNumber < minimumBeginningLineNumber) {
330
+ minimumBeginningLineNumber = childProperty.beginningLineNumber;
331
+ }
332
+ }
333
+ const diff = minimumBeginningLineNumber - propertyTree.beginningLineNumber;
334
+ beginningLineNumber = beginningLineNumber + diff;
335
+ queue.push(new SortingRange(beginningLineNumber, propertyTree.childrenProperties));
336
+ }
337
+ else if (propertyTree.type === Container.Array) {
338
+ for (const subObject of propertyTree.childrenProperties) {
339
+ let minimumBeginningLineNumber = Infinity;
340
+ for (const childProperty of subObject.childrenProperties) {
341
+ if (childProperty.beginningLineNumber < minimumBeginningLineNumber) {
342
+ minimumBeginningLineNumber = childProperty.beginningLineNumber;
343
+ }
344
+ }
345
+ const diff = minimumBeginningLineNumber - subObject.beginningLineNumber;
346
+ queue.push(new SortingRange(beginningLineNumber + subObject.beginningLineNumber - propertyTree.beginningLineNumber + diff, subObject.childrenProperties));
347
+ }
348
+ }
349
+ }
350
+ class SortingRange {
351
+ constructor(beginningLineNumber, propertyTreeArray) {
352
+ this.beginningLineNumber = beginningLineNumber;
353
+ this.propertyTreeArray = propertyTreeArray;
354
+ }
355
+ }
@@ -32,7 +32,7 @@ export function convertSimple2RegExpPattern(pattern) {
32
32
  return pattern.replace(/[\-\\\{\}\+\?\|\^\$\.\,\[\]\(\)\#\s]/g, '\\$&').replace(/[\*]/g, '.*');
33
33
  }
34
34
  export function repeat(value, count) {
35
- var s = '';
35
+ let s = '';
36
36
  while (count > 0) {
37
37
  if ((count & 1) === 1) {
38
38
  s += value;
@@ -1,4 +1,4 @@
1
- import { Thenable, ASTNode, Color, ColorInformation, ColorPresentation, LanguageServiceParams, LanguageSettings, DocumentLanguageSettings, FoldingRange, JSONSchema, SelectionRange, FoldingRangesContext, DocumentSymbolsContext, ColorInformationContext as DocumentColorsContext, TextDocument, Position, CompletionItem, CompletionList, Hover, Range, SymbolInformation, Diagnostic, TextEdit, FormattingOptions, DocumentSymbol, DefinitionLink, MatchingSchema, JSONLanguageStatus } from './jsonLanguageTypes';
1
+ import { Thenable, ASTNode, Color, ColorInformation, ColorPresentation, LanguageServiceParams, LanguageSettings, DocumentLanguageSettings, FoldingRange, JSONSchema, SelectionRange, FoldingRangesContext, DocumentSymbolsContext, ColorInformationContext as DocumentColorsContext, TextDocument, Position, CompletionItem, CompletionList, Hover, Range, SymbolInformation, Diagnostic, TextEdit, FormattingOptions, DocumentSymbol, DefinitionLink, MatchingSchema, JSONLanguageStatus, SortOptions } from './jsonLanguageTypes';
2
2
  import { DocumentLink } from 'vscode-languageserver-types';
3
3
  export type JSONDocument = {
4
4
  root: ASTNode | undefined;
@@ -20,10 +20,11 @@ export interface LanguageService {
20
20
  findDocumentColors(document: TextDocument, doc: JSONDocument, context?: DocumentColorsContext): Thenable<ColorInformation[]>;
21
21
  getColorPresentations(document: TextDocument, doc: JSONDocument, color: Color, range: Range): ColorPresentation[];
22
22
  doHover(document: TextDocument, position: Position, doc: JSONDocument): Thenable<Hover | null>;
23
- format(document: TextDocument, range: Range, options: FormattingOptions): TextEdit[];
24
23
  getFoldingRanges(document: TextDocument, context?: FoldingRangesContext): FoldingRange[];
25
24
  getSelectionRanges(document: TextDocument, positions: Position[], doc: JSONDocument): SelectionRange[];
26
25
  findDefinition(document: TextDocument, position: Position, doc: JSONDocument): Thenable<DefinitionLink[]>;
27
26
  findLinks(document: TextDocument, doc: JSONDocument): Thenable<DocumentLink[]>;
27
+ format(document: TextDocument, range: Range, options: FormattingOptions): TextEdit[];
28
+ sort(document: TextDocument, options: SortOptions): TextEdit[];
28
29
  }
29
30
  export declare function getLanguageService(params: LanguageServiceParams): LanguageService;
@@ -22,7 +22,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
22
22
  if (v !== undefined) module.exports = v;
23
23
  }
24
24
  else if (typeof define === "function" && define.amd) {
25
- define(["require", "exports", "./services/jsonCompletion", "./services/jsonHover", "./services/jsonValidation", "./services/jsonDocumentSymbols", "./parser/jsonParser", "./services/configuration", "./services/jsonSchemaService", "./services/jsonFolding", "./services/jsonSelectionRanges", "jsonc-parser", "./jsonLanguageTypes", "./services/jsonLinks", "./jsonLanguageTypes"], factory);
25
+ define(["require", "exports", "./services/jsonCompletion", "./services/jsonHover", "./services/jsonValidation", "./services/jsonDocumentSymbols", "./parser/jsonParser", "./services/configuration", "./services/jsonSchemaService", "./services/jsonFolding", "./services/jsonSelectionRanges", "./utils/sort", "./utils/format", "./services/jsonLinks", "./jsonLanguageTypes"], factory);
26
26
  }
27
27
  })(function (require, exports) {
28
28
  "use strict";
@@ -37,8 +37,8 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
37
37
  const jsonSchemaService_1 = require("./services/jsonSchemaService");
38
38
  const jsonFolding_1 = require("./services/jsonFolding");
39
39
  const jsonSelectionRanges_1 = require("./services/jsonSelectionRanges");
40
- const jsonc_parser_1 = require("jsonc-parser");
41
- const jsonLanguageTypes_1 = require("./jsonLanguageTypes");
40
+ const sort_1 = require("./utils/sort");
41
+ const format_1 = require("./utils/format");
42
42
  const jsonLinks_1 = require("./services/jsonLinks");
43
43
  __exportStar(require("./jsonLanguageTypes"), exports);
44
44
  function getLanguageService(params) {
@@ -52,11 +52,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
52
52
  return {
53
53
  configure: (settings) => {
54
54
  jsonSchemaService.clearExternalSchemas();
55
- if (settings.schemas) {
56
- settings.schemas.forEach(settings => {
57
- jsonSchemaService.registerExternalSchema(settings.uri, settings.fileMatch, settings.schema);
58
- });
59
- }
55
+ settings.schemas?.forEach(jsonSchemaService.registerExternalSchema.bind(jsonSchemaService));
60
56
  jsonValidation.configure(settings);
61
57
  },
62
58
  resetSchema: (uri) => jsonSchemaService.onResourceChange(uri),
@@ -76,18 +72,8 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
76
72
  getSelectionRanges: jsonSelectionRanges_1.getSelectionRanges,
77
73
  findDefinition: () => Promise.resolve([]),
78
74
  findLinks: jsonLinks_1.findLinks,
79
- format: (d, r, o) => {
80
- let range = undefined;
81
- if (r) {
82
- const offset = d.offsetAt(r.start);
83
- const length = d.offsetAt(r.end) - offset;
84
- range = { offset, length };
85
- }
86
- const options = { tabSize: o ? o.tabSize : 4, insertSpaces: o?.insertSpaces === true, insertFinalNewline: o?.insertFinalNewline === true, eol: '\n', keepLines: o?.keepLines === true };
87
- return (0, jsonc_parser_1.format)(d.getText(), range, options).map(e => {
88
- return jsonLanguageTypes_1.TextEdit.replace(jsonLanguageTypes_1.Range.create(d.positionAt(e.offset), d.positionAt(e.offset + e.length)), e.content);
89
- });
90
- }
75
+ format: (document, range, options) => (0, format_1.format)(document, options, range),
76
+ sort: (document, options) => (0, sort_1.sort)(document, options)
91
77
  };
92
78
  }
93
79
  exports.getLanguageService = getLanguageService;
@@ -1,8 +1,8 @@
1
1
  import { JSONWorkerContribution, JSONPath, Segment, CompletionsCollector } from './jsonContributions';
2
2
  import { JSONSchema } from './jsonSchema';
3
3
  import { Range, Position, DocumentUri, MarkupContent, MarkupKind, Color, ColorInformation, ColorPresentation, FoldingRange, FoldingRangeKind, SelectionRange, Diagnostic, DiagnosticSeverity, CompletionItem, CompletionItemKind, CompletionList, CompletionItemTag, InsertTextFormat, SymbolInformation, SymbolKind, DocumentSymbol, Location, Hover, MarkedString, FormattingOptions as LSPFormattingOptions, DefinitionLink, CodeActionContext, Command, CodeAction, DocumentHighlight, DocumentLink, WorkspaceEdit, TextEdit, CodeActionKind, TextDocumentEdit, VersionedTextDocumentIdentifier, DocumentHighlightKind } from 'vscode-languageserver-types';
4
- import { TextDocument } from 'vscode-languageserver-textdocument';
5
- export { TextDocument, Range, Position, DocumentUri, MarkupContent, MarkupKind, JSONSchema, JSONWorkerContribution, JSONPath, Segment, CompletionsCollector, Color, ColorInformation, ColorPresentation, FoldingRange, FoldingRangeKind, SelectionRange, Diagnostic, DiagnosticSeverity, CompletionItem, CompletionItemKind, CompletionList, CompletionItemTag, InsertTextFormat, DefinitionLink, SymbolInformation, SymbolKind, DocumentSymbol, Location, Hover, MarkedString, CodeActionContext, Command, CodeAction, DocumentHighlight, DocumentLink, WorkspaceEdit, TextEdit, CodeActionKind, TextDocumentEdit, VersionedTextDocumentIdentifier, DocumentHighlightKind };
4
+ import { TextDocument, TextDocumentContentChangeEvent } from 'vscode-languageserver-textdocument';
5
+ export { TextDocument, TextDocumentContentChangeEvent, Range, Position, DocumentUri, MarkupContent, MarkupKind, JSONSchema, JSONWorkerContribution, JSONPath, Segment, CompletionsCollector, Color, ColorInformation, ColorPresentation, FoldingRange, FoldingRangeKind, SelectionRange, Diagnostic, DiagnosticSeverity, CompletionItem, CompletionItemKind, CompletionList, CompletionItemTag, InsertTextFormat, DefinitionLink, SymbolInformation, SymbolKind, DocumentSymbol, Location, Hover, MarkedString, CodeActionContext, Command, CodeAction, DocumentHighlight, DocumentLink, WorkspaceEdit, TextEdit, CodeActionKind, TextDocumentEdit, VersionedTextDocumentIdentifier, DocumentHighlightKind };
6
6
  /**
7
7
  * Error codes used by diagnostics
8
8
  */
@@ -141,6 +141,11 @@ export interface SchemaConfiguration {
141
141
  * If no schema is provided, the schema will be fetched with the schema request service (if available).
142
142
  */
143
143
  schema?: JSONSchema;
144
+ /**
145
+ * A parent folder for folder specifc associations. An association that has a folder URI set is only used
146
+ * if the document that is validated has the folderUri as parent
147
+ */
148
+ folderUri?: string;
144
149
  }
145
150
  export interface WorkspaceContextService {
146
151
  resolveRelativePath(relativePath: string, resource: string): string;
@@ -290,3 +295,6 @@ export interface FormattingOptions extends LSPFormattingOptions {
290
295
  insertFinalNewline?: boolean;
291
296
  keepLines?: boolean;
292
297
  }
298
+ export interface SortOptions extends LSPFormattingOptions {
299
+ insertFinalNewline?: boolean;
300
+ }
@@ -699,7 +699,7 @@
699
699
  return text.replace(/[\\\$\}]/g, '\\$&'); // escape $, \ and }
700
700
  }
701
701
  getInsertTextForValue(value, separatorAfter) {
702
- var text = JSON.stringify(value, null, '\t');
702
+ const text = JSON.stringify(value, null, '\t');
703
703
  if (text === '{}') {
704
704
  return '{$1}' + separatorAfter;
705
705
  }
@@ -817,7 +817,7 @@
817
817
  nValueProposals += propertySchema.examples.length;
818
818
  }
819
819
  if (nValueProposals === 0) {
820
- var type = Array.isArray(propertySchema.type) ? propertySchema.type[0] : propertySchema.type;
820
+ let type = Array.isArray(propertySchema.type) ? propertySchema.type[0] : propertySchema.type;
821
821
  if (!type) {
822
822
  if (propertySchema.properties) {
823
823
  type = 'object';
@@ -857,8 +857,8 @@
857
857
  return resultText + value + separatorAfter;
858
858
  }
859
859
  getCurrentWord(document, offset) {
860
- var i = offset - 1;
861
- var text = document.getText();
860
+ let i = offset - 1;
861
+ const text = document.getText();
862
862
  while (i >= 0 && ' \t\n\r\v":{[,]}'.indexOf(text.charAt(i)) === -1) {
863
863
  i--;
864
864
  }
@@ -8,7 +8,7 @@
8
8
  if (v !== undefined) module.exports = v;
9
9
  }
10
10
  else if (typeof define === "function" && define.amd) {
11
- define(["require", "exports", "../parser/jsonParser", "../utils/strings", "../utils/colors", "../jsonLanguageTypes"], factory);
11
+ define(["require", "exports", "../parser/jsonParser", "../utils/strings", "../utils/colors", "@vscode/l10n", "../jsonLanguageTypes"], factory);
12
12
  }
13
13
  })(function (require, exports) {
14
14
  "use strict";
@@ -17,6 +17,7 @@
17
17
  const Parser = require("../parser/jsonParser");
18
18
  const Strings = require("../utils/strings");
19
19
  const colors_1 = require("../utils/colors");
20
+ const l10n = require("@vscode/l10n");
20
21
  const jsonLanguageTypes_1 = require("../jsonLanguageTypes");
21
22
  class JSONDocumentSymbols {
22
23
  constructor(schemaService) {
@@ -38,7 +39,7 @@
38
39
  for (const property of item.properties) {
39
40
  if (property.keyNode.value === 'key' && property.valueNode) {
40
41
  const location = jsonLanguageTypes_1.Location.create(document.uri, getRange(document, item));
41
- result.push({ name: Parser.getNodeValue(property.valueNode), kind: jsonLanguageTypes_1.SymbolKind.Function, location: location });
42
+ result.push({ name: getName(property.valueNode), kind: jsonLanguageTypes_1.SymbolKind.Function, location: location });
42
43
  limit--;
43
44
  if (limit <= 0) {
44
45
  if (context && context.onResultLimitExceeded) {
@@ -112,7 +113,7 @@
112
113
  if (property.keyNode.value === 'key' && property.valueNode) {
113
114
  const range = getRange(document, item);
114
115
  const selectionRange = getRange(document, property.keyNode);
115
- result.push({ name: Parser.getNodeValue(property.valueNode), kind: jsonLanguageTypes_1.SymbolKind.Function, range, selectionRange });
116
+ result.push({ name: getName(property.valueNode), kind: jsonLanguageTypes_1.SymbolKind.Function, range, selectionRange });
116
117
  limit--;
117
118
  if (limit <= 0) {
118
119
  if (context && context.onResultLimitExceeded) {
@@ -278,4 +279,7 @@
278
279
  function getRange(document, node) {
279
280
  return jsonLanguageTypes_1.Range.create(document.positionAt(node.offset), document.positionAt(node.offset + node.length));
280
281
  }
282
+ function getName(node) {
283
+ return Parser.getNodeValue(node) || l10n.t('<empty>');
284
+ }
281
285
  });
@@ -40,7 +40,7 @@
40
40
  }
41
41
  }
42
42
  const hoverRange = jsonLanguageTypes_1.Range.create(document.positionAt(hoverRangeNode.offset), document.positionAt(hoverRangeNode.offset + hoverRangeNode.length));
43
- var createHover = (contents) => {
43
+ const createHover = (contents) => {
44
44
  const result = {
45
45
  contents: contents,
46
46
  range: hoverRange
@@ -24,7 +24,9 @@
24
24
  const BANG = '!';
25
25
  const PATH_SEP = '/';
26
26
  class FilePatternAssociation {
27
- constructor(pattern, uris) {
27
+ constructor(pattern, folderUri, uris) {
28
+ this.folderUri = folderUri;
29
+ this.uris = uris;
28
30
  this.globWrappers = [];
29
31
  try {
30
32
  for (let patternString of pattern) {
@@ -43,7 +45,9 @@
43
45
  }
44
46
  }
45
47
  ;
46
- this.uris = uris;
48
+ if (folderUri && !folderUri.endsWith('/')) {
49
+ this.folderUri = folderUri + '/';
50
+ }
47
51
  }
48
52
  catch (e) {
49
53
  this.globWrappers.length = 0;
@@ -51,6 +55,9 @@
51
55
  }
52
56
  }
53
57
  matchesPattern(fileName) {
58
+ if (this.folderUri && !fileName.startsWith(this.folderUri)) {
59
+ return false;
60
+ }
54
61
  let match = false;
55
62
  for (const { regexp, include } of this.globWrappers) {
56
63
  if (regexp.test(fileName)) {
@@ -213,7 +220,7 @@
213
220
  const schemaAssociations = schemaContributions.schemaAssociations;
214
221
  for (let schemaAssociation of schemaAssociations) {
215
222
  const uris = schemaAssociation.uris.map(normalizeId);
216
- const association = this.addFilePatternAssociation(schemaAssociation.pattern, uris);
223
+ const association = this.addFilePatternAssociation(schemaAssociation.pattern, schemaAssociation.folderUri, uris);
217
224
  this.contributionAssociations.push(association);
218
225
  }
219
226
  }
@@ -226,19 +233,19 @@
226
233
  getOrAddSchemaHandle(id, unresolvedSchemaContent) {
227
234
  return this.schemasById[id] || this.addSchemaHandle(id, unresolvedSchemaContent);
228
235
  }
229
- addFilePatternAssociation(pattern, uris) {
230
- const fpa = new FilePatternAssociation(pattern, uris);
236
+ addFilePatternAssociation(pattern, folderUri, uris) {
237
+ const fpa = new FilePatternAssociation(pattern, folderUri, uris);
231
238
  this.filePatternAssociations.push(fpa);
232
239
  return fpa;
233
240
  }
234
- registerExternalSchema(uri, filePatterns, unresolvedSchemaContent) {
235
- const id = normalizeId(uri);
241
+ registerExternalSchema(config) {
242
+ const id = normalizeId(config.uri);
236
243
  this.registeredSchemasIds[id] = true;
237
244
  this.cachedSchemaForResource = undefined;
238
- if (filePatterns) {
239
- this.addFilePatternAssociation(filePatterns, [id]);
245
+ if (config.fileMatch && config.fileMatch.length) {
246
+ this.addFilePatternAssociation(config.fileMatch, config.folderUri, [id]);
240
247
  }
241
- return unresolvedSchemaContent ? this.addSchemaHandle(id, unresolvedSchemaContent) : this.getOrAddSchemaHandle(id);
248
+ return config.schema ? this.addSchemaHandle(id, config.schema) : this.getOrAddSchemaHandle(id);
242
249
  }
243
250
  clearExternalSchemas() {
244
251
  this.schemasById = {};
@@ -101,8 +101,8 @@
101
101
  return diagnostics;
102
102
  };
103
103
  if (schema) {
104
- const id = schema.id || ('schemaservice://untitled/' + idCounter++);
105
- const handle = this.jsonSchemaService.registerExternalSchema(id, [], schema);
104
+ const uri = schema.id || ('schemaservice://untitled/' + idCounter++);
105
+ const handle = this.jsonSchemaService.registerExternalSchema({ uri, schema });
106
106
  return handle.getResolvedSchema().then(resolvedSchema => {
107
107
  return getDiagnostics(resolvedSchema);
108
108
  });