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.
package/CHANGELOG.md CHANGED
@@ -1,4 +1,13 @@
1
- 5.2.0 / 2022-08/19
1
+ 5.3.0 / 2023-02-15
2
+ ================
3
+ * new API `LanguageService.sort` for sorting all properties in a JSON document
4
+ * new API `SortOptions`
5
+
6
+ 5.2.0 / 2023-02-02
7
+ ================
8
+ * Added `SchemaConfiguration.folderUri` for folder-specific schema associations
9
+
10
+ 5.1.1 / 2022-08/19
2
11
  ================
3
12
  * new type `SchemaDraft`, representing the offical JSON schema draft versions
4
13
  * new property `DocumentLanguageSettings.schemaDraft` to specify the schema version to use, if the schema does not contain a `$schema` property
@@ -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;
@@ -11,8 +11,8 @@ import { schemaContributions } from './services/configuration';
11
11
  import { JSONSchemaService } from './services/jsonSchemaService';
12
12
  import { getFoldingRanges } from './services/jsonFolding';
13
13
  import { getSelectionRanges } from './services/jsonSelectionRanges';
14
- import { format as formatJSON } from 'jsonc-parser';
15
- import { Range, TextEdit } from './jsonLanguageTypes';
14
+ import { sort } from './utils/sort';
15
+ import { format } from './utils/format';
16
16
  import { findLinks } from './services/jsonLinks';
17
17
  export * from './jsonLanguageTypes';
18
18
  export function getLanguageService(params) {
@@ -26,11 +26,7 @@ export function getLanguageService(params) {
26
26
  return {
27
27
  configure: (settings) => {
28
28
  jsonSchemaService.clearExternalSchemas();
29
- if (settings.schemas) {
30
- settings.schemas.forEach(settings => {
31
- jsonSchemaService.registerExternalSchema(settings.uri, settings.fileMatch, settings.schema);
32
- });
33
- }
29
+ settings.schemas?.forEach(jsonSchemaService.registerExternalSchema.bind(jsonSchemaService));
34
30
  jsonValidation.configure(settings);
35
31
  },
36
32
  resetSchema: (uri) => jsonSchemaService.onResourceChange(uri),
@@ -50,17 +46,7 @@ export function getLanguageService(params) {
50
46
  getSelectionRanges,
51
47
  findDefinition: () => Promise.resolve([]),
52
48
  findLinks,
53
- format: (d, r, o) => {
54
- let range = undefined;
55
- if (r) {
56
- const offset = d.offsetAt(r.start);
57
- const length = d.offsetAt(r.end) - offset;
58
- range = { offset, length };
59
- }
60
- const options = { tabSize: o ? o.tabSize : 4, insertSpaces: o?.insertSpaces === true, insertFinalNewline: o?.insertFinalNewline === true, eol: '\n', keepLines: o?.keepLines === true };
61
- return formatJSON(d.getText(), range, options).map(e => {
62
- return TextEdit.replace(Range.create(d.positionAt(e.offset), d.positionAt(e.offset + e.length)), e.content);
63
- });
64
- }
49
+ format: (document, range, options) => format(document, options, range),
50
+ sort: (document, options) => sort(document, options)
65
51
  };
66
52
  }
@@ -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
+ }
@@ -687,7 +687,7 @@ export class JSONCompletion {
687
687
  return text.replace(/[\\\$\}]/g, '\\$&'); // escape $, \ and }
688
688
  }
689
689
  getInsertTextForValue(value, separatorAfter) {
690
- var text = JSON.stringify(value, null, '\t');
690
+ const text = JSON.stringify(value, null, '\t');
691
691
  if (text === '{}') {
692
692
  return '{$1}' + separatorAfter;
693
693
  }
@@ -805,7 +805,7 @@ export class JSONCompletion {
805
805
  nValueProposals += propertySchema.examples.length;
806
806
  }
807
807
  if (nValueProposals === 0) {
808
- var type = Array.isArray(propertySchema.type) ? propertySchema.type[0] : propertySchema.type;
808
+ let type = Array.isArray(propertySchema.type) ? propertySchema.type[0] : propertySchema.type;
809
809
  if (!type) {
810
810
  if (propertySchema.properties) {
811
811
  type = 'object';
@@ -845,8 +845,8 @@ export class JSONCompletion {
845
845
  return resultText + value + separatorAfter;
846
846
  }
847
847
  getCurrentWord(document, offset) {
848
- var i = offset - 1;
849
- var text = document.getText();
848
+ let i = offset - 1;
849
+ const text = document.getText();
850
850
  while (i >= 0 && ' \t\n\r\v":{[,]}'.indexOf(text.charAt(i)) === -1) {
851
851
  i--;
852
852
  }
@@ -5,6 +5,7 @@
5
5
  import * as Parser from '../parser/jsonParser';
6
6
  import * as Strings from '../utils/strings';
7
7
  import { colorFromHex } from '../utils/colors';
8
+ import * as l10n from '@vscode/l10n';
8
9
  import { Range, TextEdit, SymbolKind, Location } from "../jsonLanguageTypes";
9
10
  export class JSONDocumentSymbols {
10
11
  constructor(schemaService) {
@@ -26,7 +27,7 @@ export class JSONDocumentSymbols {
26
27
  for (const property of item.properties) {
27
28
  if (property.keyNode.value === 'key' && property.valueNode) {
28
29
  const location = Location.create(document.uri, getRange(document, item));
29
- result.push({ name: Parser.getNodeValue(property.valueNode), kind: SymbolKind.Function, location: location });
30
+ result.push({ name: getName(property.valueNode), kind: SymbolKind.Function, location: location });
30
31
  limit--;
31
32
  if (limit <= 0) {
32
33
  if (context && context.onResultLimitExceeded) {
@@ -100,7 +101,7 @@ export class JSONDocumentSymbols {
100
101
  if (property.keyNode.value === 'key' && property.valueNode) {
101
102
  const range = getRange(document, item);
102
103
  const selectionRange = getRange(document, property.keyNode);
103
- result.push({ name: Parser.getNodeValue(property.valueNode), kind: SymbolKind.Function, range, selectionRange });
104
+ result.push({ name: getName(property.valueNode), kind: SymbolKind.Function, range, selectionRange });
104
105
  limit--;
105
106
  if (limit <= 0) {
106
107
  if (context && context.onResultLimitExceeded) {
@@ -265,3 +266,6 @@ export class JSONDocumentSymbols {
265
266
  function getRange(document, node) {
266
267
  return Range.create(document.positionAt(node.offset), document.positionAt(node.offset + node.length));
267
268
  }
269
+ function getName(node) {
270
+ return Parser.getNodeValue(node) || l10n.t('<empty>');
271
+ }
@@ -28,7 +28,7 @@ export class JSONHover {
28
28
  }
29
29
  }
30
30
  const hoverRange = Range.create(document.positionAt(hoverRangeNode.offset), document.positionAt(hoverRangeNode.offset + hoverRangeNode.length));
31
- var createHover = (contents) => {
31
+ const createHover = (contents) => {
32
32
  const result = {
33
33
  contents: contents,
34
34
  range: hoverRange
@@ -12,7 +12,9 @@ import { isObject, isString } from '../utils/objects';
12
12
  const BANG = '!';
13
13
  const PATH_SEP = '/';
14
14
  class FilePatternAssociation {
15
- constructor(pattern, uris) {
15
+ constructor(pattern, folderUri, uris) {
16
+ this.folderUri = folderUri;
17
+ this.uris = uris;
16
18
  this.globWrappers = [];
17
19
  try {
18
20
  for (let patternString of pattern) {
@@ -31,7 +33,9 @@ class FilePatternAssociation {
31
33
  }
32
34
  }
33
35
  ;
34
- this.uris = uris;
36
+ if (folderUri && !folderUri.endsWith('/')) {
37
+ this.folderUri = folderUri + '/';
38
+ }
35
39
  }
36
40
  catch (e) {
37
41
  this.globWrappers.length = 0;
@@ -39,6 +43,9 @@ class FilePatternAssociation {
39
43
  }
40
44
  }
41
45
  matchesPattern(fileName) {
46
+ if (this.folderUri && !fileName.startsWith(this.folderUri)) {
47
+ return false;
48
+ }
42
49
  let match = false;
43
50
  for (const { regexp, include } of this.globWrappers) {
44
51
  if (regexp.test(fileName)) {
@@ -199,7 +206,7 @@ export class JSONSchemaService {
199
206
  const schemaAssociations = schemaContributions.schemaAssociations;
200
207
  for (let schemaAssociation of schemaAssociations) {
201
208
  const uris = schemaAssociation.uris.map(normalizeId);
202
- const association = this.addFilePatternAssociation(schemaAssociation.pattern, uris);
209
+ const association = this.addFilePatternAssociation(schemaAssociation.pattern, schemaAssociation.folderUri, uris);
203
210
  this.contributionAssociations.push(association);
204
211
  }
205
212
  }
@@ -212,19 +219,19 @@ export class JSONSchemaService {
212
219
  getOrAddSchemaHandle(id, unresolvedSchemaContent) {
213
220
  return this.schemasById[id] || this.addSchemaHandle(id, unresolvedSchemaContent);
214
221
  }
215
- addFilePatternAssociation(pattern, uris) {
216
- const fpa = new FilePatternAssociation(pattern, uris);
222
+ addFilePatternAssociation(pattern, folderUri, uris) {
223
+ const fpa = new FilePatternAssociation(pattern, folderUri, uris);
217
224
  this.filePatternAssociations.push(fpa);
218
225
  return fpa;
219
226
  }
220
- registerExternalSchema(uri, filePatterns, unresolvedSchemaContent) {
221
- const id = normalizeId(uri);
227
+ registerExternalSchema(config) {
228
+ const id = normalizeId(config.uri);
222
229
  this.registeredSchemasIds[id] = true;
223
230
  this.cachedSchemaForResource = undefined;
224
- if (filePatterns) {
225
- this.addFilePatternAssociation(filePatterns, [id]);
231
+ if (config.fileMatch && config.fileMatch.length) {
232
+ this.addFilePatternAssociation(config.fileMatch, config.folderUri, [id]);
226
233
  }
227
- return unresolvedSchemaContent ? this.addSchemaHandle(id, unresolvedSchemaContent) : this.getOrAddSchemaHandle(id);
234
+ return config.schema ? this.addSchemaHandle(id, config.schema) : this.getOrAddSchemaHandle(id);
228
235
  }
229
236
  clearExternalSchemas() {
230
237
  this.schemasById = {};
@@ -89,8 +89,8 @@ export class JSONValidation {
89
89
  return diagnostics;
90
90
  };
91
91
  if (schema) {
92
- const id = schema.id || ('schemaservice://untitled/' + idCounter++);
93
- const handle = this.jsonSchemaService.registerExternalSchema(id, [], schema);
92
+ const uri = schema.id || ('schemaservice://untitled/' + idCounter++);
93
+ const handle = this.jsonSchemaService.registerExternalSchema({ uri, schema });
94
94
  return handle.getResolvedSchema().then(resolvedSchema => {
95
95
  return getDiagnostics(resolvedSchema);
96
96
  });
@@ -0,0 +1,20 @@
1
+ import { format as formatJSON } from 'jsonc-parser';
2
+ import { Range, TextEdit } from '../jsonLanguageTypes';
3
+ export function format(documentToFormat, formattingOptions, formattingRange) {
4
+ let range = undefined;
5
+ if (formattingRange) {
6
+ const offset = documentToFormat.offsetAt(formattingRange.start);
7
+ const length = documentToFormat.offsetAt(formattingRange.end) - offset;
8
+ range = { offset, length };
9
+ }
10
+ const options = {
11
+ tabSize: formattingOptions ? formattingOptions.tabSize : 4,
12
+ insertSpaces: formattingOptions?.insertSpaces === true,
13
+ insertFinalNewline: formattingOptions?.insertFinalNewline === true,
14
+ eol: '\n',
15
+ keepLines: formattingOptions?.keepLines === true
16
+ };
17
+ return formatJSON(documentToFormat.getText(), range, options).map(edit => {
18
+ return TextEdit.replace(Range.create(documentToFormat.positionAt(edit.offset), documentToFormat.positionAt(edit.offset + edit.length)), edit.content);
19
+ });
20
+ }
@@ -18,7 +18,7 @@ export function equals(one, other) {
18
18
  if ((Array.isArray(one)) !== (Array.isArray(other))) {
19
19
  return false;
20
20
  }
21
- var i, key;
21
+ let i, key;
22
22
  if (Array.isArray(one)) {
23
23
  if (one.length !== other.length) {
24
24
  return false;
@@ -30,12 +30,12 @@ export function equals(one, other) {
30
30
  }
31
31
  }
32
32
  else {
33
- var oneKeys = [];
33
+ const oneKeys = [];
34
34
  for (key in one) {
35
35
  oneKeys.push(key);
36
36
  }
37
37
  oneKeys.sort();
38
- var otherKeys = [];
38
+ const otherKeys = [];
39
39
  for (key in other) {
40
40
  otherKeys.push(key);
41
41
  }
@@ -0,0 +1,71 @@
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
+ export var Container;
6
+ (function (Container) {
7
+ Container[Container["Object"] = 0] = "Object";
8
+ Container[Container["Array"] = 1] = "Array";
9
+ })(Container || (Container = {}));
10
+ export class PropertyTree {
11
+ constructor(propertyName, beginningLineNumber) {
12
+ this.propertyName = propertyName ?? '';
13
+ this.beginningLineNumber = beginningLineNumber;
14
+ this.childrenProperties = [];
15
+ this.lastProperty = false;
16
+ this.noKeyName = false;
17
+ }
18
+ addChildProperty(childProperty) {
19
+ childProperty.parent = this;
20
+ if (this.childrenProperties.length > 0) {
21
+ let insertionIndex = 0;
22
+ if (childProperty.noKeyName) {
23
+ insertionIndex = this.childrenProperties.length;
24
+ }
25
+ else {
26
+ insertionIndex = binarySearchOnPropertyArray(this.childrenProperties, childProperty, compareProperties);
27
+ }
28
+ if (insertionIndex < 0) {
29
+ insertionIndex = (insertionIndex * -1) - 1;
30
+ }
31
+ this.childrenProperties.splice(insertionIndex, 0, childProperty);
32
+ }
33
+ else {
34
+ this.childrenProperties.push(childProperty);
35
+ }
36
+ return childProperty;
37
+ }
38
+ }
39
+ function compareProperties(propertyTree1, propertyTree2) {
40
+ if (propertyTree1.propertyName < propertyTree2.propertyName) {
41
+ return -1;
42
+ }
43
+ else if (propertyTree1.propertyName > propertyTree2.propertyName) {
44
+ return 1;
45
+ }
46
+ return 0;
47
+ }
48
+ function binarySearchOnPropertyArray(propertyTreeArray, propertyTree, compare_fn) {
49
+ if (propertyTree.propertyName < propertyTreeArray[0].propertyName) {
50
+ return 0;
51
+ }
52
+ if (propertyTree.propertyName > propertyTreeArray[propertyTreeArray.length - 1].propertyName) {
53
+ return propertyTreeArray.length;
54
+ }
55
+ let m = 0;
56
+ let n = propertyTreeArray.length - 1;
57
+ while (m <= n) {
58
+ let k = (n + m) >> 1;
59
+ let cmp = compare_fn(propertyTree, propertyTreeArray[k]);
60
+ if (cmp > 0) {
61
+ m = k + 1;
62
+ }
63
+ else if (cmp < 0) {
64
+ n = k - 1;
65
+ }
66
+ else {
67
+ return k;
68
+ }
69
+ }
70
+ return -m - 1;
71
+ }