Revamping to matrix style

This commit is contained in:
2026-02-16 16:37:35 -05:00
parent 71852ec99a
commit 9d0e3938e4
14958 changed files with 2089572 additions and 114 deletions

133
node_modules/vscode-json-languageservice/CHANGELOG.md generated vendored Normal file
View File

@@ -0,0 +1,133 @@
4.1.6 / 2021-07-16
================
* Replace minimatch with glob-to-regexp
4.1.0 / 2021-04-24
================
* `SchemaConfiguration.fileMatch` now supports glob patterns (e.g. /foo/**/bar.json')
4.0.0 / 2020-12-14
================
* Update to `vscode-languageserver-types@3.16`
* Removed deprecated `findColorSymbols`
3.11.0 / 2020-11-30
================
* new API `FormattingOptions.insertFinalNewline`
3.10.0 / 2020-11-03
================
* new API `findLinks` return links for local `$ref` links. Replaces `findDefinition` which no longer returns results ( kept for API compatibility)
3.9.0 / 2020-09-28
=================
* new API `DocumentLanguageSettings.schemaValidation`. The severity of problems from schema validation. If set to 'ignore', schema validation will be skipped. If not set, 'warning' is used.
* new API `DocumentLanguageSettings.schemaRequest`. The severity of problems that occurred while resolving and loading schemas. If set to 'ignore', schema resolving problems are not reported. If not set, 'warning' is used.
3.8.0 / 2020-08-02
=================
* new API `LanguageService.getMatchingSchemas`. Matches a document against its schema and list all AST nodes along with the matching sub schemas.
3.7.0 / 2020-06-04
==================
* New API `JSONSchema.suggestSortText` to set the sort order of completion proposals (VS Code specific JSON schema extension)
3.6.0 / 2020-04-27
==================
* New API `findDefinition` to find a definition of a `$ref` link
3.5.0 / 2020-02-20
==================
* Support for exclusive file pattern starting with '!'. A file match consists of an array of patterns. A match succeeds when there is at least one pattern matching and last matching pattern does not start with '!'.
3.4.4 / 2019-11-04
==================
* Using `vscode-languageserver-textdocument` for TextDocument
3.4.0 / 2019-10-28
==================
* Added `DocumentSymbolsContext` and `ColorInformationContext` with `resultLimit` and `onResultLimitExceeded`. `onResultLimitExceeded` is called when the result was cropped.
* Added commit characters for completion proposals (if supported by ClientCapabilities)
* Warn when using draft-03 or draft-08 schemas
3.3.4 / 2019-09-20
==================
* Renamed `schema.allowsTrailingCommas` -> `schema.allowTrailingCommas`
3.3.3 / 2019-08-29
==================
* Schemas can configure whether comments and/or trailing commas are permitted.
3.3.0 / 2019-06-12
==================
* New API `LanguageService.getSelectionRanges` to get semantic selection ranges.
* Manage schema dependencies so that `resetSchema` also resets schemas that depend on the schema.
3.2.0 / 2018-09-27
==================
* New API `LanguageServiceParams.ClientCapabilities` to define what LSP capabilities the client supports.
* For the best experiences, clients should always use `LanguageServiceParams.ClientCapabilities.LATEST`, which has all the latest LSP capabilities enabled.
* `LanguageServiceParams.ClientCapabilities` can allow `MarkupKind.Markdown` as valid documentationFormat (used by completions if schemas use `markdownDescription` or `markdownEnumDescriptions`).
* Snippets can now provide the description also in markdown format.
* Bundled draft-07-schema with descriptions.
* Propose `examples` in code completions.
3.1.5 / 2018-08-14
==================
* support for JSON schema draft-07
* New API `LanguageService.findDocumentSymbols2` to get document symbols as `DocumentSymbol[]`
3.1.2 / 2018-07-25
==================
* New API `LanguageService.getFoldingRanges`
* doValidation can also be used with a given schema
3.1.0 / 2018-04-09
==================
* new APIs: `newJSONDocument` to create a JSON document from a custom AST
* new API types: ObjectASTNode, PropertyASTNode, ArrayASTNode, StringASTNode, NumberASTNode, BooleanASTNode, NullASTNode that allow creating a custom AST
3.0.9 / 2018-03-07
==================
* Provide ems modules in lib/esm
3.0.2 / 2017-01-27
==================
* Added document specific validation parameters: `DocumentLanguageSettings`
* API to define the severity of reported comments and trailing commas (`DocumentLanguageSettings.comments`, `DocumentLanguageSettings.trailingCommas`)
3.0.0 / 2017-01-11
==================
* Changed parameters of API `LanguageService.getColorPresentations` to separate color and range
.
2.0.19 / 2017-09-21
===================
* New API `LanguageService.getColorPresentations` returning presentations for a given color.
* New API type `ColorPresentation` added.
2.0.15 / 2017-08-28
===================
* New API `LanguageService.findDocumentColors` returning the location and value of all colors in a document.
* New API types `ColorInformation` and `Color` added.
* Deprecated `LanguageService.findColorSymbols`. Use `LanguageService.findDocumentColors` instead.
2.0.8 / 2017-04-25
==================
* error code for CommentsNotAllowed
2.0.5 / 2017-03-27
==================
* Add new API findColorSymbols that returns all color values in a JSON document. To mark a value as a color, specify `"format": "color"` in the schema.
2.0.4 / 2017-02-27
==================
* Support for custom schema property 'patternErrorMessage'. The message is used as error message if the object is of type string and has a 'pattern' property that does not match the object to validate.
2.0.1 / 2017-02-21
==================
* Fixes for formatting content with errors
2.0.0 / 2017-02-17
==================
* Updating to [language server type 3.0](https://github.com/Microsoft/vscode-languageserver-node/tree/master/types) API

47
node_modules/vscode-json-languageservice/LICENSE.md generated vendored Normal file
View File

@@ -0,0 +1,47 @@
The MIT License (MIT)
Copyright (c) Microsoft
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Glob matching is based on code from https://github.com/fitzgen/glob-to-regexp
Copyright (c) 2013, Nick Fitzgerald
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this list
of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this list
of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
OF THE POSSIBILITY OF SUCH DAMAGE.

65
node_modules/vscode-json-languageservice/README.md generated vendored Normal file
View File

@@ -0,0 +1,65 @@
# vscode-json-languageservice
JSON language service extracted from VSCode to be reused, e.g in the Monaco editor.
[![npm Package](https://img.shields.io/npm/v/vscode-json-languageservice.svg?style=flat-square)](https://www.npmjs.org/package/vscode-json-languageservice)
[![NPM Downloads](https://img.shields.io/npm/dm/vscode-json-languageservice.svg)](https://npmjs.org/package/vscode-json-languageservice)
[![Build Status](https://travis-ci.org/Microsoft/vscode-json-languageservice.svg?branch=master)](https://travis-ci.org/Microsoft/vscode-json-languageservice)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
## Why?
The _vscode-json-languageservice_ contains the language smarts behind the JSON editing experience of Visual Studio Code
and the Monaco editor.
- _doValidation_ analyses an input string and returns syntax and lint errors.
- _doComplete_ provides completion proposals for a given location.
- _doResolve_ resolves a completion proposals.
- _doHover_ provides a hover text for a given location.
- _findDocumentSymbols_ provides all symbols in the given document.
- _findDocumentColors_ provides all color symbols in the given document.
- _getColorPresentations_ returns available color formats for a color symbol.
- _format_ formats the code at the given range.
- _getFoldingRanges_ gets folding ranges for the given document.
- _getSelectionRanges_ gets selection ranges for a given location.
- _getMatchingSchemas_ matches a document against its schema and returns all AST nodes along with the matching sub schemas.
- _parseJSONDocument_ creates a JSON document from source code.
- _newJSONDocument_ creates a JSON document from an AST.
For the complete API see [jsonLanguageService.ts](./src/jsonLanguageService.ts) and [jsonLanguageTypes.ts](./src/jsonLanguageTypes.ts)
## Installation
npm install --save vscode-json-languageservice
## Sample usage
See [sample.ts](./src/example/sample.ts) for an example on how to use the JSON language service.
To run the sample use `yarn sample`
## Development
git clone https://github.com/microsoft/vscode-json-languageservice
cd vscode-json-languageservice
yarn
Use `yarn test` to compile and run tests
### How can I run and debug the service?
- open the folder in VSCode.
- set breakpoints, e.g. in `jsonCompletion.ts`
- run the Unit tests from the run viewlet and wait until a breakpoint is hit:
![image](https://user-images.githubusercontent.com/6461412/94239202-bdad4e80-ff11-11ea-99c3-cb9dbeb1c0b2.png)
### How can I run and debug the service inside an instance of VSCode?
- run VSCode out of sources setup as described here: https://github.com/Microsoft/vscode/wiki/How-to-Contribute
- use `yarn link vscode-json-languageservice` in `vscode/extensions/json-language-features/server` to run VSCode with the latest changes from `vscode-json-languageservice`
- run VSCode out of source (`vscode/scripts/code.sh|bat`) and open a `.json` file
- in VSCode window that is open on the `vscode-json-languageservice` sources, run command `Debug: Attach to Node process` and pick the `code-oss` process with the `json-language-features` path
![image](https://user-images.githubusercontent.com/6461412/94242925-061b3b00-ff17-11ea-8c17-8da15268f1a1.png)
- set breakpoints, e.g. in `jsonCompletion.ts`
- in the instance run from sources, invoke code completion in the `.json` file

View File

@@ -0,0 +1,17 @@
import { Thenable, MarkedString, CompletionItem } from './jsonLanguageService';
export interface JSONWorkerContribution {
getInfoContribution(uri: string, location: JSONPath): Thenable<MarkedString[]>;
collectPropertyCompletions(uri: string, location: JSONPath, currentWord: string, addValue: boolean, isLast: boolean, result: CompletionsCollector): Thenable<any>;
collectValueCompletions(uri: string, location: JSONPath, propertyKey: string, result: CompletionsCollector): Thenable<any>;
collectDefaultCompletions(uri: string, result: CompletionsCollector): Thenable<any>;
resolveCompletion?(item: CompletionItem): Thenable<CompletionItem>;
}
export declare type Segment = string | number;
export declare type JSONPath = Segment[];
export interface CompletionsCollector {
add(suggestion: CompletionItem): void;
error(message: string): void;
log(message: string): void;
setAsIncomplete(): void;
getNumberOfProposals(): number;
}

View File

@@ -0,0 +1 @@
export {};

View File

@@ -0,0 +1,28 @@
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 } from './jsonLanguageTypes';
import { DocumentLink } from 'vscode-languageserver-types';
export declare type JSONDocument = {
root: ASTNode | undefined;
getNodeFromOffset(offset: number, includeRightBound?: boolean): ASTNode | undefined;
};
export * from './jsonLanguageTypes';
export interface LanguageService {
configure(settings: LanguageSettings): void;
doValidation(document: TextDocument, jsonDocument: JSONDocument, documentSettings?: DocumentLanguageSettings, schema?: JSONSchema): Thenable<Diagnostic[]>;
parseJSONDocument(document: TextDocument): JSONDocument;
newJSONDocument(rootNode: ASTNode, syntaxDiagnostics?: Diagnostic[]): JSONDocument;
resetSchema(uri: string): boolean;
getMatchingSchemas(document: TextDocument, jsonDocument: JSONDocument, schema?: JSONSchema): Thenable<MatchingSchema[]>;
doResolve(item: CompletionItem): Thenable<CompletionItem>;
doComplete(document: TextDocument, position: Position, doc: JSONDocument): Thenable<CompletionList | null>;
findDocumentSymbols(document: TextDocument, doc: JSONDocument, context?: DocumentSymbolsContext): SymbolInformation[];
findDocumentSymbols2(document: TextDocument, doc: JSONDocument, context?: DocumentSymbolsContext): DocumentSymbol[];
findDocumentColors(document: TextDocument, doc: JSONDocument, context?: DocumentColorsContext): Thenable<ColorInformation[]>;
getColorPresentations(document: TextDocument, doc: JSONDocument, color: Color, range: Range): ColorPresentation[];
doHover(document: TextDocument, position: Position, doc: JSONDocument): Thenable<Hover | null>;
format(document: TextDocument, range: Range, options: FormattingOptions): TextEdit[];
getFoldingRanges(document: TextDocument, context?: FoldingRangesContext): FoldingRange[];
getSelectionRanges(document: TextDocument, positions: Position[], doc: JSONDocument): SelectionRange[];
findDefinition(document: TextDocument, position: Position, doc: JSONDocument): Thenable<DefinitionLink[]>;
findLinks(document: TextDocument, doc: JSONDocument): Thenable<DocumentLink[]>;
}
export declare function getLanguageService(params: LanguageServiceParams): LanguageService;

View File

@@ -0,0 +1,65 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { JSONCompletion } from './services/jsonCompletion';
import { JSONHover } from './services/jsonHover';
import { JSONValidation } from './services/jsonValidation';
import { JSONDocumentSymbols } from './services/jsonDocumentSymbols';
import { parse as parseJSON, newJSONDocument } from './parser/jsonParser';
import { schemaContributions } from './services/configuration';
import { JSONSchemaService } from './services/jsonSchemaService';
import { getFoldingRanges } from './services/jsonFolding';
import { getSelectionRanges } from './services/jsonSelectionRanges';
import { format as formatJSON } from 'jsonc-parser';
import { Range, TextEdit } from './jsonLanguageTypes';
import { findLinks } from './services/jsonLinks';
export * from './jsonLanguageTypes';
export function getLanguageService(params) {
var promise = params.promiseConstructor || Promise;
var jsonSchemaService = new JSONSchemaService(params.schemaRequestService, params.workspaceContext, promise);
jsonSchemaService.setSchemaContributions(schemaContributions);
var jsonCompletion = new JSONCompletion(jsonSchemaService, params.contributions, promise, params.clientCapabilities);
var jsonHover = new JSONHover(jsonSchemaService, params.contributions, promise);
var jsonDocumentSymbols = new JSONDocumentSymbols(jsonSchemaService);
var jsonValidation = new JSONValidation(jsonSchemaService, promise);
return {
configure: function (settings) {
jsonSchemaService.clearExternalSchemas();
if (settings.schemas) {
settings.schemas.forEach(function (settings) {
jsonSchemaService.registerExternalSchema(settings.uri, settings.fileMatch, settings.schema);
});
}
jsonValidation.configure(settings);
},
resetSchema: function (uri) { return jsonSchemaService.onResourceChange(uri); },
doValidation: jsonValidation.doValidation.bind(jsonValidation),
parseJSONDocument: function (document) { return parseJSON(document, { collectComments: true }); },
newJSONDocument: function (root, diagnostics) { return newJSONDocument(root, diagnostics); },
getMatchingSchemas: jsonSchemaService.getMatchingSchemas.bind(jsonSchemaService),
doResolve: jsonCompletion.doResolve.bind(jsonCompletion),
doComplete: jsonCompletion.doComplete.bind(jsonCompletion),
findDocumentSymbols: jsonDocumentSymbols.findDocumentSymbols.bind(jsonDocumentSymbols),
findDocumentSymbols2: jsonDocumentSymbols.findDocumentSymbols2.bind(jsonDocumentSymbols),
findDocumentColors: jsonDocumentSymbols.findDocumentColors.bind(jsonDocumentSymbols),
getColorPresentations: jsonDocumentSymbols.getColorPresentations.bind(jsonDocumentSymbols),
doHover: jsonHover.doHover.bind(jsonHover),
getFoldingRanges: getFoldingRanges,
getSelectionRanges: getSelectionRanges,
findDefinition: function () { return Promise.resolve([]); },
findLinks: findLinks,
format: function (d, r, o) {
var range = undefined;
if (r) {
var offset = d.offsetAt(r.start);
var length = d.offsetAt(r.end) - offset;
range = { offset: offset, length: length };
}
var options = { tabSize: o ? o.tabSize : 4, insertSpaces: (o === null || o === void 0 ? void 0 : o.insertSpaces) === true, insertFinalNewline: (o === null || o === void 0 ? void 0 : o.insertFinalNewline) === true, eol: '\n' };
return formatJSON(d.getText(), range, options).map(function (e) {
return TextEdit.replace(Range.create(d.positionAt(e.offset), d.positionAt(e.offset + e.length)), e.content);
});
}
};
}

View File

@@ -0,0 +1,275 @@
import { JSONWorkerContribution, JSONPath, Segment, CompletionsCollector } from './jsonContributions';
import { JSONSchema } from './jsonSchema';
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';
import { TextDocument } from 'vscode-languageserver-textdocument';
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 };
/**
* Error codes used by diagnostics
*/
export declare enum ErrorCode {
Undefined = 0,
EnumValueMismatch = 1,
Deprecated = 2,
UnexpectedEndOfComment = 257,
UnexpectedEndOfString = 258,
UnexpectedEndOfNumber = 259,
InvalidUnicode = 260,
InvalidEscapeCharacter = 261,
InvalidCharacter = 262,
PropertyExpected = 513,
CommaExpected = 514,
ColonExpected = 515,
ValueExpected = 516,
CommaOrCloseBacketExpected = 517,
CommaOrCloseBraceExpected = 518,
TrailingComma = 519,
DuplicateKey = 520,
CommentNotPermitted = 521,
SchemaResolveError = 768
}
export declare type ASTNode = ObjectASTNode | PropertyASTNode | ArrayASTNode | StringASTNode | NumberASTNode | BooleanASTNode | NullASTNode;
export interface BaseASTNode {
readonly type: 'object' | 'array' | 'property' | 'string' | 'number' | 'boolean' | 'null';
readonly parent?: ASTNode;
readonly offset: number;
readonly length: number;
readonly children?: ASTNode[];
readonly value?: string | boolean | number | null;
}
export interface ObjectASTNode extends BaseASTNode {
readonly type: 'object';
readonly properties: PropertyASTNode[];
readonly children: ASTNode[];
}
export interface PropertyASTNode extends BaseASTNode {
readonly type: 'property';
readonly keyNode: StringASTNode;
readonly valueNode?: ASTNode;
readonly colonOffset?: number;
readonly children: ASTNode[];
}
export interface ArrayASTNode extends BaseASTNode {
readonly type: 'array';
readonly items: ASTNode[];
readonly children: ASTNode[];
}
export interface StringASTNode extends BaseASTNode {
readonly type: 'string';
readonly value: string;
}
export interface NumberASTNode extends BaseASTNode {
readonly type: 'number';
readonly value: number;
readonly isInteger: boolean;
}
export interface BooleanASTNode extends BaseASTNode {
readonly type: 'boolean';
readonly value: boolean;
}
export interface NullASTNode extends BaseASTNode {
readonly type: 'null';
readonly value: null;
}
export interface MatchingSchema {
node: ASTNode;
schema: JSONSchema;
}
export interface LanguageSettings {
/**
* If set, the validator will return syntax and semantic errors.
*/
validate?: boolean;
/**
* Defines whether comments are allowed or not. If set to false, comments will be reported as errors.
* DocumentLanguageSettings.allowComments will override this setting.
*/
allowComments?: boolean;
/**
* A list of known schemas and/or associations of schemas to file names.
*/
schemas?: SchemaConfiguration[];
}
export declare type SeverityLevel = 'error' | 'warning' | 'ignore';
export interface DocumentLanguageSettings {
/**
* The severity of reported comments. If not set, 'LanguageSettings.allowComments' defines whether comments are ignored or reported as errors.
*/
comments?: SeverityLevel;
/**
* The severity of reported trailing commas. If not set, trailing commas will be reported as errors.
*/
trailingCommas?: SeverityLevel;
/**
* The severity of problems from schema validation. If set to 'ignore', schema validation will be skipped. If not set, 'warning' is used.
*/
schemaValidation?: SeverityLevel;
/**
* The severity of problems that occurred when resolving and loading schemas. If set to 'ignore', schema resolving problems are not reported. If not set, 'warning' is used.
*/
schemaRequest?: SeverityLevel;
}
export interface SchemaConfiguration {
/**
* The URI of the schema, which is also the identifier of the schema.
*/
uri: string;
/**
* A list of glob patterns that describe for which file URIs the JSON schema will be used.
* '*' and '**' wildcards are supported. Exclusion patterns start with '!'.
* For example '*.schema.json', 'package.json', '!foo*.schema.json', 'foo/**\/BADRESP.json'.
* A match succeeds when there is at least one pattern matching and last matching pattern does not start with '!'.
*/
fileMatch?: string[];
/**
* The schema for the given URI.
* If no schema is provided, the schema will be fetched with the schema request service (if available).
*/
schema?: JSONSchema;
}
export interface WorkspaceContextService {
resolveRelativePath(relativePath: string, resource: string): string;
}
/**
* The schema request service is used to fetch schemas. If successful, returns a resolved promise with the content of the schema.
* In case of an error, returns a rejected promise with a displayable error string.
*/
export interface SchemaRequestService {
(uri: string): Thenable<string>;
}
export interface PromiseConstructor {
/**
* Creates a new Promise.
* @param executor A callback used to initialize the promise. This callback is passed two arguments:
* a resolve callback used resolve the promise with a value or the result of another promise,
* and a reject callback used to reject the promise with a provided reason or error.
*/
new <T>(executor: (resolve: (value?: T | Thenable<T | undefined>) => void, reject: (reason?: any) => void) => void): Thenable<T | undefined>;
/**
* Creates a Promise that is resolved with an array of results when all of the provided Promises
* resolve, or rejected when any Promise is rejected.
* @param values An array of Promises.
* @returns A new Promise.
*/
all<T>(values: Array<T | Thenable<T>>): Thenable<T[]>;
/**
* Creates a new rejected promise for the provided reason.
* @param reason The reason the promise was rejected.
* @returns A new rejected Promise.
*/
reject<T>(reason: any): Thenable<T>;
/**
* Creates a new resolved promise for the provided value.
* @param value A promise.
* @returns A promise whose internal state matches the provided promise.
*/
resolve<T>(value: T | Thenable<T>): Thenable<T>;
}
export interface Thenable<R> {
/**
* Attaches callbacks for the resolution and/or rejection of the Promise.
* @param onfulfilled The callback to execute when the Promise is resolved.
* @param onrejected The callback to execute when the Promise is rejected.
* @returns A Promise for the completion of which ever callback is executed.
*/
then<TResult>(onfulfilled?: (value: R) => TResult | Thenable<TResult>, onrejected?: (reason: any) => TResult | Thenable<TResult>): Thenable<TResult>;
then<TResult>(onfulfilled?: (value: R) => TResult | Thenable<TResult>, onrejected?: (reason: any) => void): Thenable<TResult>;
}
export interface LanguageServiceParams {
/**
* The schema request service is used to fetch schemas from a URI. The provider returns the schema file content, or,
* in case of an error, a displayable error string
*/
schemaRequestService?: SchemaRequestService;
/**
* The workspace context is used to resolve relative paths for relative schema references.
*/
workspaceContext?: WorkspaceContextService;
/**
* An optional set of completion and hover participants.
*/
contributions?: JSONWorkerContribution[];
/**
* A promise constructor. If not set, the ES5 Promise will be used.
*/
promiseConstructor?: PromiseConstructor;
/**
* Describes the LSP capabilities the client supports.
*/
clientCapabilities?: ClientCapabilities;
}
/**
* Describes what LSP capabilities the client supports
*/
export interface ClientCapabilities {
/**
* The text document client capabilities
*/
textDocument?: {
/**
* Capabilities specific to completions.
*/
completion?: {
/**
* The client supports the following `CompletionItem` specific
* capabilities.
*/
completionItem?: {
/**
* Client supports the follow content formats for the documentation
* property. The order describes the preferred format of the client.
*/
documentationFormat?: MarkupKind[];
/**
* The client supports commit characters on a completion item.
*/
commitCharactersSupport?: boolean;
};
};
/**
* Capabilities specific to hovers.
*/
hover?: {
/**
* Client supports the follow content formats for the content
* property. The order describes the preferred format of the client.
*/
contentFormat?: MarkupKind[];
};
};
}
export declare namespace ClientCapabilities {
const LATEST: ClientCapabilities;
}
export interface FoldingRangesContext {
/**
* The maximal number of ranges returned.
*/
rangeLimit?: number;
/**
* Called when the result was cropped.
*/
onRangeLimitExceeded?: (uri: string) => void;
}
export interface DocumentSymbolsContext {
/**
* The maximal number of document symbols returned.
*/
resultLimit?: number;
/**
* Called when the result was cropped.
*/
onResultLimitExceeded?: (uri: string) => void;
}
export interface ColorInformationContext {
/**
* The maximal number of color informations returned.
*/
resultLimit?: number;
/**
* Called when the result was cropped.
*/
onResultLimitExceeded?: (uri: string) => void;
}
export interface FormattingOptions extends LSPFormattingOptions {
insertFinalNewline?: boolean;
}

View File

@@ -0,0 +1,45 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Range, Position, MarkupContent, MarkupKind, Color, ColorInformation, ColorPresentation, FoldingRange, FoldingRangeKind, SelectionRange, Diagnostic, DiagnosticSeverity, CompletionItem, CompletionItemKind, CompletionList, CompletionItemTag, InsertTextFormat, SymbolInformation, SymbolKind, DocumentSymbol, Location, Hover, MarkedString, CodeActionContext, Command, CodeAction, DocumentHighlight, DocumentLink, WorkspaceEdit, TextEdit, CodeActionKind, TextDocumentEdit, VersionedTextDocumentIdentifier, DocumentHighlightKind } from 'vscode-languageserver-types';
import { TextDocument } from 'vscode-languageserver-textdocument';
export { TextDocument, Range, Position, MarkupContent, MarkupKind, Color, ColorInformation, ColorPresentation, FoldingRange, FoldingRangeKind, SelectionRange, Diagnostic, DiagnosticSeverity, CompletionItem, CompletionItemKind, CompletionList, CompletionItemTag, InsertTextFormat, SymbolInformation, SymbolKind, DocumentSymbol, Location, Hover, MarkedString, CodeActionContext, Command, CodeAction, DocumentHighlight, DocumentLink, WorkspaceEdit, TextEdit, CodeActionKind, TextDocumentEdit, VersionedTextDocumentIdentifier, DocumentHighlightKind };
/**
* Error codes used by diagnostics
*/
export var ErrorCode;
(function (ErrorCode) {
ErrorCode[ErrorCode["Undefined"] = 0] = "Undefined";
ErrorCode[ErrorCode["EnumValueMismatch"] = 1] = "EnumValueMismatch";
ErrorCode[ErrorCode["Deprecated"] = 2] = "Deprecated";
ErrorCode[ErrorCode["UnexpectedEndOfComment"] = 257] = "UnexpectedEndOfComment";
ErrorCode[ErrorCode["UnexpectedEndOfString"] = 258] = "UnexpectedEndOfString";
ErrorCode[ErrorCode["UnexpectedEndOfNumber"] = 259] = "UnexpectedEndOfNumber";
ErrorCode[ErrorCode["InvalidUnicode"] = 260] = "InvalidUnicode";
ErrorCode[ErrorCode["InvalidEscapeCharacter"] = 261] = "InvalidEscapeCharacter";
ErrorCode[ErrorCode["InvalidCharacter"] = 262] = "InvalidCharacter";
ErrorCode[ErrorCode["PropertyExpected"] = 513] = "PropertyExpected";
ErrorCode[ErrorCode["CommaExpected"] = 514] = "CommaExpected";
ErrorCode[ErrorCode["ColonExpected"] = 515] = "ColonExpected";
ErrorCode[ErrorCode["ValueExpected"] = 516] = "ValueExpected";
ErrorCode[ErrorCode["CommaOrCloseBacketExpected"] = 517] = "CommaOrCloseBacketExpected";
ErrorCode[ErrorCode["CommaOrCloseBraceExpected"] = 518] = "CommaOrCloseBraceExpected";
ErrorCode[ErrorCode["TrailingComma"] = 519] = "TrailingComma";
ErrorCode[ErrorCode["DuplicateKey"] = 520] = "DuplicateKey";
ErrorCode[ErrorCode["CommentNotPermitted"] = 521] = "CommentNotPermitted";
ErrorCode[ErrorCode["SchemaResolveError"] = 768] = "SchemaResolveError";
})(ErrorCode || (ErrorCode = {}));
export var ClientCapabilities;
(function (ClientCapabilities) {
ClientCapabilities.LATEST = {
textDocument: {
completion: {
completionItem: {
documentationFormat: [MarkupKind.Markdown, MarkupKind.PlainText],
commitCharactersSupport: true
}
}
}
};
})(ClientCapabilities || (ClientCapabilities = {}));

View File

@@ -0,0 +1,70 @@
export declare type JSONSchemaRef = JSONSchema | boolean;
export interface JSONSchema {
id?: string;
$id?: string;
$schema?: string;
type?: string | string[];
title?: string;
default?: any;
definitions?: {
[name: string]: JSONSchema;
};
description?: string;
properties?: JSONSchemaMap;
patternProperties?: JSONSchemaMap;
additionalProperties?: boolean | JSONSchemaRef;
minProperties?: number;
maxProperties?: number;
dependencies?: JSONSchemaMap | {
[prop: string]: string[];
};
items?: JSONSchemaRef | JSONSchemaRef[];
minItems?: number;
maxItems?: number;
uniqueItems?: boolean;
additionalItems?: boolean | JSONSchemaRef;
pattern?: string;
minLength?: number;
maxLength?: number;
minimum?: number;
maximum?: number;
exclusiveMinimum?: boolean | number;
exclusiveMaximum?: boolean | number;
multipleOf?: number;
required?: string[];
$ref?: string;
anyOf?: JSONSchemaRef[];
allOf?: JSONSchemaRef[];
oneOf?: JSONSchemaRef[];
not?: JSONSchemaRef;
enum?: any[];
format?: string;
const?: any;
contains?: JSONSchemaRef;
propertyNames?: JSONSchemaRef;
examples?: any[];
$comment?: string;
if?: JSONSchemaRef;
then?: JSONSchemaRef;
else?: JSONSchemaRef;
defaultSnippets?: {
label?: string;
description?: string;
markdownDescription?: string;
body?: any;
bodyText?: string;
}[];
errorMessage?: string;
patternErrorMessage?: string;
deprecationMessage?: string;
enumDescriptions?: string[];
markdownEnumDescriptions?: string[];
markdownDescription?: string;
doNotSuggest?: boolean;
suggestSortText?: string;
allowComments?: boolean;
allowTrailingCommas?: boolean;
}
export interface JSONSchemaMap {
[name: string]: JSONSchemaRef;
}

View File

@@ -0,0 +1 @@
export {};

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,528 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as nls from 'vscode-nls';
var localize = nls.loadMessageBundle();
export var schemaContributions = {
schemaAssociations: [],
schemas: {
// refer to the latest schema
'http://json-schema.org/schema#': {
$ref: 'http://json-schema.org/draft-07/schema#'
},
// bundle the schema-schema to include (localized) descriptions
'http://json-schema.org/draft-04/schema#': {
'$schema': 'http://json-schema.org/draft-04/schema#',
'definitions': {
'schemaArray': {
'type': 'array',
'minItems': 1,
'items': {
'$ref': '#'
}
},
'positiveInteger': {
'type': 'integer',
'minimum': 0
},
'positiveIntegerDefault0': {
'allOf': [
{
'$ref': '#/definitions/positiveInteger'
},
{
'default': 0
}
]
},
'simpleTypes': {
'type': 'string',
'enum': [
'array',
'boolean',
'integer',
'null',
'number',
'object',
'string'
]
},
'stringArray': {
'type': 'array',
'items': {
'type': 'string'
},
'minItems': 1,
'uniqueItems': true
}
},
'type': 'object',
'properties': {
'id': {
'type': 'string',
'format': 'uri'
},
'$schema': {
'type': 'string',
'format': 'uri'
},
'title': {
'type': 'string'
},
'description': {
'type': 'string'
},
'default': {},
'multipleOf': {
'type': 'number',
'minimum': 0,
'exclusiveMinimum': true
},
'maximum': {
'type': 'number'
},
'exclusiveMaximum': {
'type': 'boolean',
'default': false
},
'minimum': {
'type': 'number'
},
'exclusiveMinimum': {
'type': 'boolean',
'default': false
},
'maxLength': {
'allOf': [
{
'$ref': '#/definitions/positiveInteger'
}
]
},
'minLength': {
'allOf': [
{
'$ref': '#/definitions/positiveIntegerDefault0'
}
]
},
'pattern': {
'type': 'string',
'format': 'regex'
},
'additionalItems': {
'anyOf': [
{
'type': 'boolean'
},
{
'$ref': '#'
}
],
'default': {}
},
'items': {
'anyOf': [
{
'$ref': '#'
},
{
'$ref': '#/definitions/schemaArray'
}
],
'default': {}
},
'maxItems': {
'allOf': [
{
'$ref': '#/definitions/positiveInteger'
}
]
},
'minItems': {
'allOf': [
{
'$ref': '#/definitions/positiveIntegerDefault0'
}
]
},
'uniqueItems': {
'type': 'boolean',
'default': false
},
'maxProperties': {
'allOf': [
{
'$ref': '#/definitions/positiveInteger'
}
]
},
'minProperties': {
'allOf': [
{
'$ref': '#/definitions/positiveIntegerDefault0'
}
]
},
'required': {
'allOf': [
{
'$ref': '#/definitions/stringArray'
}
]
},
'additionalProperties': {
'anyOf': [
{
'type': 'boolean'
},
{
'$ref': '#'
}
],
'default': {}
},
'definitions': {
'type': 'object',
'additionalProperties': {
'$ref': '#'
},
'default': {}
},
'properties': {
'type': 'object',
'additionalProperties': {
'$ref': '#'
},
'default': {}
},
'patternProperties': {
'type': 'object',
'additionalProperties': {
'$ref': '#'
},
'default': {}
},
'dependencies': {
'type': 'object',
'additionalProperties': {
'anyOf': [
{
'$ref': '#'
},
{
'$ref': '#/definitions/stringArray'
}
]
}
},
'enum': {
'type': 'array',
'minItems': 1,
'uniqueItems': true
},
'type': {
'anyOf': [
{
'$ref': '#/definitions/simpleTypes'
},
{
'type': 'array',
'items': {
'$ref': '#/definitions/simpleTypes'
},
'minItems': 1,
'uniqueItems': true
}
]
},
'format': {
'anyOf': [
{
'type': 'string',
'enum': [
'date-time',
'uri',
'email',
'hostname',
'ipv4',
'ipv6',
'regex'
]
},
{
'type': 'string'
}
]
},
'allOf': {
'allOf': [
{
'$ref': '#/definitions/schemaArray'
}
]
},
'anyOf': {
'allOf': [
{
'$ref': '#/definitions/schemaArray'
}
]
},
'oneOf': {
'allOf': [
{
'$ref': '#/definitions/schemaArray'
}
]
},
'not': {
'allOf': [
{
'$ref': '#'
}
]
}
},
'dependencies': {
'exclusiveMaximum': [
'maximum'
],
'exclusiveMinimum': [
'minimum'
]
},
'default': {}
},
'http://json-schema.org/draft-07/schema#': {
'definitions': {
'schemaArray': {
'type': 'array',
'minItems': 1,
'items': { '$ref': '#' }
},
'nonNegativeInteger': {
'type': 'integer',
'minimum': 0
},
'nonNegativeIntegerDefault0': {
'allOf': [
{ '$ref': '#/definitions/nonNegativeInteger' },
{ 'default': 0 }
]
},
'simpleTypes': {
'enum': [
'array',
'boolean',
'integer',
'null',
'number',
'object',
'string'
]
},
'stringArray': {
'type': 'array',
'items': { 'type': 'string' },
'uniqueItems': true,
'default': []
}
},
'type': ['object', 'boolean'],
'properties': {
'$id': {
'type': 'string',
'format': 'uri-reference'
},
'$schema': {
'type': 'string',
'format': 'uri'
},
'$ref': {
'type': 'string',
'format': 'uri-reference'
},
'$comment': {
'type': 'string'
},
'title': {
'type': 'string'
},
'description': {
'type': 'string'
},
'default': true,
'readOnly': {
'type': 'boolean',
'default': false
},
'examples': {
'type': 'array',
'items': true
},
'multipleOf': {
'type': 'number',
'exclusiveMinimum': 0
},
'maximum': {
'type': 'number'
},
'exclusiveMaximum': {
'type': 'number'
},
'minimum': {
'type': 'number'
},
'exclusiveMinimum': {
'type': 'number'
},
'maxLength': { '$ref': '#/definitions/nonNegativeInteger' },
'minLength': { '$ref': '#/definitions/nonNegativeIntegerDefault0' },
'pattern': {
'type': 'string',
'format': 'regex'
},
'additionalItems': { '$ref': '#' },
'items': {
'anyOf': [
{ '$ref': '#' },
{ '$ref': '#/definitions/schemaArray' }
],
'default': true
},
'maxItems': { '$ref': '#/definitions/nonNegativeInteger' },
'minItems': { '$ref': '#/definitions/nonNegativeIntegerDefault0' },
'uniqueItems': {
'type': 'boolean',
'default': false
},
'contains': { '$ref': '#' },
'maxProperties': { '$ref': '#/definitions/nonNegativeInteger' },
'minProperties': { '$ref': '#/definitions/nonNegativeIntegerDefault0' },
'required': { '$ref': '#/definitions/stringArray' },
'additionalProperties': { '$ref': '#' },
'definitions': {
'type': 'object',
'additionalProperties': { '$ref': '#' },
'default': {}
},
'properties': {
'type': 'object',
'additionalProperties': { '$ref': '#' },
'default': {}
},
'patternProperties': {
'type': 'object',
'additionalProperties': { '$ref': '#' },
'propertyNames': { 'format': 'regex' },
'default': {}
},
'dependencies': {
'type': 'object',
'additionalProperties': {
'anyOf': [
{ '$ref': '#' },
{ '$ref': '#/definitions/stringArray' }
]
}
},
'propertyNames': { '$ref': '#' },
'const': true,
'enum': {
'type': 'array',
'items': true,
'minItems': 1,
'uniqueItems': true
},
'type': {
'anyOf': [
{ '$ref': '#/definitions/simpleTypes' },
{
'type': 'array',
'items': { '$ref': '#/definitions/simpleTypes' },
'minItems': 1,
'uniqueItems': true
}
]
},
'format': { 'type': 'string' },
'contentMediaType': { 'type': 'string' },
'contentEncoding': { 'type': 'string' },
'if': { '$ref': '#' },
'then': { '$ref': '#' },
'else': { '$ref': '#' },
'allOf': { '$ref': '#/definitions/schemaArray' },
'anyOf': { '$ref': '#/definitions/schemaArray' },
'oneOf': { '$ref': '#/definitions/schemaArray' },
'not': { '$ref': '#' }
},
'default': true
}
}
};
var descriptions = {
id: localize('schema.json.id', "A unique identifier for the schema."),
$schema: localize('schema.json.$schema', "The schema to verify this document against."),
title: localize('schema.json.title', "A descriptive title of the element."),
description: localize('schema.json.description', "A long description of the element. Used in hover menus and suggestions."),
default: localize('schema.json.default', "A default value. Used by suggestions."),
multipleOf: localize('schema.json.multipleOf', "A number that should cleanly divide the current value (i.e. have no remainder)."),
maximum: localize('schema.json.maximum', "The maximum numerical value, inclusive by default."),
exclusiveMaximum: localize('schema.json.exclusiveMaximum', "Makes the maximum property exclusive."),
minimum: localize('schema.json.minimum', "The minimum numerical value, inclusive by default."),
exclusiveMinimum: localize('schema.json.exclusiveMininum', "Makes the minimum property exclusive."),
maxLength: localize('schema.json.maxLength', "The maximum length of a string."),
minLength: localize('schema.json.minLength', "The minimum length of a string."),
pattern: localize('schema.json.pattern', "A regular expression to match the string against. It is not implicitly anchored."),
additionalItems: localize('schema.json.additionalItems', "For arrays, only when items is set as an array. If it is a schema, then this schema validates items after the ones specified by the items array. If it is false, then additional items will cause validation to fail."),
items: localize('schema.json.items', "For arrays. Can either be a schema to validate every element against or an array of schemas to validate each item against in order (the first schema will validate the first element, the second schema will validate the second element, and so on."),
maxItems: localize('schema.json.maxItems', "The maximum number of items that can be inside an array. Inclusive."),
minItems: localize('schema.json.minItems', "The minimum number of items that can be inside an array. Inclusive."),
uniqueItems: localize('schema.json.uniqueItems', "If all of the items in the array must be unique. Defaults to false."),
maxProperties: localize('schema.json.maxProperties', "The maximum number of properties an object can have. Inclusive."),
minProperties: localize('schema.json.minProperties', "The minimum number of properties an object can have. Inclusive."),
required: localize('schema.json.required', "An array of strings that lists the names of all properties required on this object."),
additionalProperties: localize('schema.json.additionalProperties', "Either a schema or a boolean. If a schema, then used to validate all properties not matched by 'properties' or 'patternProperties'. If false, then any properties not matched by either will cause this schema to fail."),
definitions: localize('schema.json.definitions', "Not used for validation. Place subschemas here that you wish to reference inline with $ref."),
properties: localize('schema.json.properties', "A map of property names to schemas for each property."),
patternProperties: localize('schema.json.patternProperties', "A map of regular expressions on property names to schemas for matching properties."),
dependencies: localize('schema.json.dependencies', "A map of property names to either an array of property names or a schema. An array of property names means the property named in the key depends on the properties in the array being present in the object in order to be valid. If the value is a schema, then the schema is only applied to the object if the property in the key exists on the object."),
enum: localize('schema.json.enum', "The set of literal values that are valid."),
type: localize('schema.json.type', "Either a string of one of the basic schema types (number, integer, null, array, object, boolean, string) or an array of strings specifying a subset of those types."),
format: localize('schema.json.format', "Describes the format expected for the value."),
allOf: localize('schema.json.allOf', "An array of schemas, all of which must match."),
anyOf: localize('schema.json.anyOf', "An array of schemas, where at least one must match."),
oneOf: localize('schema.json.oneOf', "An array of schemas, exactly one of which must match."),
not: localize('schema.json.not', "A schema which must not match."),
$id: localize('schema.json.$id', "A unique identifier for the schema."),
$ref: localize('schema.json.$ref', "Reference a definition hosted on any location."),
$comment: localize('schema.json.$comment', "Comments from schema authors to readers or maintainers of the schema."),
readOnly: localize('schema.json.readOnly', "Indicates that the value of the instance is managed exclusively by the owning authority."),
examples: localize('schema.json.examples', "Sample JSON values associated with a particular schema, for the purpose of illustrating usage."),
contains: localize('schema.json.contains', "An array instance is valid against \"contains\" if at least one of its elements is valid against the given schema."),
propertyNames: localize('schema.json.propertyNames', "If the instance is an object, this keyword validates if every property name in the instance validates against the provided schema."),
const: localize('schema.json.const', "An instance validates successfully against this keyword if its value is equal to the value of the keyword."),
contentMediaType: localize('schema.json.contentMediaType', "Describes the media type of a string property."),
contentEncoding: localize('schema.json.contentEncoding', "Describes the content encoding of a string property."),
if: localize('schema.json.if', "The validation outcome of the \"if\" subschema controls which of the \"then\" or \"else\" keywords are evaluated."),
then: localize('schema.json.then', "The \"if\" subschema is used for validation when the \"if\" subschema succeeds."),
else: localize('schema.json.else', "The \"else\" subschema is used for validation when the \"if\" subschema fails.")
};
for (var schemaName in schemaContributions.schemas) {
var schema = schemaContributions.schemas[schemaName];
for (var property in schema.properties) {
var propertyObject = schema.properties[property];
if (typeof propertyObject === 'boolean') {
propertyObject = schema.properties[property] = {};
}
var description = descriptions[property];
if (description) {
propertyObject['description'] = description;
}
else {
console.log(property + ": localize('schema.json." + property + "', \"\")");
}
}
}

View File

@@ -0,0 +1,934 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as Parser from '../parser/jsonParser';
import * as Json from 'jsonc-parser';
import { stringifyObject } from '../utils/json';
import { endsWith, extendedRegExp } from '../utils/strings';
import { isDefined } from '../utils/objects';
import { CompletionItem, CompletionItemKind, Range, TextEdit, InsertTextFormat, MarkupKind } from '../jsonLanguageTypes';
import * as nls from 'vscode-nls';
var localize = nls.loadMessageBundle();
var valueCommitCharacters = [',', '}', ']'];
var propertyCommitCharacters = [':'];
var JSONCompletion = /** @class */ (function () {
function JSONCompletion(schemaService, contributions, promiseConstructor, clientCapabilities) {
if (contributions === void 0) { contributions = []; }
if (promiseConstructor === void 0) { promiseConstructor = Promise; }
if (clientCapabilities === void 0) { clientCapabilities = {}; }
this.schemaService = schemaService;
this.contributions = contributions;
this.promiseConstructor = promiseConstructor;
this.clientCapabilities = clientCapabilities;
}
JSONCompletion.prototype.doResolve = function (item) {
for (var i = this.contributions.length - 1; i >= 0; i--) {
var resolveCompletion = this.contributions[i].resolveCompletion;
if (resolveCompletion) {
var resolver = resolveCompletion(item);
if (resolver) {
return resolver;
}
}
}
return this.promiseConstructor.resolve(item);
};
JSONCompletion.prototype.doComplete = function (document, position, doc) {
var _this = this;
var result = {
items: [],
isIncomplete: false
};
var text = document.getText();
var offset = document.offsetAt(position);
var node = doc.getNodeFromOffset(offset, true);
if (this.isInComment(document, node ? node.offset : 0, offset)) {
return Promise.resolve(result);
}
if (node && (offset === node.offset + node.length) && offset > 0) {
var ch = text[offset - 1];
if (node.type === 'object' && ch === '}' || node.type === 'array' && ch === ']') {
// after ] or }
node = node.parent;
}
}
var currentWord = this.getCurrentWord(document, offset);
var overwriteRange;
if (node && (node.type === 'string' || node.type === 'number' || node.type === 'boolean' || node.type === 'null')) {
overwriteRange = Range.create(document.positionAt(node.offset), document.positionAt(node.offset + node.length));
}
else {
var overwriteStart = offset - currentWord.length;
if (overwriteStart > 0 && text[overwriteStart - 1] === '"') {
overwriteStart--;
}
overwriteRange = Range.create(document.positionAt(overwriteStart), position);
}
var supportsCommitCharacters = false; //this.doesSupportsCommitCharacters(); disabled for now, waiting for new API: https://github.com/microsoft/vscode/issues/42544
var proposed = {};
var collector = {
add: function (suggestion) {
var label = suggestion.label;
var existing = proposed[label];
if (!existing) {
label = label.replace(/[\n]/g, '↵');
if (label.length > 60) {
var shortendedLabel = label.substr(0, 57).trim() + '...';
if (!proposed[shortendedLabel]) {
label = shortendedLabel;
}
}
if (overwriteRange && suggestion.insertText !== undefined) {
suggestion.textEdit = TextEdit.replace(overwriteRange, suggestion.insertText);
}
if (supportsCommitCharacters) {
suggestion.commitCharacters = suggestion.kind === CompletionItemKind.Property ? propertyCommitCharacters : valueCommitCharacters;
}
suggestion.label = label;
proposed[label] = suggestion;
result.items.push(suggestion);
}
else {
if (!existing.documentation) {
existing.documentation = suggestion.documentation;
}
if (!existing.detail) {
existing.detail = suggestion.detail;
}
}
},
setAsIncomplete: function () {
result.isIncomplete = true;
},
error: function (message) {
console.error(message);
},
log: function (message) {
console.log(message);
},
getNumberOfProposals: function () {
return result.items.length;
}
};
return this.schemaService.getSchemaForResource(document.uri, doc).then(function (schema) {
var collectionPromises = [];
var addValue = true;
var currentKey = '';
var currentProperty = undefined;
if (node) {
if (node.type === 'string') {
var parent = node.parent;
if (parent && parent.type === 'property' && parent.keyNode === node) {
addValue = !parent.valueNode;
currentProperty = parent;
currentKey = text.substr(node.offset + 1, node.length - 2);
if (parent) {
node = parent.parent;
}
}
}
}
// proposals for properties
if (node && node.type === 'object') {
// don't suggest keys when the cursor is just before the opening curly brace
if (node.offset === offset) {
return result;
}
// don't suggest properties that are already present
var properties = node.properties;
properties.forEach(function (p) {
if (!currentProperty || currentProperty !== p) {
proposed[p.keyNode.value] = CompletionItem.create('__');
}
});
var separatorAfter_1 = '';
if (addValue) {
separatorAfter_1 = _this.evaluateSeparatorAfter(document, document.offsetAt(overwriteRange.end));
}
if (schema) {
// property proposals with schema
_this.getPropertyCompletions(schema, doc, node, addValue, separatorAfter_1, collector);
}
else {
// property proposals without schema
_this.getSchemaLessPropertyCompletions(doc, node, currentKey, collector);
}
var location_1 = Parser.getNodePath(node);
_this.contributions.forEach(function (contribution) {
var collectPromise = contribution.collectPropertyCompletions(document.uri, location_1, currentWord, addValue, separatorAfter_1 === '', collector);
if (collectPromise) {
collectionPromises.push(collectPromise);
}
});
if ((!schema && currentWord.length > 0 && text.charAt(offset - currentWord.length - 1) !== '"')) {
collector.add({
kind: CompletionItemKind.Property,
label: _this.getLabelForValue(currentWord),
insertText: _this.getInsertTextForProperty(currentWord, undefined, false, separatorAfter_1),
insertTextFormat: InsertTextFormat.Snippet, documentation: '',
});
collector.setAsIncomplete();
}
}
// proposals for values
var types = {};
if (schema) {
// value proposals with schema
_this.getValueCompletions(schema, doc, node, offset, document, collector, types);
}
else {
// value proposals without schema
_this.getSchemaLessValueCompletions(doc, node, offset, document, collector);
}
if (_this.contributions.length > 0) {
_this.getContributedValueCompletions(doc, node, offset, document, collector, collectionPromises);
}
return _this.promiseConstructor.all(collectionPromises).then(function () {
if (collector.getNumberOfProposals() === 0) {
var offsetForSeparator = offset;
if (node && (node.type === 'string' || node.type === 'number' || node.type === 'boolean' || node.type === 'null')) {
offsetForSeparator = node.offset + node.length;
}
var separatorAfter = _this.evaluateSeparatorAfter(document, offsetForSeparator);
_this.addFillerValueCompletions(types, separatorAfter, collector);
}
return result;
});
});
};
JSONCompletion.prototype.getPropertyCompletions = function (schema, doc, node, addValue, separatorAfter, collector) {
var _this = this;
var matchingSchemas = doc.getMatchingSchemas(schema.schema, node.offset);
matchingSchemas.forEach(function (s) {
if (s.node === node && !s.inverted) {
var schemaProperties_1 = s.schema.properties;
if (schemaProperties_1) {
Object.keys(schemaProperties_1).forEach(function (key) {
var propertySchema = schemaProperties_1[key];
if (typeof propertySchema === 'object' && !propertySchema.deprecationMessage && !propertySchema.doNotSuggest) {
var proposal = {
kind: CompletionItemKind.Property,
label: key,
insertText: _this.getInsertTextForProperty(key, propertySchema, addValue, separatorAfter),
insertTextFormat: InsertTextFormat.Snippet,
filterText: _this.getFilterTextForValue(key),
documentation: _this.fromMarkup(propertySchema.markdownDescription) || propertySchema.description || '',
};
if (propertySchema.suggestSortText !== undefined) {
proposal.sortText = propertySchema.suggestSortText;
}
if (proposal.insertText && endsWith(proposal.insertText, "$1" + separatorAfter)) {
proposal.command = {
title: 'Suggest',
command: 'editor.action.triggerSuggest'
};
}
collector.add(proposal);
}
});
}
var schemaPropertyNames_1 = s.schema.propertyNames;
if (typeof schemaPropertyNames_1 === 'object' && !schemaPropertyNames_1.deprecationMessage && !schemaPropertyNames_1.doNotSuggest) {
var propertyNameCompletionItem = function (name, enumDescription) {
if (enumDescription === void 0) { enumDescription = undefined; }
var proposal = {
kind: CompletionItemKind.Property,
label: name,
insertText: _this.getInsertTextForProperty(name, undefined, addValue, separatorAfter),
insertTextFormat: InsertTextFormat.Snippet,
filterText: _this.getFilterTextForValue(name),
documentation: enumDescription || _this.fromMarkup(schemaPropertyNames_1.markdownDescription) || schemaPropertyNames_1.description || '',
};
if (schemaPropertyNames_1.suggestSortText !== undefined) {
proposal.sortText = schemaPropertyNames_1.suggestSortText;
}
if (proposal.insertText && endsWith(proposal.insertText, "$1" + separatorAfter)) {
proposal.command = {
title: 'Suggest',
command: 'editor.action.triggerSuggest'
};
}
collector.add(proposal);
};
if (schemaPropertyNames_1.enum) {
for (var i = 0; i < schemaPropertyNames_1.enum.length; i++) {
var enumDescription = undefined;
if (schemaPropertyNames_1.markdownEnumDescriptions && i < schemaPropertyNames_1.markdownEnumDescriptions.length) {
enumDescription = _this.fromMarkup(schemaPropertyNames_1.markdownEnumDescriptions[i]);
}
else if (schemaPropertyNames_1.enumDescriptions && i < schemaPropertyNames_1.enumDescriptions.length) {
enumDescription = schemaPropertyNames_1.enumDescriptions[i];
}
propertyNameCompletionItem(schemaPropertyNames_1.enum[i], enumDescription);
}
}
if (schemaPropertyNames_1.const) {
propertyNameCompletionItem(schemaPropertyNames_1.const);
}
}
}
});
};
JSONCompletion.prototype.getSchemaLessPropertyCompletions = function (doc, node, currentKey, collector) {
var _this = this;
var collectCompletionsForSimilarObject = function (obj) {
obj.properties.forEach(function (p) {
var key = p.keyNode.value;
collector.add({
kind: CompletionItemKind.Property,
label: key,
insertText: _this.getInsertTextForValue(key, ''),
insertTextFormat: InsertTextFormat.Snippet,
filterText: _this.getFilterTextForValue(key),
documentation: ''
});
});
};
if (node.parent) {
if (node.parent.type === 'property') {
// if the object is a property value, check the tree for other objects that hang under a property of the same name
var parentKey_1 = node.parent.keyNode.value;
doc.visit(function (n) {
if (n.type === 'property' && n !== node.parent && n.keyNode.value === parentKey_1 && n.valueNode && n.valueNode.type === 'object') {
collectCompletionsForSimilarObject(n.valueNode);
}
return true;
});
}
else if (node.parent.type === 'array') {
// if the object is in an array, use all other array elements as similar objects
node.parent.items.forEach(function (n) {
if (n.type === 'object' && n !== node) {
collectCompletionsForSimilarObject(n);
}
});
}
}
else if (node.type === 'object') {
collector.add({
kind: CompletionItemKind.Property,
label: '$schema',
insertText: this.getInsertTextForProperty('$schema', undefined, true, ''),
insertTextFormat: InsertTextFormat.Snippet, documentation: '',
filterText: this.getFilterTextForValue("$schema")
});
}
};
JSONCompletion.prototype.getSchemaLessValueCompletions = function (doc, node, offset, document, collector) {
var _this = this;
var offsetForSeparator = offset;
if (node && (node.type === 'string' || node.type === 'number' || node.type === 'boolean' || node.type === 'null')) {
offsetForSeparator = node.offset + node.length;
node = node.parent;
}
if (!node) {
collector.add({
kind: this.getSuggestionKind('object'),
label: 'Empty object',
insertText: this.getInsertTextForValue({}, ''),
insertTextFormat: InsertTextFormat.Snippet,
documentation: ''
});
collector.add({
kind: this.getSuggestionKind('array'),
label: 'Empty array',
insertText: this.getInsertTextForValue([], ''),
insertTextFormat: InsertTextFormat.Snippet,
documentation: ''
});
return;
}
var separatorAfter = this.evaluateSeparatorAfter(document, offsetForSeparator);
var collectSuggestionsForValues = function (value) {
if (value.parent && !Parser.contains(value.parent, offset, true)) {
collector.add({
kind: _this.getSuggestionKind(value.type),
label: _this.getLabelTextForMatchingNode(value, document),
insertText: _this.getInsertTextForMatchingNode(value, document, separatorAfter),
insertTextFormat: InsertTextFormat.Snippet, documentation: ''
});
}
if (value.type === 'boolean') {
_this.addBooleanValueCompletion(!value.value, separatorAfter, collector);
}
};
if (node.type === 'property') {
if (offset > (node.colonOffset || 0)) {
var valueNode = node.valueNode;
if (valueNode && (offset > (valueNode.offset + valueNode.length) || valueNode.type === 'object' || valueNode.type === 'array')) {
return;
}
// suggest values at the same key
var parentKey_2 = node.keyNode.value;
doc.visit(function (n) {
if (n.type === 'property' && n.keyNode.value === parentKey_2 && n.valueNode) {
collectSuggestionsForValues(n.valueNode);
}
return true;
});
if (parentKey_2 === '$schema' && node.parent && !node.parent.parent) {
this.addDollarSchemaCompletions(separatorAfter, collector);
}
}
}
if (node.type === 'array') {
if (node.parent && node.parent.type === 'property') {
// suggest items of an array at the same key
var parentKey_3 = node.parent.keyNode.value;
doc.visit(function (n) {
if (n.type === 'property' && n.keyNode.value === parentKey_3 && n.valueNode && n.valueNode.type === 'array') {
n.valueNode.items.forEach(collectSuggestionsForValues);
}
return true;
});
}
else {
// suggest items in the same array
node.items.forEach(collectSuggestionsForValues);
}
}
};
JSONCompletion.prototype.getValueCompletions = function (schema, doc, node, offset, document, collector, types) {
var offsetForSeparator = offset;
var parentKey = undefined;
var valueNode = undefined;
if (node && (node.type === 'string' || node.type === 'number' || node.type === 'boolean' || node.type === 'null')) {
offsetForSeparator = node.offset + node.length;
valueNode = node;
node = node.parent;
}
if (!node) {
this.addSchemaValueCompletions(schema.schema, '', collector, types);
return;
}
if ((node.type === 'property') && offset > (node.colonOffset || 0)) {
var valueNode_1 = node.valueNode;
if (valueNode_1 && offset > (valueNode_1.offset + valueNode_1.length)) {
return; // we are past the value node
}
parentKey = node.keyNode.value;
node = node.parent;
}
if (node && (parentKey !== undefined || node.type === 'array')) {
var separatorAfter = this.evaluateSeparatorAfter(document, offsetForSeparator);
var matchingSchemas = doc.getMatchingSchemas(schema.schema, node.offset, valueNode);
for (var _i = 0, matchingSchemas_1 = matchingSchemas; _i < matchingSchemas_1.length; _i++) {
var s = matchingSchemas_1[_i];
if (s.node === node && !s.inverted && s.schema) {
if (node.type === 'array' && s.schema.items) {
if (Array.isArray(s.schema.items)) {
var index = this.findItemAtOffset(node, document, offset);
if (index < s.schema.items.length) {
this.addSchemaValueCompletions(s.schema.items[index], separatorAfter, collector, types);
}
}
else {
this.addSchemaValueCompletions(s.schema.items, separatorAfter, collector, types);
}
}
if (parentKey !== undefined) {
var propertyMatched = false;
if (s.schema.properties) {
var propertySchema = s.schema.properties[parentKey];
if (propertySchema) {
propertyMatched = true;
this.addSchemaValueCompletions(propertySchema, separatorAfter, collector, types);
}
}
if (s.schema.patternProperties && !propertyMatched) {
for (var _a = 0, _b = Object.keys(s.schema.patternProperties); _a < _b.length; _a++) {
var pattern = _b[_a];
var regex = extendedRegExp(pattern);
if (regex.test(parentKey)) {
propertyMatched = true;
var propertySchema = s.schema.patternProperties[pattern];
this.addSchemaValueCompletions(propertySchema, separatorAfter, collector, types);
}
}
}
if (s.schema.additionalProperties && !propertyMatched) {
var propertySchema = s.schema.additionalProperties;
this.addSchemaValueCompletions(propertySchema, separatorAfter, collector, types);
}
}
}
}
if (parentKey === '$schema' && !node.parent) {
this.addDollarSchemaCompletions(separatorAfter, collector);
}
if (types['boolean']) {
this.addBooleanValueCompletion(true, separatorAfter, collector);
this.addBooleanValueCompletion(false, separatorAfter, collector);
}
if (types['null']) {
this.addNullValueCompletion(separatorAfter, collector);
}
}
};
JSONCompletion.prototype.getContributedValueCompletions = function (doc, node, offset, document, collector, collectionPromises) {
if (!node) {
this.contributions.forEach(function (contribution) {
var collectPromise = contribution.collectDefaultCompletions(document.uri, collector);
if (collectPromise) {
collectionPromises.push(collectPromise);
}
});
}
else {
if (node.type === 'string' || node.type === 'number' || node.type === 'boolean' || node.type === 'null') {
node = node.parent;
}
if (node && (node.type === 'property') && offset > (node.colonOffset || 0)) {
var parentKey_4 = node.keyNode.value;
var valueNode = node.valueNode;
if ((!valueNode || offset <= (valueNode.offset + valueNode.length)) && node.parent) {
var location_2 = Parser.getNodePath(node.parent);
this.contributions.forEach(function (contribution) {
var collectPromise = contribution.collectValueCompletions(document.uri, location_2, parentKey_4, collector);
if (collectPromise) {
collectionPromises.push(collectPromise);
}
});
}
}
}
};
JSONCompletion.prototype.addSchemaValueCompletions = function (schema, separatorAfter, collector, types) {
var _this = this;
if (typeof schema === 'object') {
this.addEnumValueCompletions(schema, separatorAfter, collector);
this.addDefaultValueCompletions(schema, separatorAfter, collector);
this.collectTypes(schema, types);
if (Array.isArray(schema.allOf)) {
schema.allOf.forEach(function (s) { return _this.addSchemaValueCompletions(s, separatorAfter, collector, types); });
}
if (Array.isArray(schema.anyOf)) {
schema.anyOf.forEach(function (s) { return _this.addSchemaValueCompletions(s, separatorAfter, collector, types); });
}
if (Array.isArray(schema.oneOf)) {
schema.oneOf.forEach(function (s) { return _this.addSchemaValueCompletions(s, separatorAfter, collector, types); });
}
}
};
JSONCompletion.prototype.addDefaultValueCompletions = function (schema, separatorAfter, collector, arrayDepth) {
var _this = this;
if (arrayDepth === void 0) { arrayDepth = 0; }
var hasProposals = false;
if (isDefined(schema.default)) {
var type = schema.type;
var value = schema.default;
for (var i = arrayDepth; i > 0; i--) {
value = [value];
type = 'array';
}
collector.add({
kind: this.getSuggestionKind(type),
label: this.getLabelForValue(value),
insertText: this.getInsertTextForValue(value, separatorAfter),
insertTextFormat: InsertTextFormat.Snippet,
detail: localize('json.suggest.default', 'Default value')
});
hasProposals = true;
}
if (Array.isArray(schema.examples)) {
schema.examples.forEach(function (example) {
var type = schema.type;
var value = example;
for (var i = arrayDepth; i > 0; i--) {
value = [value];
type = 'array';
}
collector.add({
kind: _this.getSuggestionKind(type),
label: _this.getLabelForValue(value),
insertText: _this.getInsertTextForValue(value, separatorAfter),
insertTextFormat: InsertTextFormat.Snippet
});
hasProposals = true;
});
}
if (Array.isArray(schema.defaultSnippets)) {
schema.defaultSnippets.forEach(function (s) {
var type = schema.type;
var value = s.body;
var label = s.label;
var insertText;
var filterText;
if (isDefined(value)) {
var type_1 = schema.type;
for (var i = arrayDepth; i > 0; i--) {
value = [value];
type_1 = 'array';
}
insertText = _this.getInsertTextForSnippetValue(value, separatorAfter);
filterText = _this.getFilterTextForSnippetValue(value);
label = label || _this.getLabelForSnippetValue(value);
}
else if (typeof s.bodyText === 'string') {
var prefix = '', suffix = '', indent = '';
for (var i = arrayDepth; i > 0; i--) {
prefix = prefix + indent + '[\n';
suffix = suffix + '\n' + indent + ']';
indent += '\t';
type = 'array';
}
insertText = prefix + indent + s.bodyText.split('\n').join('\n' + indent) + suffix + separatorAfter;
label = label || insertText,
filterText = insertText.replace(/[\n]/g, ''); // remove new lines
}
else {
return;
}
collector.add({
kind: _this.getSuggestionKind(type),
label: label,
documentation: _this.fromMarkup(s.markdownDescription) || s.description,
insertText: insertText,
insertTextFormat: InsertTextFormat.Snippet,
filterText: filterText
});
hasProposals = true;
});
}
if (!hasProposals && typeof schema.items === 'object' && !Array.isArray(schema.items) && arrayDepth < 5 /* beware of recursion */) {
this.addDefaultValueCompletions(schema.items, separatorAfter, collector, arrayDepth + 1);
}
};
JSONCompletion.prototype.addEnumValueCompletions = function (schema, separatorAfter, collector) {
if (isDefined(schema.const)) {
collector.add({
kind: this.getSuggestionKind(schema.type),
label: this.getLabelForValue(schema.const),
insertText: this.getInsertTextForValue(schema.const, separatorAfter),
insertTextFormat: InsertTextFormat.Snippet,
documentation: this.fromMarkup(schema.markdownDescription) || schema.description
});
}
if (Array.isArray(schema.enum)) {
for (var i = 0, length = schema.enum.length; i < length; i++) {
var enm = schema.enum[i];
var documentation = this.fromMarkup(schema.markdownDescription) || schema.description;
if (schema.markdownEnumDescriptions && i < schema.markdownEnumDescriptions.length && this.doesSupportMarkdown()) {
documentation = this.fromMarkup(schema.markdownEnumDescriptions[i]);
}
else if (schema.enumDescriptions && i < schema.enumDescriptions.length) {
documentation = schema.enumDescriptions[i];
}
collector.add({
kind: this.getSuggestionKind(schema.type),
label: this.getLabelForValue(enm),
insertText: this.getInsertTextForValue(enm, separatorAfter),
insertTextFormat: InsertTextFormat.Snippet,
documentation: documentation
});
}
}
};
JSONCompletion.prototype.collectTypes = function (schema, types) {
if (Array.isArray(schema.enum) || isDefined(schema.const)) {
return;
}
var type = schema.type;
if (Array.isArray(type)) {
type.forEach(function (t) { return types[t] = true; });
}
else if (type) {
types[type] = true;
}
};
JSONCompletion.prototype.addFillerValueCompletions = function (types, separatorAfter, collector) {
if (types['object']) {
collector.add({
kind: this.getSuggestionKind('object'),
label: '{}',
insertText: this.getInsertTextForGuessedValue({}, separatorAfter),
insertTextFormat: InsertTextFormat.Snippet,
detail: localize('defaults.object', 'New object'),
documentation: ''
});
}
if (types['array']) {
collector.add({
kind: this.getSuggestionKind('array'),
label: '[]',
insertText: this.getInsertTextForGuessedValue([], separatorAfter),
insertTextFormat: InsertTextFormat.Snippet,
detail: localize('defaults.array', 'New array'),
documentation: ''
});
}
};
JSONCompletion.prototype.addBooleanValueCompletion = function (value, separatorAfter, collector) {
collector.add({
kind: this.getSuggestionKind('boolean'),
label: value ? 'true' : 'false',
insertText: this.getInsertTextForValue(value, separatorAfter),
insertTextFormat: InsertTextFormat.Snippet,
documentation: ''
});
};
JSONCompletion.prototype.addNullValueCompletion = function (separatorAfter, collector) {
collector.add({
kind: this.getSuggestionKind('null'),
label: 'null',
insertText: 'null' + separatorAfter,
insertTextFormat: InsertTextFormat.Snippet,
documentation: ''
});
};
JSONCompletion.prototype.addDollarSchemaCompletions = function (separatorAfter, collector) {
var _this = this;
var schemaIds = this.schemaService.getRegisteredSchemaIds(function (schema) { return schema === 'http' || schema === 'https'; });
schemaIds.forEach(function (schemaId) { return collector.add({
kind: CompletionItemKind.Module,
label: _this.getLabelForValue(schemaId),
filterText: _this.getFilterTextForValue(schemaId),
insertText: _this.getInsertTextForValue(schemaId, separatorAfter),
insertTextFormat: InsertTextFormat.Snippet, documentation: ''
}); });
};
JSONCompletion.prototype.getLabelForValue = function (value) {
return JSON.stringify(value);
};
JSONCompletion.prototype.getFilterTextForValue = function (value) {
return JSON.stringify(value);
};
JSONCompletion.prototype.getFilterTextForSnippetValue = function (value) {
return JSON.stringify(value).replace(/\$\{\d+:([^}]+)\}|\$\d+/g, '$1');
};
JSONCompletion.prototype.getLabelForSnippetValue = function (value) {
var label = JSON.stringify(value);
return label.replace(/\$\{\d+:([^}]+)\}|\$\d+/g, '$1');
};
JSONCompletion.prototype.getInsertTextForPlainText = function (text) {
return text.replace(/[\\\$\}]/g, '\\$&'); // escape $, \ and }
};
JSONCompletion.prototype.getInsertTextForValue = function (value, separatorAfter) {
var text = JSON.stringify(value, null, '\t');
if (text === '{}') {
return '{$1}' + separatorAfter;
}
else if (text === '[]') {
return '[$1]' + separatorAfter;
}
return this.getInsertTextForPlainText(text + separatorAfter);
};
JSONCompletion.prototype.getInsertTextForSnippetValue = function (value, separatorAfter) {
var replacer = function (value) {
if (typeof value === 'string') {
if (value[0] === '^') {
return value.substr(1);
}
}
return JSON.stringify(value);
};
return stringifyObject(value, '', replacer) + separatorAfter;
};
JSONCompletion.prototype.getInsertTextForGuessedValue = function (value, separatorAfter) {
switch (typeof value) {
case 'object':
if (value === null) {
return '${1:null}' + separatorAfter;
}
return this.getInsertTextForValue(value, separatorAfter);
case 'string':
var snippetValue = JSON.stringify(value);
snippetValue = snippetValue.substr(1, snippetValue.length - 2); // remove quotes
snippetValue = this.getInsertTextForPlainText(snippetValue); // escape \ and }
return '"${1:' + snippetValue + '}"' + separatorAfter;
case 'number':
case 'boolean':
return '${1:' + JSON.stringify(value) + '}' + separatorAfter;
}
return this.getInsertTextForValue(value, separatorAfter);
};
JSONCompletion.prototype.getSuggestionKind = function (type) {
if (Array.isArray(type)) {
var array = type;
type = array.length > 0 ? array[0] : undefined;
}
if (!type) {
return CompletionItemKind.Value;
}
switch (type) {
case 'string': return CompletionItemKind.Value;
case 'object': return CompletionItemKind.Module;
case 'property': return CompletionItemKind.Property;
default: return CompletionItemKind.Value;
}
};
JSONCompletion.prototype.getLabelTextForMatchingNode = function (node, document) {
switch (node.type) {
case 'array':
return '[]';
case 'object':
return '{}';
default:
var content = document.getText().substr(node.offset, node.length);
return content;
}
};
JSONCompletion.prototype.getInsertTextForMatchingNode = function (node, document, separatorAfter) {
switch (node.type) {
case 'array':
return this.getInsertTextForValue([], separatorAfter);
case 'object':
return this.getInsertTextForValue({}, separatorAfter);
default:
var content = document.getText().substr(node.offset, node.length) + separatorAfter;
return this.getInsertTextForPlainText(content);
}
};
JSONCompletion.prototype.getInsertTextForProperty = function (key, propertySchema, addValue, separatorAfter) {
var propertyText = this.getInsertTextForValue(key, '');
if (!addValue) {
return propertyText;
}
var resultText = propertyText + ': ';
var value;
var nValueProposals = 0;
if (propertySchema) {
if (Array.isArray(propertySchema.defaultSnippets)) {
if (propertySchema.defaultSnippets.length === 1) {
var body = propertySchema.defaultSnippets[0].body;
if (isDefined(body)) {
value = this.getInsertTextForSnippetValue(body, '');
}
}
nValueProposals += propertySchema.defaultSnippets.length;
}
if (propertySchema.enum) {
if (!value && propertySchema.enum.length === 1) {
value = this.getInsertTextForGuessedValue(propertySchema.enum[0], '');
}
nValueProposals += propertySchema.enum.length;
}
if (isDefined(propertySchema.default)) {
if (!value) {
value = this.getInsertTextForGuessedValue(propertySchema.default, '');
}
nValueProposals++;
}
if (Array.isArray(propertySchema.examples) && propertySchema.examples.length) {
if (!value) {
value = this.getInsertTextForGuessedValue(propertySchema.examples[0], '');
}
nValueProposals += propertySchema.examples.length;
}
if (nValueProposals === 0) {
var type = Array.isArray(propertySchema.type) ? propertySchema.type[0] : propertySchema.type;
if (!type) {
if (propertySchema.properties) {
type = 'object';
}
else if (propertySchema.items) {
type = 'array';
}
}
switch (type) {
case 'boolean':
value = '$1';
break;
case 'string':
value = '"$1"';
break;
case 'object':
value = '{$1}';
break;
case 'array':
value = '[$1]';
break;
case 'number':
case 'integer':
value = '${1:0}';
break;
case 'null':
value = '${1:null}';
break;
default:
return propertyText;
}
}
}
if (!value || nValueProposals > 1) {
value = '$1';
}
return resultText + value + separatorAfter;
};
JSONCompletion.prototype.getCurrentWord = function (document, offset) {
var i = offset - 1;
var text = document.getText();
while (i >= 0 && ' \t\n\r\v":{[,]}'.indexOf(text.charAt(i)) === -1) {
i--;
}
return text.substring(i + 1, offset);
};
JSONCompletion.prototype.evaluateSeparatorAfter = function (document, offset) {
var scanner = Json.createScanner(document.getText(), true);
scanner.setPosition(offset);
var token = scanner.scan();
switch (token) {
case 5 /* CommaToken */:
case 2 /* CloseBraceToken */:
case 4 /* CloseBracketToken */:
case 17 /* EOF */:
return '';
default:
return ',';
}
};
JSONCompletion.prototype.findItemAtOffset = function (node, document, offset) {
var scanner = Json.createScanner(document.getText(), true);
var children = node.items;
for (var i = children.length - 1; i >= 0; i--) {
var child = children[i];
if (offset > child.offset + child.length) {
scanner.setPosition(child.offset + child.length);
var token = scanner.scan();
if (token === 5 /* CommaToken */ && offset >= scanner.getTokenOffset() + scanner.getTokenLength()) {
return i + 1;
}
return i;
}
else if (offset >= child.offset) {
return i;
}
}
return 0;
};
JSONCompletion.prototype.isInComment = function (document, start, offset) {
var scanner = Json.createScanner(document.getText(), false);
scanner.setPosition(start);
var token = scanner.scan();
while (token !== 17 /* EOF */ && (scanner.getTokenOffset() + scanner.getTokenLength() < offset)) {
token = scanner.scan();
}
return (token === 12 /* LineCommentTrivia */ || token === 13 /* BlockCommentTrivia */) && scanner.getTokenOffset() <= offset;
};
JSONCompletion.prototype.fromMarkup = function (markupString) {
if (markupString && this.doesSupportMarkdown()) {
return {
kind: MarkupKind.Markdown,
value: markupString
};
}
return undefined;
};
JSONCompletion.prototype.doesSupportMarkdown = function () {
if (!isDefined(this.supportsMarkdown)) {
var completion = this.clientCapabilities.textDocument && this.clientCapabilities.textDocument.completion;
this.supportsMarkdown = completion && completion.completionItem && Array.isArray(completion.completionItem.documentationFormat) && completion.completionItem.documentationFormat.indexOf(MarkupKind.Markdown) !== -1;
}
return this.supportsMarkdown;
};
JSONCompletion.prototype.doesSupportsCommitCharacters = function () {
if (!isDefined(this.supportsCommitCharacters)) {
var completion = this.clientCapabilities.textDocument && this.clientCapabilities.textDocument.completion;
this.supportsCommitCharacters = completion && completion.completionItem && !!completion.completionItem.commitCharactersSupport;
}
return this.supportsCommitCharacters;
};
return JSONCompletion;
}());
export { JSONCompletion };

View File

@@ -0,0 +1,278 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as Parser from '../parser/jsonParser';
import * as Strings from '../utils/strings';
import { colorFromHex } from '../utils/colors';
import { Range, TextEdit, SymbolKind, Location } from "../jsonLanguageTypes";
var JSONDocumentSymbols = /** @class */ (function () {
function JSONDocumentSymbols(schemaService) {
this.schemaService = schemaService;
}
JSONDocumentSymbols.prototype.findDocumentSymbols = function (document, doc, context) {
var _this = this;
if (context === void 0) { context = { resultLimit: Number.MAX_VALUE }; }
var root = doc.root;
if (!root) {
return [];
}
var limit = context.resultLimit || Number.MAX_VALUE;
// special handling for key bindings
var resourceString = document.uri;
if ((resourceString === 'vscode://defaultsettings/keybindings.json') || Strings.endsWith(resourceString.toLowerCase(), '/user/keybindings.json')) {
if (root.type === 'array') {
var result_1 = [];
for (var _i = 0, _a = root.items; _i < _a.length; _i++) {
var item = _a[_i];
if (item.type === 'object') {
for (var _b = 0, _c = item.properties; _b < _c.length; _b++) {
var property = _c[_b];
if (property.keyNode.value === 'key' && property.valueNode) {
var location = Location.create(document.uri, getRange(document, item));
result_1.push({ name: Parser.getNodeValue(property.valueNode), kind: SymbolKind.Function, location: location });
limit--;
if (limit <= 0) {
if (context && context.onResultLimitExceeded) {
context.onResultLimitExceeded(resourceString);
}
return result_1;
}
}
}
}
}
return result_1;
}
}
var toVisit = [
{ node: root, containerName: '' }
];
var nextToVisit = 0;
var limitExceeded = false;
var result = [];
var collectOutlineEntries = function (node, containerName) {
if (node.type === 'array') {
node.items.forEach(function (node) {
if (node) {
toVisit.push({ node: node, containerName: containerName });
}
});
}
else if (node.type === 'object') {
node.properties.forEach(function (property) {
var valueNode = property.valueNode;
if (valueNode) {
if (limit > 0) {
limit--;
var location = Location.create(document.uri, getRange(document, property));
var childContainerName = containerName ? containerName + '.' + property.keyNode.value : property.keyNode.value;
result.push({ name: _this.getKeyLabel(property), kind: _this.getSymbolKind(valueNode.type), location: location, containerName: containerName });
toVisit.push({ node: valueNode, containerName: childContainerName });
}
else {
limitExceeded = true;
}
}
});
}
};
// breath first traversal
while (nextToVisit < toVisit.length) {
var next = toVisit[nextToVisit++];
collectOutlineEntries(next.node, next.containerName);
}
if (limitExceeded && context && context.onResultLimitExceeded) {
context.onResultLimitExceeded(resourceString);
}
return result;
};
JSONDocumentSymbols.prototype.findDocumentSymbols2 = function (document, doc, context) {
var _this = this;
if (context === void 0) { context = { resultLimit: Number.MAX_VALUE }; }
var root = doc.root;
if (!root) {
return [];
}
var limit = context.resultLimit || Number.MAX_VALUE;
// special handling for key bindings
var resourceString = document.uri;
if ((resourceString === 'vscode://defaultsettings/keybindings.json') || Strings.endsWith(resourceString.toLowerCase(), '/user/keybindings.json')) {
if (root.type === 'array') {
var result_2 = [];
for (var _i = 0, _a = root.items; _i < _a.length; _i++) {
var item = _a[_i];
if (item.type === 'object') {
for (var _b = 0, _c = item.properties; _b < _c.length; _b++) {
var property = _c[_b];
if (property.keyNode.value === 'key' && property.valueNode) {
var range = getRange(document, item);
var selectionRange = getRange(document, property.keyNode);
result_2.push({ name: Parser.getNodeValue(property.valueNode), kind: SymbolKind.Function, range: range, selectionRange: selectionRange });
limit--;
if (limit <= 0) {
if (context && context.onResultLimitExceeded) {
context.onResultLimitExceeded(resourceString);
}
return result_2;
}
}
}
}
}
return result_2;
}
}
var result = [];
var toVisit = [
{ node: root, result: result }
];
var nextToVisit = 0;
var limitExceeded = false;
var collectOutlineEntries = function (node, result) {
if (node.type === 'array') {
node.items.forEach(function (node, index) {
if (node) {
if (limit > 0) {
limit--;
var range = getRange(document, node);
var selectionRange = range;
var name = String(index);
var symbol = { name: name, kind: _this.getSymbolKind(node.type), range: range, selectionRange: selectionRange, children: [] };
result.push(symbol);
toVisit.push({ result: symbol.children, node: node });
}
else {
limitExceeded = true;
}
}
});
}
else if (node.type === 'object') {
node.properties.forEach(function (property) {
var valueNode = property.valueNode;
if (valueNode) {
if (limit > 0) {
limit--;
var range = getRange(document, property);
var selectionRange = getRange(document, property.keyNode);
var children = [];
var symbol = { name: _this.getKeyLabel(property), kind: _this.getSymbolKind(valueNode.type), range: range, selectionRange: selectionRange, children: children, detail: _this.getDetail(valueNode) };
result.push(symbol);
toVisit.push({ result: children, node: valueNode });
}
else {
limitExceeded = true;
}
}
});
}
};
// breath first traversal
while (nextToVisit < toVisit.length) {
var next = toVisit[nextToVisit++];
collectOutlineEntries(next.node, next.result);
}
if (limitExceeded && context && context.onResultLimitExceeded) {
context.onResultLimitExceeded(resourceString);
}
return result;
};
JSONDocumentSymbols.prototype.getSymbolKind = function (nodeType) {
switch (nodeType) {
case 'object':
return SymbolKind.Module;
case 'string':
return SymbolKind.String;
case 'number':
return SymbolKind.Number;
case 'array':
return SymbolKind.Array;
case 'boolean':
return SymbolKind.Boolean;
default: // 'null'
return SymbolKind.Variable;
}
};
JSONDocumentSymbols.prototype.getKeyLabel = function (property) {
var name = property.keyNode.value;
if (name) {
name = name.replace(/[\n]/g, '↵');
}
if (name && name.trim()) {
return name;
}
return "\"" + name + "\"";
};
JSONDocumentSymbols.prototype.getDetail = function (node) {
if (!node) {
return undefined;
}
if (node.type === 'boolean' || node.type === 'number' || node.type === 'null' || node.type === 'string') {
return String(node.value);
}
else {
if (node.type === 'array') {
return node.children.length ? undefined : '[]';
}
else if (node.type === 'object') {
return node.children.length ? undefined : '{}';
}
}
return undefined;
};
JSONDocumentSymbols.prototype.findDocumentColors = function (document, doc, context) {
return this.schemaService.getSchemaForResource(document.uri, doc).then(function (schema) {
var result = [];
if (schema) {
var limit = context && typeof context.resultLimit === 'number' ? context.resultLimit : Number.MAX_VALUE;
var matchingSchemas = doc.getMatchingSchemas(schema.schema);
var visitedNode = {};
for (var _i = 0, matchingSchemas_1 = matchingSchemas; _i < matchingSchemas_1.length; _i++) {
var s = matchingSchemas_1[_i];
if (!s.inverted && s.schema && (s.schema.format === 'color' || s.schema.format === 'color-hex') && s.node && s.node.type === 'string') {
var nodeId = String(s.node.offset);
if (!visitedNode[nodeId]) {
var color = colorFromHex(Parser.getNodeValue(s.node));
if (color) {
var range = getRange(document, s.node);
result.push({ color: color, range: range });
}
visitedNode[nodeId] = true;
limit--;
if (limit <= 0) {
if (context && context.onResultLimitExceeded) {
context.onResultLimitExceeded(document.uri);
}
return result;
}
}
}
}
}
return result;
});
};
JSONDocumentSymbols.prototype.getColorPresentations = function (document, doc, color, range) {
var result = [];
var red256 = Math.round(color.red * 255), green256 = Math.round(color.green * 255), blue256 = Math.round(color.blue * 255);
function toTwoDigitHex(n) {
var r = n.toString(16);
return r.length !== 2 ? '0' + r : r;
}
var label;
if (color.alpha === 1) {
label = "#" + toTwoDigitHex(red256) + toTwoDigitHex(green256) + toTwoDigitHex(blue256);
}
else {
label = "#" + toTwoDigitHex(red256) + toTwoDigitHex(green256) + toTwoDigitHex(blue256) + toTwoDigitHex(Math.round(color.alpha * 255));
}
result.push({ label: label, textEdit: TextEdit.replace(range, JSON.stringify(label)) });
return result;
};
return JSONDocumentSymbols;
}());
export { JSONDocumentSymbols };
function getRange(document, node) {
return Range.create(document.positionAt(node.offset), document.positionAt(node.offset + node.length));
}

View File

@@ -0,0 +1,121 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { createScanner } from 'jsonc-parser';
import { FoldingRangeKind, Position } from '../jsonLanguageTypes';
export function getFoldingRanges(document, context) {
var ranges = [];
var nestingLevels = [];
var stack = [];
var prevStart = -1;
var scanner = createScanner(document.getText(), false);
var token = scanner.scan();
function addRange(range) {
ranges.push(range);
nestingLevels.push(stack.length);
}
while (token !== 17 /* EOF */) {
switch (token) {
case 1 /* OpenBraceToken */:
case 3 /* OpenBracketToken */: {
var startLine = document.positionAt(scanner.getTokenOffset()).line;
var range = { startLine: startLine, endLine: startLine, kind: token === 1 /* OpenBraceToken */ ? 'object' : 'array' };
stack.push(range);
break;
}
case 2 /* CloseBraceToken */:
case 4 /* CloseBracketToken */: {
var kind = token === 2 /* CloseBraceToken */ ? 'object' : 'array';
if (stack.length > 0 && stack[stack.length - 1].kind === kind) {
var range = stack.pop();
var line = document.positionAt(scanner.getTokenOffset()).line;
if (range && line > range.startLine + 1 && prevStart !== range.startLine) {
range.endLine = line - 1;
addRange(range);
prevStart = range.startLine;
}
}
break;
}
case 13 /* BlockCommentTrivia */: {
var startLine = document.positionAt(scanner.getTokenOffset()).line;
var endLine = document.positionAt(scanner.getTokenOffset() + scanner.getTokenLength()).line;
if (scanner.getTokenError() === 1 /* UnexpectedEndOfComment */ && startLine + 1 < document.lineCount) {
scanner.setPosition(document.offsetAt(Position.create(startLine + 1, 0)));
}
else {
if (startLine < endLine) {
addRange({ startLine: startLine, endLine: endLine, kind: FoldingRangeKind.Comment });
prevStart = startLine;
}
}
break;
}
case 12 /* LineCommentTrivia */: {
var text = document.getText().substr(scanner.getTokenOffset(), scanner.getTokenLength());
var m = text.match(/^\/\/\s*#(region\b)|(endregion\b)/);
if (m) {
var line = document.positionAt(scanner.getTokenOffset()).line;
if (m[1]) { // start pattern match
var range = { startLine: line, endLine: line, kind: FoldingRangeKind.Region };
stack.push(range);
}
else {
var i = stack.length - 1;
while (i >= 0 && stack[i].kind !== FoldingRangeKind.Region) {
i--;
}
if (i >= 0) {
var range = stack[i];
stack.length = i;
if (line > range.startLine && prevStart !== range.startLine) {
range.endLine = line;
addRange(range);
prevStart = range.startLine;
}
}
}
}
break;
}
}
token = scanner.scan();
}
var rangeLimit = context && context.rangeLimit;
if (typeof rangeLimit !== 'number' || ranges.length <= rangeLimit) {
return ranges;
}
if (context && context.onRangeLimitExceeded) {
context.onRangeLimitExceeded(document.uri);
}
var counts = [];
for (var _i = 0, nestingLevels_1 = nestingLevels; _i < nestingLevels_1.length; _i++) {
var level = nestingLevels_1[_i];
if (level < 30) {
counts[level] = (counts[level] || 0) + 1;
}
}
var entries = 0;
var maxLevel = 0;
for (var i = 0; i < counts.length; i++) {
var n = counts[i];
if (n) {
if (n + entries > rangeLimit) {
maxLevel = i;
break;
}
entries += n;
}
}
var result = [];
for (var i = 0; i < ranges.length; i++) {
var level = nestingLevels[i];
if (typeof level === 'number') {
if (level < maxLevel || (level === maxLevel && entries++ < rangeLimit)) {
result.push(ranges[i]);
}
}
}
return result;
}

View File

@@ -0,0 +1,112 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as Parser from '../parser/jsonParser';
import { Range } from '../jsonLanguageTypes';
var JSONHover = /** @class */ (function () {
function JSONHover(schemaService, contributions, promiseConstructor) {
if (contributions === void 0) { contributions = []; }
this.schemaService = schemaService;
this.contributions = contributions;
this.promise = promiseConstructor || Promise;
}
JSONHover.prototype.doHover = function (document, position, doc) {
var offset = document.offsetAt(position);
var node = doc.getNodeFromOffset(offset);
if (!node || (node.type === 'object' || node.type === 'array') && offset > node.offset + 1 && offset < node.offset + node.length - 1) {
return this.promise.resolve(null);
}
var hoverRangeNode = node;
// use the property description when hovering over an object key
if (node.type === 'string') {
var parent = node.parent;
if (parent && parent.type === 'property' && parent.keyNode === node) {
node = parent.valueNode;
if (!node) {
return this.promise.resolve(null);
}
}
}
var hoverRange = Range.create(document.positionAt(hoverRangeNode.offset), document.positionAt(hoverRangeNode.offset + hoverRangeNode.length));
var createHover = function (contents) {
var result = {
contents: contents,
range: hoverRange
};
return result;
};
var location = Parser.getNodePath(node);
for (var i = this.contributions.length - 1; i >= 0; i--) {
var contribution = this.contributions[i];
var promise = contribution.getInfoContribution(document.uri, location);
if (promise) {
return promise.then(function (htmlContent) { return createHover(htmlContent); });
}
}
return this.schemaService.getSchemaForResource(document.uri, doc).then(function (schema) {
if (schema && node) {
var matchingSchemas = doc.getMatchingSchemas(schema.schema, node.offset);
var title_1 = undefined;
var markdownDescription_1 = undefined;
var markdownEnumValueDescription_1 = undefined, enumValue_1 = undefined;
matchingSchemas.every(function (s) {
if (s.node === node && !s.inverted && s.schema) {
title_1 = title_1 || s.schema.title;
markdownDescription_1 = markdownDescription_1 || s.schema.markdownDescription || toMarkdown(s.schema.description);
if (s.schema.enum) {
var idx = s.schema.enum.indexOf(Parser.getNodeValue(node));
if (s.schema.markdownEnumDescriptions) {
markdownEnumValueDescription_1 = s.schema.markdownEnumDescriptions[idx];
}
else if (s.schema.enumDescriptions) {
markdownEnumValueDescription_1 = toMarkdown(s.schema.enumDescriptions[idx]);
}
if (markdownEnumValueDescription_1) {
enumValue_1 = s.schema.enum[idx];
if (typeof enumValue_1 !== 'string') {
enumValue_1 = JSON.stringify(enumValue_1);
}
}
}
}
return true;
});
var result = '';
if (title_1) {
result = toMarkdown(title_1);
}
if (markdownDescription_1) {
if (result.length > 0) {
result += "\n\n";
}
result += markdownDescription_1;
}
if (markdownEnumValueDescription_1) {
if (result.length > 0) {
result += "\n\n";
}
result += "`" + toMarkdownCodeBlock(enumValue_1) + "`: " + markdownEnumValueDescription_1;
}
return createHover([result]);
}
return null;
});
};
return JSONHover;
}());
export { JSONHover };
function toMarkdown(plain) {
if (plain) {
var res = plain.replace(/([^\n\r])(\r?\n)([^\n\r])/gm, '$1\n\n$3'); // single new lines to \n\n (Markdown paragraph)
return res.replace(/[\\`*_{}[\]()#+\-.!]/g, "\\$&"); // escape markdown syntax tokens: http://daringfireball.net/projects/markdown/syntax#backslash
}
return undefined;
}
function toMarkdownCodeBlock(content) {
// see https://daringfireball.net/projects/markdown/syntax#precode
if (content.indexOf('`') !== -1) {
return '`` ' + content + ' ``';
}
return content;
}

View File

@@ -0,0 +1,73 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Range } from '../jsonLanguageTypes';
export function findLinks(document, doc) {
var links = [];
doc.visit(function (node) {
var _a;
if (node.type === "property" && node.keyNode.value === "$ref" && ((_a = node.valueNode) === null || _a === void 0 ? void 0 : _a.type) === 'string') {
var path = node.valueNode.value;
var targetNode = findTargetNode(doc, path);
if (targetNode) {
var targetPos = document.positionAt(targetNode.offset);
links.push({
target: document.uri + "#" + (targetPos.line + 1) + "," + (targetPos.character + 1),
range: createRange(document, node.valueNode)
});
}
}
return true;
});
return Promise.resolve(links);
}
function createRange(document, node) {
return Range.create(document.positionAt(node.offset + 1), document.positionAt(node.offset + node.length - 1));
}
function findTargetNode(doc, path) {
var tokens = parseJSONPointer(path);
if (!tokens) {
return null;
}
return findNode(tokens, doc.root);
}
function findNode(pointer, node) {
if (!node) {
return null;
}
if (pointer.length === 0) {
return node;
}
var token = pointer.shift();
if (node && node.type === 'object') {
var propertyNode = node.properties.find(function (propertyNode) { return propertyNode.keyNode.value === token; });
if (!propertyNode) {
return null;
}
return findNode(pointer, propertyNode.valueNode);
}
else if (node && node.type === 'array') {
if (token.match(/^(0|[1-9][0-9]*)$/)) {
var index = Number.parseInt(token);
var arrayItem = node.items[index];
if (!arrayItem) {
return null;
}
return findNode(pointer, arrayItem);
}
}
return null;
}
function parseJSONPointer(path) {
if (path === "#") {
return [];
}
if (path[0] !== '#' || path[1] !== '/') {
return null;
}
return path.substring(2).split(/\//).map(unescape);
}
function unescape(str) {
return str.replace(/~1/g, '/').replace(/~0/g, '~');
}

View File

@@ -0,0 +1,535 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as Json from 'jsonc-parser';
import { URI } from 'vscode-uri';
import * as Strings from '../utils/strings';
import * as Parser from '../parser/jsonParser';
import * as nls from 'vscode-nls';
import { createRegex } from '../utils/glob';
var localize = nls.loadMessageBundle();
var BANG = '!';
var PATH_SEP = '/';
var FilePatternAssociation = /** @class */ (function () {
function FilePatternAssociation(pattern, uris) {
this.globWrappers = [];
try {
for (var _i = 0, pattern_1 = pattern; _i < pattern_1.length; _i++) {
var patternString = pattern_1[_i];
var include = patternString[0] !== BANG;
if (!include) {
patternString = patternString.substring(1);
}
if (patternString.length > 0) {
if (patternString[0] === PATH_SEP) {
patternString = patternString.substring(1);
}
this.globWrappers.push({
regexp: createRegex('**/' + patternString, { extended: true, globstar: true }),
include: include,
});
}
}
;
this.uris = uris;
}
catch (e) {
this.globWrappers.length = 0;
this.uris = [];
}
}
FilePatternAssociation.prototype.matchesPattern = function (fileName) {
var match = false;
for (var _i = 0, _a = this.globWrappers; _i < _a.length; _i++) {
var _b = _a[_i], regexp = _b.regexp, include = _b.include;
if (regexp.test(fileName)) {
match = include;
}
}
return match;
};
FilePatternAssociation.prototype.getURIs = function () {
return this.uris;
};
return FilePatternAssociation;
}());
var SchemaHandle = /** @class */ (function () {
function SchemaHandle(service, url, unresolvedSchemaContent) {
this.service = service;
this.url = url;
this.dependencies = {};
if (unresolvedSchemaContent) {
this.unresolvedSchema = this.service.promise.resolve(new UnresolvedSchema(unresolvedSchemaContent));
}
}
SchemaHandle.prototype.getUnresolvedSchema = function () {
if (!this.unresolvedSchema) {
this.unresolvedSchema = this.service.loadSchema(this.url);
}
return this.unresolvedSchema;
};
SchemaHandle.prototype.getResolvedSchema = function () {
var _this = this;
if (!this.resolvedSchema) {
this.resolvedSchema = this.getUnresolvedSchema().then(function (unresolved) {
return _this.service.resolveSchemaContent(unresolved, _this.url, _this.dependencies);
});
}
return this.resolvedSchema;
};
SchemaHandle.prototype.clearSchema = function () {
this.resolvedSchema = undefined;
this.unresolvedSchema = undefined;
this.dependencies = {};
};
return SchemaHandle;
}());
var UnresolvedSchema = /** @class */ (function () {
function UnresolvedSchema(schema, errors) {
if (errors === void 0) { errors = []; }
this.schema = schema;
this.errors = errors;
}
return UnresolvedSchema;
}());
export { UnresolvedSchema };
var ResolvedSchema = /** @class */ (function () {
function ResolvedSchema(schema, errors) {
if (errors === void 0) { errors = []; }
this.schema = schema;
this.errors = errors;
}
ResolvedSchema.prototype.getSection = function (path) {
var schemaRef = this.getSectionRecursive(path, this.schema);
if (schemaRef) {
return Parser.asSchema(schemaRef);
}
return undefined;
};
ResolvedSchema.prototype.getSectionRecursive = function (path, schema) {
if (!schema || typeof schema === 'boolean' || path.length === 0) {
return schema;
}
var next = path.shift();
if (schema.properties && typeof schema.properties[next]) {
return this.getSectionRecursive(path, schema.properties[next]);
}
else if (schema.patternProperties) {
for (var _i = 0, _a = Object.keys(schema.patternProperties); _i < _a.length; _i++) {
var pattern = _a[_i];
var regex = Strings.extendedRegExp(pattern);
if (regex.test(next)) {
return this.getSectionRecursive(path, schema.patternProperties[pattern]);
}
}
}
else if (typeof schema.additionalProperties === 'object') {
return this.getSectionRecursive(path, schema.additionalProperties);
}
else if (next.match('[0-9]+')) {
if (Array.isArray(schema.items)) {
var index = parseInt(next, 10);
if (!isNaN(index) && schema.items[index]) {
return this.getSectionRecursive(path, schema.items[index]);
}
}
else if (schema.items) {
return this.getSectionRecursive(path, schema.items);
}
}
return undefined;
};
return ResolvedSchema;
}());
export { ResolvedSchema };
var JSONSchemaService = /** @class */ (function () {
function JSONSchemaService(requestService, contextService, promiseConstructor) {
this.contextService = contextService;
this.requestService = requestService;
this.promiseConstructor = promiseConstructor || Promise;
this.callOnDispose = [];
this.contributionSchemas = {};
this.contributionAssociations = [];
this.schemasById = {};
this.filePatternAssociations = [];
this.registeredSchemasIds = {};
}
JSONSchemaService.prototype.getRegisteredSchemaIds = function (filter) {
return Object.keys(this.registeredSchemasIds).filter(function (id) {
var scheme = URI.parse(id).scheme;
return scheme !== 'schemaservice' && (!filter || filter(scheme));
});
};
Object.defineProperty(JSONSchemaService.prototype, "promise", {
get: function () {
return this.promiseConstructor;
},
enumerable: false,
configurable: true
});
JSONSchemaService.prototype.dispose = function () {
while (this.callOnDispose.length > 0) {
this.callOnDispose.pop()();
}
};
JSONSchemaService.prototype.onResourceChange = function (uri) {
var _this = this;
// always clear this local cache when a resource changes
this.cachedSchemaForResource = undefined;
var hasChanges = false;
uri = normalizeId(uri);
var toWalk = [uri];
var all = Object.keys(this.schemasById).map(function (key) { return _this.schemasById[key]; });
while (toWalk.length) {
var curr = toWalk.pop();
for (var i = 0; i < all.length; i++) {
var handle = all[i];
if (handle && (handle.url === curr || handle.dependencies[curr])) {
if (handle.url !== curr) {
toWalk.push(handle.url);
}
handle.clearSchema();
all[i] = undefined;
hasChanges = true;
}
}
}
return hasChanges;
};
JSONSchemaService.prototype.setSchemaContributions = function (schemaContributions) {
if (schemaContributions.schemas) {
var schemas = schemaContributions.schemas;
for (var id in schemas) {
var normalizedId = normalizeId(id);
this.contributionSchemas[normalizedId] = this.addSchemaHandle(normalizedId, schemas[id]);
}
}
if (Array.isArray(schemaContributions.schemaAssociations)) {
var schemaAssociations = schemaContributions.schemaAssociations;
for (var _i = 0, schemaAssociations_1 = schemaAssociations; _i < schemaAssociations_1.length; _i++) {
var schemaAssociation = schemaAssociations_1[_i];
var uris = schemaAssociation.uris.map(normalizeId);
var association = this.addFilePatternAssociation(schemaAssociation.pattern, uris);
this.contributionAssociations.push(association);
}
}
};
JSONSchemaService.prototype.addSchemaHandle = function (id, unresolvedSchemaContent) {
var schemaHandle = new SchemaHandle(this, id, unresolvedSchemaContent);
this.schemasById[id] = schemaHandle;
return schemaHandle;
};
JSONSchemaService.prototype.getOrAddSchemaHandle = function (id, unresolvedSchemaContent) {
return this.schemasById[id] || this.addSchemaHandle(id, unresolvedSchemaContent);
};
JSONSchemaService.prototype.addFilePatternAssociation = function (pattern, uris) {
var fpa = new FilePatternAssociation(pattern, uris);
this.filePatternAssociations.push(fpa);
return fpa;
};
JSONSchemaService.prototype.registerExternalSchema = function (uri, filePatterns, unresolvedSchemaContent) {
var id = normalizeId(uri);
this.registeredSchemasIds[id] = true;
this.cachedSchemaForResource = undefined;
if (filePatterns) {
this.addFilePatternAssociation(filePatterns, [uri]);
}
return unresolvedSchemaContent ? this.addSchemaHandle(id, unresolvedSchemaContent) : this.getOrAddSchemaHandle(id);
};
JSONSchemaService.prototype.clearExternalSchemas = function () {
this.schemasById = {};
this.filePatternAssociations = [];
this.registeredSchemasIds = {};
this.cachedSchemaForResource = undefined;
for (var id in this.contributionSchemas) {
this.schemasById[id] = this.contributionSchemas[id];
this.registeredSchemasIds[id] = true;
}
for (var _i = 0, _a = this.contributionAssociations; _i < _a.length; _i++) {
var contributionAssociation = _a[_i];
this.filePatternAssociations.push(contributionAssociation);
}
};
JSONSchemaService.prototype.getResolvedSchema = function (schemaId) {
var id = normalizeId(schemaId);
var schemaHandle = this.schemasById[id];
if (schemaHandle) {
return schemaHandle.getResolvedSchema();
}
return this.promise.resolve(undefined);
};
JSONSchemaService.prototype.loadSchema = function (url) {
if (!this.requestService) {
var errorMessage = localize('json.schema.norequestservice', 'Unable to load schema from \'{0}\'. No schema request service available', toDisplayString(url));
return this.promise.resolve(new UnresolvedSchema({}, [errorMessage]));
}
return this.requestService(url).then(function (content) {
if (!content) {
var errorMessage = localize('json.schema.nocontent', 'Unable to load schema from \'{0}\': No content.', toDisplayString(url));
return new UnresolvedSchema({}, [errorMessage]);
}
var schemaContent = {};
var jsonErrors = [];
schemaContent = Json.parse(content, jsonErrors);
var errors = jsonErrors.length ? [localize('json.schema.invalidFormat', 'Unable to parse content from \'{0}\': Parse error at offset {1}.', toDisplayString(url), jsonErrors[0].offset)] : [];
return new UnresolvedSchema(schemaContent, errors);
}, function (error) {
var errorMessage = error.toString();
var errorSplit = error.toString().split('Error: ');
if (errorSplit.length > 1) {
// more concise error message, URL and context are attached by caller anyways
errorMessage = errorSplit[1];
}
if (Strings.endsWith(errorMessage, '.')) {
errorMessage = errorMessage.substr(0, errorMessage.length - 1);
}
return new UnresolvedSchema({}, [localize('json.schema.nocontent', 'Unable to load schema from \'{0}\': {1}.', toDisplayString(url), errorMessage)]);
});
};
JSONSchemaService.prototype.resolveSchemaContent = function (schemaToResolve, schemaURL, dependencies) {
var _this = this;
var resolveErrors = schemaToResolve.errors.slice(0);
var schema = schemaToResolve.schema;
if (schema.$schema) {
var id = normalizeId(schema.$schema);
if (id === 'http://json-schema.org/draft-03/schema') {
return this.promise.resolve(new ResolvedSchema({}, [localize('json.schema.draft03.notsupported', "Draft-03 schemas are not supported.")]));
}
else if (id === 'https://json-schema.org/draft/2019-09/schema') {
resolveErrors.push(localize('json.schema.draft201909.notsupported', "Draft 2019-09 schemas are not yet fully supported."));
}
}
var contextService = this.contextService;
var findSection = function (schema, path) {
if (!path) {
return schema;
}
var current = schema;
if (path[0] === '/') {
path = path.substr(1);
}
path.split('/').some(function (part) {
part = part.replace(/~1/g, '/').replace(/~0/g, '~');
current = current[part];
return !current;
});
return current;
};
var merge = function (target, sourceRoot, sourceURI, refSegment) {
var path = refSegment ? decodeURIComponent(refSegment) : undefined;
var section = findSection(sourceRoot, path);
if (section) {
for (var key in section) {
if (section.hasOwnProperty(key) && !target.hasOwnProperty(key)) {
target[key] = section[key];
}
}
}
else {
resolveErrors.push(localize('json.schema.invalidref', '$ref \'{0}\' in \'{1}\' can not be resolved.', path, sourceURI));
}
};
var resolveExternalLink = function (node, uri, refSegment, parentSchemaURL, parentSchemaDependencies) {
if (contextService && !/^[A-Za-z][A-Za-z0-9+\-.+]*:\/\/.*/.test(uri)) {
uri = contextService.resolveRelativePath(uri, parentSchemaURL);
}
uri = normalizeId(uri);
var referencedHandle = _this.getOrAddSchemaHandle(uri);
return referencedHandle.getUnresolvedSchema().then(function (unresolvedSchema) {
parentSchemaDependencies[uri] = true;
if (unresolvedSchema.errors.length) {
var loc = refSegment ? uri + '#' + refSegment : uri;
resolveErrors.push(localize('json.schema.problemloadingref', 'Problems loading reference \'{0}\': {1}', loc, unresolvedSchema.errors[0]));
}
merge(node, unresolvedSchema.schema, uri, refSegment);
return resolveRefs(node, unresolvedSchema.schema, uri, referencedHandle.dependencies);
});
};
var resolveRefs = function (node, parentSchema, parentSchemaURL, parentSchemaDependencies) {
if (!node || typeof node !== 'object') {
return Promise.resolve(null);
}
var toWalk = [node];
var seen = [];
var openPromises = [];
var collectEntries = function () {
var entries = [];
for (var _i = 0; _i < arguments.length; _i++) {
entries[_i] = arguments[_i];
}
for (var _a = 0, entries_1 = entries; _a < entries_1.length; _a++) {
var entry = entries_1[_a];
if (typeof entry === 'object') {
toWalk.push(entry);
}
}
};
var collectMapEntries = function () {
var maps = [];
for (var _i = 0; _i < arguments.length; _i++) {
maps[_i] = arguments[_i];
}
for (var _a = 0, maps_1 = maps; _a < maps_1.length; _a++) {
var map = maps_1[_a];
if (typeof map === 'object') {
for (var k in map) {
var key = k;
var entry = map[key];
if (typeof entry === 'object') {
toWalk.push(entry);
}
}
}
}
};
var collectArrayEntries = function () {
var arrays = [];
for (var _i = 0; _i < arguments.length; _i++) {
arrays[_i] = arguments[_i];
}
for (var _a = 0, arrays_1 = arrays; _a < arrays_1.length; _a++) {
var array = arrays_1[_a];
if (Array.isArray(array)) {
for (var _b = 0, array_1 = array; _b < array_1.length; _b++) {
var entry = array_1[_b];
if (typeof entry === 'object') {
toWalk.push(entry);
}
}
}
}
};
var handleRef = function (next) {
var seenRefs = [];
while (next.$ref) {
var ref = next.$ref;
var segments = ref.split('#', 2);
delete next.$ref;
if (segments[0].length > 0) {
openPromises.push(resolveExternalLink(next, segments[0], segments[1], parentSchemaURL, parentSchemaDependencies));
return;
}
else {
if (seenRefs.indexOf(ref) === -1) {
merge(next, parentSchema, parentSchemaURL, segments[1]); // can set next.$ref again, use seenRefs to avoid circle
seenRefs.push(ref);
}
}
}
collectEntries(next.items, next.additionalItems, next.additionalProperties, next.not, next.contains, next.propertyNames, next.if, next.then, next.else);
collectMapEntries(next.definitions, next.properties, next.patternProperties, next.dependencies);
collectArrayEntries(next.anyOf, next.allOf, next.oneOf, next.items);
};
while (toWalk.length) {
var next = toWalk.pop();
if (seen.indexOf(next) >= 0) {
continue;
}
seen.push(next);
handleRef(next);
}
return _this.promise.all(openPromises);
};
return resolveRefs(schema, schema, schemaURL, dependencies).then(function (_) { return new ResolvedSchema(schema, resolveErrors); });
};
JSONSchemaService.prototype.getSchemaForResource = function (resource, document) {
// first use $schema if present
if (document && document.root && document.root.type === 'object') {
var schemaProperties = document.root.properties.filter(function (p) { return (p.keyNode.value === '$schema') && p.valueNode && p.valueNode.type === 'string'; });
if (schemaProperties.length > 0) {
var valueNode = schemaProperties[0].valueNode;
if (valueNode && valueNode.type === 'string') {
var schemeId = Parser.getNodeValue(valueNode);
if (schemeId && Strings.startsWith(schemeId, '.') && this.contextService) {
schemeId = this.contextService.resolveRelativePath(schemeId, resource);
}
if (schemeId) {
var id = normalizeId(schemeId);
return this.getOrAddSchemaHandle(id).getResolvedSchema();
}
}
}
}
if (this.cachedSchemaForResource && this.cachedSchemaForResource.resource === resource) {
return this.cachedSchemaForResource.resolvedSchema;
}
var seen = Object.create(null);
var schemas = [];
var normalizedResource = normalizeResourceForMatching(resource);
for (var _i = 0, _a = this.filePatternAssociations; _i < _a.length; _i++) {
var entry = _a[_i];
if (entry.matchesPattern(normalizedResource)) {
for (var _b = 0, _c = entry.getURIs(); _b < _c.length; _b++) {
var schemaId = _c[_b];
if (!seen[schemaId]) {
schemas.push(schemaId);
seen[schemaId] = true;
}
}
}
}
var resolvedSchema = schemas.length > 0 ? this.createCombinedSchema(resource, schemas).getResolvedSchema() : this.promise.resolve(undefined);
this.cachedSchemaForResource = { resource: resource, resolvedSchema: resolvedSchema };
return resolvedSchema;
};
JSONSchemaService.prototype.createCombinedSchema = function (resource, schemaIds) {
if (schemaIds.length === 1) {
return this.getOrAddSchemaHandle(schemaIds[0]);
}
else {
var combinedSchemaId = 'schemaservice://combinedSchema/' + encodeURIComponent(resource);
var combinedSchema = {
allOf: schemaIds.map(function (schemaId) { return ({ $ref: schemaId }); })
};
return this.addSchemaHandle(combinedSchemaId, combinedSchema);
}
};
JSONSchemaService.prototype.getMatchingSchemas = function (document, jsonDocument, schema) {
if (schema) {
var id = schema.id || ('schemaservice://untitled/matchingSchemas/' + idCounter++);
return this.resolveSchemaContent(new UnresolvedSchema(schema), id, {}).then(function (resolvedSchema) {
return jsonDocument.getMatchingSchemas(resolvedSchema.schema).filter(function (s) { return !s.inverted; });
});
}
return this.getSchemaForResource(document.uri, jsonDocument).then(function (schema) {
if (schema) {
return jsonDocument.getMatchingSchemas(schema.schema).filter(function (s) { return !s.inverted; });
}
return [];
});
};
return JSONSchemaService;
}());
export { JSONSchemaService };
var idCounter = 0;
function normalizeId(id) {
// remove trailing '#', normalize drive capitalization
try {
return URI.parse(id).toString();
}
catch (e) {
return id;
}
}
function normalizeResourceForMatching(resource) {
// remove queries and fragments, normalize drive capitalization
try {
return URI.parse(resource).with({ fragment: null, query: null }).toString();
}
catch (e) {
return resource;
}
}
function toDisplayString(url) {
try {
var uri = URI.parse(url);
if (uri.scheme === 'file') {
return uri.fsPath;
}
}
catch (e) {
// ignore
}
return url;
}

View File

@@ -0,0 +1,61 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Range, SelectionRange } from '../jsonLanguageTypes';
import { createScanner } from 'jsonc-parser';
export function getSelectionRanges(document, positions, doc) {
function getSelectionRange(position) {
var offset = document.offsetAt(position);
var node = doc.getNodeFromOffset(offset, true);
var result = [];
while (node) {
switch (node.type) {
case 'string':
case 'object':
case 'array':
// range without ", [ or {
var cStart = node.offset + 1, cEnd = node.offset + node.length - 1;
if (cStart < cEnd && offset >= cStart && offset <= cEnd) {
result.push(newRange(cStart, cEnd));
}
result.push(newRange(node.offset, node.offset + node.length));
break;
case 'number':
case 'boolean':
case 'null':
case 'property':
result.push(newRange(node.offset, node.offset + node.length));
break;
}
if (node.type === 'property' || node.parent && node.parent.type === 'array') {
var afterCommaOffset = getOffsetAfterNextToken(node.offset + node.length, 5 /* CommaToken */);
if (afterCommaOffset !== -1) {
result.push(newRange(node.offset, afterCommaOffset));
}
}
node = node.parent;
}
var current = undefined;
for (var index = result.length - 1; index >= 0; index--) {
current = SelectionRange.create(result[index], current);
}
if (!current) {
current = SelectionRange.create(Range.create(position, position));
}
return current;
}
function newRange(start, end) {
return Range.create(document.positionAt(start), document.positionAt(end));
}
var scanner = createScanner(document.getText(), true);
function getOffsetAfterNextToken(offset, expectedToken) {
scanner.setPosition(offset);
var token = scanner.scan();
if (token === expectedToken) {
return scanner.getTokenOffset() + scanner.getTokenLength();
}
return -1;
}
return positions.map(getSelectionRange);
}

View File

@@ -0,0 +1,146 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { UnresolvedSchema } from './jsonSchemaService';
import { ErrorCode, Diagnostic, DiagnosticSeverity, Range } from '../jsonLanguageTypes';
import * as nls from 'vscode-nls';
import { isBoolean } from '../utils/objects';
var localize = nls.loadMessageBundle();
var JSONValidation = /** @class */ (function () {
function JSONValidation(jsonSchemaService, promiseConstructor) {
this.jsonSchemaService = jsonSchemaService;
this.promise = promiseConstructor;
this.validationEnabled = true;
}
JSONValidation.prototype.configure = function (raw) {
if (raw) {
this.validationEnabled = raw.validate !== false;
this.commentSeverity = raw.allowComments ? undefined : DiagnosticSeverity.Error;
}
};
JSONValidation.prototype.doValidation = function (textDocument, jsonDocument, documentSettings, schema) {
var _this = this;
if (!this.validationEnabled) {
return this.promise.resolve([]);
}
var diagnostics = [];
var added = {};
var addProblem = function (problem) {
// remove duplicated messages
var signature = problem.range.start.line + ' ' + problem.range.start.character + ' ' + problem.message;
if (!added[signature]) {
added[signature] = true;
diagnostics.push(problem);
}
};
var getDiagnostics = function (schema) {
var trailingCommaSeverity = (documentSettings === null || documentSettings === void 0 ? void 0 : documentSettings.trailingCommas) ? toDiagnosticSeverity(documentSettings.trailingCommas) : DiagnosticSeverity.Error;
var commentSeverity = (documentSettings === null || documentSettings === void 0 ? void 0 : documentSettings.comments) ? toDiagnosticSeverity(documentSettings.comments) : _this.commentSeverity;
var schemaValidation = (documentSettings === null || documentSettings === void 0 ? void 0 : documentSettings.schemaValidation) ? toDiagnosticSeverity(documentSettings.schemaValidation) : DiagnosticSeverity.Warning;
var schemaRequest = (documentSettings === null || documentSettings === void 0 ? void 0 : documentSettings.schemaRequest) ? toDiagnosticSeverity(documentSettings.schemaRequest) : DiagnosticSeverity.Warning;
if (schema) {
if (schema.errors.length && jsonDocument.root && schemaRequest) {
var astRoot = jsonDocument.root;
var property = astRoot.type === 'object' ? astRoot.properties[0] : undefined;
if (property && property.keyNode.value === '$schema') {
var node = property.valueNode || property;
var range = Range.create(textDocument.positionAt(node.offset), textDocument.positionAt(node.offset + node.length));
addProblem(Diagnostic.create(range, schema.errors[0], schemaRequest, ErrorCode.SchemaResolveError));
}
else {
var range = Range.create(textDocument.positionAt(astRoot.offset), textDocument.positionAt(astRoot.offset + 1));
addProblem(Diagnostic.create(range, schema.errors[0], schemaRequest, ErrorCode.SchemaResolveError));
}
}
else if (schemaValidation) {
var semanticErrors = jsonDocument.validate(textDocument, schema.schema, schemaValidation);
if (semanticErrors) {
semanticErrors.forEach(addProblem);
}
}
if (schemaAllowsComments(schema.schema)) {
commentSeverity = undefined;
}
if (schemaAllowsTrailingCommas(schema.schema)) {
trailingCommaSeverity = undefined;
}
}
for (var _i = 0, _a = jsonDocument.syntaxErrors; _i < _a.length; _i++) {
var p = _a[_i];
if (p.code === ErrorCode.TrailingComma) {
if (typeof trailingCommaSeverity !== 'number') {
continue;
}
p.severity = trailingCommaSeverity;
}
addProblem(p);
}
if (typeof commentSeverity === 'number') {
var message_1 = localize('InvalidCommentToken', 'Comments are not permitted in JSON.');
jsonDocument.comments.forEach(function (c) {
addProblem(Diagnostic.create(c, message_1, commentSeverity, ErrorCode.CommentNotPermitted));
});
}
return diagnostics;
};
if (schema) {
var id = schema.id || ('schemaservice://untitled/' + idCounter++);
return this.jsonSchemaService.resolveSchemaContent(new UnresolvedSchema(schema), id, {}).then(function (resolvedSchema) {
return getDiagnostics(resolvedSchema);
});
}
return this.jsonSchemaService.getSchemaForResource(textDocument.uri, jsonDocument).then(function (schema) {
return getDiagnostics(schema);
});
};
return JSONValidation;
}());
export { JSONValidation };
var idCounter = 0;
function schemaAllowsComments(schemaRef) {
if (schemaRef && typeof schemaRef === 'object') {
if (isBoolean(schemaRef.allowComments)) {
return schemaRef.allowComments;
}
if (schemaRef.allOf) {
for (var _i = 0, _a = schemaRef.allOf; _i < _a.length; _i++) {
var schema = _a[_i];
var allow = schemaAllowsComments(schema);
if (isBoolean(allow)) {
return allow;
}
}
}
}
return undefined;
}
function schemaAllowsTrailingCommas(schemaRef) {
if (schemaRef && typeof schemaRef === 'object') {
if (isBoolean(schemaRef.allowTrailingCommas)) {
return schemaRef.allowTrailingCommas;
}
var deprSchemaRef = schemaRef;
if (isBoolean(deprSchemaRef['allowsTrailingCommas'])) { // deprecated
return deprSchemaRef['allowsTrailingCommas'];
}
if (schemaRef.allOf) {
for (var _i = 0, _a = schemaRef.allOf; _i < _a.length; _i++) {
var schema = _a[_i];
var allow = schemaAllowsTrailingCommas(schema);
if (isBoolean(allow)) {
return allow;
}
}
}
}
return undefined;
}
function toDiagnosticSeverity(severityLevel) {
switch (severityLevel) {
case 'error': return DiagnosticSeverity.Error;
case 'warning': return DiagnosticSeverity.Warning;
case 'ignore': return undefined;
}
return undefined;
}

View File

@@ -0,0 +1,69 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
var Digit0 = 48;
var Digit9 = 57;
var A = 65;
var a = 97;
var f = 102;
export function hexDigit(charCode) {
if (charCode < Digit0) {
return 0;
}
if (charCode <= Digit9) {
return charCode - Digit0;
}
if (charCode < a) {
charCode += (a - A);
}
if (charCode >= a && charCode <= f) {
return charCode - a + 10;
}
return 0;
}
export function colorFromHex(text) {
if (text[0] !== '#') {
return undefined;
}
switch (text.length) {
case 4:
return {
red: (hexDigit(text.charCodeAt(1)) * 0x11) / 255.0,
green: (hexDigit(text.charCodeAt(2)) * 0x11) / 255.0,
blue: (hexDigit(text.charCodeAt(3)) * 0x11) / 255.0,
alpha: 1
};
case 5:
return {
red: (hexDigit(text.charCodeAt(1)) * 0x11) / 255.0,
green: (hexDigit(text.charCodeAt(2)) * 0x11) / 255.0,
blue: (hexDigit(text.charCodeAt(3)) * 0x11) / 255.0,
alpha: (hexDigit(text.charCodeAt(4)) * 0x11) / 255.0,
};
case 7:
return {
red: (hexDigit(text.charCodeAt(1)) * 0x10 + hexDigit(text.charCodeAt(2))) / 255.0,
green: (hexDigit(text.charCodeAt(3)) * 0x10 + hexDigit(text.charCodeAt(4))) / 255.0,
blue: (hexDigit(text.charCodeAt(5)) * 0x10 + hexDigit(text.charCodeAt(6))) / 255.0,
alpha: 1
};
case 9:
return {
red: (hexDigit(text.charCodeAt(1)) * 0x10 + hexDigit(text.charCodeAt(2))) / 255.0,
green: (hexDigit(text.charCodeAt(3)) * 0x10 + hexDigit(text.charCodeAt(4))) / 255.0,
blue: (hexDigit(text.charCodeAt(5)) * 0x10 + hexDigit(text.charCodeAt(6))) / 255.0,
alpha: (hexDigit(text.charCodeAt(7)) * 0x10 + hexDigit(text.charCodeAt(8))) / 255.0
};
}
return undefined;
}
export function colorFrom256RGB(red, green, blue, alpha) {
if (alpha === void 0) { alpha = 1.0; }
return {
red: red / 255.0,
green: green / 255.0,
blue: blue / 255.0,
alpha: alpha
};
}

View File

@@ -0,0 +1,124 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Copyright (c) 2013, Nick Fitzgerald
* Licensed under the MIT License. See LICENCE.md in the project root for license information.
*--------------------------------------------------------------------------------------------*/
export function createRegex(glob, opts) {
if (typeof glob !== 'string') {
throw new TypeError('Expected a string');
}
var str = String(glob);
// The regexp we are building, as a string.
var reStr = "";
// Whether we are matching so called "extended" globs (like bash) and should
// support single character matching, matching ranges of characters, group
// matching, etc.
var extended = opts ? !!opts.extended : false;
// When globstar is _false_ (default), '/foo/*' is translated a regexp like
// '^\/foo\/.*$' which will match any string beginning with '/foo/'
// When globstar is _true_, '/foo/*' is translated to regexp like
// '^\/foo\/[^/]*$' which will match any string beginning with '/foo/' BUT
// which does not have a '/' to the right of it.
// E.g. with '/foo/*' these will match: '/foo/bar', '/foo/bar.txt' but
// these will not '/foo/bar/baz', '/foo/bar/baz.txt'
// Lastely, when globstar is _true_, '/foo/**' is equivelant to '/foo/*' when
// globstar is _false_
var globstar = opts ? !!opts.globstar : false;
// If we are doing extended matching, this boolean is true when we are inside
// a group (eg {*.html,*.js}), and false otherwise.
var inGroup = false;
// RegExp flags (eg "i" ) to pass in to RegExp constructor.
var flags = opts && typeof (opts.flags) === "string" ? opts.flags : "";
var c;
for (var i = 0, len = str.length; i < len; i++) {
c = str[i];
switch (c) {
case "/":
case "$":
case "^":
case "+":
case ".":
case "(":
case ")":
case "=":
case "!":
case "|":
reStr += "\\" + c;
break;
case "?":
if (extended) {
reStr += ".";
break;
}
case "[":
case "]":
if (extended) {
reStr += c;
break;
}
case "{":
if (extended) {
inGroup = true;
reStr += "(";
break;
}
case "}":
if (extended) {
inGroup = false;
reStr += ")";
break;
}
case ",":
if (inGroup) {
reStr += "|";
break;
}
reStr += "\\" + c;
break;
case "*":
// Move over all consecutive "*"'s.
// Also store the previous and next characters
var prevChar = str[i - 1];
var starCount = 1;
while (str[i + 1] === "*") {
starCount++;
i++;
}
var nextChar = str[i + 1];
if (!globstar) {
// globstar is disabled, so treat any number of "*" as one
reStr += ".*";
}
else {
// globstar is enabled, so determine if this is a globstar segment
var isGlobstar = starCount > 1 // multiple "*"'s
&& (prevChar === "/" || prevChar === undefined || prevChar === '{' || prevChar === ',') // from the start of the segment
&& (nextChar === "/" || nextChar === undefined || nextChar === ',' || nextChar === '}'); // to the end of the segment
if (isGlobstar) {
if (nextChar === "/") {
i++; // move over the "/"
}
else if (prevChar === '/' && reStr.endsWith('\\/')) {
reStr = reStr.substr(0, reStr.length - 2);
}
// it's a globstar, so match zero or more path segments
reStr += "((?:[^/]*(?:\/|$))*)";
}
else {
// it's not a globstar, so only match one path segment
reStr += "([^/]*)";
}
}
break;
default:
reStr += c;
}
}
// When regexp 'g' flag is specified don't
// constrain the regular expression with ^ & $
if (!flags || !~flags.indexOf('g')) {
reStr = "^" + reStr + "$";
}
return new RegExp(reStr, flags);
}
;

View File

@@ -0,0 +1,42 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
export function stringifyObject(obj, indent, stringifyLiteral) {
if (obj !== null && typeof obj === 'object') {
var newIndent = indent + '\t';
if (Array.isArray(obj)) {
if (obj.length === 0) {
return '[]';
}
var result = '[\n';
for (var i = 0; i < obj.length; i++) {
result += newIndent + stringifyObject(obj[i], newIndent, stringifyLiteral);
if (i < obj.length - 1) {
result += ',';
}
result += '\n';
}
result += indent + ']';
return result;
}
else {
var keys = Object.keys(obj);
if (keys.length === 0) {
return '{}';
}
var result = '{\n';
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
result += newIndent + JSON.stringify(key) + ': ' + stringifyObject(obj[key], newIndent, stringifyLiteral);
if (i < keys.length - 1) {
result += ',';
}
result += '\n';
}
result += indent + '}';
return result;
}
}
return stringifyLiteral(obj);
}

View File

@@ -0,0 +1,65 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
export function equals(one, other) {
if (one === other) {
return true;
}
if (one === null || one === undefined || other === null || other === undefined) {
return false;
}
if (typeof one !== typeof other) {
return false;
}
if (typeof one !== 'object') {
return false;
}
if ((Array.isArray(one)) !== (Array.isArray(other))) {
return false;
}
var i, key;
if (Array.isArray(one)) {
if (one.length !== other.length) {
return false;
}
for (i = 0; i < one.length; i++) {
if (!equals(one[i], other[i])) {
return false;
}
}
}
else {
var oneKeys = [];
for (key in one) {
oneKeys.push(key);
}
oneKeys.sort();
var otherKeys = [];
for (key in other) {
otherKeys.push(key);
}
otherKeys.sort();
if (!equals(oneKeys, otherKeys)) {
return false;
}
for (i = 0; i < oneKeys.length; i++) {
if (!equals(one[oneKeys[i]], other[oneKeys[i]])) {
return false;
}
}
}
return true;
}
export function isNumber(val) {
return typeof val === 'number';
}
export function isDefined(val) {
return typeof val !== 'undefined';
}
export function isBoolean(val) {
return typeof val === 'boolean';
}
export function isString(val) {
return typeof val === 'string';
}

View File

@@ -0,0 +1,52 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
export function startsWith(haystack, needle) {
if (haystack.length < needle.length) {
return false;
}
for (var i = 0; i < needle.length; i++) {
if (haystack[i] !== needle[i]) {
return false;
}
}
return true;
}
/**
* Determines if haystack ends with needle.
*/
export function endsWith(haystack, needle) {
var diff = haystack.length - needle.length;
if (diff > 0) {
return haystack.lastIndexOf(needle) === diff;
}
else if (diff === 0) {
return haystack === needle;
}
else {
return false;
}
}
export function convertSimple2RegExpPattern(pattern) {
return pattern.replace(/[\-\\\{\}\+\?\|\^\$\.\,\[\]\(\)\#\s]/g, '\\$&').replace(/[\*]/g, '.*');
}
export function repeat(value, count) {
var s = '';
while (count > 0) {
if ((count & 1) === 1) {
s += value;
}
value += value;
count = count >>> 1;
}
return s;
}
export function extendedRegExp(pattern) {
if (startsWith(pattern, '(?i)')) {
return new RegExp(pattern.substring(4), 'i');
}
else {
return new RegExp(pattern);
}
}

View File

@@ -0,0 +1,17 @@
import { Thenable, MarkedString, CompletionItem } from './jsonLanguageService';
export interface JSONWorkerContribution {
getInfoContribution(uri: string, location: JSONPath): Thenable<MarkedString[]>;
collectPropertyCompletions(uri: string, location: JSONPath, currentWord: string, addValue: boolean, isLast: boolean, result: CompletionsCollector): Thenable<any>;
collectValueCompletions(uri: string, location: JSONPath, propertyKey: string, result: CompletionsCollector): Thenable<any>;
collectDefaultCompletions(uri: string, result: CompletionsCollector): Thenable<any>;
resolveCompletion?(item: CompletionItem): Thenable<CompletionItem>;
}
export declare type Segment = string | number;
export declare type JSONPath = Segment[];
export interface CompletionsCollector {
add(suggestion: CompletionItem): void;
error(message: string): void;
log(message: string): void;
setAsIncomplete(): void;
getNumberOfProposals(): number;
}

View File

@@ -0,0 +1,12 @@
(function (factory) {
if (typeof module === "object" && typeof module.exports === "object") {
var v = factory(require, exports);
if (v !== undefined) module.exports = v;
}
else if (typeof define === "function" && define.amd) {
define(["require", "exports"], factory);
}
})(function (require, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
});

View File

@@ -0,0 +1,28 @@
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 } from './jsonLanguageTypes';
import { DocumentLink } from 'vscode-languageserver-types';
export declare type JSONDocument = {
root: ASTNode | undefined;
getNodeFromOffset(offset: number, includeRightBound?: boolean): ASTNode | undefined;
};
export * from './jsonLanguageTypes';
export interface LanguageService {
configure(settings: LanguageSettings): void;
doValidation(document: TextDocument, jsonDocument: JSONDocument, documentSettings?: DocumentLanguageSettings, schema?: JSONSchema): Thenable<Diagnostic[]>;
parseJSONDocument(document: TextDocument): JSONDocument;
newJSONDocument(rootNode: ASTNode, syntaxDiagnostics?: Diagnostic[]): JSONDocument;
resetSchema(uri: string): boolean;
getMatchingSchemas(document: TextDocument, jsonDocument: JSONDocument, schema?: JSONSchema): Thenable<MatchingSchema[]>;
doResolve(item: CompletionItem): Thenable<CompletionItem>;
doComplete(document: TextDocument, position: Position, doc: JSONDocument): Thenable<CompletionList | null>;
findDocumentSymbols(document: TextDocument, doc: JSONDocument, context?: DocumentSymbolsContext): SymbolInformation[];
findDocumentSymbols2(document: TextDocument, doc: JSONDocument, context?: DocumentSymbolsContext): DocumentSymbol[];
findDocumentColors(document: TextDocument, doc: JSONDocument, context?: DocumentColorsContext): Thenable<ColorInformation[]>;
getColorPresentations(document: TextDocument, doc: JSONDocument, color: Color, range: Range): ColorPresentation[];
doHover(document: TextDocument, position: Position, doc: JSONDocument): Thenable<Hover | null>;
format(document: TextDocument, range: Range, options: FormattingOptions): TextEdit[];
getFoldingRanges(document: TextDocument, context?: FoldingRangesContext): FoldingRange[];
getSelectionRanges(document: TextDocument, positions: Position[], doc: JSONDocument): SelectionRange[];
findDefinition(document: TextDocument, position: Position, doc: JSONDocument): Thenable<DefinitionLink[]>;
findLinks(document: TextDocument, doc: JSONDocument): Thenable<DocumentLink[]>;
}
export declare function getLanguageService(params: LanguageServiceParams): LanguageService;

View File

@@ -0,0 +1,89 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
(function (factory) {
if (typeof module === "object" && typeof module.exports === "object") {
var v = factory(require, exports);
if (v !== undefined) module.exports = v;
}
else if (typeof define === "function" && define.amd) {
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);
}
})(function (require, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getLanguageService = void 0;
var jsonCompletion_1 = require("./services/jsonCompletion");
var jsonHover_1 = require("./services/jsonHover");
var jsonValidation_1 = require("./services/jsonValidation");
var jsonDocumentSymbols_1 = require("./services/jsonDocumentSymbols");
var jsonParser_1 = require("./parser/jsonParser");
var configuration_1 = require("./services/configuration");
var jsonSchemaService_1 = require("./services/jsonSchemaService");
var jsonFolding_1 = require("./services/jsonFolding");
var jsonSelectionRanges_1 = require("./services/jsonSelectionRanges");
var jsonc_parser_1 = require("jsonc-parser");
var jsonLanguageTypes_1 = require("./jsonLanguageTypes");
var jsonLinks_1 = require("./services/jsonLinks");
__exportStar(require("./jsonLanguageTypes"), exports);
function getLanguageService(params) {
var promise = params.promiseConstructor || Promise;
var jsonSchemaService = new jsonSchemaService_1.JSONSchemaService(params.schemaRequestService, params.workspaceContext, promise);
jsonSchemaService.setSchemaContributions(configuration_1.schemaContributions);
var jsonCompletion = new jsonCompletion_1.JSONCompletion(jsonSchemaService, params.contributions, promise, params.clientCapabilities);
var jsonHover = new jsonHover_1.JSONHover(jsonSchemaService, params.contributions, promise);
var jsonDocumentSymbols = new jsonDocumentSymbols_1.JSONDocumentSymbols(jsonSchemaService);
var jsonValidation = new jsonValidation_1.JSONValidation(jsonSchemaService, promise);
return {
configure: function (settings) {
jsonSchemaService.clearExternalSchemas();
if (settings.schemas) {
settings.schemas.forEach(function (settings) {
jsonSchemaService.registerExternalSchema(settings.uri, settings.fileMatch, settings.schema);
});
}
jsonValidation.configure(settings);
},
resetSchema: function (uri) { return jsonSchemaService.onResourceChange(uri); },
doValidation: jsonValidation.doValidation.bind(jsonValidation),
parseJSONDocument: function (document) { return jsonParser_1.parse(document, { collectComments: true }); },
newJSONDocument: function (root, diagnostics) { return jsonParser_1.newJSONDocument(root, diagnostics); },
getMatchingSchemas: jsonSchemaService.getMatchingSchemas.bind(jsonSchemaService),
doResolve: jsonCompletion.doResolve.bind(jsonCompletion),
doComplete: jsonCompletion.doComplete.bind(jsonCompletion),
findDocumentSymbols: jsonDocumentSymbols.findDocumentSymbols.bind(jsonDocumentSymbols),
findDocumentSymbols2: jsonDocumentSymbols.findDocumentSymbols2.bind(jsonDocumentSymbols),
findDocumentColors: jsonDocumentSymbols.findDocumentColors.bind(jsonDocumentSymbols),
getColorPresentations: jsonDocumentSymbols.getColorPresentations.bind(jsonDocumentSymbols),
doHover: jsonHover.doHover.bind(jsonHover),
getFoldingRanges: jsonFolding_1.getFoldingRanges,
getSelectionRanges: jsonSelectionRanges_1.getSelectionRanges,
findDefinition: function () { return Promise.resolve([]); },
findLinks: jsonLinks_1.findLinks,
format: function (d, r, o) {
var range = undefined;
if (r) {
var offset = d.offsetAt(r.start);
var length = d.offsetAt(r.end) - offset;
range = { offset: offset, length: length };
}
var options = { tabSize: o ? o.tabSize : 4, insertSpaces: (o === null || o === void 0 ? void 0 : o.insertSpaces) === true, insertFinalNewline: (o === null || o === void 0 ? void 0 : o.insertFinalNewline) === true, eol: '\n' };
return jsonc_parser_1.format(d.getText(), range, options).map(function (e) {
return jsonLanguageTypes_1.TextEdit.replace(jsonLanguageTypes_1.Range.create(d.positionAt(e.offset), d.positionAt(e.offset + e.length)), e.content);
});
}
};
}
exports.getLanguageService = getLanguageService;
});

View File

@@ -0,0 +1,275 @@
import { JSONWorkerContribution, JSONPath, Segment, CompletionsCollector } from './jsonContributions';
import { JSONSchema } from './jsonSchema';
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';
import { TextDocument } from 'vscode-languageserver-textdocument';
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 };
/**
* Error codes used by diagnostics
*/
export declare enum ErrorCode {
Undefined = 0,
EnumValueMismatch = 1,
Deprecated = 2,
UnexpectedEndOfComment = 257,
UnexpectedEndOfString = 258,
UnexpectedEndOfNumber = 259,
InvalidUnicode = 260,
InvalidEscapeCharacter = 261,
InvalidCharacter = 262,
PropertyExpected = 513,
CommaExpected = 514,
ColonExpected = 515,
ValueExpected = 516,
CommaOrCloseBacketExpected = 517,
CommaOrCloseBraceExpected = 518,
TrailingComma = 519,
DuplicateKey = 520,
CommentNotPermitted = 521,
SchemaResolveError = 768
}
export declare type ASTNode = ObjectASTNode | PropertyASTNode | ArrayASTNode | StringASTNode | NumberASTNode | BooleanASTNode | NullASTNode;
export interface BaseASTNode {
readonly type: 'object' | 'array' | 'property' | 'string' | 'number' | 'boolean' | 'null';
readonly parent?: ASTNode;
readonly offset: number;
readonly length: number;
readonly children?: ASTNode[];
readonly value?: string | boolean | number | null;
}
export interface ObjectASTNode extends BaseASTNode {
readonly type: 'object';
readonly properties: PropertyASTNode[];
readonly children: ASTNode[];
}
export interface PropertyASTNode extends BaseASTNode {
readonly type: 'property';
readonly keyNode: StringASTNode;
readonly valueNode?: ASTNode;
readonly colonOffset?: number;
readonly children: ASTNode[];
}
export interface ArrayASTNode extends BaseASTNode {
readonly type: 'array';
readonly items: ASTNode[];
readonly children: ASTNode[];
}
export interface StringASTNode extends BaseASTNode {
readonly type: 'string';
readonly value: string;
}
export interface NumberASTNode extends BaseASTNode {
readonly type: 'number';
readonly value: number;
readonly isInteger: boolean;
}
export interface BooleanASTNode extends BaseASTNode {
readonly type: 'boolean';
readonly value: boolean;
}
export interface NullASTNode extends BaseASTNode {
readonly type: 'null';
readonly value: null;
}
export interface MatchingSchema {
node: ASTNode;
schema: JSONSchema;
}
export interface LanguageSettings {
/**
* If set, the validator will return syntax and semantic errors.
*/
validate?: boolean;
/**
* Defines whether comments are allowed or not. If set to false, comments will be reported as errors.
* DocumentLanguageSettings.allowComments will override this setting.
*/
allowComments?: boolean;
/**
* A list of known schemas and/or associations of schemas to file names.
*/
schemas?: SchemaConfiguration[];
}
export declare type SeverityLevel = 'error' | 'warning' | 'ignore';
export interface DocumentLanguageSettings {
/**
* The severity of reported comments. If not set, 'LanguageSettings.allowComments' defines whether comments are ignored or reported as errors.
*/
comments?: SeverityLevel;
/**
* The severity of reported trailing commas. If not set, trailing commas will be reported as errors.
*/
trailingCommas?: SeverityLevel;
/**
* The severity of problems from schema validation. If set to 'ignore', schema validation will be skipped. If not set, 'warning' is used.
*/
schemaValidation?: SeverityLevel;
/**
* The severity of problems that occurred when resolving and loading schemas. If set to 'ignore', schema resolving problems are not reported. If not set, 'warning' is used.
*/
schemaRequest?: SeverityLevel;
}
export interface SchemaConfiguration {
/**
* The URI of the schema, which is also the identifier of the schema.
*/
uri: string;
/**
* A list of glob patterns that describe for which file URIs the JSON schema will be used.
* '*' and '**' wildcards are supported. Exclusion patterns start with '!'.
* For example '*.schema.json', 'package.json', '!foo*.schema.json', 'foo/**\/BADRESP.json'.
* A match succeeds when there is at least one pattern matching and last matching pattern does not start with '!'.
*/
fileMatch?: string[];
/**
* The schema for the given URI.
* If no schema is provided, the schema will be fetched with the schema request service (if available).
*/
schema?: JSONSchema;
}
export interface WorkspaceContextService {
resolveRelativePath(relativePath: string, resource: string): string;
}
/**
* The schema request service is used to fetch schemas. If successful, returns a resolved promise with the content of the schema.
* In case of an error, returns a rejected promise with a displayable error string.
*/
export interface SchemaRequestService {
(uri: string): Thenable<string>;
}
export interface PromiseConstructor {
/**
* Creates a new Promise.
* @param executor A callback used to initialize the promise. This callback is passed two arguments:
* a resolve callback used resolve the promise with a value or the result of another promise,
* and a reject callback used to reject the promise with a provided reason or error.
*/
new <T>(executor: (resolve: (value?: T | Thenable<T | undefined>) => void, reject: (reason?: any) => void) => void): Thenable<T | undefined>;
/**
* Creates a Promise that is resolved with an array of results when all of the provided Promises
* resolve, or rejected when any Promise is rejected.
* @param values An array of Promises.
* @returns A new Promise.
*/
all<T>(values: Array<T | Thenable<T>>): Thenable<T[]>;
/**
* Creates a new rejected promise for the provided reason.
* @param reason The reason the promise was rejected.
* @returns A new rejected Promise.
*/
reject<T>(reason: any): Thenable<T>;
/**
* Creates a new resolved promise for the provided value.
* @param value A promise.
* @returns A promise whose internal state matches the provided promise.
*/
resolve<T>(value: T | Thenable<T>): Thenable<T>;
}
export interface Thenable<R> {
/**
* Attaches callbacks for the resolution and/or rejection of the Promise.
* @param onfulfilled The callback to execute when the Promise is resolved.
* @param onrejected The callback to execute when the Promise is rejected.
* @returns A Promise for the completion of which ever callback is executed.
*/
then<TResult>(onfulfilled?: (value: R) => TResult | Thenable<TResult>, onrejected?: (reason: any) => TResult | Thenable<TResult>): Thenable<TResult>;
then<TResult>(onfulfilled?: (value: R) => TResult | Thenable<TResult>, onrejected?: (reason: any) => void): Thenable<TResult>;
}
export interface LanguageServiceParams {
/**
* The schema request service is used to fetch schemas from a URI. The provider returns the schema file content, or,
* in case of an error, a displayable error string
*/
schemaRequestService?: SchemaRequestService;
/**
* The workspace context is used to resolve relative paths for relative schema references.
*/
workspaceContext?: WorkspaceContextService;
/**
* An optional set of completion and hover participants.
*/
contributions?: JSONWorkerContribution[];
/**
* A promise constructor. If not set, the ES5 Promise will be used.
*/
promiseConstructor?: PromiseConstructor;
/**
* Describes the LSP capabilities the client supports.
*/
clientCapabilities?: ClientCapabilities;
}
/**
* Describes what LSP capabilities the client supports
*/
export interface ClientCapabilities {
/**
* The text document client capabilities
*/
textDocument?: {
/**
* Capabilities specific to completions.
*/
completion?: {
/**
* The client supports the following `CompletionItem` specific
* capabilities.
*/
completionItem?: {
/**
* Client supports the follow content formats for the documentation
* property. The order describes the preferred format of the client.
*/
documentationFormat?: MarkupKind[];
/**
* The client supports commit characters on a completion item.
*/
commitCharactersSupport?: boolean;
};
};
/**
* Capabilities specific to hovers.
*/
hover?: {
/**
* Client supports the follow content formats for the content
* property. The order describes the preferred format of the client.
*/
contentFormat?: MarkupKind[];
};
};
}
export declare namespace ClientCapabilities {
const LATEST: ClientCapabilities;
}
export interface FoldingRangesContext {
/**
* The maximal number of ranges returned.
*/
rangeLimit?: number;
/**
* Called when the result was cropped.
*/
onRangeLimitExceeded?: (uri: string) => void;
}
export interface DocumentSymbolsContext {
/**
* The maximal number of document symbols returned.
*/
resultLimit?: number;
/**
* Called when the result was cropped.
*/
onResultLimitExceeded?: (uri: string) => void;
}
export interface ColorInformationContext {
/**
* The maximal number of color informations returned.
*/
resultLimit?: number;
/**
* Called when the result was cropped.
*/
onResultLimitExceeded?: (uri: string) => void;
}
export interface FormattingOptions extends LSPFormattingOptions {
insertFinalNewline?: boolean;
}

View File

@@ -0,0 +1,92 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
(function (factory) {
if (typeof module === "object" && typeof module.exports === "object") {
var v = factory(require, exports);
if (v !== undefined) module.exports = v;
}
else if (typeof define === "function" && define.amd) {
define(["require", "exports", "vscode-languageserver-types", "vscode-languageserver-textdocument"], factory);
}
})(function (require, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ClientCapabilities = exports.ErrorCode = exports.DocumentHighlightKind = exports.VersionedTextDocumentIdentifier = exports.TextDocumentEdit = exports.CodeActionKind = exports.TextEdit = exports.WorkspaceEdit = exports.DocumentLink = exports.DocumentHighlight = exports.CodeAction = exports.Command = exports.CodeActionContext = exports.MarkedString = exports.Hover = exports.Location = exports.DocumentSymbol = exports.SymbolKind = exports.SymbolInformation = exports.InsertTextFormat = exports.CompletionItemTag = exports.CompletionList = exports.CompletionItemKind = exports.CompletionItem = exports.DiagnosticSeverity = exports.Diagnostic = exports.SelectionRange = exports.FoldingRangeKind = exports.FoldingRange = exports.ColorPresentation = exports.ColorInformation = exports.Color = exports.MarkupKind = exports.MarkupContent = exports.Position = exports.Range = exports.TextDocument = void 0;
var vscode_languageserver_types_1 = require("vscode-languageserver-types");
Object.defineProperty(exports, "Range", { enumerable: true, get: function () { return vscode_languageserver_types_1.Range; } });
Object.defineProperty(exports, "Position", { enumerable: true, get: function () { return vscode_languageserver_types_1.Position; } });
Object.defineProperty(exports, "MarkupContent", { enumerable: true, get: function () { return vscode_languageserver_types_1.MarkupContent; } });
Object.defineProperty(exports, "MarkupKind", { enumerable: true, get: function () { return vscode_languageserver_types_1.MarkupKind; } });
Object.defineProperty(exports, "Color", { enumerable: true, get: function () { return vscode_languageserver_types_1.Color; } });
Object.defineProperty(exports, "ColorInformation", { enumerable: true, get: function () { return vscode_languageserver_types_1.ColorInformation; } });
Object.defineProperty(exports, "ColorPresentation", { enumerable: true, get: function () { return vscode_languageserver_types_1.ColorPresentation; } });
Object.defineProperty(exports, "FoldingRange", { enumerable: true, get: function () { return vscode_languageserver_types_1.FoldingRange; } });
Object.defineProperty(exports, "FoldingRangeKind", { enumerable: true, get: function () { return vscode_languageserver_types_1.FoldingRangeKind; } });
Object.defineProperty(exports, "SelectionRange", { enumerable: true, get: function () { return vscode_languageserver_types_1.SelectionRange; } });
Object.defineProperty(exports, "Diagnostic", { enumerable: true, get: function () { return vscode_languageserver_types_1.Diagnostic; } });
Object.defineProperty(exports, "DiagnosticSeverity", { enumerable: true, get: function () { return vscode_languageserver_types_1.DiagnosticSeverity; } });
Object.defineProperty(exports, "CompletionItem", { enumerable: true, get: function () { return vscode_languageserver_types_1.CompletionItem; } });
Object.defineProperty(exports, "CompletionItemKind", { enumerable: true, get: function () { return vscode_languageserver_types_1.CompletionItemKind; } });
Object.defineProperty(exports, "CompletionList", { enumerable: true, get: function () { return vscode_languageserver_types_1.CompletionList; } });
Object.defineProperty(exports, "CompletionItemTag", { enumerable: true, get: function () { return vscode_languageserver_types_1.CompletionItemTag; } });
Object.defineProperty(exports, "InsertTextFormat", { enumerable: true, get: function () { return vscode_languageserver_types_1.InsertTextFormat; } });
Object.defineProperty(exports, "SymbolInformation", { enumerable: true, get: function () { return vscode_languageserver_types_1.SymbolInformation; } });
Object.defineProperty(exports, "SymbolKind", { enumerable: true, get: function () { return vscode_languageserver_types_1.SymbolKind; } });
Object.defineProperty(exports, "DocumentSymbol", { enumerable: true, get: function () { return vscode_languageserver_types_1.DocumentSymbol; } });
Object.defineProperty(exports, "Location", { enumerable: true, get: function () { return vscode_languageserver_types_1.Location; } });
Object.defineProperty(exports, "Hover", { enumerable: true, get: function () { return vscode_languageserver_types_1.Hover; } });
Object.defineProperty(exports, "MarkedString", { enumerable: true, get: function () { return vscode_languageserver_types_1.MarkedString; } });
Object.defineProperty(exports, "CodeActionContext", { enumerable: true, get: function () { return vscode_languageserver_types_1.CodeActionContext; } });
Object.defineProperty(exports, "Command", { enumerable: true, get: function () { return vscode_languageserver_types_1.Command; } });
Object.defineProperty(exports, "CodeAction", { enumerable: true, get: function () { return vscode_languageserver_types_1.CodeAction; } });
Object.defineProperty(exports, "DocumentHighlight", { enumerable: true, get: function () { return vscode_languageserver_types_1.DocumentHighlight; } });
Object.defineProperty(exports, "DocumentLink", { enumerable: true, get: function () { return vscode_languageserver_types_1.DocumentLink; } });
Object.defineProperty(exports, "WorkspaceEdit", { enumerable: true, get: function () { return vscode_languageserver_types_1.WorkspaceEdit; } });
Object.defineProperty(exports, "TextEdit", { enumerable: true, get: function () { return vscode_languageserver_types_1.TextEdit; } });
Object.defineProperty(exports, "CodeActionKind", { enumerable: true, get: function () { return vscode_languageserver_types_1.CodeActionKind; } });
Object.defineProperty(exports, "TextDocumentEdit", { enumerable: true, get: function () { return vscode_languageserver_types_1.TextDocumentEdit; } });
Object.defineProperty(exports, "VersionedTextDocumentIdentifier", { enumerable: true, get: function () { return vscode_languageserver_types_1.VersionedTextDocumentIdentifier; } });
Object.defineProperty(exports, "DocumentHighlightKind", { enumerable: true, get: function () { return vscode_languageserver_types_1.DocumentHighlightKind; } });
var vscode_languageserver_textdocument_1 = require("vscode-languageserver-textdocument");
Object.defineProperty(exports, "TextDocument", { enumerable: true, get: function () { return vscode_languageserver_textdocument_1.TextDocument; } });
/**
* Error codes used by diagnostics
*/
var ErrorCode;
(function (ErrorCode) {
ErrorCode[ErrorCode["Undefined"] = 0] = "Undefined";
ErrorCode[ErrorCode["EnumValueMismatch"] = 1] = "EnumValueMismatch";
ErrorCode[ErrorCode["Deprecated"] = 2] = "Deprecated";
ErrorCode[ErrorCode["UnexpectedEndOfComment"] = 257] = "UnexpectedEndOfComment";
ErrorCode[ErrorCode["UnexpectedEndOfString"] = 258] = "UnexpectedEndOfString";
ErrorCode[ErrorCode["UnexpectedEndOfNumber"] = 259] = "UnexpectedEndOfNumber";
ErrorCode[ErrorCode["InvalidUnicode"] = 260] = "InvalidUnicode";
ErrorCode[ErrorCode["InvalidEscapeCharacter"] = 261] = "InvalidEscapeCharacter";
ErrorCode[ErrorCode["InvalidCharacter"] = 262] = "InvalidCharacter";
ErrorCode[ErrorCode["PropertyExpected"] = 513] = "PropertyExpected";
ErrorCode[ErrorCode["CommaExpected"] = 514] = "CommaExpected";
ErrorCode[ErrorCode["ColonExpected"] = 515] = "ColonExpected";
ErrorCode[ErrorCode["ValueExpected"] = 516] = "ValueExpected";
ErrorCode[ErrorCode["CommaOrCloseBacketExpected"] = 517] = "CommaOrCloseBacketExpected";
ErrorCode[ErrorCode["CommaOrCloseBraceExpected"] = 518] = "CommaOrCloseBraceExpected";
ErrorCode[ErrorCode["TrailingComma"] = 519] = "TrailingComma";
ErrorCode[ErrorCode["DuplicateKey"] = 520] = "DuplicateKey";
ErrorCode[ErrorCode["CommentNotPermitted"] = 521] = "CommentNotPermitted";
ErrorCode[ErrorCode["SchemaResolveError"] = 768] = "SchemaResolveError";
})(ErrorCode = exports.ErrorCode || (exports.ErrorCode = {}));
var ClientCapabilities;
(function (ClientCapabilities) {
ClientCapabilities.LATEST = {
textDocument: {
completion: {
completionItem: {
documentationFormat: [vscode_languageserver_types_1.MarkupKind.Markdown, vscode_languageserver_types_1.MarkupKind.PlainText],
commitCharactersSupport: true
}
}
}
};
})(ClientCapabilities = exports.ClientCapabilities || (exports.ClientCapabilities = {}));
});

View File

@@ -0,0 +1,70 @@
export declare type JSONSchemaRef = JSONSchema | boolean;
export interface JSONSchema {
id?: string;
$id?: string;
$schema?: string;
type?: string | string[];
title?: string;
default?: any;
definitions?: {
[name: string]: JSONSchema;
};
description?: string;
properties?: JSONSchemaMap;
patternProperties?: JSONSchemaMap;
additionalProperties?: boolean | JSONSchemaRef;
minProperties?: number;
maxProperties?: number;
dependencies?: JSONSchemaMap | {
[prop: string]: string[];
};
items?: JSONSchemaRef | JSONSchemaRef[];
minItems?: number;
maxItems?: number;
uniqueItems?: boolean;
additionalItems?: boolean | JSONSchemaRef;
pattern?: string;
minLength?: number;
maxLength?: number;
minimum?: number;
maximum?: number;
exclusiveMinimum?: boolean | number;
exclusiveMaximum?: boolean | number;
multipleOf?: number;
required?: string[];
$ref?: string;
anyOf?: JSONSchemaRef[];
allOf?: JSONSchemaRef[];
oneOf?: JSONSchemaRef[];
not?: JSONSchemaRef;
enum?: any[];
format?: string;
const?: any;
contains?: JSONSchemaRef;
propertyNames?: JSONSchemaRef;
examples?: any[];
$comment?: string;
if?: JSONSchemaRef;
then?: JSONSchemaRef;
else?: JSONSchemaRef;
defaultSnippets?: {
label?: string;
description?: string;
markdownDescription?: string;
body?: any;
bodyText?: string;
}[];
errorMessage?: string;
patternErrorMessage?: string;
deprecationMessage?: string;
enumDescriptions?: string[];
markdownEnumDescriptions?: string[];
markdownDescription?: string;
doNotSuggest?: boolean;
suggestSortText?: string;
allowComments?: boolean;
allowTrailingCommas?: boolean;
}
export interface JSONSchemaMap {
[name: string]: JSONSchemaRef;
}

View File

@@ -0,0 +1,12 @@
(function (factory) {
if (typeof module === "object" && typeof module.exports === "object") {
var v = factory(require, exports);
if (v !== undefined) module.exports = v;
}
else if (typeof define === "function" && define.amd) {
define(["require", "exports"], factory);
}
})(function (require, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
});

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,541 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
(function (factory) {
if (typeof module === "object" && typeof module.exports === "object") {
var v = factory(require, exports);
if (v !== undefined) module.exports = v;
}
else if (typeof define === "function" && define.amd) {
define(["require", "exports", "vscode-nls"], factory);
}
})(function (require, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.schemaContributions = void 0;
var nls = require("vscode-nls");
var localize = nls.loadMessageBundle();
exports.schemaContributions = {
schemaAssociations: [],
schemas: {
// refer to the latest schema
'http://json-schema.org/schema#': {
$ref: 'http://json-schema.org/draft-07/schema#'
},
// bundle the schema-schema to include (localized) descriptions
'http://json-schema.org/draft-04/schema#': {
'$schema': 'http://json-schema.org/draft-04/schema#',
'definitions': {
'schemaArray': {
'type': 'array',
'minItems': 1,
'items': {
'$ref': '#'
}
},
'positiveInteger': {
'type': 'integer',
'minimum': 0
},
'positiveIntegerDefault0': {
'allOf': [
{
'$ref': '#/definitions/positiveInteger'
},
{
'default': 0
}
]
},
'simpleTypes': {
'type': 'string',
'enum': [
'array',
'boolean',
'integer',
'null',
'number',
'object',
'string'
]
},
'stringArray': {
'type': 'array',
'items': {
'type': 'string'
},
'minItems': 1,
'uniqueItems': true
}
},
'type': 'object',
'properties': {
'id': {
'type': 'string',
'format': 'uri'
},
'$schema': {
'type': 'string',
'format': 'uri'
},
'title': {
'type': 'string'
},
'description': {
'type': 'string'
},
'default': {},
'multipleOf': {
'type': 'number',
'minimum': 0,
'exclusiveMinimum': true
},
'maximum': {
'type': 'number'
},
'exclusiveMaximum': {
'type': 'boolean',
'default': false
},
'minimum': {
'type': 'number'
},
'exclusiveMinimum': {
'type': 'boolean',
'default': false
},
'maxLength': {
'allOf': [
{
'$ref': '#/definitions/positiveInteger'
}
]
},
'minLength': {
'allOf': [
{
'$ref': '#/definitions/positiveIntegerDefault0'
}
]
},
'pattern': {
'type': 'string',
'format': 'regex'
},
'additionalItems': {
'anyOf': [
{
'type': 'boolean'
},
{
'$ref': '#'
}
],
'default': {}
},
'items': {
'anyOf': [
{
'$ref': '#'
},
{
'$ref': '#/definitions/schemaArray'
}
],
'default': {}
},
'maxItems': {
'allOf': [
{
'$ref': '#/definitions/positiveInteger'
}
]
},
'minItems': {
'allOf': [
{
'$ref': '#/definitions/positiveIntegerDefault0'
}
]
},
'uniqueItems': {
'type': 'boolean',
'default': false
},
'maxProperties': {
'allOf': [
{
'$ref': '#/definitions/positiveInteger'
}
]
},
'minProperties': {
'allOf': [
{
'$ref': '#/definitions/positiveIntegerDefault0'
}
]
},
'required': {
'allOf': [
{
'$ref': '#/definitions/stringArray'
}
]
},
'additionalProperties': {
'anyOf': [
{
'type': 'boolean'
},
{
'$ref': '#'
}
],
'default': {}
},
'definitions': {
'type': 'object',
'additionalProperties': {
'$ref': '#'
},
'default': {}
},
'properties': {
'type': 'object',
'additionalProperties': {
'$ref': '#'
},
'default': {}
},
'patternProperties': {
'type': 'object',
'additionalProperties': {
'$ref': '#'
},
'default': {}
},
'dependencies': {
'type': 'object',
'additionalProperties': {
'anyOf': [
{
'$ref': '#'
},
{
'$ref': '#/definitions/stringArray'
}
]
}
},
'enum': {
'type': 'array',
'minItems': 1,
'uniqueItems': true
},
'type': {
'anyOf': [
{
'$ref': '#/definitions/simpleTypes'
},
{
'type': 'array',
'items': {
'$ref': '#/definitions/simpleTypes'
},
'minItems': 1,
'uniqueItems': true
}
]
},
'format': {
'anyOf': [
{
'type': 'string',
'enum': [
'date-time',
'uri',
'email',
'hostname',
'ipv4',
'ipv6',
'regex'
]
},
{
'type': 'string'
}
]
},
'allOf': {
'allOf': [
{
'$ref': '#/definitions/schemaArray'
}
]
},
'anyOf': {
'allOf': [
{
'$ref': '#/definitions/schemaArray'
}
]
},
'oneOf': {
'allOf': [
{
'$ref': '#/definitions/schemaArray'
}
]
},
'not': {
'allOf': [
{
'$ref': '#'
}
]
}
},
'dependencies': {
'exclusiveMaximum': [
'maximum'
],
'exclusiveMinimum': [
'minimum'
]
},
'default': {}
},
'http://json-schema.org/draft-07/schema#': {
'definitions': {
'schemaArray': {
'type': 'array',
'minItems': 1,
'items': { '$ref': '#' }
},
'nonNegativeInteger': {
'type': 'integer',
'minimum': 0
},
'nonNegativeIntegerDefault0': {
'allOf': [
{ '$ref': '#/definitions/nonNegativeInteger' },
{ 'default': 0 }
]
},
'simpleTypes': {
'enum': [
'array',
'boolean',
'integer',
'null',
'number',
'object',
'string'
]
},
'stringArray': {
'type': 'array',
'items': { 'type': 'string' },
'uniqueItems': true,
'default': []
}
},
'type': ['object', 'boolean'],
'properties': {
'$id': {
'type': 'string',
'format': 'uri-reference'
},
'$schema': {
'type': 'string',
'format': 'uri'
},
'$ref': {
'type': 'string',
'format': 'uri-reference'
},
'$comment': {
'type': 'string'
},
'title': {
'type': 'string'
},
'description': {
'type': 'string'
},
'default': true,
'readOnly': {
'type': 'boolean',
'default': false
},
'examples': {
'type': 'array',
'items': true
},
'multipleOf': {
'type': 'number',
'exclusiveMinimum': 0
},
'maximum': {
'type': 'number'
},
'exclusiveMaximum': {
'type': 'number'
},
'minimum': {
'type': 'number'
},
'exclusiveMinimum': {
'type': 'number'
},
'maxLength': { '$ref': '#/definitions/nonNegativeInteger' },
'minLength': { '$ref': '#/definitions/nonNegativeIntegerDefault0' },
'pattern': {
'type': 'string',
'format': 'regex'
},
'additionalItems': { '$ref': '#' },
'items': {
'anyOf': [
{ '$ref': '#' },
{ '$ref': '#/definitions/schemaArray' }
],
'default': true
},
'maxItems': { '$ref': '#/definitions/nonNegativeInteger' },
'minItems': { '$ref': '#/definitions/nonNegativeIntegerDefault0' },
'uniqueItems': {
'type': 'boolean',
'default': false
},
'contains': { '$ref': '#' },
'maxProperties': { '$ref': '#/definitions/nonNegativeInteger' },
'minProperties': { '$ref': '#/definitions/nonNegativeIntegerDefault0' },
'required': { '$ref': '#/definitions/stringArray' },
'additionalProperties': { '$ref': '#' },
'definitions': {
'type': 'object',
'additionalProperties': { '$ref': '#' },
'default': {}
},
'properties': {
'type': 'object',
'additionalProperties': { '$ref': '#' },
'default': {}
},
'patternProperties': {
'type': 'object',
'additionalProperties': { '$ref': '#' },
'propertyNames': { 'format': 'regex' },
'default': {}
},
'dependencies': {
'type': 'object',
'additionalProperties': {
'anyOf': [
{ '$ref': '#' },
{ '$ref': '#/definitions/stringArray' }
]
}
},
'propertyNames': { '$ref': '#' },
'const': true,
'enum': {
'type': 'array',
'items': true,
'minItems': 1,
'uniqueItems': true
},
'type': {
'anyOf': [
{ '$ref': '#/definitions/simpleTypes' },
{
'type': 'array',
'items': { '$ref': '#/definitions/simpleTypes' },
'minItems': 1,
'uniqueItems': true
}
]
},
'format': { 'type': 'string' },
'contentMediaType': { 'type': 'string' },
'contentEncoding': { 'type': 'string' },
'if': { '$ref': '#' },
'then': { '$ref': '#' },
'else': { '$ref': '#' },
'allOf': { '$ref': '#/definitions/schemaArray' },
'anyOf': { '$ref': '#/definitions/schemaArray' },
'oneOf': { '$ref': '#/definitions/schemaArray' },
'not': { '$ref': '#' }
},
'default': true
}
}
};
var descriptions = {
id: localize('schema.json.id', "A unique identifier for the schema."),
$schema: localize('schema.json.$schema', "The schema to verify this document against."),
title: localize('schema.json.title', "A descriptive title of the element."),
description: localize('schema.json.description', "A long description of the element. Used in hover menus and suggestions."),
default: localize('schema.json.default', "A default value. Used by suggestions."),
multipleOf: localize('schema.json.multipleOf', "A number that should cleanly divide the current value (i.e. have no remainder)."),
maximum: localize('schema.json.maximum', "The maximum numerical value, inclusive by default."),
exclusiveMaximum: localize('schema.json.exclusiveMaximum', "Makes the maximum property exclusive."),
minimum: localize('schema.json.minimum', "The minimum numerical value, inclusive by default."),
exclusiveMinimum: localize('schema.json.exclusiveMininum', "Makes the minimum property exclusive."),
maxLength: localize('schema.json.maxLength', "The maximum length of a string."),
minLength: localize('schema.json.minLength', "The minimum length of a string."),
pattern: localize('schema.json.pattern', "A regular expression to match the string against. It is not implicitly anchored."),
additionalItems: localize('schema.json.additionalItems', "For arrays, only when items is set as an array. If it is a schema, then this schema validates items after the ones specified by the items array. If it is false, then additional items will cause validation to fail."),
items: localize('schema.json.items', "For arrays. Can either be a schema to validate every element against or an array of schemas to validate each item against in order (the first schema will validate the first element, the second schema will validate the second element, and so on."),
maxItems: localize('schema.json.maxItems', "The maximum number of items that can be inside an array. Inclusive."),
minItems: localize('schema.json.minItems', "The minimum number of items that can be inside an array. Inclusive."),
uniqueItems: localize('schema.json.uniqueItems', "If all of the items in the array must be unique. Defaults to false."),
maxProperties: localize('schema.json.maxProperties', "The maximum number of properties an object can have. Inclusive."),
minProperties: localize('schema.json.minProperties', "The minimum number of properties an object can have. Inclusive."),
required: localize('schema.json.required', "An array of strings that lists the names of all properties required on this object."),
additionalProperties: localize('schema.json.additionalProperties', "Either a schema or a boolean. If a schema, then used to validate all properties not matched by 'properties' or 'patternProperties'. If false, then any properties not matched by either will cause this schema to fail."),
definitions: localize('schema.json.definitions', "Not used for validation. Place subschemas here that you wish to reference inline with $ref."),
properties: localize('schema.json.properties', "A map of property names to schemas for each property."),
patternProperties: localize('schema.json.patternProperties', "A map of regular expressions on property names to schemas for matching properties."),
dependencies: localize('schema.json.dependencies', "A map of property names to either an array of property names or a schema. An array of property names means the property named in the key depends on the properties in the array being present in the object in order to be valid. If the value is a schema, then the schema is only applied to the object if the property in the key exists on the object."),
enum: localize('schema.json.enum', "The set of literal values that are valid."),
type: localize('schema.json.type', "Either a string of one of the basic schema types (number, integer, null, array, object, boolean, string) or an array of strings specifying a subset of those types."),
format: localize('schema.json.format', "Describes the format expected for the value."),
allOf: localize('schema.json.allOf', "An array of schemas, all of which must match."),
anyOf: localize('schema.json.anyOf', "An array of schemas, where at least one must match."),
oneOf: localize('schema.json.oneOf', "An array of schemas, exactly one of which must match."),
not: localize('schema.json.not', "A schema which must not match."),
$id: localize('schema.json.$id', "A unique identifier for the schema."),
$ref: localize('schema.json.$ref', "Reference a definition hosted on any location."),
$comment: localize('schema.json.$comment', "Comments from schema authors to readers or maintainers of the schema."),
readOnly: localize('schema.json.readOnly', "Indicates that the value of the instance is managed exclusively by the owning authority."),
examples: localize('schema.json.examples', "Sample JSON values associated with a particular schema, for the purpose of illustrating usage."),
contains: localize('schema.json.contains', "An array instance is valid against \"contains\" if at least one of its elements is valid against the given schema."),
propertyNames: localize('schema.json.propertyNames', "If the instance is an object, this keyword validates if every property name in the instance validates against the provided schema."),
const: localize('schema.json.const', "An instance validates successfully against this keyword if its value is equal to the value of the keyword."),
contentMediaType: localize('schema.json.contentMediaType', "Describes the media type of a string property."),
contentEncoding: localize('schema.json.contentEncoding', "Describes the content encoding of a string property."),
if: localize('schema.json.if', "The validation outcome of the \"if\" subschema controls which of the \"then\" or \"else\" keywords are evaluated."),
then: localize('schema.json.then', "The \"if\" subschema is used for validation when the \"if\" subschema succeeds."),
else: localize('schema.json.else', "The \"else\" subschema is used for validation when the \"if\" subschema fails.")
};
for (var schemaName in exports.schemaContributions.schemas) {
var schema = exports.schemaContributions.schemas[schemaName];
for (var property in schema.properties) {
var propertyObject = schema.properties[property];
if (typeof propertyObject === 'boolean') {
propertyObject = schema.properties[property] = {};
}
var description = descriptions[property];
if (description) {
propertyObject['description'] = description;
}
else {
console.log(property + ": localize('schema.json." + property + "', \"\")");
}
}
}
});

View File

@@ -0,0 +1,947 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
(function (factory) {
if (typeof module === "object" && typeof module.exports === "object") {
var v = factory(require, exports);
if (v !== undefined) module.exports = v;
}
else if (typeof define === "function" && define.amd) {
define(["require", "exports", "../parser/jsonParser", "jsonc-parser", "../utils/json", "../utils/strings", "../utils/objects", "../jsonLanguageTypes", "vscode-nls"], factory);
}
})(function (require, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.JSONCompletion = void 0;
var Parser = require("../parser/jsonParser");
var Json = require("jsonc-parser");
var json_1 = require("../utils/json");
var strings_1 = require("../utils/strings");
var objects_1 = require("../utils/objects");
var jsonLanguageTypes_1 = require("../jsonLanguageTypes");
var nls = require("vscode-nls");
var localize = nls.loadMessageBundle();
var valueCommitCharacters = [',', '}', ']'];
var propertyCommitCharacters = [':'];
var JSONCompletion = /** @class */ (function () {
function JSONCompletion(schemaService, contributions, promiseConstructor, clientCapabilities) {
if (contributions === void 0) { contributions = []; }
if (promiseConstructor === void 0) { promiseConstructor = Promise; }
if (clientCapabilities === void 0) { clientCapabilities = {}; }
this.schemaService = schemaService;
this.contributions = contributions;
this.promiseConstructor = promiseConstructor;
this.clientCapabilities = clientCapabilities;
}
JSONCompletion.prototype.doResolve = function (item) {
for (var i = this.contributions.length - 1; i >= 0; i--) {
var resolveCompletion = this.contributions[i].resolveCompletion;
if (resolveCompletion) {
var resolver = resolveCompletion(item);
if (resolver) {
return resolver;
}
}
}
return this.promiseConstructor.resolve(item);
};
JSONCompletion.prototype.doComplete = function (document, position, doc) {
var _this = this;
var result = {
items: [],
isIncomplete: false
};
var text = document.getText();
var offset = document.offsetAt(position);
var node = doc.getNodeFromOffset(offset, true);
if (this.isInComment(document, node ? node.offset : 0, offset)) {
return Promise.resolve(result);
}
if (node && (offset === node.offset + node.length) && offset > 0) {
var ch = text[offset - 1];
if (node.type === 'object' && ch === '}' || node.type === 'array' && ch === ']') {
// after ] or }
node = node.parent;
}
}
var currentWord = this.getCurrentWord(document, offset);
var overwriteRange;
if (node && (node.type === 'string' || node.type === 'number' || node.type === 'boolean' || node.type === 'null')) {
overwriteRange = jsonLanguageTypes_1.Range.create(document.positionAt(node.offset), document.positionAt(node.offset + node.length));
}
else {
var overwriteStart = offset - currentWord.length;
if (overwriteStart > 0 && text[overwriteStart - 1] === '"') {
overwriteStart--;
}
overwriteRange = jsonLanguageTypes_1.Range.create(document.positionAt(overwriteStart), position);
}
var supportsCommitCharacters = false; //this.doesSupportsCommitCharacters(); disabled for now, waiting for new API: https://github.com/microsoft/vscode/issues/42544
var proposed = {};
var collector = {
add: function (suggestion) {
var label = suggestion.label;
var existing = proposed[label];
if (!existing) {
label = label.replace(/[\n]/g, '↵');
if (label.length > 60) {
var shortendedLabel = label.substr(0, 57).trim() + '...';
if (!proposed[shortendedLabel]) {
label = shortendedLabel;
}
}
if (overwriteRange && suggestion.insertText !== undefined) {
suggestion.textEdit = jsonLanguageTypes_1.TextEdit.replace(overwriteRange, suggestion.insertText);
}
if (supportsCommitCharacters) {
suggestion.commitCharacters = suggestion.kind === jsonLanguageTypes_1.CompletionItemKind.Property ? propertyCommitCharacters : valueCommitCharacters;
}
suggestion.label = label;
proposed[label] = suggestion;
result.items.push(suggestion);
}
else {
if (!existing.documentation) {
existing.documentation = suggestion.documentation;
}
if (!existing.detail) {
existing.detail = suggestion.detail;
}
}
},
setAsIncomplete: function () {
result.isIncomplete = true;
},
error: function (message) {
console.error(message);
},
log: function (message) {
console.log(message);
},
getNumberOfProposals: function () {
return result.items.length;
}
};
return this.schemaService.getSchemaForResource(document.uri, doc).then(function (schema) {
var collectionPromises = [];
var addValue = true;
var currentKey = '';
var currentProperty = undefined;
if (node) {
if (node.type === 'string') {
var parent = node.parent;
if (parent && parent.type === 'property' && parent.keyNode === node) {
addValue = !parent.valueNode;
currentProperty = parent;
currentKey = text.substr(node.offset + 1, node.length - 2);
if (parent) {
node = parent.parent;
}
}
}
}
// proposals for properties
if (node && node.type === 'object') {
// don't suggest keys when the cursor is just before the opening curly brace
if (node.offset === offset) {
return result;
}
// don't suggest properties that are already present
var properties = node.properties;
properties.forEach(function (p) {
if (!currentProperty || currentProperty !== p) {
proposed[p.keyNode.value] = jsonLanguageTypes_1.CompletionItem.create('__');
}
});
var separatorAfter_1 = '';
if (addValue) {
separatorAfter_1 = _this.evaluateSeparatorAfter(document, document.offsetAt(overwriteRange.end));
}
if (schema) {
// property proposals with schema
_this.getPropertyCompletions(schema, doc, node, addValue, separatorAfter_1, collector);
}
else {
// property proposals without schema
_this.getSchemaLessPropertyCompletions(doc, node, currentKey, collector);
}
var location_1 = Parser.getNodePath(node);
_this.contributions.forEach(function (contribution) {
var collectPromise = contribution.collectPropertyCompletions(document.uri, location_1, currentWord, addValue, separatorAfter_1 === '', collector);
if (collectPromise) {
collectionPromises.push(collectPromise);
}
});
if ((!schema && currentWord.length > 0 && text.charAt(offset - currentWord.length - 1) !== '"')) {
collector.add({
kind: jsonLanguageTypes_1.CompletionItemKind.Property,
label: _this.getLabelForValue(currentWord),
insertText: _this.getInsertTextForProperty(currentWord, undefined, false, separatorAfter_1),
insertTextFormat: jsonLanguageTypes_1.InsertTextFormat.Snippet, documentation: '',
});
collector.setAsIncomplete();
}
}
// proposals for values
var types = {};
if (schema) {
// value proposals with schema
_this.getValueCompletions(schema, doc, node, offset, document, collector, types);
}
else {
// value proposals without schema
_this.getSchemaLessValueCompletions(doc, node, offset, document, collector);
}
if (_this.contributions.length > 0) {
_this.getContributedValueCompletions(doc, node, offset, document, collector, collectionPromises);
}
return _this.promiseConstructor.all(collectionPromises).then(function () {
if (collector.getNumberOfProposals() === 0) {
var offsetForSeparator = offset;
if (node && (node.type === 'string' || node.type === 'number' || node.type === 'boolean' || node.type === 'null')) {
offsetForSeparator = node.offset + node.length;
}
var separatorAfter = _this.evaluateSeparatorAfter(document, offsetForSeparator);
_this.addFillerValueCompletions(types, separatorAfter, collector);
}
return result;
});
});
};
JSONCompletion.prototype.getPropertyCompletions = function (schema, doc, node, addValue, separatorAfter, collector) {
var _this = this;
var matchingSchemas = doc.getMatchingSchemas(schema.schema, node.offset);
matchingSchemas.forEach(function (s) {
if (s.node === node && !s.inverted) {
var schemaProperties_1 = s.schema.properties;
if (schemaProperties_1) {
Object.keys(schemaProperties_1).forEach(function (key) {
var propertySchema = schemaProperties_1[key];
if (typeof propertySchema === 'object' && !propertySchema.deprecationMessage && !propertySchema.doNotSuggest) {
var proposal = {
kind: jsonLanguageTypes_1.CompletionItemKind.Property,
label: key,
insertText: _this.getInsertTextForProperty(key, propertySchema, addValue, separatorAfter),
insertTextFormat: jsonLanguageTypes_1.InsertTextFormat.Snippet,
filterText: _this.getFilterTextForValue(key),
documentation: _this.fromMarkup(propertySchema.markdownDescription) || propertySchema.description || '',
};
if (propertySchema.suggestSortText !== undefined) {
proposal.sortText = propertySchema.suggestSortText;
}
if (proposal.insertText && strings_1.endsWith(proposal.insertText, "$1" + separatorAfter)) {
proposal.command = {
title: 'Suggest',
command: 'editor.action.triggerSuggest'
};
}
collector.add(proposal);
}
});
}
var schemaPropertyNames_1 = s.schema.propertyNames;
if (typeof schemaPropertyNames_1 === 'object' && !schemaPropertyNames_1.deprecationMessage && !schemaPropertyNames_1.doNotSuggest) {
var propertyNameCompletionItem = function (name, enumDescription) {
if (enumDescription === void 0) { enumDescription = undefined; }
var proposal = {
kind: jsonLanguageTypes_1.CompletionItemKind.Property,
label: name,
insertText: _this.getInsertTextForProperty(name, undefined, addValue, separatorAfter),
insertTextFormat: jsonLanguageTypes_1.InsertTextFormat.Snippet,
filterText: _this.getFilterTextForValue(name),
documentation: enumDescription || _this.fromMarkup(schemaPropertyNames_1.markdownDescription) || schemaPropertyNames_1.description || '',
};
if (schemaPropertyNames_1.suggestSortText !== undefined) {
proposal.sortText = schemaPropertyNames_1.suggestSortText;
}
if (proposal.insertText && strings_1.endsWith(proposal.insertText, "$1" + separatorAfter)) {
proposal.command = {
title: 'Suggest',
command: 'editor.action.triggerSuggest'
};
}
collector.add(proposal);
};
if (schemaPropertyNames_1.enum) {
for (var i = 0; i < schemaPropertyNames_1.enum.length; i++) {
var enumDescription = undefined;
if (schemaPropertyNames_1.markdownEnumDescriptions && i < schemaPropertyNames_1.markdownEnumDescriptions.length) {
enumDescription = _this.fromMarkup(schemaPropertyNames_1.markdownEnumDescriptions[i]);
}
else if (schemaPropertyNames_1.enumDescriptions && i < schemaPropertyNames_1.enumDescriptions.length) {
enumDescription = schemaPropertyNames_1.enumDescriptions[i];
}
propertyNameCompletionItem(schemaPropertyNames_1.enum[i], enumDescription);
}
}
if (schemaPropertyNames_1.const) {
propertyNameCompletionItem(schemaPropertyNames_1.const);
}
}
}
});
};
JSONCompletion.prototype.getSchemaLessPropertyCompletions = function (doc, node, currentKey, collector) {
var _this = this;
var collectCompletionsForSimilarObject = function (obj) {
obj.properties.forEach(function (p) {
var key = p.keyNode.value;
collector.add({
kind: jsonLanguageTypes_1.CompletionItemKind.Property,
label: key,
insertText: _this.getInsertTextForValue(key, ''),
insertTextFormat: jsonLanguageTypes_1.InsertTextFormat.Snippet,
filterText: _this.getFilterTextForValue(key),
documentation: ''
});
});
};
if (node.parent) {
if (node.parent.type === 'property') {
// if the object is a property value, check the tree for other objects that hang under a property of the same name
var parentKey_1 = node.parent.keyNode.value;
doc.visit(function (n) {
if (n.type === 'property' && n !== node.parent && n.keyNode.value === parentKey_1 && n.valueNode && n.valueNode.type === 'object') {
collectCompletionsForSimilarObject(n.valueNode);
}
return true;
});
}
else if (node.parent.type === 'array') {
// if the object is in an array, use all other array elements as similar objects
node.parent.items.forEach(function (n) {
if (n.type === 'object' && n !== node) {
collectCompletionsForSimilarObject(n);
}
});
}
}
else if (node.type === 'object') {
collector.add({
kind: jsonLanguageTypes_1.CompletionItemKind.Property,
label: '$schema',
insertText: this.getInsertTextForProperty('$schema', undefined, true, ''),
insertTextFormat: jsonLanguageTypes_1.InsertTextFormat.Snippet, documentation: '',
filterText: this.getFilterTextForValue("$schema")
});
}
};
JSONCompletion.prototype.getSchemaLessValueCompletions = function (doc, node, offset, document, collector) {
var _this = this;
var offsetForSeparator = offset;
if (node && (node.type === 'string' || node.type === 'number' || node.type === 'boolean' || node.type === 'null')) {
offsetForSeparator = node.offset + node.length;
node = node.parent;
}
if (!node) {
collector.add({
kind: this.getSuggestionKind('object'),
label: 'Empty object',
insertText: this.getInsertTextForValue({}, ''),
insertTextFormat: jsonLanguageTypes_1.InsertTextFormat.Snippet,
documentation: ''
});
collector.add({
kind: this.getSuggestionKind('array'),
label: 'Empty array',
insertText: this.getInsertTextForValue([], ''),
insertTextFormat: jsonLanguageTypes_1.InsertTextFormat.Snippet,
documentation: ''
});
return;
}
var separatorAfter = this.evaluateSeparatorAfter(document, offsetForSeparator);
var collectSuggestionsForValues = function (value) {
if (value.parent && !Parser.contains(value.parent, offset, true)) {
collector.add({
kind: _this.getSuggestionKind(value.type),
label: _this.getLabelTextForMatchingNode(value, document),
insertText: _this.getInsertTextForMatchingNode(value, document, separatorAfter),
insertTextFormat: jsonLanguageTypes_1.InsertTextFormat.Snippet, documentation: ''
});
}
if (value.type === 'boolean') {
_this.addBooleanValueCompletion(!value.value, separatorAfter, collector);
}
};
if (node.type === 'property') {
if (offset > (node.colonOffset || 0)) {
var valueNode = node.valueNode;
if (valueNode && (offset > (valueNode.offset + valueNode.length) || valueNode.type === 'object' || valueNode.type === 'array')) {
return;
}
// suggest values at the same key
var parentKey_2 = node.keyNode.value;
doc.visit(function (n) {
if (n.type === 'property' && n.keyNode.value === parentKey_2 && n.valueNode) {
collectSuggestionsForValues(n.valueNode);
}
return true;
});
if (parentKey_2 === '$schema' && node.parent && !node.parent.parent) {
this.addDollarSchemaCompletions(separatorAfter, collector);
}
}
}
if (node.type === 'array') {
if (node.parent && node.parent.type === 'property') {
// suggest items of an array at the same key
var parentKey_3 = node.parent.keyNode.value;
doc.visit(function (n) {
if (n.type === 'property' && n.keyNode.value === parentKey_3 && n.valueNode && n.valueNode.type === 'array') {
n.valueNode.items.forEach(collectSuggestionsForValues);
}
return true;
});
}
else {
// suggest items in the same array
node.items.forEach(collectSuggestionsForValues);
}
}
};
JSONCompletion.prototype.getValueCompletions = function (schema, doc, node, offset, document, collector, types) {
var offsetForSeparator = offset;
var parentKey = undefined;
var valueNode = undefined;
if (node && (node.type === 'string' || node.type === 'number' || node.type === 'boolean' || node.type === 'null')) {
offsetForSeparator = node.offset + node.length;
valueNode = node;
node = node.parent;
}
if (!node) {
this.addSchemaValueCompletions(schema.schema, '', collector, types);
return;
}
if ((node.type === 'property') && offset > (node.colonOffset || 0)) {
var valueNode_1 = node.valueNode;
if (valueNode_1 && offset > (valueNode_1.offset + valueNode_1.length)) {
return; // we are past the value node
}
parentKey = node.keyNode.value;
node = node.parent;
}
if (node && (parentKey !== undefined || node.type === 'array')) {
var separatorAfter = this.evaluateSeparatorAfter(document, offsetForSeparator);
var matchingSchemas = doc.getMatchingSchemas(schema.schema, node.offset, valueNode);
for (var _i = 0, matchingSchemas_1 = matchingSchemas; _i < matchingSchemas_1.length; _i++) {
var s = matchingSchemas_1[_i];
if (s.node === node && !s.inverted && s.schema) {
if (node.type === 'array' && s.schema.items) {
if (Array.isArray(s.schema.items)) {
var index = this.findItemAtOffset(node, document, offset);
if (index < s.schema.items.length) {
this.addSchemaValueCompletions(s.schema.items[index], separatorAfter, collector, types);
}
}
else {
this.addSchemaValueCompletions(s.schema.items, separatorAfter, collector, types);
}
}
if (parentKey !== undefined) {
var propertyMatched = false;
if (s.schema.properties) {
var propertySchema = s.schema.properties[parentKey];
if (propertySchema) {
propertyMatched = true;
this.addSchemaValueCompletions(propertySchema, separatorAfter, collector, types);
}
}
if (s.schema.patternProperties && !propertyMatched) {
for (var _a = 0, _b = Object.keys(s.schema.patternProperties); _a < _b.length; _a++) {
var pattern = _b[_a];
var regex = strings_1.extendedRegExp(pattern);
if (regex.test(parentKey)) {
propertyMatched = true;
var propertySchema = s.schema.patternProperties[pattern];
this.addSchemaValueCompletions(propertySchema, separatorAfter, collector, types);
}
}
}
if (s.schema.additionalProperties && !propertyMatched) {
var propertySchema = s.schema.additionalProperties;
this.addSchemaValueCompletions(propertySchema, separatorAfter, collector, types);
}
}
}
}
if (parentKey === '$schema' && !node.parent) {
this.addDollarSchemaCompletions(separatorAfter, collector);
}
if (types['boolean']) {
this.addBooleanValueCompletion(true, separatorAfter, collector);
this.addBooleanValueCompletion(false, separatorAfter, collector);
}
if (types['null']) {
this.addNullValueCompletion(separatorAfter, collector);
}
}
};
JSONCompletion.prototype.getContributedValueCompletions = function (doc, node, offset, document, collector, collectionPromises) {
if (!node) {
this.contributions.forEach(function (contribution) {
var collectPromise = contribution.collectDefaultCompletions(document.uri, collector);
if (collectPromise) {
collectionPromises.push(collectPromise);
}
});
}
else {
if (node.type === 'string' || node.type === 'number' || node.type === 'boolean' || node.type === 'null') {
node = node.parent;
}
if (node && (node.type === 'property') && offset > (node.colonOffset || 0)) {
var parentKey_4 = node.keyNode.value;
var valueNode = node.valueNode;
if ((!valueNode || offset <= (valueNode.offset + valueNode.length)) && node.parent) {
var location_2 = Parser.getNodePath(node.parent);
this.contributions.forEach(function (contribution) {
var collectPromise = contribution.collectValueCompletions(document.uri, location_2, parentKey_4, collector);
if (collectPromise) {
collectionPromises.push(collectPromise);
}
});
}
}
}
};
JSONCompletion.prototype.addSchemaValueCompletions = function (schema, separatorAfter, collector, types) {
var _this = this;
if (typeof schema === 'object') {
this.addEnumValueCompletions(schema, separatorAfter, collector);
this.addDefaultValueCompletions(schema, separatorAfter, collector);
this.collectTypes(schema, types);
if (Array.isArray(schema.allOf)) {
schema.allOf.forEach(function (s) { return _this.addSchemaValueCompletions(s, separatorAfter, collector, types); });
}
if (Array.isArray(schema.anyOf)) {
schema.anyOf.forEach(function (s) { return _this.addSchemaValueCompletions(s, separatorAfter, collector, types); });
}
if (Array.isArray(schema.oneOf)) {
schema.oneOf.forEach(function (s) { return _this.addSchemaValueCompletions(s, separatorAfter, collector, types); });
}
}
};
JSONCompletion.prototype.addDefaultValueCompletions = function (schema, separatorAfter, collector, arrayDepth) {
var _this = this;
if (arrayDepth === void 0) { arrayDepth = 0; }
var hasProposals = false;
if (objects_1.isDefined(schema.default)) {
var type = schema.type;
var value = schema.default;
for (var i = arrayDepth; i > 0; i--) {
value = [value];
type = 'array';
}
collector.add({
kind: this.getSuggestionKind(type),
label: this.getLabelForValue(value),
insertText: this.getInsertTextForValue(value, separatorAfter),
insertTextFormat: jsonLanguageTypes_1.InsertTextFormat.Snippet,
detail: localize('json.suggest.default', 'Default value')
});
hasProposals = true;
}
if (Array.isArray(schema.examples)) {
schema.examples.forEach(function (example) {
var type = schema.type;
var value = example;
for (var i = arrayDepth; i > 0; i--) {
value = [value];
type = 'array';
}
collector.add({
kind: _this.getSuggestionKind(type),
label: _this.getLabelForValue(value),
insertText: _this.getInsertTextForValue(value, separatorAfter),
insertTextFormat: jsonLanguageTypes_1.InsertTextFormat.Snippet
});
hasProposals = true;
});
}
if (Array.isArray(schema.defaultSnippets)) {
schema.defaultSnippets.forEach(function (s) {
var type = schema.type;
var value = s.body;
var label = s.label;
var insertText;
var filterText;
if (objects_1.isDefined(value)) {
var type_1 = schema.type;
for (var i = arrayDepth; i > 0; i--) {
value = [value];
type_1 = 'array';
}
insertText = _this.getInsertTextForSnippetValue(value, separatorAfter);
filterText = _this.getFilterTextForSnippetValue(value);
label = label || _this.getLabelForSnippetValue(value);
}
else if (typeof s.bodyText === 'string') {
var prefix = '', suffix = '', indent = '';
for (var i = arrayDepth; i > 0; i--) {
prefix = prefix + indent + '[\n';
suffix = suffix + '\n' + indent + ']';
indent += '\t';
type = 'array';
}
insertText = prefix + indent + s.bodyText.split('\n').join('\n' + indent) + suffix + separatorAfter;
label = label || insertText,
filterText = insertText.replace(/[\n]/g, ''); // remove new lines
}
else {
return;
}
collector.add({
kind: _this.getSuggestionKind(type),
label: label,
documentation: _this.fromMarkup(s.markdownDescription) || s.description,
insertText: insertText,
insertTextFormat: jsonLanguageTypes_1.InsertTextFormat.Snippet,
filterText: filterText
});
hasProposals = true;
});
}
if (!hasProposals && typeof schema.items === 'object' && !Array.isArray(schema.items) && arrayDepth < 5 /* beware of recursion */) {
this.addDefaultValueCompletions(schema.items, separatorAfter, collector, arrayDepth + 1);
}
};
JSONCompletion.prototype.addEnumValueCompletions = function (schema, separatorAfter, collector) {
if (objects_1.isDefined(schema.const)) {
collector.add({
kind: this.getSuggestionKind(schema.type),
label: this.getLabelForValue(schema.const),
insertText: this.getInsertTextForValue(schema.const, separatorAfter),
insertTextFormat: jsonLanguageTypes_1.InsertTextFormat.Snippet,
documentation: this.fromMarkup(schema.markdownDescription) || schema.description
});
}
if (Array.isArray(schema.enum)) {
for (var i = 0, length = schema.enum.length; i < length; i++) {
var enm = schema.enum[i];
var documentation = this.fromMarkup(schema.markdownDescription) || schema.description;
if (schema.markdownEnumDescriptions && i < schema.markdownEnumDescriptions.length && this.doesSupportMarkdown()) {
documentation = this.fromMarkup(schema.markdownEnumDescriptions[i]);
}
else if (schema.enumDescriptions && i < schema.enumDescriptions.length) {
documentation = schema.enumDescriptions[i];
}
collector.add({
kind: this.getSuggestionKind(schema.type),
label: this.getLabelForValue(enm),
insertText: this.getInsertTextForValue(enm, separatorAfter),
insertTextFormat: jsonLanguageTypes_1.InsertTextFormat.Snippet,
documentation: documentation
});
}
}
};
JSONCompletion.prototype.collectTypes = function (schema, types) {
if (Array.isArray(schema.enum) || objects_1.isDefined(schema.const)) {
return;
}
var type = schema.type;
if (Array.isArray(type)) {
type.forEach(function (t) { return types[t] = true; });
}
else if (type) {
types[type] = true;
}
};
JSONCompletion.prototype.addFillerValueCompletions = function (types, separatorAfter, collector) {
if (types['object']) {
collector.add({
kind: this.getSuggestionKind('object'),
label: '{}',
insertText: this.getInsertTextForGuessedValue({}, separatorAfter),
insertTextFormat: jsonLanguageTypes_1.InsertTextFormat.Snippet,
detail: localize('defaults.object', 'New object'),
documentation: ''
});
}
if (types['array']) {
collector.add({
kind: this.getSuggestionKind('array'),
label: '[]',
insertText: this.getInsertTextForGuessedValue([], separatorAfter),
insertTextFormat: jsonLanguageTypes_1.InsertTextFormat.Snippet,
detail: localize('defaults.array', 'New array'),
documentation: ''
});
}
};
JSONCompletion.prototype.addBooleanValueCompletion = function (value, separatorAfter, collector) {
collector.add({
kind: this.getSuggestionKind('boolean'),
label: value ? 'true' : 'false',
insertText: this.getInsertTextForValue(value, separatorAfter),
insertTextFormat: jsonLanguageTypes_1.InsertTextFormat.Snippet,
documentation: ''
});
};
JSONCompletion.prototype.addNullValueCompletion = function (separatorAfter, collector) {
collector.add({
kind: this.getSuggestionKind('null'),
label: 'null',
insertText: 'null' + separatorAfter,
insertTextFormat: jsonLanguageTypes_1.InsertTextFormat.Snippet,
documentation: ''
});
};
JSONCompletion.prototype.addDollarSchemaCompletions = function (separatorAfter, collector) {
var _this = this;
var schemaIds = this.schemaService.getRegisteredSchemaIds(function (schema) { return schema === 'http' || schema === 'https'; });
schemaIds.forEach(function (schemaId) { return collector.add({
kind: jsonLanguageTypes_1.CompletionItemKind.Module,
label: _this.getLabelForValue(schemaId),
filterText: _this.getFilterTextForValue(schemaId),
insertText: _this.getInsertTextForValue(schemaId, separatorAfter),
insertTextFormat: jsonLanguageTypes_1.InsertTextFormat.Snippet, documentation: ''
}); });
};
JSONCompletion.prototype.getLabelForValue = function (value) {
return JSON.stringify(value);
};
JSONCompletion.prototype.getFilterTextForValue = function (value) {
return JSON.stringify(value);
};
JSONCompletion.prototype.getFilterTextForSnippetValue = function (value) {
return JSON.stringify(value).replace(/\$\{\d+:([^}]+)\}|\$\d+/g, '$1');
};
JSONCompletion.prototype.getLabelForSnippetValue = function (value) {
var label = JSON.stringify(value);
return label.replace(/\$\{\d+:([^}]+)\}|\$\d+/g, '$1');
};
JSONCompletion.prototype.getInsertTextForPlainText = function (text) {
return text.replace(/[\\\$\}]/g, '\\$&'); // escape $, \ and }
};
JSONCompletion.prototype.getInsertTextForValue = function (value, separatorAfter) {
var text = JSON.stringify(value, null, '\t');
if (text === '{}') {
return '{$1}' + separatorAfter;
}
else if (text === '[]') {
return '[$1]' + separatorAfter;
}
return this.getInsertTextForPlainText(text + separatorAfter);
};
JSONCompletion.prototype.getInsertTextForSnippetValue = function (value, separatorAfter) {
var replacer = function (value) {
if (typeof value === 'string') {
if (value[0] === '^') {
return value.substr(1);
}
}
return JSON.stringify(value);
};
return json_1.stringifyObject(value, '', replacer) + separatorAfter;
};
JSONCompletion.prototype.getInsertTextForGuessedValue = function (value, separatorAfter) {
switch (typeof value) {
case 'object':
if (value === null) {
return '${1:null}' + separatorAfter;
}
return this.getInsertTextForValue(value, separatorAfter);
case 'string':
var snippetValue = JSON.stringify(value);
snippetValue = snippetValue.substr(1, snippetValue.length - 2); // remove quotes
snippetValue = this.getInsertTextForPlainText(snippetValue); // escape \ and }
return '"${1:' + snippetValue + '}"' + separatorAfter;
case 'number':
case 'boolean':
return '${1:' + JSON.stringify(value) + '}' + separatorAfter;
}
return this.getInsertTextForValue(value, separatorAfter);
};
JSONCompletion.prototype.getSuggestionKind = function (type) {
if (Array.isArray(type)) {
var array = type;
type = array.length > 0 ? array[0] : undefined;
}
if (!type) {
return jsonLanguageTypes_1.CompletionItemKind.Value;
}
switch (type) {
case 'string': return jsonLanguageTypes_1.CompletionItemKind.Value;
case 'object': return jsonLanguageTypes_1.CompletionItemKind.Module;
case 'property': return jsonLanguageTypes_1.CompletionItemKind.Property;
default: return jsonLanguageTypes_1.CompletionItemKind.Value;
}
};
JSONCompletion.prototype.getLabelTextForMatchingNode = function (node, document) {
switch (node.type) {
case 'array':
return '[]';
case 'object':
return '{}';
default:
var content = document.getText().substr(node.offset, node.length);
return content;
}
};
JSONCompletion.prototype.getInsertTextForMatchingNode = function (node, document, separatorAfter) {
switch (node.type) {
case 'array':
return this.getInsertTextForValue([], separatorAfter);
case 'object':
return this.getInsertTextForValue({}, separatorAfter);
default:
var content = document.getText().substr(node.offset, node.length) + separatorAfter;
return this.getInsertTextForPlainText(content);
}
};
JSONCompletion.prototype.getInsertTextForProperty = function (key, propertySchema, addValue, separatorAfter) {
var propertyText = this.getInsertTextForValue(key, '');
if (!addValue) {
return propertyText;
}
var resultText = propertyText + ': ';
var value;
var nValueProposals = 0;
if (propertySchema) {
if (Array.isArray(propertySchema.defaultSnippets)) {
if (propertySchema.defaultSnippets.length === 1) {
var body = propertySchema.defaultSnippets[0].body;
if (objects_1.isDefined(body)) {
value = this.getInsertTextForSnippetValue(body, '');
}
}
nValueProposals += propertySchema.defaultSnippets.length;
}
if (propertySchema.enum) {
if (!value && propertySchema.enum.length === 1) {
value = this.getInsertTextForGuessedValue(propertySchema.enum[0], '');
}
nValueProposals += propertySchema.enum.length;
}
if (objects_1.isDefined(propertySchema.default)) {
if (!value) {
value = this.getInsertTextForGuessedValue(propertySchema.default, '');
}
nValueProposals++;
}
if (Array.isArray(propertySchema.examples) && propertySchema.examples.length) {
if (!value) {
value = this.getInsertTextForGuessedValue(propertySchema.examples[0], '');
}
nValueProposals += propertySchema.examples.length;
}
if (nValueProposals === 0) {
var type = Array.isArray(propertySchema.type) ? propertySchema.type[0] : propertySchema.type;
if (!type) {
if (propertySchema.properties) {
type = 'object';
}
else if (propertySchema.items) {
type = 'array';
}
}
switch (type) {
case 'boolean':
value = '$1';
break;
case 'string':
value = '"$1"';
break;
case 'object':
value = '{$1}';
break;
case 'array':
value = '[$1]';
break;
case 'number':
case 'integer':
value = '${1:0}';
break;
case 'null':
value = '${1:null}';
break;
default:
return propertyText;
}
}
}
if (!value || nValueProposals > 1) {
value = '$1';
}
return resultText + value + separatorAfter;
};
JSONCompletion.prototype.getCurrentWord = function (document, offset) {
var i = offset - 1;
var text = document.getText();
while (i >= 0 && ' \t\n\r\v":{[,]}'.indexOf(text.charAt(i)) === -1) {
i--;
}
return text.substring(i + 1, offset);
};
JSONCompletion.prototype.evaluateSeparatorAfter = function (document, offset) {
var scanner = Json.createScanner(document.getText(), true);
scanner.setPosition(offset);
var token = scanner.scan();
switch (token) {
case 5 /* CommaToken */:
case 2 /* CloseBraceToken */:
case 4 /* CloseBracketToken */:
case 17 /* EOF */:
return '';
default:
return ',';
}
};
JSONCompletion.prototype.findItemAtOffset = function (node, document, offset) {
var scanner = Json.createScanner(document.getText(), true);
var children = node.items;
for (var i = children.length - 1; i >= 0; i--) {
var child = children[i];
if (offset > child.offset + child.length) {
scanner.setPosition(child.offset + child.length);
var token = scanner.scan();
if (token === 5 /* CommaToken */ && offset >= scanner.getTokenOffset() + scanner.getTokenLength()) {
return i + 1;
}
return i;
}
else if (offset >= child.offset) {
return i;
}
}
return 0;
};
JSONCompletion.prototype.isInComment = function (document, start, offset) {
var scanner = Json.createScanner(document.getText(), false);
scanner.setPosition(start);
var token = scanner.scan();
while (token !== 17 /* EOF */ && (scanner.getTokenOffset() + scanner.getTokenLength() < offset)) {
token = scanner.scan();
}
return (token === 12 /* LineCommentTrivia */ || token === 13 /* BlockCommentTrivia */) && scanner.getTokenOffset() <= offset;
};
JSONCompletion.prototype.fromMarkup = function (markupString) {
if (markupString && this.doesSupportMarkdown()) {
return {
kind: jsonLanguageTypes_1.MarkupKind.Markdown,
value: markupString
};
}
return undefined;
};
JSONCompletion.prototype.doesSupportMarkdown = function () {
if (!objects_1.isDefined(this.supportsMarkdown)) {
var completion = this.clientCapabilities.textDocument && this.clientCapabilities.textDocument.completion;
this.supportsMarkdown = completion && completion.completionItem && Array.isArray(completion.completionItem.documentationFormat) && completion.completionItem.documentationFormat.indexOf(jsonLanguageTypes_1.MarkupKind.Markdown) !== -1;
}
return this.supportsMarkdown;
};
JSONCompletion.prototype.doesSupportsCommitCharacters = function () {
if (!objects_1.isDefined(this.supportsCommitCharacters)) {
var completion = this.clientCapabilities.textDocument && this.clientCapabilities.textDocument.completion;
this.supportsCommitCharacters = completion && completion.completionItem && !!completion.completionItem.commitCharactersSupport;
}
return this.supportsCommitCharacters;
};
return JSONCompletion;
}());
exports.JSONCompletion = JSONCompletion;
});

View File

@@ -0,0 +1,291 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
(function (factory) {
if (typeof module === "object" && typeof module.exports === "object") {
var v = factory(require, exports);
if (v !== undefined) module.exports = v;
}
else if (typeof define === "function" && define.amd) {
define(["require", "exports", "../parser/jsonParser", "../utils/strings", "../utils/colors", "../jsonLanguageTypes"], factory);
}
})(function (require, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.JSONDocumentSymbols = void 0;
var Parser = require("../parser/jsonParser");
var Strings = require("../utils/strings");
var colors_1 = require("../utils/colors");
var jsonLanguageTypes_1 = require("../jsonLanguageTypes");
var JSONDocumentSymbols = /** @class */ (function () {
function JSONDocumentSymbols(schemaService) {
this.schemaService = schemaService;
}
JSONDocumentSymbols.prototype.findDocumentSymbols = function (document, doc, context) {
var _this = this;
if (context === void 0) { context = { resultLimit: Number.MAX_VALUE }; }
var root = doc.root;
if (!root) {
return [];
}
var limit = context.resultLimit || Number.MAX_VALUE;
// special handling for key bindings
var resourceString = document.uri;
if ((resourceString === 'vscode://defaultsettings/keybindings.json') || Strings.endsWith(resourceString.toLowerCase(), '/user/keybindings.json')) {
if (root.type === 'array') {
var result_1 = [];
for (var _i = 0, _a = root.items; _i < _a.length; _i++) {
var item = _a[_i];
if (item.type === 'object') {
for (var _b = 0, _c = item.properties; _b < _c.length; _b++) {
var property = _c[_b];
if (property.keyNode.value === 'key' && property.valueNode) {
var location = jsonLanguageTypes_1.Location.create(document.uri, getRange(document, item));
result_1.push({ name: Parser.getNodeValue(property.valueNode), kind: jsonLanguageTypes_1.SymbolKind.Function, location: location });
limit--;
if (limit <= 0) {
if (context && context.onResultLimitExceeded) {
context.onResultLimitExceeded(resourceString);
}
return result_1;
}
}
}
}
}
return result_1;
}
}
var toVisit = [
{ node: root, containerName: '' }
];
var nextToVisit = 0;
var limitExceeded = false;
var result = [];
var collectOutlineEntries = function (node, containerName) {
if (node.type === 'array') {
node.items.forEach(function (node) {
if (node) {
toVisit.push({ node: node, containerName: containerName });
}
});
}
else if (node.type === 'object') {
node.properties.forEach(function (property) {
var valueNode = property.valueNode;
if (valueNode) {
if (limit > 0) {
limit--;
var location = jsonLanguageTypes_1.Location.create(document.uri, getRange(document, property));
var childContainerName = containerName ? containerName + '.' + property.keyNode.value : property.keyNode.value;
result.push({ name: _this.getKeyLabel(property), kind: _this.getSymbolKind(valueNode.type), location: location, containerName: containerName });
toVisit.push({ node: valueNode, containerName: childContainerName });
}
else {
limitExceeded = true;
}
}
});
}
};
// breath first traversal
while (nextToVisit < toVisit.length) {
var next = toVisit[nextToVisit++];
collectOutlineEntries(next.node, next.containerName);
}
if (limitExceeded && context && context.onResultLimitExceeded) {
context.onResultLimitExceeded(resourceString);
}
return result;
};
JSONDocumentSymbols.prototype.findDocumentSymbols2 = function (document, doc, context) {
var _this = this;
if (context === void 0) { context = { resultLimit: Number.MAX_VALUE }; }
var root = doc.root;
if (!root) {
return [];
}
var limit = context.resultLimit || Number.MAX_VALUE;
// special handling for key bindings
var resourceString = document.uri;
if ((resourceString === 'vscode://defaultsettings/keybindings.json') || Strings.endsWith(resourceString.toLowerCase(), '/user/keybindings.json')) {
if (root.type === 'array') {
var result_2 = [];
for (var _i = 0, _a = root.items; _i < _a.length; _i++) {
var item = _a[_i];
if (item.type === 'object') {
for (var _b = 0, _c = item.properties; _b < _c.length; _b++) {
var property = _c[_b];
if (property.keyNode.value === 'key' && property.valueNode) {
var range = getRange(document, item);
var selectionRange = getRange(document, property.keyNode);
result_2.push({ name: Parser.getNodeValue(property.valueNode), kind: jsonLanguageTypes_1.SymbolKind.Function, range: range, selectionRange: selectionRange });
limit--;
if (limit <= 0) {
if (context && context.onResultLimitExceeded) {
context.onResultLimitExceeded(resourceString);
}
return result_2;
}
}
}
}
}
return result_2;
}
}
var result = [];
var toVisit = [
{ node: root, result: result }
];
var nextToVisit = 0;
var limitExceeded = false;
var collectOutlineEntries = function (node, result) {
if (node.type === 'array') {
node.items.forEach(function (node, index) {
if (node) {
if (limit > 0) {
limit--;
var range = getRange(document, node);
var selectionRange = range;
var name = String(index);
var symbol = { name: name, kind: _this.getSymbolKind(node.type), range: range, selectionRange: selectionRange, children: [] };
result.push(symbol);
toVisit.push({ result: symbol.children, node: node });
}
else {
limitExceeded = true;
}
}
});
}
else if (node.type === 'object') {
node.properties.forEach(function (property) {
var valueNode = property.valueNode;
if (valueNode) {
if (limit > 0) {
limit--;
var range = getRange(document, property);
var selectionRange = getRange(document, property.keyNode);
var children = [];
var symbol = { name: _this.getKeyLabel(property), kind: _this.getSymbolKind(valueNode.type), range: range, selectionRange: selectionRange, children: children, detail: _this.getDetail(valueNode) };
result.push(symbol);
toVisit.push({ result: children, node: valueNode });
}
else {
limitExceeded = true;
}
}
});
}
};
// breath first traversal
while (nextToVisit < toVisit.length) {
var next = toVisit[nextToVisit++];
collectOutlineEntries(next.node, next.result);
}
if (limitExceeded && context && context.onResultLimitExceeded) {
context.onResultLimitExceeded(resourceString);
}
return result;
};
JSONDocumentSymbols.prototype.getSymbolKind = function (nodeType) {
switch (nodeType) {
case 'object':
return jsonLanguageTypes_1.SymbolKind.Module;
case 'string':
return jsonLanguageTypes_1.SymbolKind.String;
case 'number':
return jsonLanguageTypes_1.SymbolKind.Number;
case 'array':
return jsonLanguageTypes_1.SymbolKind.Array;
case 'boolean':
return jsonLanguageTypes_1.SymbolKind.Boolean;
default: // 'null'
return jsonLanguageTypes_1.SymbolKind.Variable;
}
};
JSONDocumentSymbols.prototype.getKeyLabel = function (property) {
var name = property.keyNode.value;
if (name) {
name = name.replace(/[\n]/g, '↵');
}
if (name && name.trim()) {
return name;
}
return "\"" + name + "\"";
};
JSONDocumentSymbols.prototype.getDetail = function (node) {
if (!node) {
return undefined;
}
if (node.type === 'boolean' || node.type === 'number' || node.type === 'null' || node.type === 'string') {
return String(node.value);
}
else {
if (node.type === 'array') {
return node.children.length ? undefined : '[]';
}
else if (node.type === 'object') {
return node.children.length ? undefined : '{}';
}
}
return undefined;
};
JSONDocumentSymbols.prototype.findDocumentColors = function (document, doc, context) {
return this.schemaService.getSchemaForResource(document.uri, doc).then(function (schema) {
var result = [];
if (schema) {
var limit = context && typeof context.resultLimit === 'number' ? context.resultLimit : Number.MAX_VALUE;
var matchingSchemas = doc.getMatchingSchemas(schema.schema);
var visitedNode = {};
for (var _i = 0, matchingSchemas_1 = matchingSchemas; _i < matchingSchemas_1.length; _i++) {
var s = matchingSchemas_1[_i];
if (!s.inverted && s.schema && (s.schema.format === 'color' || s.schema.format === 'color-hex') && s.node && s.node.type === 'string') {
var nodeId = String(s.node.offset);
if (!visitedNode[nodeId]) {
var color = colors_1.colorFromHex(Parser.getNodeValue(s.node));
if (color) {
var range = getRange(document, s.node);
result.push({ color: color, range: range });
}
visitedNode[nodeId] = true;
limit--;
if (limit <= 0) {
if (context && context.onResultLimitExceeded) {
context.onResultLimitExceeded(document.uri);
}
return result;
}
}
}
}
}
return result;
});
};
JSONDocumentSymbols.prototype.getColorPresentations = function (document, doc, color, range) {
var result = [];
var red256 = Math.round(color.red * 255), green256 = Math.round(color.green * 255), blue256 = Math.round(color.blue * 255);
function toTwoDigitHex(n) {
var r = n.toString(16);
return r.length !== 2 ? '0' + r : r;
}
var label;
if (color.alpha === 1) {
label = "#" + toTwoDigitHex(red256) + toTwoDigitHex(green256) + toTwoDigitHex(blue256);
}
else {
label = "#" + toTwoDigitHex(red256) + toTwoDigitHex(green256) + toTwoDigitHex(blue256) + toTwoDigitHex(Math.round(color.alpha * 255));
}
result.push({ label: label, textEdit: jsonLanguageTypes_1.TextEdit.replace(range, JSON.stringify(label)) });
return result;
};
return JSONDocumentSymbols;
}());
exports.JSONDocumentSymbols = JSONDocumentSymbols;
function getRange(document, node) {
return jsonLanguageTypes_1.Range.create(document.positionAt(node.offset), document.positionAt(node.offset + node.length));
}
});

View File

@@ -0,0 +1,135 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
(function (factory) {
if (typeof module === "object" && typeof module.exports === "object") {
var v = factory(require, exports);
if (v !== undefined) module.exports = v;
}
else if (typeof define === "function" && define.amd) {
define(["require", "exports", "jsonc-parser", "../jsonLanguageTypes"], factory);
}
})(function (require, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getFoldingRanges = void 0;
var jsonc_parser_1 = require("jsonc-parser");
var jsonLanguageTypes_1 = require("../jsonLanguageTypes");
function getFoldingRanges(document, context) {
var ranges = [];
var nestingLevels = [];
var stack = [];
var prevStart = -1;
var scanner = jsonc_parser_1.createScanner(document.getText(), false);
var token = scanner.scan();
function addRange(range) {
ranges.push(range);
nestingLevels.push(stack.length);
}
while (token !== 17 /* EOF */) {
switch (token) {
case 1 /* OpenBraceToken */:
case 3 /* OpenBracketToken */: {
var startLine = document.positionAt(scanner.getTokenOffset()).line;
var range = { startLine: startLine, endLine: startLine, kind: token === 1 /* OpenBraceToken */ ? 'object' : 'array' };
stack.push(range);
break;
}
case 2 /* CloseBraceToken */:
case 4 /* CloseBracketToken */: {
var kind = token === 2 /* CloseBraceToken */ ? 'object' : 'array';
if (stack.length > 0 && stack[stack.length - 1].kind === kind) {
var range = stack.pop();
var line = document.positionAt(scanner.getTokenOffset()).line;
if (range && line > range.startLine + 1 && prevStart !== range.startLine) {
range.endLine = line - 1;
addRange(range);
prevStart = range.startLine;
}
}
break;
}
case 13 /* BlockCommentTrivia */: {
var startLine = document.positionAt(scanner.getTokenOffset()).line;
var endLine = document.positionAt(scanner.getTokenOffset() + scanner.getTokenLength()).line;
if (scanner.getTokenError() === 1 /* UnexpectedEndOfComment */ && startLine + 1 < document.lineCount) {
scanner.setPosition(document.offsetAt(jsonLanguageTypes_1.Position.create(startLine + 1, 0)));
}
else {
if (startLine < endLine) {
addRange({ startLine: startLine, endLine: endLine, kind: jsonLanguageTypes_1.FoldingRangeKind.Comment });
prevStart = startLine;
}
}
break;
}
case 12 /* LineCommentTrivia */: {
var text = document.getText().substr(scanner.getTokenOffset(), scanner.getTokenLength());
var m = text.match(/^\/\/\s*#(region\b)|(endregion\b)/);
if (m) {
var line = document.positionAt(scanner.getTokenOffset()).line;
if (m[1]) { // start pattern match
var range = { startLine: line, endLine: line, kind: jsonLanguageTypes_1.FoldingRangeKind.Region };
stack.push(range);
}
else {
var i = stack.length - 1;
while (i >= 0 && stack[i].kind !== jsonLanguageTypes_1.FoldingRangeKind.Region) {
i--;
}
if (i >= 0) {
var range = stack[i];
stack.length = i;
if (line > range.startLine && prevStart !== range.startLine) {
range.endLine = line;
addRange(range);
prevStart = range.startLine;
}
}
}
}
break;
}
}
token = scanner.scan();
}
var rangeLimit = context && context.rangeLimit;
if (typeof rangeLimit !== 'number' || ranges.length <= rangeLimit) {
return ranges;
}
if (context && context.onRangeLimitExceeded) {
context.onRangeLimitExceeded(document.uri);
}
var counts = [];
for (var _i = 0, nestingLevels_1 = nestingLevels; _i < nestingLevels_1.length; _i++) {
var level = nestingLevels_1[_i];
if (level < 30) {
counts[level] = (counts[level] || 0) + 1;
}
}
var entries = 0;
var maxLevel = 0;
for (var i = 0; i < counts.length; i++) {
var n = counts[i];
if (n) {
if (n + entries > rangeLimit) {
maxLevel = i;
break;
}
entries += n;
}
}
var result = [];
for (var i = 0; i < ranges.length; i++) {
var level = nestingLevels[i];
if (typeof level === 'number') {
if (level < maxLevel || (level === maxLevel && entries++ < rangeLimit)) {
result.push(ranges[i]);
}
}
}
return result;
}
exports.getFoldingRanges = getFoldingRanges;
});

View File

@@ -0,0 +1,125 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
(function (factory) {
if (typeof module === "object" && typeof module.exports === "object") {
var v = factory(require, exports);
if (v !== undefined) module.exports = v;
}
else if (typeof define === "function" && define.amd) {
define(["require", "exports", "../parser/jsonParser", "../jsonLanguageTypes"], factory);
}
})(function (require, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.JSONHover = void 0;
var Parser = require("../parser/jsonParser");
var jsonLanguageTypes_1 = require("../jsonLanguageTypes");
var JSONHover = /** @class */ (function () {
function JSONHover(schemaService, contributions, promiseConstructor) {
if (contributions === void 0) { contributions = []; }
this.schemaService = schemaService;
this.contributions = contributions;
this.promise = promiseConstructor || Promise;
}
JSONHover.prototype.doHover = function (document, position, doc) {
var offset = document.offsetAt(position);
var node = doc.getNodeFromOffset(offset);
if (!node || (node.type === 'object' || node.type === 'array') && offset > node.offset + 1 && offset < node.offset + node.length - 1) {
return this.promise.resolve(null);
}
var hoverRangeNode = node;
// use the property description when hovering over an object key
if (node.type === 'string') {
var parent = node.parent;
if (parent && parent.type === 'property' && parent.keyNode === node) {
node = parent.valueNode;
if (!node) {
return this.promise.resolve(null);
}
}
}
var hoverRange = jsonLanguageTypes_1.Range.create(document.positionAt(hoverRangeNode.offset), document.positionAt(hoverRangeNode.offset + hoverRangeNode.length));
var createHover = function (contents) {
var result = {
contents: contents,
range: hoverRange
};
return result;
};
var location = Parser.getNodePath(node);
for (var i = this.contributions.length - 1; i >= 0; i--) {
var contribution = this.contributions[i];
var promise = contribution.getInfoContribution(document.uri, location);
if (promise) {
return promise.then(function (htmlContent) { return createHover(htmlContent); });
}
}
return this.schemaService.getSchemaForResource(document.uri, doc).then(function (schema) {
if (schema && node) {
var matchingSchemas = doc.getMatchingSchemas(schema.schema, node.offset);
var title_1 = undefined;
var markdownDescription_1 = undefined;
var markdownEnumValueDescription_1 = undefined, enumValue_1 = undefined;
matchingSchemas.every(function (s) {
if (s.node === node && !s.inverted && s.schema) {
title_1 = title_1 || s.schema.title;
markdownDescription_1 = markdownDescription_1 || s.schema.markdownDescription || toMarkdown(s.schema.description);
if (s.schema.enum) {
var idx = s.schema.enum.indexOf(Parser.getNodeValue(node));
if (s.schema.markdownEnumDescriptions) {
markdownEnumValueDescription_1 = s.schema.markdownEnumDescriptions[idx];
}
else if (s.schema.enumDescriptions) {
markdownEnumValueDescription_1 = toMarkdown(s.schema.enumDescriptions[idx]);
}
if (markdownEnumValueDescription_1) {
enumValue_1 = s.schema.enum[idx];
if (typeof enumValue_1 !== 'string') {
enumValue_1 = JSON.stringify(enumValue_1);
}
}
}
}
return true;
});
var result = '';
if (title_1) {
result = toMarkdown(title_1);
}
if (markdownDescription_1) {
if (result.length > 0) {
result += "\n\n";
}
result += markdownDescription_1;
}
if (markdownEnumValueDescription_1) {
if (result.length > 0) {
result += "\n\n";
}
result += "`" + toMarkdownCodeBlock(enumValue_1) + "`: " + markdownEnumValueDescription_1;
}
return createHover([result]);
}
return null;
});
};
return JSONHover;
}());
exports.JSONHover = JSONHover;
function toMarkdown(plain) {
if (plain) {
var res = plain.replace(/([^\n\r])(\r?\n)([^\n\r])/gm, '$1\n\n$3'); // single new lines to \n\n (Markdown paragraph)
return res.replace(/[\\`*_{}[\]()#+\-.!]/g, "\\$&"); // escape markdown syntax tokens: http://daringfireball.net/projects/markdown/syntax#backslash
}
return undefined;
}
function toMarkdownCodeBlock(content) {
// see https://daringfireball.net/projects/markdown/syntax#precode
if (content.indexOf('`') !== -1) {
return '`` ' + content + ' ``';
}
return content;
}
});

View File

@@ -0,0 +1,87 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
(function (factory) {
if (typeof module === "object" && typeof module.exports === "object") {
var v = factory(require, exports);
if (v !== undefined) module.exports = v;
}
else if (typeof define === "function" && define.amd) {
define(["require", "exports", "../jsonLanguageTypes"], factory);
}
})(function (require, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.findLinks = void 0;
var jsonLanguageTypes_1 = require("../jsonLanguageTypes");
function findLinks(document, doc) {
var links = [];
doc.visit(function (node) {
var _a;
if (node.type === "property" && node.keyNode.value === "$ref" && ((_a = node.valueNode) === null || _a === void 0 ? void 0 : _a.type) === 'string') {
var path = node.valueNode.value;
var targetNode = findTargetNode(doc, path);
if (targetNode) {
var targetPos = document.positionAt(targetNode.offset);
links.push({
target: document.uri + "#" + (targetPos.line + 1) + "," + (targetPos.character + 1),
range: createRange(document, node.valueNode)
});
}
}
return true;
});
return Promise.resolve(links);
}
exports.findLinks = findLinks;
function createRange(document, node) {
return jsonLanguageTypes_1.Range.create(document.positionAt(node.offset + 1), document.positionAt(node.offset + node.length - 1));
}
function findTargetNode(doc, path) {
var tokens = parseJSONPointer(path);
if (!tokens) {
return null;
}
return findNode(tokens, doc.root);
}
function findNode(pointer, node) {
if (!node) {
return null;
}
if (pointer.length === 0) {
return node;
}
var token = pointer.shift();
if (node && node.type === 'object') {
var propertyNode = node.properties.find(function (propertyNode) { return propertyNode.keyNode.value === token; });
if (!propertyNode) {
return null;
}
return findNode(pointer, propertyNode.valueNode);
}
else if (node && node.type === 'array') {
if (token.match(/^(0|[1-9][0-9]*)$/)) {
var index = Number.parseInt(token);
var arrayItem = node.items[index];
if (!arrayItem) {
return null;
}
return findNode(pointer, arrayItem);
}
}
return null;
}
function parseJSONPointer(path) {
if (path === "#") {
return [];
}
if (path[0] !== '#' || path[1] !== '/') {
return null;
}
return path.substring(2).split(/\//).map(unescape);
}
function unescape(str) {
return str.replace(/~1/g, '/').replace(/~0/g, '~');
}
});

View File

@@ -0,0 +1,548 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
(function (factory) {
if (typeof module === "object" && typeof module.exports === "object") {
var v = factory(require, exports);
if (v !== undefined) module.exports = v;
}
else if (typeof define === "function" && define.amd) {
define(["require", "exports", "jsonc-parser", "vscode-uri", "../utils/strings", "../parser/jsonParser", "vscode-nls", "../utils/glob"], factory);
}
})(function (require, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.JSONSchemaService = exports.ResolvedSchema = exports.UnresolvedSchema = void 0;
var Json = require("jsonc-parser");
var vscode_uri_1 = require("vscode-uri");
var Strings = require("../utils/strings");
var Parser = require("../parser/jsonParser");
var nls = require("vscode-nls");
var glob_1 = require("../utils/glob");
var localize = nls.loadMessageBundle();
var BANG = '!';
var PATH_SEP = '/';
var FilePatternAssociation = /** @class */ (function () {
function FilePatternAssociation(pattern, uris) {
this.globWrappers = [];
try {
for (var _i = 0, pattern_1 = pattern; _i < pattern_1.length; _i++) {
var patternString = pattern_1[_i];
var include = patternString[0] !== BANG;
if (!include) {
patternString = patternString.substring(1);
}
if (patternString.length > 0) {
if (patternString[0] === PATH_SEP) {
patternString = patternString.substring(1);
}
this.globWrappers.push({
regexp: glob_1.createRegex('**/' + patternString, { extended: true, globstar: true }),
include: include,
});
}
}
;
this.uris = uris;
}
catch (e) {
this.globWrappers.length = 0;
this.uris = [];
}
}
FilePatternAssociation.prototype.matchesPattern = function (fileName) {
var match = false;
for (var _i = 0, _a = this.globWrappers; _i < _a.length; _i++) {
var _b = _a[_i], regexp = _b.regexp, include = _b.include;
if (regexp.test(fileName)) {
match = include;
}
}
return match;
};
FilePatternAssociation.prototype.getURIs = function () {
return this.uris;
};
return FilePatternAssociation;
}());
var SchemaHandle = /** @class */ (function () {
function SchemaHandle(service, url, unresolvedSchemaContent) {
this.service = service;
this.url = url;
this.dependencies = {};
if (unresolvedSchemaContent) {
this.unresolvedSchema = this.service.promise.resolve(new UnresolvedSchema(unresolvedSchemaContent));
}
}
SchemaHandle.prototype.getUnresolvedSchema = function () {
if (!this.unresolvedSchema) {
this.unresolvedSchema = this.service.loadSchema(this.url);
}
return this.unresolvedSchema;
};
SchemaHandle.prototype.getResolvedSchema = function () {
var _this = this;
if (!this.resolvedSchema) {
this.resolvedSchema = this.getUnresolvedSchema().then(function (unresolved) {
return _this.service.resolveSchemaContent(unresolved, _this.url, _this.dependencies);
});
}
return this.resolvedSchema;
};
SchemaHandle.prototype.clearSchema = function () {
this.resolvedSchema = undefined;
this.unresolvedSchema = undefined;
this.dependencies = {};
};
return SchemaHandle;
}());
var UnresolvedSchema = /** @class */ (function () {
function UnresolvedSchema(schema, errors) {
if (errors === void 0) { errors = []; }
this.schema = schema;
this.errors = errors;
}
return UnresolvedSchema;
}());
exports.UnresolvedSchema = UnresolvedSchema;
var ResolvedSchema = /** @class */ (function () {
function ResolvedSchema(schema, errors) {
if (errors === void 0) { errors = []; }
this.schema = schema;
this.errors = errors;
}
ResolvedSchema.prototype.getSection = function (path) {
var schemaRef = this.getSectionRecursive(path, this.schema);
if (schemaRef) {
return Parser.asSchema(schemaRef);
}
return undefined;
};
ResolvedSchema.prototype.getSectionRecursive = function (path, schema) {
if (!schema || typeof schema === 'boolean' || path.length === 0) {
return schema;
}
var next = path.shift();
if (schema.properties && typeof schema.properties[next]) {
return this.getSectionRecursive(path, schema.properties[next]);
}
else if (schema.patternProperties) {
for (var _i = 0, _a = Object.keys(schema.patternProperties); _i < _a.length; _i++) {
var pattern = _a[_i];
var regex = Strings.extendedRegExp(pattern);
if (regex.test(next)) {
return this.getSectionRecursive(path, schema.patternProperties[pattern]);
}
}
}
else if (typeof schema.additionalProperties === 'object') {
return this.getSectionRecursive(path, schema.additionalProperties);
}
else if (next.match('[0-9]+')) {
if (Array.isArray(schema.items)) {
var index = parseInt(next, 10);
if (!isNaN(index) && schema.items[index]) {
return this.getSectionRecursive(path, schema.items[index]);
}
}
else if (schema.items) {
return this.getSectionRecursive(path, schema.items);
}
}
return undefined;
};
return ResolvedSchema;
}());
exports.ResolvedSchema = ResolvedSchema;
var JSONSchemaService = /** @class */ (function () {
function JSONSchemaService(requestService, contextService, promiseConstructor) {
this.contextService = contextService;
this.requestService = requestService;
this.promiseConstructor = promiseConstructor || Promise;
this.callOnDispose = [];
this.contributionSchemas = {};
this.contributionAssociations = [];
this.schemasById = {};
this.filePatternAssociations = [];
this.registeredSchemasIds = {};
}
JSONSchemaService.prototype.getRegisteredSchemaIds = function (filter) {
return Object.keys(this.registeredSchemasIds).filter(function (id) {
var scheme = vscode_uri_1.URI.parse(id).scheme;
return scheme !== 'schemaservice' && (!filter || filter(scheme));
});
};
Object.defineProperty(JSONSchemaService.prototype, "promise", {
get: function () {
return this.promiseConstructor;
},
enumerable: false,
configurable: true
});
JSONSchemaService.prototype.dispose = function () {
while (this.callOnDispose.length > 0) {
this.callOnDispose.pop()();
}
};
JSONSchemaService.prototype.onResourceChange = function (uri) {
var _this = this;
// always clear this local cache when a resource changes
this.cachedSchemaForResource = undefined;
var hasChanges = false;
uri = normalizeId(uri);
var toWalk = [uri];
var all = Object.keys(this.schemasById).map(function (key) { return _this.schemasById[key]; });
while (toWalk.length) {
var curr = toWalk.pop();
for (var i = 0; i < all.length; i++) {
var handle = all[i];
if (handle && (handle.url === curr || handle.dependencies[curr])) {
if (handle.url !== curr) {
toWalk.push(handle.url);
}
handle.clearSchema();
all[i] = undefined;
hasChanges = true;
}
}
}
return hasChanges;
};
JSONSchemaService.prototype.setSchemaContributions = function (schemaContributions) {
if (schemaContributions.schemas) {
var schemas = schemaContributions.schemas;
for (var id in schemas) {
var normalizedId = normalizeId(id);
this.contributionSchemas[normalizedId] = this.addSchemaHandle(normalizedId, schemas[id]);
}
}
if (Array.isArray(schemaContributions.schemaAssociations)) {
var schemaAssociations = schemaContributions.schemaAssociations;
for (var _i = 0, schemaAssociations_1 = schemaAssociations; _i < schemaAssociations_1.length; _i++) {
var schemaAssociation = schemaAssociations_1[_i];
var uris = schemaAssociation.uris.map(normalizeId);
var association = this.addFilePatternAssociation(schemaAssociation.pattern, uris);
this.contributionAssociations.push(association);
}
}
};
JSONSchemaService.prototype.addSchemaHandle = function (id, unresolvedSchemaContent) {
var schemaHandle = new SchemaHandle(this, id, unresolvedSchemaContent);
this.schemasById[id] = schemaHandle;
return schemaHandle;
};
JSONSchemaService.prototype.getOrAddSchemaHandle = function (id, unresolvedSchemaContent) {
return this.schemasById[id] || this.addSchemaHandle(id, unresolvedSchemaContent);
};
JSONSchemaService.prototype.addFilePatternAssociation = function (pattern, uris) {
var fpa = new FilePatternAssociation(pattern, uris);
this.filePatternAssociations.push(fpa);
return fpa;
};
JSONSchemaService.prototype.registerExternalSchema = function (uri, filePatterns, unresolvedSchemaContent) {
var id = normalizeId(uri);
this.registeredSchemasIds[id] = true;
this.cachedSchemaForResource = undefined;
if (filePatterns) {
this.addFilePatternAssociation(filePatterns, [uri]);
}
return unresolvedSchemaContent ? this.addSchemaHandle(id, unresolvedSchemaContent) : this.getOrAddSchemaHandle(id);
};
JSONSchemaService.prototype.clearExternalSchemas = function () {
this.schemasById = {};
this.filePatternAssociations = [];
this.registeredSchemasIds = {};
this.cachedSchemaForResource = undefined;
for (var id in this.contributionSchemas) {
this.schemasById[id] = this.contributionSchemas[id];
this.registeredSchemasIds[id] = true;
}
for (var _i = 0, _a = this.contributionAssociations; _i < _a.length; _i++) {
var contributionAssociation = _a[_i];
this.filePatternAssociations.push(contributionAssociation);
}
};
JSONSchemaService.prototype.getResolvedSchema = function (schemaId) {
var id = normalizeId(schemaId);
var schemaHandle = this.schemasById[id];
if (schemaHandle) {
return schemaHandle.getResolvedSchema();
}
return this.promise.resolve(undefined);
};
JSONSchemaService.prototype.loadSchema = function (url) {
if (!this.requestService) {
var errorMessage = localize('json.schema.norequestservice', 'Unable to load schema from \'{0}\'. No schema request service available', toDisplayString(url));
return this.promise.resolve(new UnresolvedSchema({}, [errorMessage]));
}
return this.requestService(url).then(function (content) {
if (!content) {
var errorMessage = localize('json.schema.nocontent', 'Unable to load schema from \'{0}\': No content.', toDisplayString(url));
return new UnresolvedSchema({}, [errorMessage]);
}
var schemaContent = {};
var jsonErrors = [];
schemaContent = Json.parse(content, jsonErrors);
var errors = jsonErrors.length ? [localize('json.schema.invalidFormat', 'Unable to parse content from \'{0}\': Parse error at offset {1}.', toDisplayString(url), jsonErrors[0].offset)] : [];
return new UnresolvedSchema(schemaContent, errors);
}, function (error) {
var errorMessage = error.toString();
var errorSplit = error.toString().split('Error: ');
if (errorSplit.length > 1) {
// more concise error message, URL and context are attached by caller anyways
errorMessage = errorSplit[1];
}
if (Strings.endsWith(errorMessage, '.')) {
errorMessage = errorMessage.substr(0, errorMessage.length - 1);
}
return new UnresolvedSchema({}, [localize('json.schema.nocontent', 'Unable to load schema from \'{0}\': {1}.', toDisplayString(url), errorMessage)]);
});
};
JSONSchemaService.prototype.resolveSchemaContent = function (schemaToResolve, schemaURL, dependencies) {
var _this = this;
var resolveErrors = schemaToResolve.errors.slice(0);
var schema = schemaToResolve.schema;
if (schema.$schema) {
var id = normalizeId(schema.$schema);
if (id === 'http://json-schema.org/draft-03/schema') {
return this.promise.resolve(new ResolvedSchema({}, [localize('json.schema.draft03.notsupported', "Draft-03 schemas are not supported.")]));
}
else if (id === 'https://json-schema.org/draft/2019-09/schema') {
resolveErrors.push(localize('json.schema.draft201909.notsupported', "Draft 2019-09 schemas are not yet fully supported."));
}
}
var contextService = this.contextService;
var findSection = function (schema, path) {
if (!path) {
return schema;
}
var current = schema;
if (path[0] === '/') {
path = path.substr(1);
}
path.split('/').some(function (part) {
part = part.replace(/~1/g, '/').replace(/~0/g, '~');
current = current[part];
return !current;
});
return current;
};
var merge = function (target, sourceRoot, sourceURI, refSegment) {
var path = refSegment ? decodeURIComponent(refSegment) : undefined;
var section = findSection(sourceRoot, path);
if (section) {
for (var key in section) {
if (section.hasOwnProperty(key) && !target.hasOwnProperty(key)) {
target[key] = section[key];
}
}
}
else {
resolveErrors.push(localize('json.schema.invalidref', '$ref \'{0}\' in \'{1}\' can not be resolved.', path, sourceURI));
}
};
var resolveExternalLink = function (node, uri, refSegment, parentSchemaURL, parentSchemaDependencies) {
if (contextService && !/^[A-Za-z][A-Za-z0-9+\-.+]*:\/\/.*/.test(uri)) {
uri = contextService.resolveRelativePath(uri, parentSchemaURL);
}
uri = normalizeId(uri);
var referencedHandle = _this.getOrAddSchemaHandle(uri);
return referencedHandle.getUnresolvedSchema().then(function (unresolvedSchema) {
parentSchemaDependencies[uri] = true;
if (unresolvedSchema.errors.length) {
var loc = refSegment ? uri + '#' + refSegment : uri;
resolveErrors.push(localize('json.schema.problemloadingref', 'Problems loading reference \'{0}\': {1}', loc, unresolvedSchema.errors[0]));
}
merge(node, unresolvedSchema.schema, uri, refSegment);
return resolveRefs(node, unresolvedSchema.schema, uri, referencedHandle.dependencies);
});
};
var resolveRefs = function (node, parentSchema, parentSchemaURL, parentSchemaDependencies) {
if (!node || typeof node !== 'object') {
return Promise.resolve(null);
}
var toWalk = [node];
var seen = [];
var openPromises = [];
var collectEntries = function () {
var entries = [];
for (var _i = 0; _i < arguments.length; _i++) {
entries[_i] = arguments[_i];
}
for (var _a = 0, entries_1 = entries; _a < entries_1.length; _a++) {
var entry = entries_1[_a];
if (typeof entry === 'object') {
toWalk.push(entry);
}
}
};
var collectMapEntries = function () {
var maps = [];
for (var _i = 0; _i < arguments.length; _i++) {
maps[_i] = arguments[_i];
}
for (var _a = 0, maps_1 = maps; _a < maps_1.length; _a++) {
var map = maps_1[_a];
if (typeof map === 'object') {
for (var k in map) {
var key = k;
var entry = map[key];
if (typeof entry === 'object') {
toWalk.push(entry);
}
}
}
}
};
var collectArrayEntries = function () {
var arrays = [];
for (var _i = 0; _i < arguments.length; _i++) {
arrays[_i] = arguments[_i];
}
for (var _a = 0, arrays_1 = arrays; _a < arrays_1.length; _a++) {
var array = arrays_1[_a];
if (Array.isArray(array)) {
for (var _b = 0, array_1 = array; _b < array_1.length; _b++) {
var entry = array_1[_b];
if (typeof entry === 'object') {
toWalk.push(entry);
}
}
}
}
};
var handleRef = function (next) {
var seenRefs = [];
while (next.$ref) {
var ref = next.$ref;
var segments = ref.split('#', 2);
delete next.$ref;
if (segments[0].length > 0) {
openPromises.push(resolveExternalLink(next, segments[0], segments[1], parentSchemaURL, parentSchemaDependencies));
return;
}
else {
if (seenRefs.indexOf(ref) === -1) {
merge(next, parentSchema, parentSchemaURL, segments[1]); // can set next.$ref again, use seenRefs to avoid circle
seenRefs.push(ref);
}
}
}
collectEntries(next.items, next.additionalItems, next.additionalProperties, next.not, next.contains, next.propertyNames, next.if, next.then, next.else);
collectMapEntries(next.definitions, next.properties, next.patternProperties, next.dependencies);
collectArrayEntries(next.anyOf, next.allOf, next.oneOf, next.items);
};
while (toWalk.length) {
var next = toWalk.pop();
if (seen.indexOf(next) >= 0) {
continue;
}
seen.push(next);
handleRef(next);
}
return _this.promise.all(openPromises);
};
return resolveRefs(schema, schema, schemaURL, dependencies).then(function (_) { return new ResolvedSchema(schema, resolveErrors); });
};
JSONSchemaService.prototype.getSchemaForResource = function (resource, document) {
// first use $schema if present
if (document && document.root && document.root.type === 'object') {
var schemaProperties = document.root.properties.filter(function (p) { return (p.keyNode.value === '$schema') && p.valueNode && p.valueNode.type === 'string'; });
if (schemaProperties.length > 0) {
var valueNode = schemaProperties[0].valueNode;
if (valueNode && valueNode.type === 'string') {
var schemeId = Parser.getNodeValue(valueNode);
if (schemeId && Strings.startsWith(schemeId, '.') && this.contextService) {
schemeId = this.contextService.resolveRelativePath(schemeId, resource);
}
if (schemeId) {
var id = normalizeId(schemeId);
return this.getOrAddSchemaHandle(id).getResolvedSchema();
}
}
}
}
if (this.cachedSchemaForResource && this.cachedSchemaForResource.resource === resource) {
return this.cachedSchemaForResource.resolvedSchema;
}
var seen = Object.create(null);
var schemas = [];
var normalizedResource = normalizeResourceForMatching(resource);
for (var _i = 0, _a = this.filePatternAssociations; _i < _a.length; _i++) {
var entry = _a[_i];
if (entry.matchesPattern(normalizedResource)) {
for (var _b = 0, _c = entry.getURIs(); _b < _c.length; _b++) {
var schemaId = _c[_b];
if (!seen[schemaId]) {
schemas.push(schemaId);
seen[schemaId] = true;
}
}
}
}
var resolvedSchema = schemas.length > 0 ? this.createCombinedSchema(resource, schemas).getResolvedSchema() : this.promise.resolve(undefined);
this.cachedSchemaForResource = { resource: resource, resolvedSchema: resolvedSchema };
return resolvedSchema;
};
JSONSchemaService.prototype.createCombinedSchema = function (resource, schemaIds) {
if (schemaIds.length === 1) {
return this.getOrAddSchemaHandle(schemaIds[0]);
}
else {
var combinedSchemaId = 'schemaservice://combinedSchema/' + encodeURIComponent(resource);
var combinedSchema = {
allOf: schemaIds.map(function (schemaId) { return ({ $ref: schemaId }); })
};
return this.addSchemaHandle(combinedSchemaId, combinedSchema);
}
};
JSONSchemaService.prototype.getMatchingSchemas = function (document, jsonDocument, schema) {
if (schema) {
var id = schema.id || ('schemaservice://untitled/matchingSchemas/' + idCounter++);
return this.resolveSchemaContent(new UnresolvedSchema(schema), id, {}).then(function (resolvedSchema) {
return jsonDocument.getMatchingSchemas(resolvedSchema.schema).filter(function (s) { return !s.inverted; });
});
}
return this.getSchemaForResource(document.uri, jsonDocument).then(function (schema) {
if (schema) {
return jsonDocument.getMatchingSchemas(schema.schema).filter(function (s) { return !s.inverted; });
}
return [];
});
};
return JSONSchemaService;
}());
exports.JSONSchemaService = JSONSchemaService;
var idCounter = 0;
function normalizeId(id) {
// remove trailing '#', normalize drive capitalization
try {
return vscode_uri_1.URI.parse(id).toString();
}
catch (e) {
return id;
}
}
function normalizeResourceForMatching(resource) {
// remove queries and fragments, normalize drive capitalization
try {
return vscode_uri_1.URI.parse(resource).with({ fragment: null, query: null }).toString();
}
catch (e) {
return resource;
}
}
function toDisplayString(url) {
try {
var uri = vscode_uri_1.URI.parse(url);
if (uri.scheme === 'file') {
return uri.fsPath;
}
}
catch (e) {
// ignore
}
return url;
}
});

View File

@@ -0,0 +1,75 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
(function (factory) {
if (typeof module === "object" && typeof module.exports === "object") {
var v = factory(require, exports);
if (v !== undefined) module.exports = v;
}
else if (typeof define === "function" && define.amd) {
define(["require", "exports", "../jsonLanguageTypes", "jsonc-parser"], factory);
}
})(function (require, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getSelectionRanges = void 0;
var jsonLanguageTypes_1 = require("../jsonLanguageTypes");
var jsonc_parser_1 = require("jsonc-parser");
function getSelectionRanges(document, positions, doc) {
function getSelectionRange(position) {
var offset = document.offsetAt(position);
var node = doc.getNodeFromOffset(offset, true);
var result = [];
while (node) {
switch (node.type) {
case 'string':
case 'object':
case 'array':
// range without ", [ or {
var cStart = node.offset + 1, cEnd = node.offset + node.length - 1;
if (cStart < cEnd && offset >= cStart && offset <= cEnd) {
result.push(newRange(cStart, cEnd));
}
result.push(newRange(node.offset, node.offset + node.length));
break;
case 'number':
case 'boolean':
case 'null':
case 'property':
result.push(newRange(node.offset, node.offset + node.length));
break;
}
if (node.type === 'property' || node.parent && node.parent.type === 'array') {
var afterCommaOffset = getOffsetAfterNextToken(node.offset + node.length, 5 /* CommaToken */);
if (afterCommaOffset !== -1) {
result.push(newRange(node.offset, afterCommaOffset));
}
}
node = node.parent;
}
var current = undefined;
for (var index = result.length - 1; index >= 0; index--) {
current = jsonLanguageTypes_1.SelectionRange.create(result[index], current);
}
if (!current) {
current = jsonLanguageTypes_1.SelectionRange.create(jsonLanguageTypes_1.Range.create(position, position));
}
return current;
}
function newRange(start, end) {
return jsonLanguageTypes_1.Range.create(document.positionAt(start), document.positionAt(end));
}
var scanner = jsonc_parser_1.createScanner(document.getText(), true);
function getOffsetAfterNextToken(offset, expectedToken) {
scanner.setPosition(offset);
var token = scanner.scan();
if (token === expectedToken) {
return scanner.getTokenOffset() + scanner.getTokenLength();
}
return -1;
}
return positions.map(getSelectionRange);
}
exports.getSelectionRanges = getSelectionRanges;
});

View File

@@ -0,0 +1,159 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
(function (factory) {
if (typeof module === "object" && typeof module.exports === "object") {
var v = factory(require, exports);
if (v !== undefined) module.exports = v;
}
else if (typeof define === "function" && define.amd) {
define(["require", "exports", "./jsonSchemaService", "../jsonLanguageTypes", "vscode-nls", "../utils/objects"], factory);
}
})(function (require, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.JSONValidation = void 0;
var jsonSchemaService_1 = require("./jsonSchemaService");
var jsonLanguageTypes_1 = require("../jsonLanguageTypes");
var nls = require("vscode-nls");
var objects_1 = require("../utils/objects");
var localize = nls.loadMessageBundle();
var JSONValidation = /** @class */ (function () {
function JSONValidation(jsonSchemaService, promiseConstructor) {
this.jsonSchemaService = jsonSchemaService;
this.promise = promiseConstructor;
this.validationEnabled = true;
}
JSONValidation.prototype.configure = function (raw) {
if (raw) {
this.validationEnabled = raw.validate !== false;
this.commentSeverity = raw.allowComments ? undefined : jsonLanguageTypes_1.DiagnosticSeverity.Error;
}
};
JSONValidation.prototype.doValidation = function (textDocument, jsonDocument, documentSettings, schema) {
var _this = this;
if (!this.validationEnabled) {
return this.promise.resolve([]);
}
var diagnostics = [];
var added = {};
var addProblem = function (problem) {
// remove duplicated messages
var signature = problem.range.start.line + ' ' + problem.range.start.character + ' ' + problem.message;
if (!added[signature]) {
added[signature] = true;
diagnostics.push(problem);
}
};
var getDiagnostics = function (schema) {
var trailingCommaSeverity = (documentSettings === null || documentSettings === void 0 ? void 0 : documentSettings.trailingCommas) ? toDiagnosticSeverity(documentSettings.trailingCommas) : jsonLanguageTypes_1.DiagnosticSeverity.Error;
var commentSeverity = (documentSettings === null || documentSettings === void 0 ? void 0 : documentSettings.comments) ? toDiagnosticSeverity(documentSettings.comments) : _this.commentSeverity;
var schemaValidation = (documentSettings === null || documentSettings === void 0 ? void 0 : documentSettings.schemaValidation) ? toDiagnosticSeverity(documentSettings.schemaValidation) : jsonLanguageTypes_1.DiagnosticSeverity.Warning;
var schemaRequest = (documentSettings === null || documentSettings === void 0 ? void 0 : documentSettings.schemaRequest) ? toDiagnosticSeverity(documentSettings.schemaRequest) : jsonLanguageTypes_1.DiagnosticSeverity.Warning;
if (schema) {
if (schema.errors.length && jsonDocument.root && schemaRequest) {
var astRoot = jsonDocument.root;
var property = astRoot.type === 'object' ? astRoot.properties[0] : undefined;
if (property && property.keyNode.value === '$schema') {
var node = property.valueNode || property;
var range = jsonLanguageTypes_1.Range.create(textDocument.positionAt(node.offset), textDocument.positionAt(node.offset + node.length));
addProblem(jsonLanguageTypes_1.Diagnostic.create(range, schema.errors[0], schemaRequest, jsonLanguageTypes_1.ErrorCode.SchemaResolveError));
}
else {
var range = jsonLanguageTypes_1.Range.create(textDocument.positionAt(astRoot.offset), textDocument.positionAt(astRoot.offset + 1));
addProblem(jsonLanguageTypes_1.Diagnostic.create(range, schema.errors[0], schemaRequest, jsonLanguageTypes_1.ErrorCode.SchemaResolveError));
}
}
else if (schemaValidation) {
var semanticErrors = jsonDocument.validate(textDocument, schema.schema, schemaValidation);
if (semanticErrors) {
semanticErrors.forEach(addProblem);
}
}
if (schemaAllowsComments(schema.schema)) {
commentSeverity = undefined;
}
if (schemaAllowsTrailingCommas(schema.schema)) {
trailingCommaSeverity = undefined;
}
}
for (var _i = 0, _a = jsonDocument.syntaxErrors; _i < _a.length; _i++) {
var p = _a[_i];
if (p.code === jsonLanguageTypes_1.ErrorCode.TrailingComma) {
if (typeof trailingCommaSeverity !== 'number') {
continue;
}
p.severity = trailingCommaSeverity;
}
addProblem(p);
}
if (typeof commentSeverity === 'number') {
var message_1 = localize('InvalidCommentToken', 'Comments are not permitted in JSON.');
jsonDocument.comments.forEach(function (c) {
addProblem(jsonLanguageTypes_1.Diagnostic.create(c, message_1, commentSeverity, jsonLanguageTypes_1.ErrorCode.CommentNotPermitted));
});
}
return diagnostics;
};
if (schema) {
var id = schema.id || ('schemaservice://untitled/' + idCounter++);
return this.jsonSchemaService.resolveSchemaContent(new jsonSchemaService_1.UnresolvedSchema(schema), id, {}).then(function (resolvedSchema) {
return getDiagnostics(resolvedSchema);
});
}
return this.jsonSchemaService.getSchemaForResource(textDocument.uri, jsonDocument).then(function (schema) {
return getDiagnostics(schema);
});
};
return JSONValidation;
}());
exports.JSONValidation = JSONValidation;
var idCounter = 0;
function schemaAllowsComments(schemaRef) {
if (schemaRef && typeof schemaRef === 'object') {
if (objects_1.isBoolean(schemaRef.allowComments)) {
return schemaRef.allowComments;
}
if (schemaRef.allOf) {
for (var _i = 0, _a = schemaRef.allOf; _i < _a.length; _i++) {
var schema = _a[_i];
var allow = schemaAllowsComments(schema);
if (objects_1.isBoolean(allow)) {
return allow;
}
}
}
}
return undefined;
}
function schemaAllowsTrailingCommas(schemaRef) {
if (schemaRef && typeof schemaRef === 'object') {
if (objects_1.isBoolean(schemaRef.allowTrailingCommas)) {
return schemaRef.allowTrailingCommas;
}
var deprSchemaRef = schemaRef;
if (objects_1.isBoolean(deprSchemaRef['allowsTrailingCommas'])) { // deprecated
return deprSchemaRef['allowsTrailingCommas'];
}
if (schemaRef.allOf) {
for (var _i = 0, _a = schemaRef.allOf; _i < _a.length; _i++) {
var schema = _a[_i];
var allow = schemaAllowsTrailingCommas(schema);
if (objects_1.isBoolean(allow)) {
return allow;
}
}
}
}
return undefined;
}
function toDiagnosticSeverity(severityLevel) {
switch (severityLevel) {
case 'error': return jsonLanguageTypes_1.DiagnosticSeverity.Error;
case 'warning': return jsonLanguageTypes_1.DiagnosticSeverity.Warning;
case 'ignore': return undefined;
}
return undefined;
}
});

View File

@@ -0,0 +1,85 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
(function (factory) {
if (typeof module === "object" && typeof module.exports === "object") {
var v = factory(require, exports);
if (v !== undefined) module.exports = v;
}
else if (typeof define === "function" && define.amd) {
define(["require", "exports"], factory);
}
})(function (require, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.colorFrom256RGB = exports.colorFromHex = exports.hexDigit = void 0;
var Digit0 = 48;
var Digit9 = 57;
var A = 65;
var a = 97;
var f = 102;
function hexDigit(charCode) {
if (charCode < Digit0) {
return 0;
}
if (charCode <= Digit9) {
return charCode - Digit0;
}
if (charCode < a) {
charCode += (a - A);
}
if (charCode >= a && charCode <= f) {
return charCode - a + 10;
}
return 0;
}
exports.hexDigit = hexDigit;
function colorFromHex(text) {
if (text[0] !== '#') {
return undefined;
}
switch (text.length) {
case 4:
return {
red: (hexDigit(text.charCodeAt(1)) * 0x11) / 255.0,
green: (hexDigit(text.charCodeAt(2)) * 0x11) / 255.0,
blue: (hexDigit(text.charCodeAt(3)) * 0x11) / 255.0,
alpha: 1
};
case 5:
return {
red: (hexDigit(text.charCodeAt(1)) * 0x11) / 255.0,
green: (hexDigit(text.charCodeAt(2)) * 0x11) / 255.0,
blue: (hexDigit(text.charCodeAt(3)) * 0x11) / 255.0,
alpha: (hexDigit(text.charCodeAt(4)) * 0x11) / 255.0,
};
case 7:
return {
red: (hexDigit(text.charCodeAt(1)) * 0x10 + hexDigit(text.charCodeAt(2))) / 255.0,
green: (hexDigit(text.charCodeAt(3)) * 0x10 + hexDigit(text.charCodeAt(4))) / 255.0,
blue: (hexDigit(text.charCodeAt(5)) * 0x10 + hexDigit(text.charCodeAt(6))) / 255.0,
alpha: 1
};
case 9:
return {
red: (hexDigit(text.charCodeAt(1)) * 0x10 + hexDigit(text.charCodeAt(2))) / 255.0,
green: (hexDigit(text.charCodeAt(3)) * 0x10 + hexDigit(text.charCodeAt(4))) / 255.0,
blue: (hexDigit(text.charCodeAt(5)) * 0x10 + hexDigit(text.charCodeAt(6))) / 255.0,
alpha: (hexDigit(text.charCodeAt(7)) * 0x10 + hexDigit(text.charCodeAt(8))) / 255.0
};
}
return undefined;
}
exports.colorFromHex = colorFromHex;
function colorFrom256RGB(red, green, blue, alpha) {
if (alpha === void 0) { alpha = 1.0; }
return {
red: red / 255.0,
green: green / 255.0,
blue: blue / 255.0,
alpha: alpha
};
}
exports.colorFrom256RGB = colorFrom256RGB;
});

View File

@@ -0,0 +1,138 @@
(function (factory) {
if (typeof module === "object" && typeof module.exports === "object") {
var v = factory(require, exports);
if (v !== undefined) module.exports = v;
}
else if (typeof define === "function" && define.amd) {
define(["require", "exports"], factory);
}
})(function (require, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createRegex = void 0;
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Copyright (c) 2013, Nick Fitzgerald
* Licensed under the MIT License. See LICENCE.md in the project root for license information.
*--------------------------------------------------------------------------------------------*/
function createRegex(glob, opts) {
if (typeof glob !== 'string') {
throw new TypeError('Expected a string');
}
var str = String(glob);
// The regexp we are building, as a string.
var reStr = "";
// Whether we are matching so called "extended" globs (like bash) and should
// support single character matching, matching ranges of characters, group
// matching, etc.
var extended = opts ? !!opts.extended : false;
// When globstar is _false_ (default), '/foo/*' is translated a regexp like
// '^\/foo\/.*$' which will match any string beginning with '/foo/'
// When globstar is _true_, '/foo/*' is translated to regexp like
// '^\/foo\/[^/]*$' which will match any string beginning with '/foo/' BUT
// which does not have a '/' to the right of it.
// E.g. with '/foo/*' these will match: '/foo/bar', '/foo/bar.txt' but
// these will not '/foo/bar/baz', '/foo/bar/baz.txt'
// Lastely, when globstar is _true_, '/foo/**' is equivelant to '/foo/*' when
// globstar is _false_
var globstar = opts ? !!opts.globstar : false;
// If we are doing extended matching, this boolean is true when we are inside
// a group (eg {*.html,*.js}), and false otherwise.
var inGroup = false;
// RegExp flags (eg "i" ) to pass in to RegExp constructor.
var flags = opts && typeof (opts.flags) === "string" ? opts.flags : "";
var c;
for (var i = 0, len = str.length; i < len; i++) {
c = str[i];
switch (c) {
case "/":
case "$":
case "^":
case "+":
case ".":
case "(":
case ")":
case "=":
case "!":
case "|":
reStr += "\\" + c;
break;
case "?":
if (extended) {
reStr += ".";
break;
}
case "[":
case "]":
if (extended) {
reStr += c;
break;
}
case "{":
if (extended) {
inGroup = true;
reStr += "(";
break;
}
case "}":
if (extended) {
inGroup = false;
reStr += ")";
break;
}
case ",":
if (inGroup) {
reStr += "|";
break;
}
reStr += "\\" + c;
break;
case "*":
// Move over all consecutive "*"'s.
// Also store the previous and next characters
var prevChar = str[i - 1];
var starCount = 1;
while (str[i + 1] === "*") {
starCount++;
i++;
}
var nextChar = str[i + 1];
if (!globstar) {
// globstar is disabled, so treat any number of "*" as one
reStr += ".*";
}
else {
// globstar is enabled, so determine if this is a globstar segment
var isGlobstar = starCount > 1 // multiple "*"'s
&& (prevChar === "/" || prevChar === undefined || prevChar === '{' || prevChar === ',') // from the start of the segment
&& (nextChar === "/" || nextChar === undefined || nextChar === ',' || nextChar === '}'); // to the end of the segment
if (isGlobstar) {
if (nextChar === "/") {
i++; // move over the "/"
}
else if (prevChar === '/' && reStr.endsWith('\\/')) {
reStr = reStr.substr(0, reStr.length - 2);
}
// it's a globstar, so match zero or more path segments
reStr += "((?:[^/]*(?:\/|$))*)";
}
else {
// it's not a globstar, so only match one path segment
reStr += "([^/]*)";
}
}
break;
default:
reStr += c;
}
}
// When regexp 'g' flag is specified don't
// constrain the regular expression with ^ & $
if (!flags || !~flags.indexOf('g')) {
reStr = "^" + reStr + "$";
}
return new RegExp(reStr, flags);
}
exports.createRegex = createRegex;
;
});

View File

@@ -0,0 +1,56 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
(function (factory) {
if (typeof module === "object" && typeof module.exports === "object") {
var v = factory(require, exports);
if (v !== undefined) module.exports = v;
}
else if (typeof define === "function" && define.amd) {
define(["require", "exports"], factory);
}
})(function (require, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.stringifyObject = void 0;
function stringifyObject(obj, indent, stringifyLiteral) {
if (obj !== null && typeof obj === 'object') {
var newIndent = indent + '\t';
if (Array.isArray(obj)) {
if (obj.length === 0) {
return '[]';
}
var result = '[\n';
for (var i = 0; i < obj.length; i++) {
result += newIndent + stringifyObject(obj[i], newIndent, stringifyLiteral);
if (i < obj.length - 1) {
result += ',';
}
result += '\n';
}
result += indent + ']';
return result;
}
else {
var keys = Object.keys(obj);
if (keys.length === 0) {
return '{}';
}
var result = '{\n';
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
result += newIndent + JSON.stringify(key) + ': ' + stringifyObject(obj[key], newIndent, stringifyLiteral);
if (i < keys.length - 1) {
result += ',';
}
result += '\n';
}
result += indent + '}';
return result;
}
}
return stringifyLiteral(obj);
}
exports.stringifyObject = stringifyObject;
});

View File

@@ -0,0 +1,83 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
(function (factory) {
if (typeof module === "object" && typeof module.exports === "object") {
var v = factory(require, exports);
if (v !== undefined) module.exports = v;
}
else if (typeof define === "function" && define.amd) {
define(["require", "exports"], factory);
}
})(function (require, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.isString = exports.isBoolean = exports.isDefined = exports.isNumber = exports.equals = void 0;
function equals(one, other) {
if (one === other) {
return true;
}
if (one === null || one === undefined || other === null || other === undefined) {
return false;
}
if (typeof one !== typeof other) {
return false;
}
if (typeof one !== 'object') {
return false;
}
if ((Array.isArray(one)) !== (Array.isArray(other))) {
return false;
}
var i, key;
if (Array.isArray(one)) {
if (one.length !== other.length) {
return false;
}
for (i = 0; i < one.length; i++) {
if (!equals(one[i], other[i])) {
return false;
}
}
}
else {
var oneKeys = [];
for (key in one) {
oneKeys.push(key);
}
oneKeys.sort();
var otherKeys = [];
for (key in other) {
otherKeys.push(key);
}
otherKeys.sort();
if (!equals(oneKeys, otherKeys)) {
return false;
}
for (i = 0; i < oneKeys.length; i++) {
if (!equals(one[oneKeys[i]], other[oneKeys[i]])) {
return false;
}
}
}
return true;
}
exports.equals = equals;
function isNumber(val) {
return typeof val === 'number';
}
exports.isNumber = isNumber;
function isDefined(val) {
return typeof val !== 'undefined';
}
exports.isDefined = isDefined;
function isBoolean(val) {
return typeof val === 'boolean';
}
exports.isBoolean = isBoolean;
function isString(val) {
return typeof val === 'string';
}
exports.isString = isString;
});

View File

@@ -0,0 +1,70 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
(function (factory) {
if (typeof module === "object" && typeof module.exports === "object") {
var v = factory(require, exports);
if (v !== undefined) module.exports = v;
}
else if (typeof define === "function" && define.amd) {
define(["require", "exports"], factory);
}
})(function (require, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.extendedRegExp = exports.repeat = exports.convertSimple2RegExpPattern = exports.endsWith = exports.startsWith = void 0;
function startsWith(haystack, needle) {
if (haystack.length < needle.length) {
return false;
}
for (var i = 0; i < needle.length; i++) {
if (haystack[i] !== needle[i]) {
return false;
}
}
return true;
}
exports.startsWith = startsWith;
/**
* Determines if haystack ends with needle.
*/
function endsWith(haystack, needle) {
var diff = haystack.length - needle.length;
if (diff > 0) {
return haystack.lastIndexOf(needle) === diff;
}
else if (diff === 0) {
return haystack === needle;
}
else {
return false;
}
}
exports.endsWith = endsWith;
function convertSimple2RegExpPattern(pattern) {
return pattern.replace(/[\-\\\{\}\+\?\|\^\$\.\,\[\]\(\)\#\s]/g, '\\$&').replace(/[\*]/g, '.*');
}
exports.convertSimple2RegExpPattern = convertSimple2RegExpPattern;
function repeat(value, count) {
var s = '';
while (count > 0) {
if ((count & 1) === 1) {
s += value;
}
value += value;
count = count >>> 1;
}
return s;
}
exports.repeat = repeat;
function extendedRegExp(pattern) {
if (startsWith(pattern, '(?i)')) {
return new RegExp(pattern.substring(4), 'i');
}
else {
return new RegExp(pattern);
}
}
exports.extendedRegExp = extendedRegExp;
});

View File

@@ -0,0 +1,76 @@
3.3.0 2022-06-24
=================
- `JSONVisitor.onObjectBegin` and `JSONVisitor.onArrayBegin` can now return `false` to instruct the visitor that no children should be visited.
3.2.0 2022-08-30
=================
- update the version of the bundled Javascript files to `es2020`.
- include all `const enum` values in the bundled JavaScript files (`ScanError`, `SyntaxKind`, `ParseErrorCode`).
3.1.0 2022-07-07
==================
* added new API `FormattingOptions.keepLines` : It leaves the initial line positions in the formatting.
3.0.0 2020-11-13
==================
* fixed API spec for `parseTree`. Can return `undefine` for empty input.
* added new API `FormattingOptions.insertFinalNewline`.
2.3.0 2020-07-03
==================
* new API `ModificationOptions.isArrayInsertion`: If `JSONPath` refers to an index of an array and `isArrayInsertion` is `true`, then `modify` will insert a new item at that location instead of overwriting its contents.
* `ModificationOptions.formattingOptions` is now optional. If not set, newly inserted content will not be formatted.
2.2.0 2019-10-25
==================
* added `ParseOptions.allowEmptyContent`. Default is `false`.
* new API `getNodeType`: Returns the type of a value returned by parse.
* `parse`: Fix issue with empty property name
2.1.0 2019-03-29
==================
* `JSONScanner` and `JSONVisitor` return lineNumber / character.
2.0.0 2018-04-12
==================
* renamed `Node.columnOffset` to `Node.colonOffset`
* new API `getNodePath`: Gets the JSON path of the given JSON DOM node
* new API `findNodeAtOffset`: Finds the most inner node at the given offset. If `includeRightBound` is set, also finds nodes that end at the given offset.
1.0.3 2018-03-07
==================
* provide ems modules
1.0.2 2018-03-05
==================
* added the `visit.onComment` API, reported when comments are allowed.
* added the `ParseErrorCode.InvalidCommentToken` enum value, reported when comments are disallowed.
1.0.1
==================
* added the `format` API: computes edits to format a JSON document.
* added the `modify` API: computes edits to insert, remove or replace a property or value in a JSON document.
* added the `allyEdits` API: applies edits to a document
1.0.0
==================
* remove nls dependency (remove `getParseErrorMessage`)
0.4.2 / 2017-05-05
==================
* added `ParseError.offset` & `ParseError.length`
0.4.1 / 2017-04-02
==================
* added `ParseOptions.allowTrailingComma`
0.4.0 / 2017-02-23
==================
* fix for `getLocation`. Now `getLocation` inside an object will always return a property from inside that property. Can be empty string if the object has no properties or if the offset is before a actual property `{ "a": { | }} will return location ['a', ' ']`
0.3.0 / 2017-01-17
==================
* Updating to typescript 2.0

View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) Microsoft
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,364 @@
# jsonc-parser
Scanner and parser for JSON with comments.
[![npm Package](https://img.shields.io/npm/v/jsonc-parser.svg?style=flat-square)](https://www.npmjs.org/package/jsonc-parser)
[![NPM Downloads](https://img.shields.io/npm/dm/jsonc-parser.svg)](https://npmjs.org/package/jsonc-parser)
[![Build Status](https://github.com/microsoft/node-jsonc-parser/workflows/Tests/badge.svg)](https://github.com/microsoft/node-jsonc-parser/workflows/Tests)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
Why?
----
JSONC is JSON with JavaScript style comments. This node module provides a scanner and fault tolerant parser that can process JSONC but is also useful for standard JSON.
- the *scanner* tokenizes the input string into tokens and token offsets
- the *visit* function implements a 'SAX' style parser with callbacks for the encountered properties and values.
- the *parseTree* function computes a hierarchical DOM with offsets representing the encountered properties and values.
- the *parse* function evaluates the JavaScript object represented by JSON string in a fault tolerant fashion.
- the *getLocation* API returns a location object that describes the property or value located at a given offset in a JSON document.
- the *findNodeAtLocation* API finds the node at a given location path in a JSON DOM.
- the *format* API computes edits to format a JSON document.
- the *modify* API computes edits to insert, remove or replace a property or value in a JSON document.
- the *applyEdits* API applies edits to a document.
Installation
------------
```
npm install --save jsonc-parser
```
API
---
### Scanner:
```typescript
/**
* Creates a JSON scanner on the given text.
* If ignoreTrivia is set, whitespaces or comments are ignored.
*/
export function createScanner(text: string, ignoreTrivia: boolean = false): JSONScanner;
/**
* The scanner object, representing a JSON scanner at a position in the input string.
*/
export interface JSONScanner {
/**
* Sets the scan position to a new offset. A call to 'scan' is needed to get the first token.
*/
setPosition(pos: number): any;
/**
* Read the next token. Returns the token code.
*/
scan(): SyntaxKind;
/**
* Returns the zero-based current scan position, which is after the last read token.
*/
getPosition(): number;
/**
* Returns the last read token.
*/
getToken(): SyntaxKind;
/**
* Returns the last read token value. The value for strings is the decoded string content. For numbers it's of type number, for boolean it's true or false.
*/
getTokenValue(): string;
/**
* The zero-based start offset of the last read token.
*/
getTokenOffset(): number;
/**
* The length of the last read token.
*/
getTokenLength(): number;
/**
* The zero-based start line number of the last read token.
*/
getTokenStartLine(): number;
/**
* The zero-based start character (column) of the last read token.
*/
getTokenStartCharacter(): number;
/**
* An error code of the last scan.
*/
getTokenError(): ScanError;
}
```
### Parser:
```typescript
export interface ParseOptions {
disallowComments?: boolean;
allowTrailingComma?: boolean;
allowEmptyContent?: boolean;
}
/**
* Parses the given text and returns the object the JSON content represents. On invalid input, the parser tries to be as fault tolerant as possible, but still return a result.
* Therefore always check the errors list to find out if the input was valid.
*/
export declare function parse(text: string, errors?: {error: ParseErrorCode;}[], options?: ParseOptions): any;
/**
* Parses the given text and invokes the visitor functions for each object, array and literal reached.
*/
export declare function visit(text: string, visitor: JSONVisitor, options?: ParseOptions): any;
/**
* Visitor called by {@linkcode visit} when parsing JSON.
*
* The visitor functions have the following common parameters:
* - `offset`: Global offset within the JSON document, starting at 0
* - `startLine`: Line number, starting at 0
* - `startCharacter`: Start character (column) within the current line, starting at 0
*
* Additionally some functions have a `pathSupplier` parameter which can be used to obtain the
* current `JSONPath` within the document.
*/
export interface JSONVisitor {
/**
* Invoked when an open brace is encountered and an object is started. The offset and length represent the location of the open brace.
* When `false` is returned, the array items will not be visited.
*/
onObjectBegin?: (offset: number, length: number, startLine: number, startCharacter: number, pathSupplier: () => JSONPath) => void | boolean;
/**
* Invoked when a property is encountered. The offset and length represent the location of the property name.
* The `JSONPath` created by the `pathSupplier` refers to the enclosing JSON object, it does not include the
* property name yet.
*/
onObjectProperty?: (property: string, offset: number, length: number, startLine: number, startCharacter: number, pathSupplier: () => JSONPath) => void;
/**
* Invoked when a closing brace is encountered and an object is completed. The offset and length represent the location of the closing brace.
*/
onObjectEnd?: (offset: number, length: number, startLine: number, startCharacter: number) => void;
/**
* Invoked when an open bracket is encountered. The offset and length represent the location of the open bracket.
* When `false` is returned, the array items will not be visited.*
*/
onArrayBegin?: (offset: number, length: number, startLine: number, startCharacter: number, pathSupplier: () => JSONPath) => void | boolean;
/**
* Invoked when a closing bracket is encountered. The offset and length represent the location of the closing bracket.
*/
onArrayEnd?: (offset: number, length: number, startLine: number, startCharacter: number) => void;
/**
* Invoked when a literal value is encountered. The offset and length represent the location of the literal value.
*/
onLiteralValue?: (value: any, offset: number, length: number, startLine: number, startCharacter: number, pathSupplier: () => JSONPath) => void;
/**
* Invoked when a comma or colon separator is encountered. The offset and length represent the location of the separator.
*/
onSeparator?: (character: string, offset: number, length: number, startLine: number, startCharacter: number) => void;
/**
* When comments are allowed, invoked when a line or block comment is encountered. The offset and length represent the location of the comment.
*/
onComment?: (offset: number, length: number, startLine: number, startCharacter: number) => void;
/**
* Invoked on an error.
*/
onError?: (error: ParseErrorCode, offset: number, length: number, startLine: number, startCharacter: number) => void;
}
/**
* Parses the given text and returns a tree representation the JSON content. On invalid input, the parser tries to be as fault tolerant as possible, but still return a result.
*/
export declare function parseTree(text: string, errors?: ParseError[], options?: ParseOptions): Node | undefined;
export declare type NodeType = "object" | "array" | "property" | "string" | "number" | "boolean" | "null";
export interface Node {
type: NodeType;
value?: any;
offset: number;
length: number;
colonOffset?: number;
parent?: Node;
children?: Node[];
}
```
### Utilities:
```typescript
/**
* Takes JSON with JavaScript-style comments and remove
* them. Optionally replaces every none-newline character
* of comments with a replaceCharacter
*/
export declare function stripComments(text: string, replaceCh?: string): string;
/**
* For a given offset, evaluate the location in the JSON document. Each segment in the location path is either a property name or an array index.
*/
export declare function getLocation(text: string, position: number): Location;
/**
* A {@linkcode JSONPath} segment. Either a string representing an object property name
* or a number (starting at 0) for array indices.
*/
export declare type Segment = string | number;
export declare type JSONPath = Segment[];
export interface Location {
/**
* The previous property key or literal value (string, number, boolean or null) or undefined.
*/
previousNode?: Node;
/**
* The path describing the location in the JSON document. The path consists of a sequence strings
* representing an object property or numbers for array indices.
*/
path: JSONPath;
/**
* Matches the locations path against a pattern consisting of strings (for properties) and numbers (for array indices).
* '*' will match a single segment, of any property name or index.
* '**' will match a sequence of segments or no segment, of any property name or index.
*/
matches: (patterns: JSONPath) => boolean;
/**
* If set, the location's offset is at a property key.
*/
isAtPropertyKey: boolean;
}
/**
* Finds the node at the given path in a JSON DOM.
*/
export function findNodeAtLocation(root: Node, path: JSONPath): Node | undefined;
/**
* Finds the most inner node at the given offset. If includeRightBound is set, also finds nodes that end at the given offset.
*/
export function findNodeAtOffset(root: Node, offset: number, includeRightBound?: boolean) : Node | undefined;
/**
* Gets the JSON path of the given JSON DOM node
*/
export function getNodePath(node: Node): JSONPath;
/**
* Evaluates the JavaScript object of the given JSON DOM node
*/
export function getNodeValue(node: Node): any;
/**
* Computes the edit operations needed to format a JSON document.
*
* @param documentText The input text
* @param range The range to format or `undefined` to format the full content
* @param options The formatting options
* @returns The edit operations describing the formatting changes to the original document following the format described in {@linkcode EditResult}.
* To apply the edit operations to the input, use {@linkcode applyEdits}.
*/
export function format(documentText: string, range: Range, options: FormattingOptions): EditResult;
/**
* Computes the edit operations needed to modify a value in the JSON document.
*
* @param documentText The input text
* @param path The path of the value to change. The path represents either to the document root, a property or an array item.
* If the path points to an non-existing property or item, it will be created.
* @param value The new value for the specified property or item. If the value is undefined,
* the property or item will be removed.
* @param options Options
* @returns The edit operations describing the changes to the original document, following the format described in {@linkcode EditResult}.
* To apply the edit operations to the input, use {@linkcode applyEdits}.
*/
export function modify(text: string, path: JSONPath, value: any, options: ModificationOptions): EditResult;
/**
* Applies edits to an input string.
* @param text The input text
* @param edits Edit operations following the format described in {@linkcode EditResult}.
* @returns The text with the applied edits.
* @throws An error if the edit operations are not well-formed as described in {@linkcode EditResult}.
*/
export function applyEdits(text: string, edits: EditResult): string;
/**
* An edit result describes a textual edit operation. It is the result of a {@linkcode format} and {@linkcode modify} operation.
* It consist of one or more edits describing insertions, replacements or removals of text segments.
* * The offsets of the edits refer to the original state of the document.
* * No two edits change or remove the same range of text in the original document.
* * Multiple edits can have the same offset if they are multiple inserts, or an insert followed by a remove or replace.
* * The order in the array defines which edit is applied first.
* To apply an edit result use {@linkcode applyEdits}.
* In general multiple EditResults must not be concatenated because they might impact each other, producing incorrect or malformed JSON data.
*/
export type EditResult = Edit[];
/**
* Represents a text modification
*/
export interface Edit {
/**
* The start offset of the modification.
*/
offset: number;
/**
* The length of the modification. Must not be negative. Empty length represents an *insert*.
*/
length: number;
/**
* The new content. Empty content represents a *remove*.
*/
content: string;
}
/**
* A text range in the document
*/
export interface Range {
/**
* The start offset of the range.
*/
offset: number;
/**
* The length of the range. Must not be negative.
*/
length: number;
}
/**
* Options used by {@linkcode format} when computing the formatting edit operations
*/
export interface FormattingOptions {
/**
* If indentation is based on spaces (`insertSpaces` = true), then what is the number of spaces that make an indent?
*/
tabSize: number;
/**
* Is indentation based on spaces?
*/
insertSpaces: boolean;
/**
* The default 'end of line' character
*/
eol: string;
}
/**
* Options used by {@linkcode modify} when computing the modification edit operations
*/
export interface ModificationOptions {
/**
* Formatting options. If undefined, the newly inserted code will be inserted unformatted.
*/
formattingOptions?: FormattingOptions;
/**
* Default false. If `JSONPath` refers to an index of an array and `isArrayInsertion` is `true`, then
* {@linkcode modify} will insert a new item at that location instead of overwriting its contents.
*/
isArrayInsertion?: boolean;
/**
* Optional function to define the insertion index given an existing list of properties.
*/
getInsertionIndex?: (properties: string[]) => number;
}
```
License
-------
(MIT License)
Copyright 2018, Microsoft

View File

@@ -0,0 +1,41 @@
<!-- BEGIN MICROSOFT SECURITY.MD V0.0.5 BLOCK -->
## Security
Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/).
If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://docs.microsoft.com/en-us/previous-versions/tn-archive/cc751383(v=technet.10)), please report it to us as described below.
## Reporting Security Issues
**Please do not report security vulnerabilities through public GitHub issues.**
Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://msrc.microsoft.com/create-report).
If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://www.microsoft.com/en-us/msrc/pgp-key-msrc).
You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc).
Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue:
* Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)
* Full paths of source file(s) related to the manifestation of the issue
* The location of the affected source code (tag/branch/commit or direct URL)
* Any special configuration required to reproduce the issue
* Step-by-step instructions to reproduce the issue
* Proof-of-concept or exploit code (if possible)
* Impact of the issue, including how an attacker might exploit the issue
This information will help us triage your report more quickly.
If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://microsoft.com/msrc/bounty) page for more details about our active programs.
## Preferred Languages
We prefer all communications to be in English.
## Policy
Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://www.microsoft.com/en-us/msrc/cvd).
<!-- END MICROSOFT SECURITY.MD BLOCK -->

View File

@@ -0,0 +1,185 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { format, isEOL } from './format';
import { parseTree, findNodeAtLocation } from './parser';
export function removeProperty(text, path, options) {
return setProperty(text, path, void 0, options);
}
export function setProperty(text, originalPath, value, options) {
const path = originalPath.slice();
const errors = [];
const root = parseTree(text, errors);
let parent = void 0;
let lastSegment = void 0;
while (path.length > 0) {
lastSegment = path.pop();
parent = findNodeAtLocation(root, path);
if (parent === void 0 && value !== void 0) {
if (typeof lastSegment === 'string') {
value = { [lastSegment]: value };
}
else {
value = [value];
}
}
else {
break;
}
}
if (!parent) {
// empty document
if (value === void 0) { // delete
throw new Error('Can not delete in empty document');
}
return withFormatting(text, { offset: root ? root.offset : 0, length: root ? root.length : 0, content: JSON.stringify(value) }, options);
}
else if (parent.type === 'object' && typeof lastSegment === 'string' && Array.isArray(parent.children)) {
const existing = findNodeAtLocation(parent, [lastSegment]);
if (existing !== void 0) {
if (value === void 0) { // delete
if (!existing.parent) {
throw new Error('Malformed AST');
}
const propertyIndex = parent.children.indexOf(existing.parent);
let removeBegin;
let removeEnd = existing.parent.offset + existing.parent.length;
if (propertyIndex > 0) {
// remove the comma of the previous node
let previous = parent.children[propertyIndex - 1];
removeBegin = previous.offset + previous.length;
}
else {
removeBegin = parent.offset + 1;
if (parent.children.length > 1) {
// remove the comma of the next node
let next = parent.children[1];
removeEnd = next.offset;
}
}
return withFormatting(text, { offset: removeBegin, length: removeEnd - removeBegin, content: '' }, options);
}
else {
// set value of existing property
return withFormatting(text, { offset: existing.offset, length: existing.length, content: JSON.stringify(value) }, options);
}
}
else {
if (value === void 0) { // delete
return []; // property does not exist, nothing to do
}
const newProperty = `${JSON.stringify(lastSegment)}: ${JSON.stringify(value)}`;
const index = options.getInsertionIndex ? options.getInsertionIndex(parent.children.map(p => p.children[0].value)) : parent.children.length;
let edit;
if (index > 0) {
let previous = parent.children[index - 1];
edit = { offset: previous.offset + previous.length, length: 0, content: ',' + newProperty };
}
else if (parent.children.length === 0) {
edit = { offset: parent.offset + 1, length: 0, content: newProperty };
}
else {
edit = { offset: parent.offset + 1, length: 0, content: newProperty + ',' };
}
return withFormatting(text, edit, options);
}
}
else if (parent.type === 'array' && typeof lastSegment === 'number' && Array.isArray(parent.children)) {
const insertIndex = lastSegment;
if (insertIndex === -1) {
// Insert
const newProperty = `${JSON.stringify(value)}`;
let edit;
if (parent.children.length === 0) {
edit = { offset: parent.offset + 1, length: 0, content: newProperty };
}
else {
const previous = parent.children[parent.children.length - 1];
edit = { offset: previous.offset + previous.length, length: 0, content: ',' + newProperty };
}
return withFormatting(text, edit, options);
}
else if (value === void 0 && parent.children.length >= 0) {
// Removal
const removalIndex = lastSegment;
const toRemove = parent.children[removalIndex];
let edit;
if (parent.children.length === 1) {
// only item
edit = { offset: parent.offset + 1, length: parent.length - 2, content: '' };
}
else if (parent.children.length - 1 === removalIndex) {
// last item
let previous = parent.children[removalIndex - 1];
let offset = previous.offset + previous.length;
let parentEndOffset = parent.offset + parent.length;
edit = { offset, length: parentEndOffset - 2 - offset, content: '' };
}
else {
edit = { offset: toRemove.offset, length: parent.children[removalIndex + 1].offset - toRemove.offset, content: '' };
}
return withFormatting(text, edit, options);
}
else if (value !== void 0) {
let edit;
const newProperty = `${JSON.stringify(value)}`;
if (!options.isArrayInsertion && parent.children.length > lastSegment) {
const toModify = parent.children[lastSegment];
edit = { offset: toModify.offset, length: toModify.length, content: newProperty };
}
else if (parent.children.length === 0 || lastSegment === 0) {
edit = { offset: parent.offset + 1, length: 0, content: parent.children.length === 0 ? newProperty : newProperty + ',' };
}
else {
const index = lastSegment > parent.children.length ? parent.children.length : lastSegment;
const previous = parent.children[index - 1];
edit = { offset: previous.offset + previous.length, length: 0, content: ',' + newProperty };
}
return withFormatting(text, edit, options);
}
else {
throw new Error(`Can not ${value === void 0 ? 'remove' : (options.isArrayInsertion ? 'insert' : 'modify')} Array index ${insertIndex} as length is not sufficient`);
}
}
else {
throw new Error(`Can not add ${typeof lastSegment !== 'number' ? 'index' : 'property'} to parent of type ${parent.type}`);
}
}
function withFormatting(text, edit, options) {
if (!options.formattingOptions) {
return [edit];
}
// apply the edit
let newText = applyEdit(text, edit);
// format the new text
let begin = edit.offset;
let end = edit.offset + edit.content.length;
if (edit.length === 0 || edit.content.length === 0) { // insert or remove
while (begin > 0 && !isEOL(newText, begin - 1)) {
begin--;
}
while (end < newText.length && !isEOL(newText, end)) {
end++;
}
}
const edits = format(newText, { offset: begin, length: end - begin }, { ...options.formattingOptions, keepLines: false });
// apply the formatting edits and track the begin and end offsets of the changes
for (let i = edits.length - 1; i >= 0; i--) {
const edit = edits[i];
newText = applyEdit(newText, edit);
begin = Math.min(begin, edit.offset);
end = Math.max(end, edit.offset + edit.length);
end += edit.content.length - edit.length;
}
// create a single edit with all changes
const editLength = text.length - (newText.length - end) - begin;
return [{ offset: begin, length: editLength, content: newText.substring(begin, end) }];
}
export function applyEdit(text, edit) {
return text.substring(0, edit.offset) + edit.content + text.substring(edit.offset + edit.length);
}
export function isWS(text, offset) {
return '\r\n \t'.indexOf(text.charAt(offset)) !== -1;
}

View File

@@ -0,0 +1,261 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { createScanner } from './scanner';
import { cachedSpaces, cachedBreakLinesWithSpaces, supportedEols } from './string-intern';
export function format(documentText, range, options) {
let initialIndentLevel;
let formatText;
let formatTextStart;
let rangeStart;
let rangeEnd;
if (range) {
rangeStart = range.offset;
rangeEnd = rangeStart + range.length;
formatTextStart = rangeStart;
while (formatTextStart > 0 && !isEOL(documentText, formatTextStart - 1)) {
formatTextStart--;
}
let endOffset = rangeEnd;
while (endOffset < documentText.length && !isEOL(documentText, endOffset)) {
endOffset++;
}
formatText = documentText.substring(formatTextStart, endOffset);
initialIndentLevel = computeIndentLevel(formatText, options);
}
else {
formatText = documentText;
initialIndentLevel = 0;
formatTextStart = 0;
rangeStart = 0;
rangeEnd = documentText.length;
}
const eol = getEOL(options, documentText);
const eolFastPathSupported = supportedEols.includes(eol);
let numberLineBreaks = 0;
let indentLevel = 0;
let indentValue;
if (options.insertSpaces) {
indentValue = cachedSpaces[options.tabSize || 4] ?? repeat(cachedSpaces[1], options.tabSize || 4);
}
else {
indentValue = '\t';
}
const indentType = indentValue === '\t' ? '\t' : ' ';
let scanner = createScanner(formatText, false);
let hasError = false;
function newLinesAndIndent() {
if (numberLineBreaks > 1) {
return repeat(eol, numberLineBreaks) + repeat(indentValue, initialIndentLevel + indentLevel);
}
const amountOfSpaces = indentValue.length * (initialIndentLevel + indentLevel);
if (!eolFastPathSupported || amountOfSpaces > cachedBreakLinesWithSpaces[indentType][eol].length) {
return eol + repeat(indentValue, initialIndentLevel + indentLevel);
}
if (amountOfSpaces <= 0) {
return eol;
}
return cachedBreakLinesWithSpaces[indentType][eol][amountOfSpaces];
}
function scanNext() {
let token = scanner.scan();
numberLineBreaks = 0;
while (token === 15 /* SyntaxKind.Trivia */ || token === 14 /* SyntaxKind.LineBreakTrivia */) {
if (token === 14 /* SyntaxKind.LineBreakTrivia */ && options.keepLines) {
numberLineBreaks += 1;
}
else if (token === 14 /* SyntaxKind.LineBreakTrivia */) {
numberLineBreaks = 1;
}
token = scanner.scan();
}
hasError = token === 16 /* SyntaxKind.Unknown */ || scanner.getTokenError() !== 0 /* ScanError.None */;
return token;
}
const editOperations = [];
function addEdit(text, startOffset, endOffset) {
if (!hasError && (!range || (startOffset < rangeEnd && endOffset > rangeStart)) && documentText.substring(startOffset, endOffset) !== text) {
editOperations.push({ offset: startOffset, length: endOffset - startOffset, content: text });
}
}
let firstToken = scanNext();
if (options.keepLines && numberLineBreaks > 0) {
addEdit(repeat(eol, numberLineBreaks), 0, 0);
}
if (firstToken !== 17 /* SyntaxKind.EOF */) {
let firstTokenStart = scanner.getTokenOffset() + formatTextStart;
let initialIndent = (indentValue.length * initialIndentLevel < 20) && options.insertSpaces
? cachedSpaces[indentValue.length * initialIndentLevel]
: repeat(indentValue, initialIndentLevel);
addEdit(initialIndent, formatTextStart, firstTokenStart);
}
while (firstToken !== 17 /* SyntaxKind.EOF */) {
let firstTokenEnd = scanner.getTokenOffset() + scanner.getTokenLength() + formatTextStart;
let secondToken = scanNext();
let replaceContent = '';
let needsLineBreak = false;
while (numberLineBreaks === 0 && (secondToken === 12 /* SyntaxKind.LineCommentTrivia */ || secondToken === 13 /* SyntaxKind.BlockCommentTrivia */)) {
let commentTokenStart = scanner.getTokenOffset() + formatTextStart;
addEdit(cachedSpaces[1], firstTokenEnd, commentTokenStart);
firstTokenEnd = scanner.getTokenOffset() + scanner.getTokenLength() + formatTextStart;
needsLineBreak = secondToken === 12 /* SyntaxKind.LineCommentTrivia */;
replaceContent = needsLineBreak ? newLinesAndIndent() : '';
secondToken = scanNext();
}
if (secondToken === 2 /* SyntaxKind.CloseBraceToken */) {
if (firstToken !== 1 /* SyntaxKind.OpenBraceToken */) {
indentLevel--;
}
;
if (options.keepLines && numberLineBreaks > 0 || !options.keepLines && firstToken !== 1 /* SyntaxKind.OpenBraceToken */) {
replaceContent = newLinesAndIndent();
}
else if (options.keepLines) {
replaceContent = cachedSpaces[1];
}
}
else if (secondToken === 4 /* SyntaxKind.CloseBracketToken */) {
if (firstToken !== 3 /* SyntaxKind.OpenBracketToken */) {
indentLevel--;
}
;
if (options.keepLines && numberLineBreaks > 0 || !options.keepLines && firstToken !== 3 /* SyntaxKind.OpenBracketToken */) {
replaceContent = newLinesAndIndent();
}
else if (options.keepLines) {
replaceContent = cachedSpaces[1];
}
}
else {
switch (firstToken) {
case 3 /* SyntaxKind.OpenBracketToken */:
case 1 /* SyntaxKind.OpenBraceToken */:
indentLevel++;
if (options.keepLines && numberLineBreaks > 0 || !options.keepLines) {
replaceContent = newLinesAndIndent();
}
else {
replaceContent = cachedSpaces[1];
}
break;
case 5 /* SyntaxKind.CommaToken */:
if (options.keepLines && numberLineBreaks > 0 || !options.keepLines) {
replaceContent = newLinesAndIndent();
}
else {
replaceContent = cachedSpaces[1];
}
break;
case 12 /* SyntaxKind.LineCommentTrivia */:
replaceContent = newLinesAndIndent();
break;
case 13 /* SyntaxKind.BlockCommentTrivia */:
if (numberLineBreaks > 0) {
replaceContent = newLinesAndIndent();
}
else if (!needsLineBreak) {
replaceContent = cachedSpaces[1];
}
break;
case 6 /* SyntaxKind.ColonToken */:
if (options.keepLines && numberLineBreaks > 0) {
replaceContent = newLinesAndIndent();
}
else if (!needsLineBreak) {
replaceContent = cachedSpaces[1];
}
break;
case 10 /* SyntaxKind.StringLiteral */:
if (options.keepLines && numberLineBreaks > 0) {
replaceContent = newLinesAndIndent();
}
else if (secondToken === 6 /* SyntaxKind.ColonToken */ && !needsLineBreak) {
replaceContent = '';
}
break;
case 7 /* SyntaxKind.NullKeyword */:
case 8 /* SyntaxKind.TrueKeyword */:
case 9 /* SyntaxKind.FalseKeyword */:
case 11 /* SyntaxKind.NumericLiteral */:
case 2 /* SyntaxKind.CloseBraceToken */:
case 4 /* SyntaxKind.CloseBracketToken */:
if (options.keepLines && numberLineBreaks > 0) {
replaceContent = newLinesAndIndent();
}
else {
if ((secondToken === 12 /* SyntaxKind.LineCommentTrivia */ || secondToken === 13 /* SyntaxKind.BlockCommentTrivia */) && !needsLineBreak) {
replaceContent = cachedSpaces[1];
}
else if (secondToken !== 5 /* SyntaxKind.CommaToken */ && secondToken !== 17 /* SyntaxKind.EOF */) {
hasError = true;
}
}
break;
case 16 /* SyntaxKind.Unknown */:
hasError = true;
break;
}
if (numberLineBreaks > 0 && (secondToken === 12 /* SyntaxKind.LineCommentTrivia */ || secondToken === 13 /* SyntaxKind.BlockCommentTrivia */)) {
replaceContent = newLinesAndIndent();
}
}
if (secondToken === 17 /* SyntaxKind.EOF */) {
if (options.keepLines && numberLineBreaks > 0) {
replaceContent = newLinesAndIndent();
}
else {
replaceContent = options.insertFinalNewline ? eol : '';
}
}
const secondTokenStart = scanner.getTokenOffset() + formatTextStart;
addEdit(replaceContent, firstTokenEnd, secondTokenStart);
firstToken = secondToken;
}
return editOperations;
}
function repeat(s, count) {
let result = '';
for (let i = 0; i < count; i++) {
result += s;
}
return result;
}
function computeIndentLevel(content, options) {
let i = 0;
let nChars = 0;
const tabSize = options.tabSize || 4;
while (i < content.length) {
let ch = content.charAt(i);
if (ch === cachedSpaces[1]) {
nChars++;
}
else if (ch === '\t') {
nChars += tabSize;
}
else {
break;
}
i++;
}
return Math.floor(nChars / tabSize);
}
function getEOL(options, text) {
for (let i = 0; i < text.length; i++) {
const ch = text.charAt(i);
if (ch === '\r') {
if (i + 1 < text.length && text.charAt(i + 1) === '\n') {
return '\r\n';
}
return '\r';
}
else if (ch === '\n') {
return '\n';
}
}
return (options && options.eol) || '\n';
}
export function isEOL(text, offset) {
return '\r\n'.indexOf(text.charAt(offset)) !== -1;
}

View File

@@ -0,0 +1,659 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { createScanner } from './scanner';
var ParseOptions;
(function (ParseOptions) {
ParseOptions.DEFAULT = {
allowTrailingComma: false
};
})(ParseOptions || (ParseOptions = {}));
/**
* For a given offset, evaluate the location in the JSON document. Each segment in the location path is either a property name or an array index.
*/
export function getLocation(text, position) {
const segments = []; // strings or numbers
const earlyReturnException = new Object();
let previousNode = undefined;
const previousNodeInst = {
value: {},
offset: 0,
length: 0,
type: 'object',
parent: undefined
};
let isAtPropertyKey = false;
function setPreviousNode(value, offset, length, type) {
previousNodeInst.value = value;
previousNodeInst.offset = offset;
previousNodeInst.length = length;
previousNodeInst.type = type;
previousNodeInst.colonOffset = undefined;
previousNode = previousNodeInst;
}
try {
visit(text, {
onObjectBegin: (offset, length) => {
if (position <= offset) {
throw earlyReturnException;
}
previousNode = undefined;
isAtPropertyKey = position > offset;
segments.push(''); // push a placeholder (will be replaced)
},
onObjectProperty: (name, offset, length) => {
if (position < offset) {
throw earlyReturnException;
}
setPreviousNode(name, offset, length, 'property');
segments[segments.length - 1] = name;
if (position <= offset + length) {
throw earlyReturnException;
}
},
onObjectEnd: (offset, length) => {
if (position <= offset) {
throw earlyReturnException;
}
previousNode = undefined;
segments.pop();
},
onArrayBegin: (offset, length) => {
if (position <= offset) {
throw earlyReturnException;
}
previousNode = undefined;
segments.push(0);
},
onArrayEnd: (offset, length) => {
if (position <= offset) {
throw earlyReturnException;
}
previousNode = undefined;
segments.pop();
},
onLiteralValue: (value, offset, length) => {
if (position < offset) {
throw earlyReturnException;
}
setPreviousNode(value, offset, length, getNodeType(value));
if (position <= offset + length) {
throw earlyReturnException;
}
},
onSeparator: (sep, offset, length) => {
if (position <= offset) {
throw earlyReturnException;
}
if (sep === ':' && previousNode && previousNode.type === 'property') {
previousNode.colonOffset = offset;
isAtPropertyKey = false;
previousNode = undefined;
}
else if (sep === ',') {
const last = segments[segments.length - 1];
if (typeof last === 'number') {
segments[segments.length - 1] = last + 1;
}
else {
isAtPropertyKey = true;
segments[segments.length - 1] = '';
}
previousNode = undefined;
}
}
});
}
catch (e) {
if (e !== earlyReturnException) {
throw e;
}
}
return {
path: segments,
previousNode,
isAtPropertyKey,
matches: (pattern) => {
let k = 0;
for (let i = 0; k < pattern.length && i < segments.length; i++) {
if (pattern[k] === segments[i] || pattern[k] === '*') {
k++;
}
else if (pattern[k] !== '**') {
return false;
}
}
return k === pattern.length;
}
};
}
/**
* Parses the given text and returns the object the JSON content represents. On invalid input, the parser tries to be as fault tolerant as possible, but still return a result.
* Therefore always check the errors list to find out if the input was valid.
*/
export function parse(text, errors = [], options = ParseOptions.DEFAULT) {
let currentProperty = null;
let currentParent = [];
const previousParents = [];
function onValue(value) {
if (Array.isArray(currentParent)) {
currentParent.push(value);
}
else if (currentProperty !== null) {
currentParent[currentProperty] = value;
}
}
const visitor = {
onObjectBegin: () => {
const object = {};
onValue(object);
previousParents.push(currentParent);
currentParent = object;
currentProperty = null;
},
onObjectProperty: (name) => {
currentProperty = name;
},
onObjectEnd: () => {
currentParent = previousParents.pop();
},
onArrayBegin: () => {
const array = [];
onValue(array);
previousParents.push(currentParent);
currentParent = array;
currentProperty = null;
},
onArrayEnd: () => {
currentParent = previousParents.pop();
},
onLiteralValue: onValue,
onError: (error, offset, length) => {
errors.push({ error, offset, length });
}
};
visit(text, visitor, options);
return currentParent[0];
}
/**
* Parses the given text and returns a tree representation the JSON content. On invalid input, the parser tries to be as fault tolerant as possible, but still return a result.
*/
export function parseTree(text, errors = [], options = ParseOptions.DEFAULT) {
let currentParent = { type: 'array', offset: -1, length: -1, children: [], parent: undefined }; // artificial root
function ensurePropertyComplete(endOffset) {
if (currentParent.type === 'property') {
currentParent.length = endOffset - currentParent.offset;
currentParent = currentParent.parent;
}
}
function onValue(valueNode) {
currentParent.children.push(valueNode);
return valueNode;
}
const visitor = {
onObjectBegin: (offset) => {
currentParent = onValue({ type: 'object', offset, length: -1, parent: currentParent, children: [] });
},
onObjectProperty: (name, offset, length) => {
currentParent = onValue({ type: 'property', offset, length: -1, parent: currentParent, children: [] });
currentParent.children.push({ type: 'string', value: name, offset, length, parent: currentParent });
},
onObjectEnd: (offset, length) => {
ensurePropertyComplete(offset + length); // in case of a missing value for a property: make sure property is complete
currentParent.length = offset + length - currentParent.offset;
currentParent = currentParent.parent;
ensurePropertyComplete(offset + length);
},
onArrayBegin: (offset, length) => {
currentParent = onValue({ type: 'array', offset, length: -1, parent: currentParent, children: [] });
},
onArrayEnd: (offset, length) => {
currentParent.length = offset + length - currentParent.offset;
currentParent = currentParent.parent;
ensurePropertyComplete(offset + length);
},
onLiteralValue: (value, offset, length) => {
onValue({ type: getNodeType(value), offset, length, parent: currentParent, value });
ensurePropertyComplete(offset + length);
},
onSeparator: (sep, offset, length) => {
if (currentParent.type === 'property') {
if (sep === ':') {
currentParent.colonOffset = offset;
}
else if (sep === ',') {
ensurePropertyComplete(offset);
}
}
},
onError: (error, offset, length) => {
errors.push({ error, offset, length });
}
};
visit(text, visitor, options);
const result = currentParent.children[0];
if (result) {
delete result.parent;
}
return result;
}
/**
* Finds the node at the given path in a JSON DOM.
*/
export function findNodeAtLocation(root, path) {
if (!root) {
return undefined;
}
let node = root;
for (let segment of path) {
if (typeof segment === 'string') {
if (node.type !== 'object' || !Array.isArray(node.children)) {
return undefined;
}
let found = false;
for (const propertyNode of node.children) {
if (Array.isArray(propertyNode.children) && propertyNode.children[0].value === segment && propertyNode.children.length === 2) {
node = propertyNode.children[1];
found = true;
break;
}
}
if (!found) {
return undefined;
}
}
else {
const index = segment;
if (node.type !== 'array' || index < 0 || !Array.isArray(node.children) || index >= node.children.length) {
return undefined;
}
node = node.children[index];
}
}
return node;
}
/**
* Gets the JSON path of the given JSON DOM node
*/
export function getNodePath(node) {
if (!node.parent || !node.parent.children) {
return [];
}
const path = getNodePath(node.parent);
if (node.parent.type === 'property') {
const key = node.parent.children[0].value;
path.push(key);
}
else if (node.parent.type === 'array') {
const index = node.parent.children.indexOf(node);
if (index !== -1) {
path.push(index);
}
}
return path;
}
/**
* Evaluates the JavaScript object of the given JSON DOM node
*/
export function getNodeValue(node) {
switch (node.type) {
case 'array':
return node.children.map(getNodeValue);
case 'object':
const obj = Object.create(null);
for (let prop of node.children) {
const valueNode = prop.children[1];
if (valueNode) {
obj[prop.children[0].value] = getNodeValue(valueNode);
}
}
return obj;
case 'null':
case 'string':
case 'number':
case 'boolean':
return node.value;
default:
return undefined;
}
}
export function contains(node, offset, includeRightBound = false) {
return (offset >= node.offset && offset < (node.offset + node.length)) || includeRightBound && (offset === (node.offset + node.length));
}
/**
* Finds the most inner node at the given offset. If includeRightBound is set, also finds nodes that end at the given offset.
*/
export function findNodeAtOffset(node, offset, includeRightBound = false) {
if (contains(node, offset, includeRightBound)) {
const children = node.children;
if (Array.isArray(children)) {
for (let i = 0; i < children.length && children[i].offset <= offset; i++) {
const item = findNodeAtOffset(children[i], offset, includeRightBound);
if (item) {
return item;
}
}
}
return node;
}
return undefined;
}
/**
* Parses the given text and invokes the visitor functions for each object, array and literal reached.
*/
export function visit(text, visitor, options = ParseOptions.DEFAULT) {
const _scanner = createScanner(text, false);
// Important: Only pass copies of this to visitor functions to prevent accidental modification, and
// to not affect visitor functions which stored a reference to a previous JSONPath
const _jsonPath = [];
// Depth of onXXXBegin() callbacks suppressed. onXXXEnd() decrements this if it isn't 0 already.
// Callbacks are only called when this value is 0.
let suppressedCallbacks = 0;
function toNoArgVisit(visitFunction) {
return visitFunction ? () => suppressedCallbacks === 0 && visitFunction(_scanner.getTokenOffset(), _scanner.getTokenLength(), _scanner.getTokenStartLine(), _scanner.getTokenStartCharacter()) : () => true;
}
function toOneArgVisit(visitFunction) {
return visitFunction ? (arg) => suppressedCallbacks === 0 && visitFunction(arg, _scanner.getTokenOffset(), _scanner.getTokenLength(), _scanner.getTokenStartLine(), _scanner.getTokenStartCharacter()) : () => true;
}
function toOneArgVisitWithPath(visitFunction) {
return visitFunction ? (arg) => suppressedCallbacks === 0 && visitFunction(arg, _scanner.getTokenOffset(), _scanner.getTokenLength(), _scanner.getTokenStartLine(), _scanner.getTokenStartCharacter(), () => _jsonPath.slice()) : () => true;
}
function toBeginVisit(visitFunction) {
return visitFunction ?
() => {
if (suppressedCallbacks > 0) {
suppressedCallbacks++;
}
else {
let cbReturn = visitFunction(_scanner.getTokenOffset(), _scanner.getTokenLength(), _scanner.getTokenStartLine(), _scanner.getTokenStartCharacter(), () => _jsonPath.slice());
if (cbReturn === false) {
suppressedCallbacks = 1;
}
}
}
: () => true;
}
function toEndVisit(visitFunction) {
return visitFunction ?
() => {
if (suppressedCallbacks > 0) {
suppressedCallbacks--;
}
if (suppressedCallbacks === 0) {
visitFunction(_scanner.getTokenOffset(), _scanner.getTokenLength(), _scanner.getTokenStartLine(), _scanner.getTokenStartCharacter());
}
}
: () => true;
}
const onObjectBegin = toBeginVisit(visitor.onObjectBegin), onObjectProperty = toOneArgVisitWithPath(visitor.onObjectProperty), onObjectEnd = toEndVisit(visitor.onObjectEnd), onArrayBegin = toBeginVisit(visitor.onArrayBegin), onArrayEnd = toEndVisit(visitor.onArrayEnd), onLiteralValue = toOneArgVisitWithPath(visitor.onLiteralValue), onSeparator = toOneArgVisit(visitor.onSeparator), onComment = toNoArgVisit(visitor.onComment), onError = toOneArgVisit(visitor.onError);
const disallowComments = options && options.disallowComments;
const allowTrailingComma = options && options.allowTrailingComma;
function scanNext() {
while (true) {
const token = _scanner.scan();
switch (_scanner.getTokenError()) {
case 4 /* ScanError.InvalidUnicode */:
handleError(14 /* ParseErrorCode.InvalidUnicode */);
break;
case 5 /* ScanError.InvalidEscapeCharacter */:
handleError(15 /* ParseErrorCode.InvalidEscapeCharacter */);
break;
case 3 /* ScanError.UnexpectedEndOfNumber */:
handleError(13 /* ParseErrorCode.UnexpectedEndOfNumber */);
break;
case 1 /* ScanError.UnexpectedEndOfComment */:
if (!disallowComments) {
handleError(11 /* ParseErrorCode.UnexpectedEndOfComment */);
}
break;
case 2 /* ScanError.UnexpectedEndOfString */:
handleError(12 /* ParseErrorCode.UnexpectedEndOfString */);
break;
case 6 /* ScanError.InvalidCharacter */:
handleError(16 /* ParseErrorCode.InvalidCharacter */);
break;
}
switch (token) {
case 12 /* SyntaxKind.LineCommentTrivia */:
case 13 /* SyntaxKind.BlockCommentTrivia */:
if (disallowComments) {
handleError(10 /* ParseErrorCode.InvalidCommentToken */);
}
else {
onComment();
}
break;
case 16 /* SyntaxKind.Unknown */:
handleError(1 /* ParseErrorCode.InvalidSymbol */);
break;
case 15 /* SyntaxKind.Trivia */:
case 14 /* SyntaxKind.LineBreakTrivia */:
break;
default:
return token;
}
}
}
function handleError(error, skipUntilAfter = [], skipUntil = []) {
onError(error);
if (skipUntilAfter.length + skipUntil.length > 0) {
let token = _scanner.getToken();
while (token !== 17 /* SyntaxKind.EOF */) {
if (skipUntilAfter.indexOf(token) !== -1) {
scanNext();
break;
}
else if (skipUntil.indexOf(token) !== -1) {
break;
}
token = scanNext();
}
}
}
function parseString(isValue) {
const value = _scanner.getTokenValue();
if (isValue) {
onLiteralValue(value);
}
else {
onObjectProperty(value);
// add property name afterwards
_jsonPath.push(value);
}
scanNext();
return true;
}
function parseLiteral() {
switch (_scanner.getToken()) {
case 11 /* SyntaxKind.NumericLiteral */:
const tokenValue = _scanner.getTokenValue();
let value = Number(tokenValue);
if (isNaN(value)) {
handleError(2 /* ParseErrorCode.InvalidNumberFormat */);
value = 0;
}
onLiteralValue(value);
break;
case 7 /* SyntaxKind.NullKeyword */:
onLiteralValue(null);
break;
case 8 /* SyntaxKind.TrueKeyword */:
onLiteralValue(true);
break;
case 9 /* SyntaxKind.FalseKeyword */:
onLiteralValue(false);
break;
default:
return false;
}
scanNext();
return true;
}
function parseProperty() {
if (_scanner.getToken() !== 10 /* SyntaxKind.StringLiteral */) {
handleError(3 /* ParseErrorCode.PropertyNameExpected */, [], [2 /* SyntaxKind.CloseBraceToken */, 5 /* SyntaxKind.CommaToken */]);
return false;
}
parseString(false);
if (_scanner.getToken() === 6 /* SyntaxKind.ColonToken */) {
onSeparator(':');
scanNext(); // consume colon
if (!parseValue()) {
handleError(4 /* ParseErrorCode.ValueExpected */, [], [2 /* SyntaxKind.CloseBraceToken */, 5 /* SyntaxKind.CommaToken */]);
}
}
else {
handleError(5 /* ParseErrorCode.ColonExpected */, [], [2 /* SyntaxKind.CloseBraceToken */, 5 /* SyntaxKind.CommaToken */]);
}
_jsonPath.pop(); // remove processed property name
return true;
}
function parseObject() {
onObjectBegin();
scanNext(); // consume open brace
let needsComma = false;
while (_scanner.getToken() !== 2 /* SyntaxKind.CloseBraceToken */ && _scanner.getToken() !== 17 /* SyntaxKind.EOF */) {
if (_scanner.getToken() === 5 /* SyntaxKind.CommaToken */) {
if (!needsComma) {
handleError(4 /* ParseErrorCode.ValueExpected */, [], []);
}
onSeparator(',');
scanNext(); // consume comma
if (_scanner.getToken() === 2 /* SyntaxKind.CloseBraceToken */ && allowTrailingComma) {
break;
}
}
else if (needsComma) {
handleError(6 /* ParseErrorCode.CommaExpected */, [], []);
}
if (!parseProperty()) {
handleError(4 /* ParseErrorCode.ValueExpected */, [], [2 /* SyntaxKind.CloseBraceToken */, 5 /* SyntaxKind.CommaToken */]);
}
needsComma = true;
}
onObjectEnd();
if (_scanner.getToken() !== 2 /* SyntaxKind.CloseBraceToken */) {
handleError(7 /* ParseErrorCode.CloseBraceExpected */, [2 /* SyntaxKind.CloseBraceToken */], []);
}
else {
scanNext(); // consume close brace
}
return true;
}
function parseArray() {
onArrayBegin();
scanNext(); // consume open bracket
let isFirstElement = true;
let needsComma = false;
while (_scanner.getToken() !== 4 /* SyntaxKind.CloseBracketToken */ && _scanner.getToken() !== 17 /* SyntaxKind.EOF */) {
if (_scanner.getToken() === 5 /* SyntaxKind.CommaToken */) {
if (!needsComma) {
handleError(4 /* ParseErrorCode.ValueExpected */, [], []);
}
onSeparator(',');
scanNext(); // consume comma
if (_scanner.getToken() === 4 /* SyntaxKind.CloseBracketToken */ && allowTrailingComma) {
break;
}
}
else if (needsComma) {
handleError(6 /* ParseErrorCode.CommaExpected */, [], []);
}
if (isFirstElement) {
_jsonPath.push(0);
isFirstElement = false;
}
else {
_jsonPath[_jsonPath.length - 1]++;
}
if (!parseValue()) {
handleError(4 /* ParseErrorCode.ValueExpected */, [], [4 /* SyntaxKind.CloseBracketToken */, 5 /* SyntaxKind.CommaToken */]);
}
needsComma = true;
}
onArrayEnd();
if (!isFirstElement) {
_jsonPath.pop(); // remove array index
}
if (_scanner.getToken() !== 4 /* SyntaxKind.CloseBracketToken */) {
handleError(8 /* ParseErrorCode.CloseBracketExpected */, [4 /* SyntaxKind.CloseBracketToken */], []);
}
else {
scanNext(); // consume close bracket
}
return true;
}
function parseValue() {
switch (_scanner.getToken()) {
case 3 /* SyntaxKind.OpenBracketToken */:
return parseArray();
case 1 /* SyntaxKind.OpenBraceToken */:
return parseObject();
case 10 /* SyntaxKind.StringLiteral */:
return parseString(true);
default:
return parseLiteral();
}
}
scanNext();
if (_scanner.getToken() === 17 /* SyntaxKind.EOF */) {
if (options.allowEmptyContent) {
return true;
}
handleError(4 /* ParseErrorCode.ValueExpected */, [], []);
return false;
}
if (!parseValue()) {
handleError(4 /* ParseErrorCode.ValueExpected */, [], []);
return false;
}
if (_scanner.getToken() !== 17 /* SyntaxKind.EOF */) {
handleError(9 /* ParseErrorCode.EndOfFileExpected */, [], []);
}
return true;
}
/**
* Takes JSON with JavaScript-style comments and remove
* them. Optionally replaces every none-newline character
* of comments with a replaceCharacter
*/
export function stripComments(text, replaceCh) {
let _scanner = createScanner(text), parts = [], kind, offset = 0, pos;
do {
pos = _scanner.getPosition();
kind = _scanner.scan();
switch (kind) {
case 12 /* SyntaxKind.LineCommentTrivia */:
case 13 /* SyntaxKind.BlockCommentTrivia */:
case 17 /* SyntaxKind.EOF */:
if (offset !== pos) {
parts.push(text.substring(offset, pos));
}
if (replaceCh !== undefined) {
parts.push(_scanner.getTokenValue().replace(/[^\r\n]/g, replaceCh));
}
offset = _scanner.getPosition();
break;
}
} while (kind !== 17 /* SyntaxKind.EOF */);
return parts.join('');
}
export function getNodeType(value) {
switch (typeof value) {
case 'boolean': return 'boolean';
case 'number': return 'number';
case 'string': return 'string';
case 'object': {
if (!value) {
return 'null';
}
else if (Array.isArray(value)) {
return 'array';
}
return 'object';
}
default: return 'null';
}
}

View File

@@ -0,0 +1,443 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
/**
* Creates a JSON scanner on the given text.
* If ignoreTrivia is set, whitespaces or comments are ignored.
*/
export function createScanner(text, ignoreTrivia = false) {
const len = text.length;
let pos = 0, value = '', tokenOffset = 0, token = 16 /* SyntaxKind.Unknown */, lineNumber = 0, lineStartOffset = 0, tokenLineStartOffset = 0, prevTokenLineStartOffset = 0, scanError = 0 /* ScanError.None */;
function scanHexDigits(count, exact) {
let digits = 0;
let value = 0;
while (digits < count || !exact) {
let ch = text.charCodeAt(pos);
if (ch >= 48 /* CharacterCodes._0 */ && ch <= 57 /* CharacterCodes._9 */) {
value = value * 16 + ch - 48 /* CharacterCodes._0 */;
}
else if (ch >= 65 /* CharacterCodes.A */ && ch <= 70 /* CharacterCodes.F */) {
value = value * 16 + ch - 65 /* CharacterCodes.A */ + 10;
}
else if (ch >= 97 /* CharacterCodes.a */ && ch <= 102 /* CharacterCodes.f */) {
value = value * 16 + ch - 97 /* CharacterCodes.a */ + 10;
}
else {
break;
}
pos++;
digits++;
}
if (digits < count) {
value = -1;
}
return value;
}
function setPosition(newPosition) {
pos = newPosition;
value = '';
tokenOffset = 0;
token = 16 /* SyntaxKind.Unknown */;
scanError = 0 /* ScanError.None */;
}
function scanNumber() {
let start = pos;
if (text.charCodeAt(pos) === 48 /* CharacterCodes._0 */) {
pos++;
}
else {
pos++;
while (pos < text.length && isDigit(text.charCodeAt(pos))) {
pos++;
}
}
if (pos < text.length && text.charCodeAt(pos) === 46 /* CharacterCodes.dot */) {
pos++;
if (pos < text.length && isDigit(text.charCodeAt(pos))) {
pos++;
while (pos < text.length && isDigit(text.charCodeAt(pos))) {
pos++;
}
}
else {
scanError = 3 /* ScanError.UnexpectedEndOfNumber */;
return text.substring(start, pos);
}
}
let end = pos;
if (pos < text.length && (text.charCodeAt(pos) === 69 /* CharacterCodes.E */ || text.charCodeAt(pos) === 101 /* CharacterCodes.e */)) {
pos++;
if (pos < text.length && text.charCodeAt(pos) === 43 /* CharacterCodes.plus */ || text.charCodeAt(pos) === 45 /* CharacterCodes.minus */) {
pos++;
}
if (pos < text.length && isDigit(text.charCodeAt(pos))) {
pos++;
while (pos < text.length && isDigit(text.charCodeAt(pos))) {
pos++;
}
end = pos;
}
else {
scanError = 3 /* ScanError.UnexpectedEndOfNumber */;
}
}
return text.substring(start, end);
}
function scanString() {
let result = '', start = pos;
while (true) {
if (pos >= len) {
result += text.substring(start, pos);
scanError = 2 /* ScanError.UnexpectedEndOfString */;
break;
}
const ch = text.charCodeAt(pos);
if (ch === 34 /* CharacterCodes.doubleQuote */) {
result += text.substring(start, pos);
pos++;
break;
}
if (ch === 92 /* CharacterCodes.backslash */) {
result += text.substring(start, pos);
pos++;
if (pos >= len) {
scanError = 2 /* ScanError.UnexpectedEndOfString */;
break;
}
const ch2 = text.charCodeAt(pos++);
switch (ch2) {
case 34 /* CharacterCodes.doubleQuote */:
result += '\"';
break;
case 92 /* CharacterCodes.backslash */:
result += '\\';
break;
case 47 /* CharacterCodes.slash */:
result += '/';
break;
case 98 /* CharacterCodes.b */:
result += '\b';
break;
case 102 /* CharacterCodes.f */:
result += '\f';
break;
case 110 /* CharacterCodes.n */:
result += '\n';
break;
case 114 /* CharacterCodes.r */:
result += '\r';
break;
case 116 /* CharacterCodes.t */:
result += '\t';
break;
case 117 /* CharacterCodes.u */:
const ch3 = scanHexDigits(4, true);
if (ch3 >= 0) {
result += String.fromCharCode(ch3);
}
else {
scanError = 4 /* ScanError.InvalidUnicode */;
}
break;
default:
scanError = 5 /* ScanError.InvalidEscapeCharacter */;
}
start = pos;
continue;
}
if (ch >= 0 && ch <= 0x1f) {
if (isLineBreak(ch)) {
result += text.substring(start, pos);
scanError = 2 /* ScanError.UnexpectedEndOfString */;
break;
}
else {
scanError = 6 /* ScanError.InvalidCharacter */;
// mark as error but continue with string
}
}
pos++;
}
return result;
}
function scanNext() {
value = '';
scanError = 0 /* ScanError.None */;
tokenOffset = pos;
lineStartOffset = lineNumber;
prevTokenLineStartOffset = tokenLineStartOffset;
if (pos >= len) {
// at the end
tokenOffset = len;
return token = 17 /* SyntaxKind.EOF */;
}
let code = text.charCodeAt(pos);
// trivia: whitespace
if (isWhiteSpace(code)) {
do {
pos++;
value += String.fromCharCode(code);
code = text.charCodeAt(pos);
} while (isWhiteSpace(code));
return token = 15 /* SyntaxKind.Trivia */;
}
// trivia: newlines
if (isLineBreak(code)) {
pos++;
value += String.fromCharCode(code);
if (code === 13 /* CharacterCodes.carriageReturn */ && text.charCodeAt(pos) === 10 /* CharacterCodes.lineFeed */) {
pos++;
value += '\n';
}
lineNumber++;
tokenLineStartOffset = pos;
return token = 14 /* SyntaxKind.LineBreakTrivia */;
}
switch (code) {
// tokens: []{}:,
case 123 /* CharacterCodes.openBrace */:
pos++;
return token = 1 /* SyntaxKind.OpenBraceToken */;
case 125 /* CharacterCodes.closeBrace */:
pos++;
return token = 2 /* SyntaxKind.CloseBraceToken */;
case 91 /* CharacterCodes.openBracket */:
pos++;
return token = 3 /* SyntaxKind.OpenBracketToken */;
case 93 /* CharacterCodes.closeBracket */:
pos++;
return token = 4 /* SyntaxKind.CloseBracketToken */;
case 58 /* CharacterCodes.colon */:
pos++;
return token = 6 /* SyntaxKind.ColonToken */;
case 44 /* CharacterCodes.comma */:
pos++;
return token = 5 /* SyntaxKind.CommaToken */;
// strings
case 34 /* CharacterCodes.doubleQuote */:
pos++;
value = scanString();
return token = 10 /* SyntaxKind.StringLiteral */;
// comments
case 47 /* CharacterCodes.slash */:
const start = pos - 1;
// Single-line comment
if (text.charCodeAt(pos + 1) === 47 /* CharacterCodes.slash */) {
pos += 2;
while (pos < len) {
if (isLineBreak(text.charCodeAt(pos))) {
break;
}
pos++;
}
value = text.substring(start, pos);
return token = 12 /* SyntaxKind.LineCommentTrivia */;
}
// Multi-line comment
if (text.charCodeAt(pos + 1) === 42 /* CharacterCodes.asterisk */) {
pos += 2;
const safeLength = len - 1; // For lookahead.
let commentClosed = false;
while (pos < safeLength) {
const ch = text.charCodeAt(pos);
if (ch === 42 /* CharacterCodes.asterisk */ && text.charCodeAt(pos + 1) === 47 /* CharacterCodes.slash */) {
pos += 2;
commentClosed = true;
break;
}
pos++;
if (isLineBreak(ch)) {
if (ch === 13 /* CharacterCodes.carriageReturn */ && text.charCodeAt(pos) === 10 /* CharacterCodes.lineFeed */) {
pos++;
}
lineNumber++;
tokenLineStartOffset = pos;
}
}
if (!commentClosed) {
pos++;
scanError = 1 /* ScanError.UnexpectedEndOfComment */;
}
value = text.substring(start, pos);
return token = 13 /* SyntaxKind.BlockCommentTrivia */;
}
// just a single slash
value += String.fromCharCode(code);
pos++;
return token = 16 /* SyntaxKind.Unknown */;
// numbers
case 45 /* CharacterCodes.minus */:
value += String.fromCharCode(code);
pos++;
if (pos === len || !isDigit(text.charCodeAt(pos))) {
return token = 16 /* SyntaxKind.Unknown */;
}
// found a minus, followed by a number so
// we fall through to proceed with scanning
// numbers
case 48 /* CharacterCodes._0 */:
case 49 /* CharacterCodes._1 */:
case 50 /* CharacterCodes._2 */:
case 51 /* CharacterCodes._3 */:
case 52 /* CharacterCodes._4 */:
case 53 /* CharacterCodes._5 */:
case 54 /* CharacterCodes._6 */:
case 55 /* CharacterCodes._7 */:
case 56 /* CharacterCodes._8 */:
case 57 /* CharacterCodes._9 */:
value += scanNumber();
return token = 11 /* SyntaxKind.NumericLiteral */;
// literals and unknown symbols
default:
// is a literal? Read the full word.
while (pos < len && isUnknownContentCharacter(code)) {
pos++;
code = text.charCodeAt(pos);
}
if (tokenOffset !== pos) {
value = text.substring(tokenOffset, pos);
// keywords: true, false, null
switch (value) {
case 'true': return token = 8 /* SyntaxKind.TrueKeyword */;
case 'false': return token = 9 /* SyntaxKind.FalseKeyword */;
case 'null': return token = 7 /* SyntaxKind.NullKeyword */;
}
return token = 16 /* SyntaxKind.Unknown */;
}
// some
value += String.fromCharCode(code);
pos++;
return token = 16 /* SyntaxKind.Unknown */;
}
}
function isUnknownContentCharacter(code) {
if (isWhiteSpace(code) || isLineBreak(code)) {
return false;
}
switch (code) {
case 125 /* CharacterCodes.closeBrace */:
case 93 /* CharacterCodes.closeBracket */:
case 123 /* CharacterCodes.openBrace */:
case 91 /* CharacterCodes.openBracket */:
case 34 /* CharacterCodes.doubleQuote */:
case 58 /* CharacterCodes.colon */:
case 44 /* CharacterCodes.comma */:
case 47 /* CharacterCodes.slash */:
return false;
}
return true;
}
function scanNextNonTrivia() {
let result;
do {
result = scanNext();
} while (result >= 12 /* SyntaxKind.LineCommentTrivia */ && result <= 15 /* SyntaxKind.Trivia */);
return result;
}
return {
setPosition: setPosition,
getPosition: () => pos,
scan: ignoreTrivia ? scanNextNonTrivia : scanNext,
getToken: () => token,
getTokenValue: () => value,
getTokenOffset: () => tokenOffset,
getTokenLength: () => pos - tokenOffset,
getTokenStartLine: () => lineStartOffset,
getTokenStartCharacter: () => tokenOffset - prevTokenLineStartOffset,
getTokenError: () => scanError,
};
}
function isWhiteSpace(ch) {
return ch === 32 /* CharacterCodes.space */ || ch === 9 /* CharacterCodes.tab */;
}
function isLineBreak(ch) {
return ch === 10 /* CharacterCodes.lineFeed */ || ch === 13 /* CharacterCodes.carriageReturn */;
}
function isDigit(ch) {
return ch >= 48 /* CharacterCodes._0 */ && ch <= 57 /* CharacterCodes._9 */;
}
var CharacterCodes;
(function (CharacterCodes) {
CharacterCodes[CharacterCodes["lineFeed"] = 10] = "lineFeed";
CharacterCodes[CharacterCodes["carriageReturn"] = 13] = "carriageReturn";
CharacterCodes[CharacterCodes["space"] = 32] = "space";
CharacterCodes[CharacterCodes["_0"] = 48] = "_0";
CharacterCodes[CharacterCodes["_1"] = 49] = "_1";
CharacterCodes[CharacterCodes["_2"] = 50] = "_2";
CharacterCodes[CharacterCodes["_3"] = 51] = "_3";
CharacterCodes[CharacterCodes["_4"] = 52] = "_4";
CharacterCodes[CharacterCodes["_5"] = 53] = "_5";
CharacterCodes[CharacterCodes["_6"] = 54] = "_6";
CharacterCodes[CharacterCodes["_7"] = 55] = "_7";
CharacterCodes[CharacterCodes["_8"] = 56] = "_8";
CharacterCodes[CharacterCodes["_9"] = 57] = "_9";
CharacterCodes[CharacterCodes["a"] = 97] = "a";
CharacterCodes[CharacterCodes["b"] = 98] = "b";
CharacterCodes[CharacterCodes["c"] = 99] = "c";
CharacterCodes[CharacterCodes["d"] = 100] = "d";
CharacterCodes[CharacterCodes["e"] = 101] = "e";
CharacterCodes[CharacterCodes["f"] = 102] = "f";
CharacterCodes[CharacterCodes["g"] = 103] = "g";
CharacterCodes[CharacterCodes["h"] = 104] = "h";
CharacterCodes[CharacterCodes["i"] = 105] = "i";
CharacterCodes[CharacterCodes["j"] = 106] = "j";
CharacterCodes[CharacterCodes["k"] = 107] = "k";
CharacterCodes[CharacterCodes["l"] = 108] = "l";
CharacterCodes[CharacterCodes["m"] = 109] = "m";
CharacterCodes[CharacterCodes["n"] = 110] = "n";
CharacterCodes[CharacterCodes["o"] = 111] = "o";
CharacterCodes[CharacterCodes["p"] = 112] = "p";
CharacterCodes[CharacterCodes["q"] = 113] = "q";
CharacterCodes[CharacterCodes["r"] = 114] = "r";
CharacterCodes[CharacterCodes["s"] = 115] = "s";
CharacterCodes[CharacterCodes["t"] = 116] = "t";
CharacterCodes[CharacterCodes["u"] = 117] = "u";
CharacterCodes[CharacterCodes["v"] = 118] = "v";
CharacterCodes[CharacterCodes["w"] = 119] = "w";
CharacterCodes[CharacterCodes["x"] = 120] = "x";
CharacterCodes[CharacterCodes["y"] = 121] = "y";
CharacterCodes[CharacterCodes["z"] = 122] = "z";
CharacterCodes[CharacterCodes["A"] = 65] = "A";
CharacterCodes[CharacterCodes["B"] = 66] = "B";
CharacterCodes[CharacterCodes["C"] = 67] = "C";
CharacterCodes[CharacterCodes["D"] = 68] = "D";
CharacterCodes[CharacterCodes["E"] = 69] = "E";
CharacterCodes[CharacterCodes["F"] = 70] = "F";
CharacterCodes[CharacterCodes["G"] = 71] = "G";
CharacterCodes[CharacterCodes["H"] = 72] = "H";
CharacterCodes[CharacterCodes["I"] = 73] = "I";
CharacterCodes[CharacterCodes["J"] = 74] = "J";
CharacterCodes[CharacterCodes["K"] = 75] = "K";
CharacterCodes[CharacterCodes["L"] = 76] = "L";
CharacterCodes[CharacterCodes["M"] = 77] = "M";
CharacterCodes[CharacterCodes["N"] = 78] = "N";
CharacterCodes[CharacterCodes["O"] = 79] = "O";
CharacterCodes[CharacterCodes["P"] = 80] = "P";
CharacterCodes[CharacterCodes["Q"] = 81] = "Q";
CharacterCodes[CharacterCodes["R"] = 82] = "R";
CharacterCodes[CharacterCodes["S"] = 83] = "S";
CharacterCodes[CharacterCodes["T"] = 84] = "T";
CharacterCodes[CharacterCodes["U"] = 85] = "U";
CharacterCodes[CharacterCodes["V"] = 86] = "V";
CharacterCodes[CharacterCodes["W"] = 87] = "W";
CharacterCodes[CharacterCodes["X"] = 88] = "X";
CharacterCodes[CharacterCodes["Y"] = 89] = "Y";
CharacterCodes[CharacterCodes["Z"] = 90] = "Z";
CharacterCodes[CharacterCodes["asterisk"] = 42] = "asterisk";
CharacterCodes[CharacterCodes["backslash"] = 92] = "backslash";
CharacterCodes[CharacterCodes["closeBrace"] = 125] = "closeBrace";
CharacterCodes[CharacterCodes["closeBracket"] = 93] = "closeBracket";
CharacterCodes[CharacterCodes["colon"] = 58] = "colon";
CharacterCodes[CharacterCodes["comma"] = 44] = "comma";
CharacterCodes[CharacterCodes["dot"] = 46] = "dot";
CharacterCodes[CharacterCodes["doubleQuote"] = 34] = "doubleQuote";
CharacterCodes[CharacterCodes["minus"] = 45] = "minus";
CharacterCodes[CharacterCodes["openBrace"] = 123] = "openBrace";
CharacterCodes[CharacterCodes["openBracket"] = 91] = "openBracket";
CharacterCodes[CharacterCodes["plus"] = 43] = "plus";
CharacterCodes[CharacterCodes["slash"] = 47] = "slash";
CharacterCodes[CharacterCodes["formFeed"] = 12] = "formFeed";
CharacterCodes[CharacterCodes["tab"] = 9] = "tab";
})(CharacterCodes || (CharacterCodes = {}));

View File

@@ -0,0 +1,29 @@
export const cachedSpaces = new Array(20).fill(0).map((_, index) => {
return ' '.repeat(index);
});
const maxCachedValues = 200;
export const cachedBreakLinesWithSpaces = {
' ': {
'\n': new Array(maxCachedValues).fill(0).map((_, index) => {
return '\n' + ' '.repeat(index);
}),
'\r': new Array(maxCachedValues).fill(0).map((_, index) => {
return '\r' + ' '.repeat(index);
}),
'\r\n': new Array(maxCachedValues).fill(0).map((_, index) => {
return '\r\n' + ' '.repeat(index);
}),
},
'\t': {
'\n': new Array(maxCachedValues).fill(0).map((_, index) => {
return '\n' + '\t'.repeat(index);
}),
'\r': new Array(maxCachedValues).fill(0).map((_, index) => {
return '\r' + '\t'.repeat(index);
}),
'\r\n': new Array(maxCachedValues).fill(0).map((_, index) => {
return '\r\n' + '\t'.repeat(index);
}),
}
};
export const supportedEols = ['\n', '\r', '\r\n'];

View File

@@ -0,0 +1,351 @@
/**
* Creates a JSON scanner on the given text.
* If ignoreTrivia is set, whitespaces or comments are ignored.
*/
export declare const createScanner: (text: string, ignoreTrivia?: boolean) => JSONScanner;
export declare const enum ScanError {
None = 0,
UnexpectedEndOfComment = 1,
UnexpectedEndOfString = 2,
UnexpectedEndOfNumber = 3,
InvalidUnicode = 4,
InvalidEscapeCharacter = 5,
InvalidCharacter = 6
}
export declare const enum SyntaxKind {
OpenBraceToken = 1,
CloseBraceToken = 2,
OpenBracketToken = 3,
CloseBracketToken = 4,
CommaToken = 5,
ColonToken = 6,
NullKeyword = 7,
TrueKeyword = 8,
FalseKeyword = 9,
StringLiteral = 10,
NumericLiteral = 11,
LineCommentTrivia = 12,
BlockCommentTrivia = 13,
LineBreakTrivia = 14,
Trivia = 15,
Unknown = 16,
EOF = 17
}
/**
* The scanner object, representing a JSON scanner at a position in the input string.
*/
export interface JSONScanner {
/**
* Sets the scan position to a new offset. A call to 'scan' is needed to get the first token.
*/
setPosition(pos: number): void;
/**
* Read the next token. Returns the token code.
*/
scan(): SyntaxKind;
/**
* Returns the zero-based current scan position, which is after the last read token.
*/
getPosition(): number;
/**
* Returns the last read token.
*/
getToken(): SyntaxKind;
/**
* Returns the last read token value. The value for strings is the decoded string content. For numbers it's of type number, for boolean it's true or false.
*/
getTokenValue(): string;
/**
* The zero-based start offset of the last read token.
*/
getTokenOffset(): number;
/**
* The length of the last read token.
*/
getTokenLength(): number;
/**
* The zero-based start line number of the last read token.
*/
getTokenStartLine(): number;
/**
* The zero-based start character (column) of the last read token.
*/
getTokenStartCharacter(): number;
/**
* An error code of the last scan.
*/
getTokenError(): ScanError;
}
/**
* For a given offset, evaluate the location in the JSON document. Each segment in the location path is either a property name or an array index.
*/
export declare const getLocation: (text: string, position: number) => Location;
/**
* Parses the given text and returns the object the JSON content represents. On invalid input, the parser tries to be as fault tolerant as possible, but still return a result.
* Therefore, always check the errors list to find out if the input was valid.
*/
export declare const parse: (text: string, errors?: ParseError[], options?: ParseOptions) => any;
/**
* Parses the given text and returns a tree representation the JSON content. On invalid input, the parser tries to be as fault tolerant as possible, but still return a result.
*/
export declare const parseTree: (text: string, errors?: ParseError[], options?: ParseOptions) => Node | undefined;
/**
* Finds the node at the given path in a JSON DOM.
*/
export declare const findNodeAtLocation: (root: Node, path: JSONPath) => Node | undefined;
/**
* Finds the innermost node at the given offset. If includeRightBound is set, also finds nodes that end at the given offset.
*/
export declare const findNodeAtOffset: (root: Node, offset: number, includeRightBound?: boolean) => Node | undefined;
/**
* Gets the JSON path of the given JSON DOM node
*/
export declare const getNodePath: (node: Node) => JSONPath;
/**
* Evaluates the JavaScript object of the given JSON DOM node
*/
export declare const getNodeValue: (node: Node) => any;
/**
* Parses the given text and invokes the visitor functions for each object, array and literal reached.
*/
export declare const visit: (text: string, visitor: JSONVisitor, options?: ParseOptions) => any;
/**
* Takes JSON with JavaScript-style comments and remove
* them. Optionally replaces every none-newline character
* of comments with a replaceCharacter
*/
export declare const stripComments: (text: string, replaceCh?: string) => string;
export interface ParseError {
error: ParseErrorCode;
offset: number;
length: number;
}
export declare const enum ParseErrorCode {
InvalidSymbol = 1,
InvalidNumberFormat = 2,
PropertyNameExpected = 3,
ValueExpected = 4,
ColonExpected = 5,
CommaExpected = 6,
CloseBraceExpected = 7,
CloseBracketExpected = 8,
EndOfFileExpected = 9,
InvalidCommentToken = 10,
UnexpectedEndOfComment = 11,
UnexpectedEndOfString = 12,
UnexpectedEndOfNumber = 13,
InvalidUnicode = 14,
InvalidEscapeCharacter = 15,
InvalidCharacter = 16
}
export declare function printParseErrorCode(code: ParseErrorCode): "InvalidSymbol" | "InvalidNumberFormat" | "PropertyNameExpected" | "ValueExpected" | "ColonExpected" | "CommaExpected" | "CloseBraceExpected" | "CloseBracketExpected" | "EndOfFileExpected" | "InvalidCommentToken" | "UnexpectedEndOfComment" | "UnexpectedEndOfString" | "UnexpectedEndOfNumber" | "InvalidUnicode" | "InvalidEscapeCharacter" | "InvalidCharacter" | "<unknown ParseErrorCode>";
export type NodeType = 'object' | 'array' | 'property' | 'string' | 'number' | 'boolean' | 'null';
export interface Node {
readonly type: NodeType;
readonly value?: any;
readonly offset: number;
readonly length: number;
readonly colonOffset?: number;
readonly parent?: Node;
readonly children?: Node[];
}
/**
* A {@linkcode JSONPath} segment. Either a string representing an object property name
* or a number (starting at 0) for array indices.
*/
export type Segment = string | number;
export type JSONPath = Segment[];
export interface Location {
/**
* The previous property key or literal value (string, number, boolean or null) or undefined.
*/
previousNode?: Node;
/**
* The path describing the location in the JSON document. The path consists of a sequence of strings
* representing an object property or numbers for array indices.
*/
path: JSONPath;
/**
* Matches the locations path against a pattern consisting of strings (for properties) and numbers (for array indices).
* '*' will match a single segment of any property name or index.
* '**' will match a sequence of segments of any property name or index, or no segment.
*/
matches: (patterns: JSONPath) => boolean;
/**
* If set, the location's offset is at a property key.
*/
isAtPropertyKey: boolean;
}
export interface ParseOptions {
disallowComments?: boolean;
allowTrailingComma?: boolean;
allowEmptyContent?: boolean;
}
/**
* Visitor called by {@linkcode visit} when parsing JSON.
*
* The visitor functions have the following common parameters:
* - `offset`: Global offset within the JSON document, starting at 0
* - `startLine`: Line number, starting at 0
* - `startCharacter`: Start character (column) within the current line, starting at 0
*
* Additionally some functions have a `pathSupplier` parameter which can be used to obtain the
* current `JSONPath` within the document.
*/
export interface JSONVisitor {
/**
* Invoked when an open brace is encountered and an object is started. The offset and length represent the location of the open brace.
* When `false` is returned, the object properties will not be visited.
*/
onObjectBegin?: (offset: number, length: number, startLine: number, startCharacter: number, pathSupplier: () => JSONPath) => boolean | void;
/**
* Invoked when a property is encountered. The offset and length represent the location of the property name.
* The `JSONPath` created by the `pathSupplier` refers to the enclosing JSON object, it does not include the
* property name yet.
*/
onObjectProperty?: (property: string, offset: number, length: number, startLine: number, startCharacter: number, pathSupplier: () => JSONPath) => void;
/**
* Invoked when a closing brace is encountered and an object is completed. The offset and length represent the location of the closing brace.
*/
onObjectEnd?: (offset: number, length: number, startLine: number, startCharacter: number) => void;
/**
* Invoked when an open bracket is encountered. The offset and length represent the location of the open bracket.
* When `false` is returned, the array items will not be visited.
*/
onArrayBegin?: (offset: number, length: number, startLine: number, startCharacter: number, pathSupplier: () => JSONPath) => boolean | void;
/**
* Invoked when a closing bracket is encountered. The offset and length represent the location of the closing bracket.
*/
onArrayEnd?: (offset: number, length: number, startLine: number, startCharacter: number) => void;
/**
* Invoked when a literal value is encountered. The offset and length represent the location of the literal value.
*/
onLiteralValue?: (value: any, offset: number, length: number, startLine: number, startCharacter: number, pathSupplier: () => JSONPath) => void;
/**
* Invoked when a comma or colon separator is encountered. The offset and length represent the location of the separator.
*/
onSeparator?: (character: string, offset: number, length: number, startLine: number, startCharacter: number) => void;
/**
* When comments are allowed, invoked when a line or block comment is encountered. The offset and length represent the location of the comment.
*/
onComment?: (offset: number, length: number, startLine: number, startCharacter: number) => void;
/**
* Invoked on an error.
*/
onError?: (error: ParseErrorCode, offset: number, length: number, startLine: number, startCharacter: number) => void;
}
/**
* An edit result describes a textual edit operation. It is the result of a {@linkcode format} and {@linkcode modify} operation.
* It consist of one or more edits describing insertions, replacements or removals of text segments.
* * The offsets of the edits refer to the original state of the document.
* * No two edits change or remove the same range of text in the original document.
* * Multiple edits can have the same offset if they are multiple inserts, or an insert followed by a remove or replace.
* * The order in the array defines which edit is applied first.
* To apply an edit result use {@linkcode applyEdits}.
* In general multiple EditResults must not be concatenated because they might impact each other, producing incorrect or malformed JSON data.
*/
export type EditResult = Edit[];
/**
* Represents a text modification
*/
export interface Edit {
/**
* The start offset of the modification.
*/
offset: number;
/**
* The length of the modification. Must not be negative. Empty length represents an *insert*.
*/
length: number;
/**
* The new content. Empty content represents a *remove*.
*/
content: string;
}
/**
* A text range in the document
*/
export interface Range {
/**
* The start offset of the range.
*/
offset: number;
/**
* The length of the range. Must not be negative.
*/
length: number;
}
/**
* Options used by {@linkcode format} when computing the formatting edit operations
*/
export interface FormattingOptions {
/**
* If indentation is based on spaces (`insertSpaces` = true), the number of spaces that make an indent.
*/
tabSize?: number;
/**
* Is indentation based on spaces?
*/
insertSpaces?: boolean;
/**
* The default 'end of line' character. If not set, '\n' is used as default.
*/
eol?: string;
/**
* If set, will add a new line at the end of the document.
*/
insertFinalNewline?: boolean;
/**
* If true, will keep line positions as is in the formatting
*/
keepLines?: boolean;
}
/**
* Computes the edit operations needed to format a JSON document.
*
* @param documentText The input text
* @param range The range to format or `undefined` to format the full content
* @param options The formatting options
* @returns The edit operations describing the formatting changes to the original document following the format described in {@linkcode EditResult}.
* To apply the edit operations to the input, use {@linkcode applyEdits}.
*/
export declare function format(documentText: string, range: Range | undefined, options: FormattingOptions): EditResult;
/**
* Options used by {@linkcode modify} when computing the modification edit operations
*/
export interface ModificationOptions {
/**
* Formatting options. If undefined, the newly inserted code will be inserted unformatted.
*/
formattingOptions?: FormattingOptions;
/**
* Default false. If `JSONPath` refers to an index of an array and `isArrayInsertion` is `true`, then
* {@linkcode modify} will insert a new item at that location instead of overwriting its contents.
*/
isArrayInsertion?: boolean;
/**
* Optional function to define the insertion index given an existing list of properties.
*/
getInsertionIndex?: (properties: string[]) => number;
}
/**
* Computes the edit operations needed to modify a value in the JSON document.
*
* @param documentText The input text
* @param path The path of the value to change. The path represents either to the document root, a property or an array item.
* If the path points to an non-existing property or item, it will be created.
* @param value The new value for the specified property or item. If the value is undefined,
* the property or item will be removed.
* @param options Options
* @returns The edit operations describing the changes to the original document, following the format described in {@linkcode EditResult}.
* To apply the edit operations to the input, use {@linkcode applyEdits}.
*/
export declare function modify(text: string, path: JSONPath, value: any, options: ModificationOptions): EditResult;
/**
* Applies edits to an input string.
* @param text The input text
* @param edits Edit operations following the format described in {@linkcode EditResult}.
* @returns The text with the applied edits.
* @throws An error if the edit operations are not well-formed as described in {@linkcode EditResult}.
*/
export declare function applyEdits(text: string, edits: EditResult): string;

View File

@@ -0,0 +1,178 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as formatter from './impl/format';
import * as edit from './impl/edit';
import * as scanner from './impl/scanner';
import * as parser from './impl/parser';
/**
* Creates a JSON scanner on the given text.
* If ignoreTrivia is set, whitespaces or comments are ignored.
*/
export const createScanner = scanner.createScanner;
export var ScanError;
(function (ScanError) {
ScanError[ScanError["None"] = 0] = "None";
ScanError[ScanError["UnexpectedEndOfComment"] = 1] = "UnexpectedEndOfComment";
ScanError[ScanError["UnexpectedEndOfString"] = 2] = "UnexpectedEndOfString";
ScanError[ScanError["UnexpectedEndOfNumber"] = 3] = "UnexpectedEndOfNumber";
ScanError[ScanError["InvalidUnicode"] = 4] = "InvalidUnicode";
ScanError[ScanError["InvalidEscapeCharacter"] = 5] = "InvalidEscapeCharacter";
ScanError[ScanError["InvalidCharacter"] = 6] = "InvalidCharacter";
})(ScanError || (ScanError = {}));
export var SyntaxKind;
(function (SyntaxKind) {
SyntaxKind[SyntaxKind["OpenBraceToken"] = 1] = "OpenBraceToken";
SyntaxKind[SyntaxKind["CloseBraceToken"] = 2] = "CloseBraceToken";
SyntaxKind[SyntaxKind["OpenBracketToken"] = 3] = "OpenBracketToken";
SyntaxKind[SyntaxKind["CloseBracketToken"] = 4] = "CloseBracketToken";
SyntaxKind[SyntaxKind["CommaToken"] = 5] = "CommaToken";
SyntaxKind[SyntaxKind["ColonToken"] = 6] = "ColonToken";
SyntaxKind[SyntaxKind["NullKeyword"] = 7] = "NullKeyword";
SyntaxKind[SyntaxKind["TrueKeyword"] = 8] = "TrueKeyword";
SyntaxKind[SyntaxKind["FalseKeyword"] = 9] = "FalseKeyword";
SyntaxKind[SyntaxKind["StringLiteral"] = 10] = "StringLiteral";
SyntaxKind[SyntaxKind["NumericLiteral"] = 11] = "NumericLiteral";
SyntaxKind[SyntaxKind["LineCommentTrivia"] = 12] = "LineCommentTrivia";
SyntaxKind[SyntaxKind["BlockCommentTrivia"] = 13] = "BlockCommentTrivia";
SyntaxKind[SyntaxKind["LineBreakTrivia"] = 14] = "LineBreakTrivia";
SyntaxKind[SyntaxKind["Trivia"] = 15] = "Trivia";
SyntaxKind[SyntaxKind["Unknown"] = 16] = "Unknown";
SyntaxKind[SyntaxKind["EOF"] = 17] = "EOF";
})(SyntaxKind || (SyntaxKind = {}));
/**
* For a given offset, evaluate the location in the JSON document. Each segment in the location path is either a property name or an array index.
*/
export const getLocation = parser.getLocation;
/**
* Parses the given text and returns the object the JSON content represents. On invalid input, the parser tries to be as fault tolerant as possible, but still return a result.
* Therefore, always check the errors list to find out if the input was valid.
*/
export const parse = parser.parse;
/**
* Parses the given text and returns a tree representation the JSON content. On invalid input, the parser tries to be as fault tolerant as possible, but still return a result.
*/
export const parseTree = parser.parseTree;
/**
* Finds the node at the given path in a JSON DOM.
*/
export const findNodeAtLocation = parser.findNodeAtLocation;
/**
* Finds the innermost node at the given offset. If includeRightBound is set, also finds nodes that end at the given offset.
*/
export const findNodeAtOffset = parser.findNodeAtOffset;
/**
* Gets the JSON path of the given JSON DOM node
*/
export const getNodePath = parser.getNodePath;
/**
* Evaluates the JavaScript object of the given JSON DOM node
*/
export const getNodeValue = parser.getNodeValue;
/**
* Parses the given text and invokes the visitor functions for each object, array and literal reached.
*/
export const visit = parser.visit;
/**
* Takes JSON with JavaScript-style comments and remove
* them. Optionally replaces every none-newline character
* of comments with a replaceCharacter
*/
export const stripComments = parser.stripComments;
export var ParseErrorCode;
(function (ParseErrorCode) {
ParseErrorCode[ParseErrorCode["InvalidSymbol"] = 1] = "InvalidSymbol";
ParseErrorCode[ParseErrorCode["InvalidNumberFormat"] = 2] = "InvalidNumberFormat";
ParseErrorCode[ParseErrorCode["PropertyNameExpected"] = 3] = "PropertyNameExpected";
ParseErrorCode[ParseErrorCode["ValueExpected"] = 4] = "ValueExpected";
ParseErrorCode[ParseErrorCode["ColonExpected"] = 5] = "ColonExpected";
ParseErrorCode[ParseErrorCode["CommaExpected"] = 6] = "CommaExpected";
ParseErrorCode[ParseErrorCode["CloseBraceExpected"] = 7] = "CloseBraceExpected";
ParseErrorCode[ParseErrorCode["CloseBracketExpected"] = 8] = "CloseBracketExpected";
ParseErrorCode[ParseErrorCode["EndOfFileExpected"] = 9] = "EndOfFileExpected";
ParseErrorCode[ParseErrorCode["InvalidCommentToken"] = 10] = "InvalidCommentToken";
ParseErrorCode[ParseErrorCode["UnexpectedEndOfComment"] = 11] = "UnexpectedEndOfComment";
ParseErrorCode[ParseErrorCode["UnexpectedEndOfString"] = 12] = "UnexpectedEndOfString";
ParseErrorCode[ParseErrorCode["UnexpectedEndOfNumber"] = 13] = "UnexpectedEndOfNumber";
ParseErrorCode[ParseErrorCode["InvalidUnicode"] = 14] = "InvalidUnicode";
ParseErrorCode[ParseErrorCode["InvalidEscapeCharacter"] = 15] = "InvalidEscapeCharacter";
ParseErrorCode[ParseErrorCode["InvalidCharacter"] = 16] = "InvalidCharacter";
})(ParseErrorCode || (ParseErrorCode = {}));
export function printParseErrorCode(code) {
switch (code) {
case 1 /* ParseErrorCode.InvalidSymbol */: return 'InvalidSymbol';
case 2 /* ParseErrorCode.InvalidNumberFormat */: return 'InvalidNumberFormat';
case 3 /* ParseErrorCode.PropertyNameExpected */: return 'PropertyNameExpected';
case 4 /* ParseErrorCode.ValueExpected */: return 'ValueExpected';
case 5 /* ParseErrorCode.ColonExpected */: return 'ColonExpected';
case 6 /* ParseErrorCode.CommaExpected */: return 'CommaExpected';
case 7 /* ParseErrorCode.CloseBraceExpected */: return 'CloseBraceExpected';
case 8 /* ParseErrorCode.CloseBracketExpected */: return 'CloseBracketExpected';
case 9 /* ParseErrorCode.EndOfFileExpected */: return 'EndOfFileExpected';
case 10 /* ParseErrorCode.InvalidCommentToken */: return 'InvalidCommentToken';
case 11 /* ParseErrorCode.UnexpectedEndOfComment */: return 'UnexpectedEndOfComment';
case 12 /* ParseErrorCode.UnexpectedEndOfString */: return 'UnexpectedEndOfString';
case 13 /* ParseErrorCode.UnexpectedEndOfNumber */: return 'UnexpectedEndOfNumber';
case 14 /* ParseErrorCode.InvalidUnicode */: return 'InvalidUnicode';
case 15 /* ParseErrorCode.InvalidEscapeCharacter */: return 'InvalidEscapeCharacter';
case 16 /* ParseErrorCode.InvalidCharacter */: return 'InvalidCharacter';
}
return '<unknown ParseErrorCode>';
}
/**
* Computes the edit operations needed to format a JSON document.
*
* @param documentText The input text
* @param range The range to format or `undefined` to format the full content
* @param options The formatting options
* @returns The edit operations describing the formatting changes to the original document following the format described in {@linkcode EditResult}.
* To apply the edit operations to the input, use {@linkcode applyEdits}.
*/
export function format(documentText, range, options) {
return formatter.format(documentText, range, options);
}
/**
* Computes the edit operations needed to modify a value in the JSON document.
*
* @param documentText The input text
* @param path The path of the value to change. The path represents either to the document root, a property or an array item.
* If the path points to an non-existing property or item, it will be created.
* @param value The new value for the specified property or item. If the value is undefined,
* the property or item will be removed.
* @param options Options
* @returns The edit operations describing the changes to the original document, following the format described in {@linkcode EditResult}.
* To apply the edit operations to the input, use {@linkcode applyEdits}.
*/
export function modify(text, path, value, options) {
return edit.setProperty(text, path, value, options);
}
/**
* Applies edits to an input string.
* @param text The input text
* @param edits Edit operations following the format described in {@linkcode EditResult}.
* @returns The text with the applied edits.
* @throws An error if the edit operations are not well-formed as described in {@linkcode EditResult}.
*/
export function applyEdits(text, edits) {
let sortedEdits = edits.slice(0).sort((a, b) => {
const diff = a.offset - b.offset;
if (diff === 0) {
return a.length - b.length;
}
return diff;
});
let lastModifiedOffset = text.length;
for (let i = sortedEdits.length - 1; i >= 0; i--) {
let e = sortedEdits[i];
if (e.offset + e.length <= lastModifiedOffset) {
text = edit.applyEdit(text, e);
}
else {
throw new Error('Overlapping edit');
}
lastModifiedOffset = e.offset;
}
return text;
}

View File

@@ -0,0 +1,201 @@
(function (factory) {
if (typeof module === "object" && typeof module.exports === "object") {
var v = factory(require, exports);
if (v !== undefined) module.exports = v;
}
else if (typeof define === "function" && define.amd) {
define(["require", "exports", "./format", "./parser"], factory);
}
})(function (require, exports) {
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
Object.defineProperty(exports, "__esModule", { value: true });
exports.isWS = exports.applyEdit = exports.setProperty = exports.removeProperty = void 0;
const format_1 = require("./format");
const parser_1 = require("./parser");
function removeProperty(text, path, options) {
return setProperty(text, path, void 0, options);
}
exports.removeProperty = removeProperty;
function setProperty(text, originalPath, value, options) {
const path = originalPath.slice();
const errors = [];
const root = (0, parser_1.parseTree)(text, errors);
let parent = void 0;
let lastSegment = void 0;
while (path.length > 0) {
lastSegment = path.pop();
parent = (0, parser_1.findNodeAtLocation)(root, path);
if (parent === void 0 && value !== void 0) {
if (typeof lastSegment === 'string') {
value = { [lastSegment]: value };
}
else {
value = [value];
}
}
else {
break;
}
}
if (!parent) {
// empty document
if (value === void 0) { // delete
throw new Error('Can not delete in empty document');
}
return withFormatting(text, { offset: root ? root.offset : 0, length: root ? root.length : 0, content: JSON.stringify(value) }, options);
}
else if (parent.type === 'object' && typeof lastSegment === 'string' && Array.isArray(parent.children)) {
const existing = (0, parser_1.findNodeAtLocation)(parent, [lastSegment]);
if (existing !== void 0) {
if (value === void 0) { // delete
if (!existing.parent) {
throw new Error('Malformed AST');
}
const propertyIndex = parent.children.indexOf(existing.parent);
let removeBegin;
let removeEnd = existing.parent.offset + existing.parent.length;
if (propertyIndex > 0) {
// remove the comma of the previous node
let previous = parent.children[propertyIndex - 1];
removeBegin = previous.offset + previous.length;
}
else {
removeBegin = parent.offset + 1;
if (parent.children.length > 1) {
// remove the comma of the next node
let next = parent.children[1];
removeEnd = next.offset;
}
}
return withFormatting(text, { offset: removeBegin, length: removeEnd - removeBegin, content: '' }, options);
}
else {
// set value of existing property
return withFormatting(text, { offset: existing.offset, length: existing.length, content: JSON.stringify(value) }, options);
}
}
else {
if (value === void 0) { // delete
return []; // property does not exist, nothing to do
}
const newProperty = `${JSON.stringify(lastSegment)}: ${JSON.stringify(value)}`;
const index = options.getInsertionIndex ? options.getInsertionIndex(parent.children.map(p => p.children[0].value)) : parent.children.length;
let edit;
if (index > 0) {
let previous = parent.children[index - 1];
edit = { offset: previous.offset + previous.length, length: 0, content: ',' + newProperty };
}
else if (parent.children.length === 0) {
edit = { offset: parent.offset + 1, length: 0, content: newProperty };
}
else {
edit = { offset: parent.offset + 1, length: 0, content: newProperty + ',' };
}
return withFormatting(text, edit, options);
}
}
else if (parent.type === 'array' && typeof lastSegment === 'number' && Array.isArray(parent.children)) {
const insertIndex = lastSegment;
if (insertIndex === -1) {
// Insert
const newProperty = `${JSON.stringify(value)}`;
let edit;
if (parent.children.length === 0) {
edit = { offset: parent.offset + 1, length: 0, content: newProperty };
}
else {
const previous = parent.children[parent.children.length - 1];
edit = { offset: previous.offset + previous.length, length: 0, content: ',' + newProperty };
}
return withFormatting(text, edit, options);
}
else if (value === void 0 && parent.children.length >= 0) {
// Removal
const removalIndex = lastSegment;
const toRemove = parent.children[removalIndex];
let edit;
if (parent.children.length === 1) {
// only item
edit = { offset: parent.offset + 1, length: parent.length - 2, content: '' };
}
else if (parent.children.length - 1 === removalIndex) {
// last item
let previous = parent.children[removalIndex - 1];
let offset = previous.offset + previous.length;
let parentEndOffset = parent.offset + parent.length;
edit = { offset, length: parentEndOffset - 2 - offset, content: '' };
}
else {
edit = { offset: toRemove.offset, length: parent.children[removalIndex + 1].offset - toRemove.offset, content: '' };
}
return withFormatting(text, edit, options);
}
else if (value !== void 0) {
let edit;
const newProperty = `${JSON.stringify(value)}`;
if (!options.isArrayInsertion && parent.children.length > lastSegment) {
const toModify = parent.children[lastSegment];
edit = { offset: toModify.offset, length: toModify.length, content: newProperty };
}
else if (parent.children.length === 0 || lastSegment === 0) {
edit = { offset: parent.offset + 1, length: 0, content: parent.children.length === 0 ? newProperty : newProperty + ',' };
}
else {
const index = lastSegment > parent.children.length ? parent.children.length : lastSegment;
const previous = parent.children[index - 1];
edit = { offset: previous.offset + previous.length, length: 0, content: ',' + newProperty };
}
return withFormatting(text, edit, options);
}
else {
throw new Error(`Can not ${value === void 0 ? 'remove' : (options.isArrayInsertion ? 'insert' : 'modify')} Array index ${insertIndex} as length is not sufficient`);
}
}
else {
throw new Error(`Can not add ${typeof lastSegment !== 'number' ? 'index' : 'property'} to parent of type ${parent.type}`);
}
}
exports.setProperty = setProperty;
function withFormatting(text, edit, options) {
if (!options.formattingOptions) {
return [edit];
}
// apply the edit
let newText = applyEdit(text, edit);
// format the new text
let begin = edit.offset;
let end = edit.offset + edit.content.length;
if (edit.length === 0 || edit.content.length === 0) { // insert or remove
while (begin > 0 && !(0, format_1.isEOL)(newText, begin - 1)) {
begin--;
}
while (end < newText.length && !(0, format_1.isEOL)(newText, end)) {
end++;
}
}
const edits = (0, format_1.format)(newText, { offset: begin, length: end - begin }, { ...options.formattingOptions, keepLines: false });
// apply the formatting edits and track the begin and end offsets of the changes
for (let i = edits.length - 1; i >= 0; i--) {
const edit = edits[i];
newText = applyEdit(newText, edit);
begin = Math.min(begin, edit.offset);
end = Math.max(end, edit.offset + edit.length);
end += edit.content.length - edit.length;
}
// create a single edit with all changes
const editLength = text.length - (newText.length - end) - begin;
return [{ offset: begin, length: editLength, content: newText.substring(begin, end) }];
}
function applyEdit(text, edit) {
return text.substring(0, edit.offset) + edit.content + text.substring(edit.offset + edit.length);
}
exports.applyEdit = applyEdit;
function isWS(text, offset) {
return '\r\n \t'.indexOf(text.charAt(offset)) !== -1;
}
exports.isWS = isWS;
});

View File

@@ -0,0 +1,275 @@
(function (factory) {
if (typeof module === "object" && typeof module.exports === "object") {
var v = factory(require, exports);
if (v !== undefined) module.exports = v;
}
else if (typeof define === "function" && define.amd) {
define(["require", "exports", "./scanner", "./string-intern"], factory);
}
})(function (require, exports) {
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
Object.defineProperty(exports, "__esModule", { value: true });
exports.isEOL = exports.format = void 0;
const scanner_1 = require("./scanner");
const string_intern_1 = require("./string-intern");
function format(documentText, range, options) {
let initialIndentLevel;
let formatText;
let formatTextStart;
let rangeStart;
let rangeEnd;
if (range) {
rangeStart = range.offset;
rangeEnd = rangeStart + range.length;
formatTextStart = rangeStart;
while (formatTextStart > 0 && !isEOL(documentText, formatTextStart - 1)) {
formatTextStart--;
}
let endOffset = rangeEnd;
while (endOffset < documentText.length && !isEOL(documentText, endOffset)) {
endOffset++;
}
formatText = documentText.substring(formatTextStart, endOffset);
initialIndentLevel = computeIndentLevel(formatText, options);
}
else {
formatText = documentText;
initialIndentLevel = 0;
formatTextStart = 0;
rangeStart = 0;
rangeEnd = documentText.length;
}
const eol = getEOL(options, documentText);
const eolFastPathSupported = string_intern_1.supportedEols.includes(eol);
let numberLineBreaks = 0;
let indentLevel = 0;
let indentValue;
if (options.insertSpaces) {
indentValue = string_intern_1.cachedSpaces[options.tabSize || 4] ?? repeat(string_intern_1.cachedSpaces[1], options.tabSize || 4);
}
else {
indentValue = '\t';
}
const indentType = indentValue === '\t' ? '\t' : ' ';
let scanner = (0, scanner_1.createScanner)(formatText, false);
let hasError = false;
function newLinesAndIndent() {
if (numberLineBreaks > 1) {
return repeat(eol, numberLineBreaks) + repeat(indentValue, initialIndentLevel + indentLevel);
}
const amountOfSpaces = indentValue.length * (initialIndentLevel + indentLevel);
if (!eolFastPathSupported || amountOfSpaces > string_intern_1.cachedBreakLinesWithSpaces[indentType][eol].length) {
return eol + repeat(indentValue, initialIndentLevel + indentLevel);
}
if (amountOfSpaces <= 0) {
return eol;
}
return string_intern_1.cachedBreakLinesWithSpaces[indentType][eol][amountOfSpaces];
}
function scanNext() {
let token = scanner.scan();
numberLineBreaks = 0;
while (token === 15 /* SyntaxKind.Trivia */ || token === 14 /* SyntaxKind.LineBreakTrivia */) {
if (token === 14 /* SyntaxKind.LineBreakTrivia */ && options.keepLines) {
numberLineBreaks += 1;
}
else if (token === 14 /* SyntaxKind.LineBreakTrivia */) {
numberLineBreaks = 1;
}
token = scanner.scan();
}
hasError = token === 16 /* SyntaxKind.Unknown */ || scanner.getTokenError() !== 0 /* ScanError.None */;
return token;
}
const editOperations = [];
function addEdit(text, startOffset, endOffset) {
if (!hasError && (!range || (startOffset < rangeEnd && endOffset > rangeStart)) && documentText.substring(startOffset, endOffset) !== text) {
editOperations.push({ offset: startOffset, length: endOffset - startOffset, content: text });
}
}
let firstToken = scanNext();
if (options.keepLines && numberLineBreaks > 0) {
addEdit(repeat(eol, numberLineBreaks), 0, 0);
}
if (firstToken !== 17 /* SyntaxKind.EOF */) {
let firstTokenStart = scanner.getTokenOffset() + formatTextStart;
let initialIndent = (indentValue.length * initialIndentLevel < 20) && options.insertSpaces
? string_intern_1.cachedSpaces[indentValue.length * initialIndentLevel]
: repeat(indentValue, initialIndentLevel);
addEdit(initialIndent, formatTextStart, firstTokenStart);
}
while (firstToken !== 17 /* SyntaxKind.EOF */) {
let firstTokenEnd = scanner.getTokenOffset() + scanner.getTokenLength() + formatTextStart;
let secondToken = scanNext();
let replaceContent = '';
let needsLineBreak = false;
while (numberLineBreaks === 0 && (secondToken === 12 /* SyntaxKind.LineCommentTrivia */ || secondToken === 13 /* SyntaxKind.BlockCommentTrivia */)) {
let commentTokenStart = scanner.getTokenOffset() + formatTextStart;
addEdit(string_intern_1.cachedSpaces[1], firstTokenEnd, commentTokenStart);
firstTokenEnd = scanner.getTokenOffset() + scanner.getTokenLength() + formatTextStart;
needsLineBreak = secondToken === 12 /* SyntaxKind.LineCommentTrivia */;
replaceContent = needsLineBreak ? newLinesAndIndent() : '';
secondToken = scanNext();
}
if (secondToken === 2 /* SyntaxKind.CloseBraceToken */) {
if (firstToken !== 1 /* SyntaxKind.OpenBraceToken */) {
indentLevel--;
}
;
if (options.keepLines && numberLineBreaks > 0 || !options.keepLines && firstToken !== 1 /* SyntaxKind.OpenBraceToken */) {
replaceContent = newLinesAndIndent();
}
else if (options.keepLines) {
replaceContent = string_intern_1.cachedSpaces[1];
}
}
else if (secondToken === 4 /* SyntaxKind.CloseBracketToken */) {
if (firstToken !== 3 /* SyntaxKind.OpenBracketToken */) {
indentLevel--;
}
;
if (options.keepLines && numberLineBreaks > 0 || !options.keepLines && firstToken !== 3 /* SyntaxKind.OpenBracketToken */) {
replaceContent = newLinesAndIndent();
}
else if (options.keepLines) {
replaceContent = string_intern_1.cachedSpaces[1];
}
}
else {
switch (firstToken) {
case 3 /* SyntaxKind.OpenBracketToken */:
case 1 /* SyntaxKind.OpenBraceToken */:
indentLevel++;
if (options.keepLines && numberLineBreaks > 0 || !options.keepLines) {
replaceContent = newLinesAndIndent();
}
else {
replaceContent = string_intern_1.cachedSpaces[1];
}
break;
case 5 /* SyntaxKind.CommaToken */:
if (options.keepLines && numberLineBreaks > 0 || !options.keepLines) {
replaceContent = newLinesAndIndent();
}
else {
replaceContent = string_intern_1.cachedSpaces[1];
}
break;
case 12 /* SyntaxKind.LineCommentTrivia */:
replaceContent = newLinesAndIndent();
break;
case 13 /* SyntaxKind.BlockCommentTrivia */:
if (numberLineBreaks > 0) {
replaceContent = newLinesAndIndent();
}
else if (!needsLineBreak) {
replaceContent = string_intern_1.cachedSpaces[1];
}
break;
case 6 /* SyntaxKind.ColonToken */:
if (options.keepLines && numberLineBreaks > 0) {
replaceContent = newLinesAndIndent();
}
else if (!needsLineBreak) {
replaceContent = string_intern_1.cachedSpaces[1];
}
break;
case 10 /* SyntaxKind.StringLiteral */:
if (options.keepLines && numberLineBreaks > 0) {
replaceContent = newLinesAndIndent();
}
else if (secondToken === 6 /* SyntaxKind.ColonToken */ && !needsLineBreak) {
replaceContent = '';
}
break;
case 7 /* SyntaxKind.NullKeyword */:
case 8 /* SyntaxKind.TrueKeyword */:
case 9 /* SyntaxKind.FalseKeyword */:
case 11 /* SyntaxKind.NumericLiteral */:
case 2 /* SyntaxKind.CloseBraceToken */:
case 4 /* SyntaxKind.CloseBracketToken */:
if (options.keepLines && numberLineBreaks > 0) {
replaceContent = newLinesAndIndent();
}
else {
if ((secondToken === 12 /* SyntaxKind.LineCommentTrivia */ || secondToken === 13 /* SyntaxKind.BlockCommentTrivia */) && !needsLineBreak) {
replaceContent = string_intern_1.cachedSpaces[1];
}
else if (secondToken !== 5 /* SyntaxKind.CommaToken */ && secondToken !== 17 /* SyntaxKind.EOF */) {
hasError = true;
}
}
break;
case 16 /* SyntaxKind.Unknown */:
hasError = true;
break;
}
if (numberLineBreaks > 0 && (secondToken === 12 /* SyntaxKind.LineCommentTrivia */ || secondToken === 13 /* SyntaxKind.BlockCommentTrivia */)) {
replaceContent = newLinesAndIndent();
}
}
if (secondToken === 17 /* SyntaxKind.EOF */) {
if (options.keepLines && numberLineBreaks > 0) {
replaceContent = newLinesAndIndent();
}
else {
replaceContent = options.insertFinalNewline ? eol : '';
}
}
const secondTokenStart = scanner.getTokenOffset() + formatTextStart;
addEdit(replaceContent, firstTokenEnd, secondTokenStart);
firstToken = secondToken;
}
return editOperations;
}
exports.format = format;
function repeat(s, count) {
let result = '';
for (let i = 0; i < count; i++) {
result += s;
}
return result;
}
function computeIndentLevel(content, options) {
let i = 0;
let nChars = 0;
const tabSize = options.tabSize || 4;
while (i < content.length) {
let ch = content.charAt(i);
if (ch === string_intern_1.cachedSpaces[1]) {
nChars++;
}
else if (ch === '\t') {
nChars += tabSize;
}
else {
break;
}
i++;
}
return Math.floor(nChars / tabSize);
}
function getEOL(options, text) {
for (let i = 0; i < text.length; i++) {
const ch = text.charAt(i);
if (ch === '\r') {
if (i + 1 < text.length && text.charAt(i + 1) === '\n') {
return '\r\n';
}
return '\r';
}
else if (ch === '\n') {
return '\n';
}
}
return (options && options.eol) || '\n';
}
function isEOL(text, offset) {
return '\r\n'.indexOf(text.charAt(offset)) !== -1;
}
exports.isEOL = isEOL;
});

View File

@@ -0,0 +1,682 @@
(function (factory) {
if (typeof module === "object" && typeof module.exports === "object") {
var v = factory(require, exports);
if (v !== undefined) module.exports = v;
}
else if (typeof define === "function" && define.amd) {
define(["require", "exports", "./scanner"], factory);
}
})(function (require, exports) {
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
Object.defineProperty(exports, "__esModule", { value: true });
exports.getNodeType = exports.stripComments = exports.visit = exports.findNodeAtOffset = exports.contains = exports.getNodeValue = exports.getNodePath = exports.findNodeAtLocation = exports.parseTree = exports.parse = exports.getLocation = void 0;
const scanner_1 = require("./scanner");
var ParseOptions;
(function (ParseOptions) {
ParseOptions.DEFAULT = {
allowTrailingComma: false
};
})(ParseOptions || (ParseOptions = {}));
/**
* For a given offset, evaluate the location in the JSON document. Each segment in the location path is either a property name or an array index.
*/
function getLocation(text, position) {
const segments = []; // strings or numbers
const earlyReturnException = new Object();
let previousNode = undefined;
const previousNodeInst = {
value: {},
offset: 0,
length: 0,
type: 'object',
parent: undefined
};
let isAtPropertyKey = false;
function setPreviousNode(value, offset, length, type) {
previousNodeInst.value = value;
previousNodeInst.offset = offset;
previousNodeInst.length = length;
previousNodeInst.type = type;
previousNodeInst.colonOffset = undefined;
previousNode = previousNodeInst;
}
try {
visit(text, {
onObjectBegin: (offset, length) => {
if (position <= offset) {
throw earlyReturnException;
}
previousNode = undefined;
isAtPropertyKey = position > offset;
segments.push(''); // push a placeholder (will be replaced)
},
onObjectProperty: (name, offset, length) => {
if (position < offset) {
throw earlyReturnException;
}
setPreviousNode(name, offset, length, 'property');
segments[segments.length - 1] = name;
if (position <= offset + length) {
throw earlyReturnException;
}
},
onObjectEnd: (offset, length) => {
if (position <= offset) {
throw earlyReturnException;
}
previousNode = undefined;
segments.pop();
},
onArrayBegin: (offset, length) => {
if (position <= offset) {
throw earlyReturnException;
}
previousNode = undefined;
segments.push(0);
},
onArrayEnd: (offset, length) => {
if (position <= offset) {
throw earlyReturnException;
}
previousNode = undefined;
segments.pop();
},
onLiteralValue: (value, offset, length) => {
if (position < offset) {
throw earlyReturnException;
}
setPreviousNode(value, offset, length, getNodeType(value));
if (position <= offset + length) {
throw earlyReturnException;
}
},
onSeparator: (sep, offset, length) => {
if (position <= offset) {
throw earlyReturnException;
}
if (sep === ':' && previousNode && previousNode.type === 'property') {
previousNode.colonOffset = offset;
isAtPropertyKey = false;
previousNode = undefined;
}
else if (sep === ',') {
const last = segments[segments.length - 1];
if (typeof last === 'number') {
segments[segments.length - 1] = last + 1;
}
else {
isAtPropertyKey = true;
segments[segments.length - 1] = '';
}
previousNode = undefined;
}
}
});
}
catch (e) {
if (e !== earlyReturnException) {
throw e;
}
}
return {
path: segments,
previousNode,
isAtPropertyKey,
matches: (pattern) => {
let k = 0;
for (let i = 0; k < pattern.length && i < segments.length; i++) {
if (pattern[k] === segments[i] || pattern[k] === '*') {
k++;
}
else if (pattern[k] !== '**') {
return false;
}
}
return k === pattern.length;
}
};
}
exports.getLocation = getLocation;
/**
* Parses the given text and returns the object the JSON content represents. On invalid input, the parser tries to be as fault tolerant as possible, but still return a result.
* Therefore always check the errors list to find out if the input was valid.
*/
function parse(text, errors = [], options = ParseOptions.DEFAULT) {
let currentProperty = null;
let currentParent = [];
const previousParents = [];
function onValue(value) {
if (Array.isArray(currentParent)) {
currentParent.push(value);
}
else if (currentProperty !== null) {
currentParent[currentProperty] = value;
}
}
const visitor = {
onObjectBegin: () => {
const object = {};
onValue(object);
previousParents.push(currentParent);
currentParent = object;
currentProperty = null;
},
onObjectProperty: (name) => {
currentProperty = name;
},
onObjectEnd: () => {
currentParent = previousParents.pop();
},
onArrayBegin: () => {
const array = [];
onValue(array);
previousParents.push(currentParent);
currentParent = array;
currentProperty = null;
},
onArrayEnd: () => {
currentParent = previousParents.pop();
},
onLiteralValue: onValue,
onError: (error, offset, length) => {
errors.push({ error, offset, length });
}
};
visit(text, visitor, options);
return currentParent[0];
}
exports.parse = parse;
/**
* Parses the given text and returns a tree representation the JSON content. On invalid input, the parser tries to be as fault tolerant as possible, but still return a result.
*/
function parseTree(text, errors = [], options = ParseOptions.DEFAULT) {
let currentParent = { type: 'array', offset: -1, length: -1, children: [], parent: undefined }; // artificial root
function ensurePropertyComplete(endOffset) {
if (currentParent.type === 'property') {
currentParent.length = endOffset - currentParent.offset;
currentParent = currentParent.parent;
}
}
function onValue(valueNode) {
currentParent.children.push(valueNode);
return valueNode;
}
const visitor = {
onObjectBegin: (offset) => {
currentParent = onValue({ type: 'object', offset, length: -1, parent: currentParent, children: [] });
},
onObjectProperty: (name, offset, length) => {
currentParent = onValue({ type: 'property', offset, length: -1, parent: currentParent, children: [] });
currentParent.children.push({ type: 'string', value: name, offset, length, parent: currentParent });
},
onObjectEnd: (offset, length) => {
ensurePropertyComplete(offset + length); // in case of a missing value for a property: make sure property is complete
currentParent.length = offset + length - currentParent.offset;
currentParent = currentParent.parent;
ensurePropertyComplete(offset + length);
},
onArrayBegin: (offset, length) => {
currentParent = onValue({ type: 'array', offset, length: -1, parent: currentParent, children: [] });
},
onArrayEnd: (offset, length) => {
currentParent.length = offset + length - currentParent.offset;
currentParent = currentParent.parent;
ensurePropertyComplete(offset + length);
},
onLiteralValue: (value, offset, length) => {
onValue({ type: getNodeType(value), offset, length, parent: currentParent, value });
ensurePropertyComplete(offset + length);
},
onSeparator: (sep, offset, length) => {
if (currentParent.type === 'property') {
if (sep === ':') {
currentParent.colonOffset = offset;
}
else if (sep === ',') {
ensurePropertyComplete(offset);
}
}
},
onError: (error, offset, length) => {
errors.push({ error, offset, length });
}
};
visit(text, visitor, options);
const result = currentParent.children[0];
if (result) {
delete result.parent;
}
return result;
}
exports.parseTree = parseTree;
/**
* Finds the node at the given path in a JSON DOM.
*/
function findNodeAtLocation(root, path) {
if (!root) {
return undefined;
}
let node = root;
for (let segment of path) {
if (typeof segment === 'string') {
if (node.type !== 'object' || !Array.isArray(node.children)) {
return undefined;
}
let found = false;
for (const propertyNode of node.children) {
if (Array.isArray(propertyNode.children) && propertyNode.children[0].value === segment && propertyNode.children.length === 2) {
node = propertyNode.children[1];
found = true;
break;
}
}
if (!found) {
return undefined;
}
}
else {
const index = segment;
if (node.type !== 'array' || index < 0 || !Array.isArray(node.children) || index >= node.children.length) {
return undefined;
}
node = node.children[index];
}
}
return node;
}
exports.findNodeAtLocation = findNodeAtLocation;
/**
* Gets the JSON path of the given JSON DOM node
*/
function getNodePath(node) {
if (!node.parent || !node.parent.children) {
return [];
}
const path = getNodePath(node.parent);
if (node.parent.type === 'property') {
const key = node.parent.children[0].value;
path.push(key);
}
else if (node.parent.type === 'array') {
const index = node.parent.children.indexOf(node);
if (index !== -1) {
path.push(index);
}
}
return path;
}
exports.getNodePath = getNodePath;
/**
* Evaluates the JavaScript object of the given JSON DOM node
*/
function getNodeValue(node) {
switch (node.type) {
case 'array':
return node.children.map(getNodeValue);
case 'object':
const obj = Object.create(null);
for (let prop of node.children) {
const valueNode = prop.children[1];
if (valueNode) {
obj[prop.children[0].value] = getNodeValue(valueNode);
}
}
return obj;
case 'null':
case 'string':
case 'number':
case 'boolean':
return node.value;
default:
return undefined;
}
}
exports.getNodeValue = getNodeValue;
function contains(node, offset, includeRightBound = false) {
return (offset >= node.offset && offset < (node.offset + node.length)) || includeRightBound && (offset === (node.offset + node.length));
}
exports.contains = contains;
/**
* Finds the most inner node at the given offset. If includeRightBound is set, also finds nodes that end at the given offset.
*/
function findNodeAtOffset(node, offset, includeRightBound = false) {
if (contains(node, offset, includeRightBound)) {
const children = node.children;
if (Array.isArray(children)) {
for (let i = 0; i < children.length && children[i].offset <= offset; i++) {
const item = findNodeAtOffset(children[i], offset, includeRightBound);
if (item) {
return item;
}
}
}
return node;
}
return undefined;
}
exports.findNodeAtOffset = findNodeAtOffset;
/**
* Parses the given text and invokes the visitor functions for each object, array and literal reached.
*/
function visit(text, visitor, options = ParseOptions.DEFAULT) {
const _scanner = (0, scanner_1.createScanner)(text, false);
// Important: Only pass copies of this to visitor functions to prevent accidental modification, and
// to not affect visitor functions which stored a reference to a previous JSONPath
const _jsonPath = [];
// Depth of onXXXBegin() callbacks suppressed. onXXXEnd() decrements this if it isn't 0 already.
// Callbacks are only called when this value is 0.
let suppressedCallbacks = 0;
function toNoArgVisit(visitFunction) {
return visitFunction ? () => suppressedCallbacks === 0 && visitFunction(_scanner.getTokenOffset(), _scanner.getTokenLength(), _scanner.getTokenStartLine(), _scanner.getTokenStartCharacter()) : () => true;
}
function toOneArgVisit(visitFunction) {
return visitFunction ? (arg) => suppressedCallbacks === 0 && visitFunction(arg, _scanner.getTokenOffset(), _scanner.getTokenLength(), _scanner.getTokenStartLine(), _scanner.getTokenStartCharacter()) : () => true;
}
function toOneArgVisitWithPath(visitFunction) {
return visitFunction ? (arg) => suppressedCallbacks === 0 && visitFunction(arg, _scanner.getTokenOffset(), _scanner.getTokenLength(), _scanner.getTokenStartLine(), _scanner.getTokenStartCharacter(), () => _jsonPath.slice()) : () => true;
}
function toBeginVisit(visitFunction) {
return visitFunction ?
() => {
if (suppressedCallbacks > 0) {
suppressedCallbacks++;
}
else {
let cbReturn = visitFunction(_scanner.getTokenOffset(), _scanner.getTokenLength(), _scanner.getTokenStartLine(), _scanner.getTokenStartCharacter(), () => _jsonPath.slice());
if (cbReturn === false) {
suppressedCallbacks = 1;
}
}
}
: () => true;
}
function toEndVisit(visitFunction) {
return visitFunction ?
() => {
if (suppressedCallbacks > 0) {
suppressedCallbacks--;
}
if (suppressedCallbacks === 0) {
visitFunction(_scanner.getTokenOffset(), _scanner.getTokenLength(), _scanner.getTokenStartLine(), _scanner.getTokenStartCharacter());
}
}
: () => true;
}
const onObjectBegin = toBeginVisit(visitor.onObjectBegin), onObjectProperty = toOneArgVisitWithPath(visitor.onObjectProperty), onObjectEnd = toEndVisit(visitor.onObjectEnd), onArrayBegin = toBeginVisit(visitor.onArrayBegin), onArrayEnd = toEndVisit(visitor.onArrayEnd), onLiteralValue = toOneArgVisitWithPath(visitor.onLiteralValue), onSeparator = toOneArgVisit(visitor.onSeparator), onComment = toNoArgVisit(visitor.onComment), onError = toOneArgVisit(visitor.onError);
const disallowComments = options && options.disallowComments;
const allowTrailingComma = options && options.allowTrailingComma;
function scanNext() {
while (true) {
const token = _scanner.scan();
switch (_scanner.getTokenError()) {
case 4 /* ScanError.InvalidUnicode */:
handleError(14 /* ParseErrorCode.InvalidUnicode */);
break;
case 5 /* ScanError.InvalidEscapeCharacter */:
handleError(15 /* ParseErrorCode.InvalidEscapeCharacter */);
break;
case 3 /* ScanError.UnexpectedEndOfNumber */:
handleError(13 /* ParseErrorCode.UnexpectedEndOfNumber */);
break;
case 1 /* ScanError.UnexpectedEndOfComment */:
if (!disallowComments) {
handleError(11 /* ParseErrorCode.UnexpectedEndOfComment */);
}
break;
case 2 /* ScanError.UnexpectedEndOfString */:
handleError(12 /* ParseErrorCode.UnexpectedEndOfString */);
break;
case 6 /* ScanError.InvalidCharacter */:
handleError(16 /* ParseErrorCode.InvalidCharacter */);
break;
}
switch (token) {
case 12 /* SyntaxKind.LineCommentTrivia */:
case 13 /* SyntaxKind.BlockCommentTrivia */:
if (disallowComments) {
handleError(10 /* ParseErrorCode.InvalidCommentToken */);
}
else {
onComment();
}
break;
case 16 /* SyntaxKind.Unknown */:
handleError(1 /* ParseErrorCode.InvalidSymbol */);
break;
case 15 /* SyntaxKind.Trivia */:
case 14 /* SyntaxKind.LineBreakTrivia */:
break;
default:
return token;
}
}
}
function handleError(error, skipUntilAfter = [], skipUntil = []) {
onError(error);
if (skipUntilAfter.length + skipUntil.length > 0) {
let token = _scanner.getToken();
while (token !== 17 /* SyntaxKind.EOF */) {
if (skipUntilAfter.indexOf(token) !== -1) {
scanNext();
break;
}
else if (skipUntil.indexOf(token) !== -1) {
break;
}
token = scanNext();
}
}
}
function parseString(isValue) {
const value = _scanner.getTokenValue();
if (isValue) {
onLiteralValue(value);
}
else {
onObjectProperty(value);
// add property name afterwards
_jsonPath.push(value);
}
scanNext();
return true;
}
function parseLiteral() {
switch (_scanner.getToken()) {
case 11 /* SyntaxKind.NumericLiteral */:
const tokenValue = _scanner.getTokenValue();
let value = Number(tokenValue);
if (isNaN(value)) {
handleError(2 /* ParseErrorCode.InvalidNumberFormat */);
value = 0;
}
onLiteralValue(value);
break;
case 7 /* SyntaxKind.NullKeyword */:
onLiteralValue(null);
break;
case 8 /* SyntaxKind.TrueKeyword */:
onLiteralValue(true);
break;
case 9 /* SyntaxKind.FalseKeyword */:
onLiteralValue(false);
break;
default:
return false;
}
scanNext();
return true;
}
function parseProperty() {
if (_scanner.getToken() !== 10 /* SyntaxKind.StringLiteral */) {
handleError(3 /* ParseErrorCode.PropertyNameExpected */, [], [2 /* SyntaxKind.CloseBraceToken */, 5 /* SyntaxKind.CommaToken */]);
return false;
}
parseString(false);
if (_scanner.getToken() === 6 /* SyntaxKind.ColonToken */) {
onSeparator(':');
scanNext(); // consume colon
if (!parseValue()) {
handleError(4 /* ParseErrorCode.ValueExpected */, [], [2 /* SyntaxKind.CloseBraceToken */, 5 /* SyntaxKind.CommaToken */]);
}
}
else {
handleError(5 /* ParseErrorCode.ColonExpected */, [], [2 /* SyntaxKind.CloseBraceToken */, 5 /* SyntaxKind.CommaToken */]);
}
_jsonPath.pop(); // remove processed property name
return true;
}
function parseObject() {
onObjectBegin();
scanNext(); // consume open brace
let needsComma = false;
while (_scanner.getToken() !== 2 /* SyntaxKind.CloseBraceToken */ && _scanner.getToken() !== 17 /* SyntaxKind.EOF */) {
if (_scanner.getToken() === 5 /* SyntaxKind.CommaToken */) {
if (!needsComma) {
handleError(4 /* ParseErrorCode.ValueExpected */, [], []);
}
onSeparator(',');
scanNext(); // consume comma
if (_scanner.getToken() === 2 /* SyntaxKind.CloseBraceToken */ && allowTrailingComma) {
break;
}
}
else if (needsComma) {
handleError(6 /* ParseErrorCode.CommaExpected */, [], []);
}
if (!parseProperty()) {
handleError(4 /* ParseErrorCode.ValueExpected */, [], [2 /* SyntaxKind.CloseBraceToken */, 5 /* SyntaxKind.CommaToken */]);
}
needsComma = true;
}
onObjectEnd();
if (_scanner.getToken() !== 2 /* SyntaxKind.CloseBraceToken */) {
handleError(7 /* ParseErrorCode.CloseBraceExpected */, [2 /* SyntaxKind.CloseBraceToken */], []);
}
else {
scanNext(); // consume close brace
}
return true;
}
function parseArray() {
onArrayBegin();
scanNext(); // consume open bracket
let isFirstElement = true;
let needsComma = false;
while (_scanner.getToken() !== 4 /* SyntaxKind.CloseBracketToken */ && _scanner.getToken() !== 17 /* SyntaxKind.EOF */) {
if (_scanner.getToken() === 5 /* SyntaxKind.CommaToken */) {
if (!needsComma) {
handleError(4 /* ParseErrorCode.ValueExpected */, [], []);
}
onSeparator(',');
scanNext(); // consume comma
if (_scanner.getToken() === 4 /* SyntaxKind.CloseBracketToken */ && allowTrailingComma) {
break;
}
}
else if (needsComma) {
handleError(6 /* ParseErrorCode.CommaExpected */, [], []);
}
if (isFirstElement) {
_jsonPath.push(0);
isFirstElement = false;
}
else {
_jsonPath[_jsonPath.length - 1]++;
}
if (!parseValue()) {
handleError(4 /* ParseErrorCode.ValueExpected */, [], [4 /* SyntaxKind.CloseBracketToken */, 5 /* SyntaxKind.CommaToken */]);
}
needsComma = true;
}
onArrayEnd();
if (!isFirstElement) {
_jsonPath.pop(); // remove array index
}
if (_scanner.getToken() !== 4 /* SyntaxKind.CloseBracketToken */) {
handleError(8 /* ParseErrorCode.CloseBracketExpected */, [4 /* SyntaxKind.CloseBracketToken */], []);
}
else {
scanNext(); // consume close bracket
}
return true;
}
function parseValue() {
switch (_scanner.getToken()) {
case 3 /* SyntaxKind.OpenBracketToken */:
return parseArray();
case 1 /* SyntaxKind.OpenBraceToken */:
return parseObject();
case 10 /* SyntaxKind.StringLiteral */:
return parseString(true);
default:
return parseLiteral();
}
}
scanNext();
if (_scanner.getToken() === 17 /* SyntaxKind.EOF */) {
if (options.allowEmptyContent) {
return true;
}
handleError(4 /* ParseErrorCode.ValueExpected */, [], []);
return false;
}
if (!parseValue()) {
handleError(4 /* ParseErrorCode.ValueExpected */, [], []);
return false;
}
if (_scanner.getToken() !== 17 /* SyntaxKind.EOF */) {
handleError(9 /* ParseErrorCode.EndOfFileExpected */, [], []);
}
return true;
}
exports.visit = visit;
/**
* Takes JSON with JavaScript-style comments and remove
* them. Optionally replaces every none-newline character
* of comments with a replaceCharacter
*/
function stripComments(text, replaceCh) {
let _scanner = (0, scanner_1.createScanner)(text), parts = [], kind, offset = 0, pos;
do {
pos = _scanner.getPosition();
kind = _scanner.scan();
switch (kind) {
case 12 /* SyntaxKind.LineCommentTrivia */:
case 13 /* SyntaxKind.BlockCommentTrivia */:
case 17 /* SyntaxKind.EOF */:
if (offset !== pos) {
parts.push(text.substring(offset, pos));
}
if (replaceCh !== undefined) {
parts.push(_scanner.getTokenValue().replace(/[^\r\n]/g, replaceCh));
}
offset = _scanner.getPosition();
break;
}
} while (kind !== 17 /* SyntaxKind.EOF */);
return parts.join('');
}
exports.stripComments = stripComments;
function getNodeType(value) {
switch (typeof value) {
case 'boolean': return 'boolean';
case 'number': return 'number';
case 'string': return 'string';
case 'object': {
if (!value) {
return 'null';
}
else if (Array.isArray(value)) {
return 'array';
}
return 'object';
}
default: return 'null';
}
}
exports.getNodeType = getNodeType;
});

View File

@@ -0,0 +1,456 @@
(function (factory) {
if (typeof module === "object" && typeof module.exports === "object") {
var v = factory(require, exports);
if (v !== undefined) module.exports = v;
}
else if (typeof define === "function" && define.amd) {
define(["require", "exports"], factory);
}
})(function (require, exports) {
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
Object.defineProperty(exports, "__esModule", { value: true });
exports.createScanner = void 0;
/**
* Creates a JSON scanner on the given text.
* If ignoreTrivia is set, whitespaces or comments are ignored.
*/
function createScanner(text, ignoreTrivia = false) {
const len = text.length;
let pos = 0, value = '', tokenOffset = 0, token = 16 /* SyntaxKind.Unknown */, lineNumber = 0, lineStartOffset = 0, tokenLineStartOffset = 0, prevTokenLineStartOffset = 0, scanError = 0 /* ScanError.None */;
function scanHexDigits(count, exact) {
let digits = 0;
let value = 0;
while (digits < count || !exact) {
let ch = text.charCodeAt(pos);
if (ch >= 48 /* CharacterCodes._0 */ && ch <= 57 /* CharacterCodes._9 */) {
value = value * 16 + ch - 48 /* CharacterCodes._0 */;
}
else if (ch >= 65 /* CharacterCodes.A */ && ch <= 70 /* CharacterCodes.F */) {
value = value * 16 + ch - 65 /* CharacterCodes.A */ + 10;
}
else if (ch >= 97 /* CharacterCodes.a */ && ch <= 102 /* CharacterCodes.f */) {
value = value * 16 + ch - 97 /* CharacterCodes.a */ + 10;
}
else {
break;
}
pos++;
digits++;
}
if (digits < count) {
value = -1;
}
return value;
}
function setPosition(newPosition) {
pos = newPosition;
value = '';
tokenOffset = 0;
token = 16 /* SyntaxKind.Unknown */;
scanError = 0 /* ScanError.None */;
}
function scanNumber() {
let start = pos;
if (text.charCodeAt(pos) === 48 /* CharacterCodes._0 */) {
pos++;
}
else {
pos++;
while (pos < text.length && isDigit(text.charCodeAt(pos))) {
pos++;
}
}
if (pos < text.length && text.charCodeAt(pos) === 46 /* CharacterCodes.dot */) {
pos++;
if (pos < text.length && isDigit(text.charCodeAt(pos))) {
pos++;
while (pos < text.length && isDigit(text.charCodeAt(pos))) {
pos++;
}
}
else {
scanError = 3 /* ScanError.UnexpectedEndOfNumber */;
return text.substring(start, pos);
}
}
let end = pos;
if (pos < text.length && (text.charCodeAt(pos) === 69 /* CharacterCodes.E */ || text.charCodeAt(pos) === 101 /* CharacterCodes.e */)) {
pos++;
if (pos < text.length && text.charCodeAt(pos) === 43 /* CharacterCodes.plus */ || text.charCodeAt(pos) === 45 /* CharacterCodes.minus */) {
pos++;
}
if (pos < text.length && isDigit(text.charCodeAt(pos))) {
pos++;
while (pos < text.length && isDigit(text.charCodeAt(pos))) {
pos++;
}
end = pos;
}
else {
scanError = 3 /* ScanError.UnexpectedEndOfNumber */;
}
}
return text.substring(start, end);
}
function scanString() {
let result = '', start = pos;
while (true) {
if (pos >= len) {
result += text.substring(start, pos);
scanError = 2 /* ScanError.UnexpectedEndOfString */;
break;
}
const ch = text.charCodeAt(pos);
if (ch === 34 /* CharacterCodes.doubleQuote */) {
result += text.substring(start, pos);
pos++;
break;
}
if (ch === 92 /* CharacterCodes.backslash */) {
result += text.substring(start, pos);
pos++;
if (pos >= len) {
scanError = 2 /* ScanError.UnexpectedEndOfString */;
break;
}
const ch2 = text.charCodeAt(pos++);
switch (ch2) {
case 34 /* CharacterCodes.doubleQuote */:
result += '\"';
break;
case 92 /* CharacterCodes.backslash */:
result += '\\';
break;
case 47 /* CharacterCodes.slash */:
result += '/';
break;
case 98 /* CharacterCodes.b */:
result += '\b';
break;
case 102 /* CharacterCodes.f */:
result += '\f';
break;
case 110 /* CharacterCodes.n */:
result += '\n';
break;
case 114 /* CharacterCodes.r */:
result += '\r';
break;
case 116 /* CharacterCodes.t */:
result += '\t';
break;
case 117 /* CharacterCodes.u */:
const ch3 = scanHexDigits(4, true);
if (ch3 >= 0) {
result += String.fromCharCode(ch3);
}
else {
scanError = 4 /* ScanError.InvalidUnicode */;
}
break;
default:
scanError = 5 /* ScanError.InvalidEscapeCharacter */;
}
start = pos;
continue;
}
if (ch >= 0 && ch <= 0x1f) {
if (isLineBreak(ch)) {
result += text.substring(start, pos);
scanError = 2 /* ScanError.UnexpectedEndOfString */;
break;
}
else {
scanError = 6 /* ScanError.InvalidCharacter */;
// mark as error but continue with string
}
}
pos++;
}
return result;
}
function scanNext() {
value = '';
scanError = 0 /* ScanError.None */;
tokenOffset = pos;
lineStartOffset = lineNumber;
prevTokenLineStartOffset = tokenLineStartOffset;
if (pos >= len) {
// at the end
tokenOffset = len;
return token = 17 /* SyntaxKind.EOF */;
}
let code = text.charCodeAt(pos);
// trivia: whitespace
if (isWhiteSpace(code)) {
do {
pos++;
value += String.fromCharCode(code);
code = text.charCodeAt(pos);
} while (isWhiteSpace(code));
return token = 15 /* SyntaxKind.Trivia */;
}
// trivia: newlines
if (isLineBreak(code)) {
pos++;
value += String.fromCharCode(code);
if (code === 13 /* CharacterCodes.carriageReturn */ && text.charCodeAt(pos) === 10 /* CharacterCodes.lineFeed */) {
pos++;
value += '\n';
}
lineNumber++;
tokenLineStartOffset = pos;
return token = 14 /* SyntaxKind.LineBreakTrivia */;
}
switch (code) {
// tokens: []{}:,
case 123 /* CharacterCodes.openBrace */:
pos++;
return token = 1 /* SyntaxKind.OpenBraceToken */;
case 125 /* CharacterCodes.closeBrace */:
pos++;
return token = 2 /* SyntaxKind.CloseBraceToken */;
case 91 /* CharacterCodes.openBracket */:
pos++;
return token = 3 /* SyntaxKind.OpenBracketToken */;
case 93 /* CharacterCodes.closeBracket */:
pos++;
return token = 4 /* SyntaxKind.CloseBracketToken */;
case 58 /* CharacterCodes.colon */:
pos++;
return token = 6 /* SyntaxKind.ColonToken */;
case 44 /* CharacterCodes.comma */:
pos++;
return token = 5 /* SyntaxKind.CommaToken */;
// strings
case 34 /* CharacterCodes.doubleQuote */:
pos++;
value = scanString();
return token = 10 /* SyntaxKind.StringLiteral */;
// comments
case 47 /* CharacterCodes.slash */:
const start = pos - 1;
// Single-line comment
if (text.charCodeAt(pos + 1) === 47 /* CharacterCodes.slash */) {
pos += 2;
while (pos < len) {
if (isLineBreak(text.charCodeAt(pos))) {
break;
}
pos++;
}
value = text.substring(start, pos);
return token = 12 /* SyntaxKind.LineCommentTrivia */;
}
// Multi-line comment
if (text.charCodeAt(pos + 1) === 42 /* CharacterCodes.asterisk */) {
pos += 2;
const safeLength = len - 1; // For lookahead.
let commentClosed = false;
while (pos < safeLength) {
const ch = text.charCodeAt(pos);
if (ch === 42 /* CharacterCodes.asterisk */ && text.charCodeAt(pos + 1) === 47 /* CharacterCodes.slash */) {
pos += 2;
commentClosed = true;
break;
}
pos++;
if (isLineBreak(ch)) {
if (ch === 13 /* CharacterCodes.carriageReturn */ && text.charCodeAt(pos) === 10 /* CharacterCodes.lineFeed */) {
pos++;
}
lineNumber++;
tokenLineStartOffset = pos;
}
}
if (!commentClosed) {
pos++;
scanError = 1 /* ScanError.UnexpectedEndOfComment */;
}
value = text.substring(start, pos);
return token = 13 /* SyntaxKind.BlockCommentTrivia */;
}
// just a single slash
value += String.fromCharCode(code);
pos++;
return token = 16 /* SyntaxKind.Unknown */;
// numbers
case 45 /* CharacterCodes.minus */:
value += String.fromCharCode(code);
pos++;
if (pos === len || !isDigit(text.charCodeAt(pos))) {
return token = 16 /* SyntaxKind.Unknown */;
}
// found a minus, followed by a number so
// we fall through to proceed with scanning
// numbers
case 48 /* CharacterCodes._0 */:
case 49 /* CharacterCodes._1 */:
case 50 /* CharacterCodes._2 */:
case 51 /* CharacterCodes._3 */:
case 52 /* CharacterCodes._4 */:
case 53 /* CharacterCodes._5 */:
case 54 /* CharacterCodes._6 */:
case 55 /* CharacterCodes._7 */:
case 56 /* CharacterCodes._8 */:
case 57 /* CharacterCodes._9 */:
value += scanNumber();
return token = 11 /* SyntaxKind.NumericLiteral */;
// literals and unknown symbols
default:
// is a literal? Read the full word.
while (pos < len && isUnknownContentCharacter(code)) {
pos++;
code = text.charCodeAt(pos);
}
if (tokenOffset !== pos) {
value = text.substring(tokenOffset, pos);
// keywords: true, false, null
switch (value) {
case 'true': return token = 8 /* SyntaxKind.TrueKeyword */;
case 'false': return token = 9 /* SyntaxKind.FalseKeyword */;
case 'null': return token = 7 /* SyntaxKind.NullKeyword */;
}
return token = 16 /* SyntaxKind.Unknown */;
}
// some
value += String.fromCharCode(code);
pos++;
return token = 16 /* SyntaxKind.Unknown */;
}
}
function isUnknownContentCharacter(code) {
if (isWhiteSpace(code) || isLineBreak(code)) {
return false;
}
switch (code) {
case 125 /* CharacterCodes.closeBrace */:
case 93 /* CharacterCodes.closeBracket */:
case 123 /* CharacterCodes.openBrace */:
case 91 /* CharacterCodes.openBracket */:
case 34 /* CharacterCodes.doubleQuote */:
case 58 /* CharacterCodes.colon */:
case 44 /* CharacterCodes.comma */:
case 47 /* CharacterCodes.slash */:
return false;
}
return true;
}
function scanNextNonTrivia() {
let result;
do {
result = scanNext();
} while (result >= 12 /* SyntaxKind.LineCommentTrivia */ && result <= 15 /* SyntaxKind.Trivia */);
return result;
}
return {
setPosition: setPosition,
getPosition: () => pos,
scan: ignoreTrivia ? scanNextNonTrivia : scanNext,
getToken: () => token,
getTokenValue: () => value,
getTokenOffset: () => tokenOffset,
getTokenLength: () => pos - tokenOffset,
getTokenStartLine: () => lineStartOffset,
getTokenStartCharacter: () => tokenOffset - prevTokenLineStartOffset,
getTokenError: () => scanError,
};
}
exports.createScanner = createScanner;
function isWhiteSpace(ch) {
return ch === 32 /* CharacterCodes.space */ || ch === 9 /* CharacterCodes.tab */;
}
function isLineBreak(ch) {
return ch === 10 /* CharacterCodes.lineFeed */ || ch === 13 /* CharacterCodes.carriageReturn */;
}
function isDigit(ch) {
return ch >= 48 /* CharacterCodes._0 */ && ch <= 57 /* CharacterCodes._9 */;
}
var CharacterCodes;
(function (CharacterCodes) {
CharacterCodes[CharacterCodes["lineFeed"] = 10] = "lineFeed";
CharacterCodes[CharacterCodes["carriageReturn"] = 13] = "carriageReturn";
CharacterCodes[CharacterCodes["space"] = 32] = "space";
CharacterCodes[CharacterCodes["_0"] = 48] = "_0";
CharacterCodes[CharacterCodes["_1"] = 49] = "_1";
CharacterCodes[CharacterCodes["_2"] = 50] = "_2";
CharacterCodes[CharacterCodes["_3"] = 51] = "_3";
CharacterCodes[CharacterCodes["_4"] = 52] = "_4";
CharacterCodes[CharacterCodes["_5"] = 53] = "_5";
CharacterCodes[CharacterCodes["_6"] = 54] = "_6";
CharacterCodes[CharacterCodes["_7"] = 55] = "_7";
CharacterCodes[CharacterCodes["_8"] = 56] = "_8";
CharacterCodes[CharacterCodes["_9"] = 57] = "_9";
CharacterCodes[CharacterCodes["a"] = 97] = "a";
CharacterCodes[CharacterCodes["b"] = 98] = "b";
CharacterCodes[CharacterCodes["c"] = 99] = "c";
CharacterCodes[CharacterCodes["d"] = 100] = "d";
CharacterCodes[CharacterCodes["e"] = 101] = "e";
CharacterCodes[CharacterCodes["f"] = 102] = "f";
CharacterCodes[CharacterCodes["g"] = 103] = "g";
CharacterCodes[CharacterCodes["h"] = 104] = "h";
CharacterCodes[CharacterCodes["i"] = 105] = "i";
CharacterCodes[CharacterCodes["j"] = 106] = "j";
CharacterCodes[CharacterCodes["k"] = 107] = "k";
CharacterCodes[CharacterCodes["l"] = 108] = "l";
CharacterCodes[CharacterCodes["m"] = 109] = "m";
CharacterCodes[CharacterCodes["n"] = 110] = "n";
CharacterCodes[CharacterCodes["o"] = 111] = "o";
CharacterCodes[CharacterCodes["p"] = 112] = "p";
CharacterCodes[CharacterCodes["q"] = 113] = "q";
CharacterCodes[CharacterCodes["r"] = 114] = "r";
CharacterCodes[CharacterCodes["s"] = 115] = "s";
CharacterCodes[CharacterCodes["t"] = 116] = "t";
CharacterCodes[CharacterCodes["u"] = 117] = "u";
CharacterCodes[CharacterCodes["v"] = 118] = "v";
CharacterCodes[CharacterCodes["w"] = 119] = "w";
CharacterCodes[CharacterCodes["x"] = 120] = "x";
CharacterCodes[CharacterCodes["y"] = 121] = "y";
CharacterCodes[CharacterCodes["z"] = 122] = "z";
CharacterCodes[CharacterCodes["A"] = 65] = "A";
CharacterCodes[CharacterCodes["B"] = 66] = "B";
CharacterCodes[CharacterCodes["C"] = 67] = "C";
CharacterCodes[CharacterCodes["D"] = 68] = "D";
CharacterCodes[CharacterCodes["E"] = 69] = "E";
CharacterCodes[CharacterCodes["F"] = 70] = "F";
CharacterCodes[CharacterCodes["G"] = 71] = "G";
CharacterCodes[CharacterCodes["H"] = 72] = "H";
CharacterCodes[CharacterCodes["I"] = 73] = "I";
CharacterCodes[CharacterCodes["J"] = 74] = "J";
CharacterCodes[CharacterCodes["K"] = 75] = "K";
CharacterCodes[CharacterCodes["L"] = 76] = "L";
CharacterCodes[CharacterCodes["M"] = 77] = "M";
CharacterCodes[CharacterCodes["N"] = 78] = "N";
CharacterCodes[CharacterCodes["O"] = 79] = "O";
CharacterCodes[CharacterCodes["P"] = 80] = "P";
CharacterCodes[CharacterCodes["Q"] = 81] = "Q";
CharacterCodes[CharacterCodes["R"] = 82] = "R";
CharacterCodes[CharacterCodes["S"] = 83] = "S";
CharacterCodes[CharacterCodes["T"] = 84] = "T";
CharacterCodes[CharacterCodes["U"] = 85] = "U";
CharacterCodes[CharacterCodes["V"] = 86] = "V";
CharacterCodes[CharacterCodes["W"] = 87] = "W";
CharacterCodes[CharacterCodes["X"] = 88] = "X";
CharacterCodes[CharacterCodes["Y"] = 89] = "Y";
CharacterCodes[CharacterCodes["Z"] = 90] = "Z";
CharacterCodes[CharacterCodes["asterisk"] = 42] = "asterisk";
CharacterCodes[CharacterCodes["backslash"] = 92] = "backslash";
CharacterCodes[CharacterCodes["closeBrace"] = 125] = "closeBrace";
CharacterCodes[CharacterCodes["closeBracket"] = 93] = "closeBracket";
CharacterCodes[CharacterCodes["colon"] = 58] = "colon";
CharacterCodes[CharacterCodes["comma"] = 44] = "comma";
CharacterCodes[CharacterCodes["dot"] = 46] = "dot";
CharacterCodes[CharacterCodes["doubleQuote"] = 34] = "doubleQuote";
CharacterCodes[CharacterCodes["minus"] = 45] = "minus";
CharacterCodes[CharacterCodes["openBrace"] = 123] = "openBrace";
CharacterCodes[CharacterCodes["openBracket"] = 91] = "openBracket";
CharacterCodes[CharacterCodes["plus"] = 43] = "plus";
CharacterCodes[CharacterCodes["slash"] = 47] = "slash";
CharacterCodes[CharacterCodes["formFeed"] = 12] = "formFeed";
CharacterCodes[CharacterCodes["tab"] = 9] = "tab";
})(CharacterCodes || (CharacterCodes = {}));
});

View File

@@ -0,0 +1,42 @@
(function (factory) {
if (typeof module === "object" && typeof module.exports === "object") {
var v = factory(require, exports);
if (v !== undefined) module.exports = v;
}
else if (typeof define === "function" && define.amd) {
define(["require", "exports"], factory);
}
})(function (require, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.supportedEols = exports.cachedBreakLinesWithSpaces = exports.cachedSpaces = void 0;
exports.cachedSpaces = new Array(20).fill(0).map((_, index) => {
return ' '.repeat(index);
});
const maxCachedValues = 200;
exports.cachedBreakLinesWithSpaces = {
' ': {
'\n': new Array(maxCachedValues).fill(0).map((_, index) => {
return '\n' + ' '.repeat(index);
}),
'\r': new Array(maxCachedValues).fill(0).map((_, index) => {
return '\r' + ' '.repeat(index);
}),
'\r\n': new Array(maxCachedValues).fill(0).map((_, index) => {
return '\r\n' + ' '.repeat(index);
}),
},
'\t': {
'\n': new Array(maxCachedValues).fill(0).map((_, index) => {
return '\n' + '\t'.repeat(index);
}),
'\r': new Array(maxCachedValues).fill(0).map((_, index) => {
return '\r' + '\t'.repeat(index);
}),
'\r\n': new Array(maxCachedValues).fill(0).map((_, index) => {
return '\r\n' + '\t'.repeat(index);
}),
}
};
exports.supportedEols = ['\n', '\r', '\r\n'];
});

View File

@@ -0,0 +1,351 @@
/**
* Creates a JSON scanner on the given text.
* If ignoreTrivia is set, whitespaces or comments are ignored.
*/
export declare const createScanner: (text: string, ignoreTrivia?: boolean) => JSONScanner;
export declare const enum ScanError {
None = 0,
UnexpectedEndOfComment = 1,
UnexpectedEndOfString = 2,
UnexpectedEndOfNumber = 3,
InvalidUnicode = 4,
InvalidEscapeCharacter = 5,
InvalidCharacter = 6
}
export declare const enum SyntaxKind {
OpenBraceToken = 1,
CloseBraceToken = 2,
OpenBracketToken = 3,
CloseBracketToken = 4,
CommaToken = 5,
ColonToken = 6,
NullKeyword = 7,
TrueKeyword = 8,
FalseKeyword = 9,
StringLiteral = 10,
NumericLiteral = 11,
LineCommentTrivia = 12,
BlockCommentTrivia = 13,
LineBreakTrivia = 14,
Trivia = 15,
Unknown = 16,
EOF = 17
}
/**
* The scanner object, representing a JSON scanner at a position in the input string.
*/
export interface JSONScanner {
/**
* Sets the scan position to a new offset. A call to 'scan' is needed to get the first token.
*/
setPosition(pos: number): void;
/**
* Read the next token. Returns the token code.
*/
scan(): SyntaxKind;
/**
* Returns the zero-based current scan position, which is after the last read token.
*/
getPosition(): number;
/**
* Returns the last read token.
*/
getToken(): SyntaxKind;
/**
* Returns the last read token value. The value for strings is the decoded string content. For numbers it's of type number, for boolean it's true or false.
*/
getTokenValue(): string;
/**
* The zero-based start offset of the last read token.
*/
getTokenOffset(): number;
/**
* The length of the last read token.
*/
getTokenLength(): number;
/**
* The zero-based start line number of the last read token.
*/
getTokenStartLine(): number;
/**
* The zero-based start character (column) of the last read token.
*/
getTokenStartCharacter(): number;
/**
* An error code of the last scan.
*/
getTokenError(): ScanError;
}
/**
* For a given offset, evaluate the location in the JSON document. Each segment in the location path is either a property name or an array index.
*/
export declare const getLocation: (text: string, position: number) => Location;
/**
* Parses the given text and returns the object the JSON content represents. On invalid input, the parser tries to be as fault tolerant as possible, but still return a result.
* Therefore, always check the errors list to find out if the input was valid.
*/
export declare const parse: (text: string, errors?: ParseError[], options?: ParseOptions) => any;
/**
* Parses the given text and returns a tree representation the JSON content. On invalid input, the parser tries to be as fault tolerant as possible, but still return a result.
*/
export declare const parseTree: (text: string, errors?: ParseError[], options?: ParseOptions) => Node | undefined;
/**
* Finds the node at the given path in a JSON DOM.
*/
export declare const findNodeAtLocation: (root: Node, path: JSONPath) => Node | undefined;
/**
* Finds the innermost node at the given offset. If includeRightBound is set, also finds nodes that end at the given offset.
*/
export declare const findNodeAtOffset: (root: Node, offset: number, includeRightBound?: boolean) => Node | undefined;
/**
* Gets the JSON path of the given JSON DOM node
*/
export declare const getNodePath: (node: Node) => JSONPath;
/**
* Evaluates the JavaScript object of the given JSON DOM node
*/
export declare const getNodeValue: (node: Node) => any;
/**
* Parses the given text and invokes the visitor functions for each object, array and literal reached.
*/
export declare const visit: (text: string, visitor: JSONVisitor, options?: ParseOptions) => any;
/**
* Takes JSON with JavaScript-style comments and remove
* them. Optionally replaces every none-newline character
* of comments with a replaceCharacter
*/
export declare const stripComments: (text: string, replaceCh?: string) => string;
export interface ParseError {
error: ParseErrorCode;
offset: number;
length: number;
}
export declare const enum ParseErrorCode {
InvalidSymbol = 1,
InvalidNumberFormat = 2,
PropertyNameExpected = 3,
ValueExpected = 4,
ColonExpected = 5,
CommaExpected = 6,
CloseBraceExpected = 7,
CloseBracketExpected = 8,
EndOfFileExpected = 9,
InvalidCommentToken = 10,
UnexpectedEndOfComment = 11,
UnexpectedEndOfString = 12,
UnexpectedEndOfNumber = 13,
InvalidUnicode = 14,
InvalidEscapeCharacter = 15,
InvalidCharacter = 16
}
export declare function printParseErrorCode(code: ParseErrorCode): "InvalidSymbol" | "InvalidNumberFormat" | "PropertyNameExpected" | "ValueExpected" | "ColonExpected" | "CommaExpected" | "CloseBraceExpected" | "CloseBracketExpected" | "EndOfFileExpected" | "InvalidCommentToken" | "UnexpectedEndOfComment" | "UnexpectedEndOfString" | "UnexpectedEndOfNumber" | "InvalidUnicode" | "InvalidEscapeCharacter" | "InvalidCharacter" | "<unknown ParseErrorCode>";
export type NodeType = 'object' | 'array' | 'property' | 'string' | 'number' | 'boolean' | 'null';
export interface Node {
readonly type: NodeType;
readonly value?: any;
readonly offset: number;
readonly length: number;
readonly colonOffset?: number;
readonly parent?: Node;
readonly children?: Node[];
}
/**
* A {@linkcode JSONPath} segment. Either a string representing an object property name
* or a number (starting at 0) for array indices.
*/
export type Segment = string | number;
export type JSONPath = Segment[];
export interface Location {
/**
* The previous property key or literal value (string, number, boolean or null) or undefined.
*/
previousNode?: Node;
/**
* The path describing the location in the JSON document. The path consists of a sequence of strings
* representing an object property or numbers for array indices.
*/
path: JSONPath;
/**
* Matches the locations path against a pattern consisting of strings (for properties) and numbers (for array indices).
* '*' will match a single segment of any property name or index.
* '**' will match a sequence of segments of any property name or index, or no segment.
*/
matches: (patterns: JSONPath) => boolean;
/**
* If set, the location's offset is at a property key.
*/
isAtPropertyKey: boolean;
}
export interface ParseOptions {
disallowComments?: boolean;
allowTrailingComma?: boolean;
allowEmptyContent?: boolean;
}
/**
* Visitor called by {@linkcode visit} when parsing JSON.
*
* The visitor functions have the following common parameters:
* - `offset`: Global offset within the JSON document, starting at 0
* - `startLine`: Line number, starting at 0
* - `startCharacter`: Start character (column) within the current line, starting at 0
*
* Additionally some functions have a `pathSupplier` parameter which can be used to obtain the
* current `JSONPath` within the document.
*/
export interface JSONVisitor {
/**
* Invoked when an open brace is encountered and an object is started. The offset and length represent the location of the open brace.
* When `false` is returned, the object properties will not be visited.
*/
onObjectBegin?: (offset: number, length: number, startLine: number, startCharacter: number, pathSupplier: () => JSONPath) => boolean | void;
/**
* Invoked when a property is encountered. The offset and length represent the location of the property name.
* The `JSONPath` created by the `pathSupplier` refers to the enclosing JSON object, it does not include the
* property name yet.
*/
onObjectProperty?: (property: string, offset: number, length: number, startLine: number, startCharacter: number, pathSupplier: () => JSONPath) => void;
/**
* Invoked when a closing brace is encountered and an object is completed. The offset and length represent the location of the closing brace.
*/
onObjectEnd?: (offset: number, length: number, startLine: number, startCharacter: number) => void;
/**
* Invoked when an open bracket is encountered. The offset and length represent the location of the open bracket.
* When `false` is returned, the array items will not be visited.
*/
onArrayBegin?: (offset: number, length: number, startLine: number, startCharacter: number, pathSupplier: () => JSONPath) => boolean | void;
/**
* Invoked when a closing bracket is encountered. The offset and length represent the location of the closing bracket.
*/
onArrayEnd?: (offset: number, length: number, startLine: number, startCharacter: number) => void;
/**
* Invoked when a literal value is encountered. The offset and length represent the location of the literal value.
*/
onLiteralValue?: (value: any, offset: number, length: number, startLine: number, startCharacter: number, pathSupplier: () => JSONPath) => void;
/**
* Invoked when a comma or colon separator is encountered. The offset and length represent the location of the separator.
*/
onSeparator?: (character: string, offset: number, length: number, startLine: number, startCharacter: number) => void;
/**
* When comments are allowed, invoked when a line or block comment is encountered. The offset and length represent the location of the comment.
*/
onComment?: (offset: number, length: number, startLine: number, startCharacter: number) => void;
/**
* Invoked on an error.
*/
onError?: (error: ParseErrorCode, offset: number, length: number, startLine: number, startCharacter: number) => void;
}
/**
* An edit result describes a textual edit operation. It is the result of a {@linkcode format} and {@linkcode modify} operation.
* It consist of one or more edits describing insertions, replacements or removals of text segments.
* * The offsets of the edits refer to the original state of the document.
* * No two edits change or remove the same range of text in the original document.
* * Multiple edits can have the same offset if they are multiple inserts, or an insert followed by a remove or replace.
* * The order in the array defines which edit is applied first.
* To apply an edit result use {@linkcode applyEdits}.
* In general multiple EditResults must not be concatenated because they might impact each other, producing incorrect or malformed JSON data.
*/
export type EditResult = Edit[];
/**
* Represents a text modification
*/
export interface Edit {
/**
* The start offset of the modification.
*/
offset: number;
/**
* The length of the modification. Must not be negative. Empty length represents an *insert*.
*/
length: number;
/**
* The new content. Empty content represents a *remove*.
*/
content: string;
}
/**
* A text range in the document
*/
export interface Range {
/**
* The start offset of the range.
*/
offset: number;
/**
* The length of the range. Must not be negative.
*/
length: number;
}
/**
* Options used by {@linkcode format} when computing the formatting edit operations
*/
export interface FormattingOptions {
/**
* If indentation is based on spaces (`insertSpaces` = true), the number of spaces that make an indent.
*/
tabSize?: number;
/**
* Is indentation based on spaces?
*/
insertSpaces?: boolean;
/**
* The default 'end of line' character. If not set, '\n' is used as default.
*/
eol?: string;
/**
* If set, will add a new line at the end of the document.
*/
insertFinalNewline?: boolean;
/**
* If true, will keep line positions as is in the formatting
*/
keepLines?: boolean;
}
/**
* Computes the edit operations needed to format a JSON document.
*
* @param documentText The input text
* @param range The range to format or `undefined` to format the full content
* @param options The formatting options
* @returns The edit operations describing the formatting changes to the original document following the format described in {@linkcode EditResult}.
* To apply the edit operations to the input, use {@linkcode applyEdits}.
*/
export declare function format(documentText: string, range: Range | undefined, options: FormattingOptions): EditResult;
/**
* Options used by {@linkcode modify} when computing the modification edit operations
*/
export interface ModificationOptions {
/**
* Formatting options. If undefined, the newly inserted code will be inserted unformatted.
*/
formattingOptions?: FormattingOptions;
/**
* Default false. If `JSONPath` refers to an index of an array and `isArrayInsertion` is `true`, then
* {@linkcode modify} will insert a new item at that location instead of overwriting its contents.
*/
isArrayInsertion?: boolean;
/**
* Optional function to define the insertion index given an existing list of properties.
*/
getInsertionIndex?: (properties: string[]) => number;
}
/**
* Computes the edit operations needed to modify a value in the JSON document.
*
* @param documentText The input text
* @param path The path of the value to change. The path represents either to the document root, a property or an array item.
* If the path points to an non-existing property or item, it will be created.
* @param value The new value for the specified property or item. If the value is undefined,
* the property or item will be removed.
* @param options Options
* @returns The edit operations describing the changes to the original document, following the format described in {@linkcode EditResult}.
* To apply the edit operations to the input, use {@linkcode applyEdits}.
*/
export declare function modify(text: string, path: JSONPath, value: any, options: ModificationOptions): EditResult;
/**
* Applies edits to an input string.
* @param text The input text
* @param edits Edit operations following the format described in {@linkcode EditResult}.
* @returns The text with the applied edits.
* @throws An error if the edit operations are not well-formed as described in {@linkcode EditResult}.
*/
export declare function applyEdits(text: string, edits: EditResult): string;

View File

@@ -0,0 +1,194 @@
(function (factory) {
if (typeof module === "object" && typeof module.exports === "object") {
var v = factory(require, exports);
if (v !== undefined) module.exports = v;
}
else if (typeof define === "function" && define.amd) {
define(["require", "exports", "./impl/format", "./impl/edit", "./impl/scanner", "./impl/parser"], factory);
}
})(function (require, exports) {
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
Object.defineProperty(exports, "__esModule", { value: true });
exports.applyEdits = exports.modify = exports.format = exports.printParseErrorCode = exports.ParseErrorCode = exports.stripComments = exports.visit = exports.getNodeValue = exports.getNodePath = exports.findNodeAtOffset = exports.findNodeAtLocation = exports.parseTree = exports.parse = exports.getLocation = exports.SyntaxKind = exports.ScanError = exports.createScanner = void 0;
const formatter = require("./impl/format");
const edit = require("./impl/edit");
const scanner = require("./impl/scanner");
const parser = require("./impl/parser");
/**
* Creates a JSON scanner on the given text.
* If ignoreTrivia is set, whitespaces or comments are ignored.
*/
exports.createScanner = scanner.createScanner;
var ScanError;
(function (ScanError) {
ScanError[ScanError["None"] = 0] = "None";
ScanError[ScanError["UnexpectedEndOfComment"] = 1] = "UnexpectedEndOfComment";
ScanError[ScanError["UnexpectedEndOfString"] = 2] = "UnexpectedEndOfString";
ScanError[ScanError["UnexpectedEndOfNumber"] = 3] = "UnexpectedEndOfNumber";
ScanError[ScanError["InvalidUnicode"] = 4] = "InvalidUnicode";
ScanError[ScanError["InvalidEscapeCharacter"] = 5] = "InvalidEscapeCharacter";
ScanError[ScanError["InvalidCharacter"] = 6] = "InvalidCharacter";
})(ScanError || (exports.ScanError = ScanError = {}));
var SyntaxKind;
(function (SyntaxKind) {
SyntaxKind[SyntaxKind["OpenBraceToken"] = 1] = "OpenBraceToken";
SyntaxKind[SyntaxKind["CloseBraceToken"] = 2] = "CloseBraceToken";
SyntaxKind[SyntaxKind["OpenBracketToken"] = 3] = "OpenBracketToken";
SyntaxKind[SyntaxKind["CloseBracketToken"] = 4] = "CloseBracketToken";
SyntaxKind[SyntaxKind["CommaToken"] = 5] = "CommaToken";
SyntaxKind[SyntaxKind["ColonToken"] = 6] = "ColonToken";
SyntaxKind[SyntaxKind["NullKeyword"] = 7] = "NullKeyword";
SyntaxKind[SyntaxKind["TrueKeyword"] = 8] = "TrueKeyword";
SyntaxKind[SyntaxKind["FalseKeyword"] = 9] = "FalseKeyword";
SyntaxKind[SyntaxKind["StringLiteral"] = 10] = "StringLiteral";
SyntaxKind[SyntaxKind["NumericLiteral"] = 11] = "NumericLiteral";
SyntaxKind[SyntaxKind["LineCommentTrivia"] = 12] = "LineCommentTrivia";
SyntaxKind[SyntaxKind["BlockCommentTrivia"] = 13] = "BlockCommentTrivia";
SyntaxKind[SyntaxKind["LineBreakTrivia"] = 14] = "LineBreakTrivia";
SyntaxKind[SyntaxKind["Trivia"] = 15] = "Trivia";
SyntaxKind[SyntaxKind["Unknown"] = 16] = "Unknown";
SyntaxKind[SyntaxKind["EOF"] = 17] = "EOF";
})(SyntaxKind || (exports.SyntaxKind = SyntaxKind = {}));
/**
* For a given offset, evaluate the location in the JSON document. Each segment in the location path is either a property name or an array index.
*/
exports.getLocation = parser.getLocation;
/**
* Parses the given text and returns the object the JSON content represents. On invalid input, the parser tries to be as fault tolerant as possible, but still return a result.
* Therefore, always check the errors list to find out if the input was valid.
*/
exports.parse = parser.parse;
/**
* Parses the given text and returns a tree representation the JSON content. On invalid input, the parser tries to be as fault tolerant as possible, but still return a result.
*/
exports.parseTree = parser.parseTree;
/**
* Finds the node at the given path in a JSON DOM.
*/
exports.findNodeAtLocation = parser.findNodeAtLocation;
/**
* Finds the innermost node at the given offset. If includeRightBound is set, also finds nodes that end at the given offset.
*/
exports.findNodeAtOffset = parser.findNodeAtOffset;
/**
* Gets the JSON path of the given JSON DOM node
*/
exports.getNodePath = parser.getNodePath;
/**
* Evaluates the JavaScript object of the given JSON DOM node
*/
exports.getNodeValue = parser.getNodeValue;
/**
* Parses the given text and invokes the visitor functions for each object, array and literal reached.
*/
exports.visit = parser.visit;
/**
* Takes JSON with JavaScript-style comments and remove
* them. Optionally replaces every none-newline character
* of comments with a replaceCharacter
*/
exports.stripComments = parser.stripComments;
var ParseErrorCode;
(function (ParseErrorCode) {
ParseErrorCode[ParseErrorCode["InvalidSymbol"] = 1] = "InvalidSymbol";
ParseErrorCode[ParseErrorCode["InvalidNumberFormat"] = 2] = "InvalidNumberFormat";
ParseErrorCode[ParseErrorCode["PropertyNameExpected"] = 3] = "PropertyNameExpected";
ParseErrorCode[ParseErrorCode["ValueExpected"] = 4] = "ValueExpected";
ParseErrorCode[ParseErrorCode["ColonExpected"] = 5] = "ColonExpected";
ParseErrorCode[ParseErrorCode["CommaExpected"] = 6] = "CommaExpected";
ParseErrorCode[ParseErrorCode["CloseBraceExpected"] = 7] = "CloseBraceExpected";
ParseErrorCode[ParseErrorCode["CloseBracketExpected"] = 8] = "CloseBracketExpected";
ParseErrorCode[ParseErrorCode["EndOfFileExpected"] = 9] = "EndOfFileExpected";
ParseErrorCode[ParseErrorCode["InvalidCommentToken"] = 10] = "InvalidCommentToken";
ParseErrorCode[ParseErrorCode["UnexpectedEndOfComment"] = 11] = "UnexpectedEndOfComment";
ParseErrorCode[ParseErrorCode["UnexpectedEndOfString"] = 12] = "UnexpectedEndOfString";
ParseErrorCode[ParseErrorCode["UnexpectedEndOfNumber"] = 13] = "UnexpectedEndOfNumber";
ParseErrorCode[ParseErrorCode["InvalidUnicode"] = 14] = "InvalidUnicode";
ParseErrorCode[ParseErrorCode["InvalidEscapeCharacter"] = 15] = "InvalidEscapeCharacter";
ParseErrorCode[ParseErrorCode["InvalidCharacter"] = 16] = "InvalidCharacter";
})(ParseErrorCode || (exports.ParseErrorCode = ParseErrorCode = {}));
function printParseErrorCode(code) {
switch (code) {
case 1 /* ParseErrorCode.InvalidSymbol */: return 'InvalidSymbol';
case 2 /* ParseErrorCode.InvalidNumberFormat */: return 'InvalidNumberFormat';
case 3 /* ParseErrorCode.PropertyNameExpected */: return 'PropertyNameExpected';
case 4 /* ParseErrorCode.ValueExpected */: return 'ValueExpected';
case 5 /* ParseErrorCode.ColonExpected */: return 'ColonExpected';
case 6 /* ParseErrorCode.CommaExpected */: return 'CommaExpected';
case 7 /* ParseErrorCode.CloseBraceExpected */: return 'CloseBraceExpected';
case 8 /* ParseErrorCode.CloseBracketExpected */: return 'CloseBracketExpected';
case 9 /* ParseErrorCode.EndOfFileExpected */: return 'EndOfFileExpected';
case 10 /* ParseErrorCode.InvalidCommentToken */: return 'InvalidCommentToken';
case 11 /* ParseErrorCode.UnexpectedEndOfComment */: return 'UnexpectedEndOfComment';
case 12 /* ParseErrorCode.UnexpectedEndOfString */: return 'UnexpectedEndOfString';
case 13 /* ParseErrorCode.UnexpectedEndOfNumber */: return 'UnexpectedEndOfNumber';
case 14 /* ParseErrorCode.InvalidUnicode */: return 'InvalidUnicode';
case 15 /* ParseErrorCode.InvalidEscapeCharacter */: return 'InvalidEscapeCharacter';
case 16 /* ParseErrorCode.InvalidCharacter */: return 'InvalidCharacter';
}
return '<unknown ParseErrorCode>';
}
exports.printParseErrorCode = printParseErrorCode;
/**
* Computes the edit operations needed to format a JSON document.
*
* @param documentText The input text
* @param range The range to format or `undefined` to format the full content
* @param options The formatting options
* @returns The edit operations describing the formatting changes to the original document following the format described in {@linkcode EditResult}.
* To apply the edit operations to the input, use {@linkcode applyEdits}.
*/
function format(documentText, range, options) {
return formatter.format(documentText, range, options);
}
exports.format = format;
/**
* Computes the edit operations needed to modify a value in the JSON document.
*
* @param documentText The input text
* @param path The path of the value to change. The path represents either to the document root, a property or an array item.
* If the path points to an non-existing property or item, it will be created.
* @param value The new value for the specified property or item. If the value is undefined,
* the property or item will be removed.
* @param options Options
* @returns The edit operations describing the changes to the original document, following the format described in {@linkcode EditResult}.
* To apply the edit operations to the input, use {@linkcode applyEdits}.
*/
function modify(text, path, value, options) {
return edit.setProperty(text, path, value, options);
}
exports.modify = modify;
/**
* Applies edits to an input string.
* @param text The input text
* @param edits Edit operations following the format described in {@linkcode EditResult}.
* @returns The text with the applied edits.
* @throws An error if the edit operations are not well-formed as described in {@linkcode EditResult}.
*/
function applyEdits(text, edits) {
let sortedEdits = edits.slice(0).sort((a, b) => {
const diff = a.offset - b.offset;
if (diff === 0) {
return a.length - b.length;
}
return diff;
});
let lastModifiedOffset = text.length;
for (let i = sortedEdits.length - 1; i >= 0; i--) {
let e = sortedEdits[i];
if (e.offset + e.length <= lastModifiedOffset) {
text = edit.applyEdit(text, e);
}
else {
throw new Error('Overlapping edit');
}
lastModifiedOffset = e.offset;
}
return text;
}
exports.applyEdits = applyEdits;
});

View File

@@ -0,0 +1,37 @@
{
"name": "jsonc-parser",
"version": "3.3.1",
"description": "Scanner and parser for JSON with comments.",
"main": "./lib/umd/main.js",
"typings": "./lib/umd/main.d.ts",
"module": "./lib/esm/main.js",
"author": "Microsoft Corporation",
"repository": {
"type": "git",
"url": "https://github.com/microsoft/node-jsonc-parser"
},
"license": "MIT",
"bugs": {
"url": "https://github.com/microsoft/node-jsonc-parser/issues"
},
"devDependencies": {
"@types/mocha": "^10.0.7",
"@types/node": "^18.x",
"@typescript-eslint/eslint-plugin": "^7.13.1",
"@typescript-eslint/parser": "^7.13.1",
"eslint": "^8.57.0",
"mocha": "^10.4.0",
"rimraf": "^5.0.7",
"typescript": "^5.4.2"
},
"scripts": {
"prepack": "npm run clean && npm run compile-esm && npm run test && npm run remove-sourcemap-refs",
"compile": "tsc -p ./src && npm run lint",
"compile-esm": "tsc -p ./src/tsconfig.esm.json",
"remove-sourcemap-refs": "node ./build/remove-sourcemap-refs.js",
"clean": "rimraf lib",
"watch": "tsc -w -p ./src",
"test": "npm run compile && mocha ./lib/umd/test",
"lint": "eslint src/**/*.ts"
}
}

55
node_modules/vscode-json-languageservice/package.json generated vendored Normal file
View File

@@ -0,0 +1,55 @@
{
"name": "vscode-json-languageservice",
"version": "4.1.8",
"description": "Language service for JSON",
"main": "./lib/umd/jsonLanguageService.js",
"typings": "./lib/umd/jsonLanguageService",
"module": "./lib/esm/jsonLanguageService.js",
"author": "Microsoft Corporation",
"repository": {
"type": "git",
"url": "https://github.com/Microsoft/vscode-json-languageservice"
},
"license": "MIT",
"bugs": {
"url": "https://github.com/Microsoft/vscode-json-languageservice"
},
"engines": {
"npm": ">=7.0.0"
},
"devDependencies": {
"@types/mocha": "^8.2.0",
"@types/node": "^10.12.21",
"@typescript-eslint/eslint-plugin": "^4.14.0",
"@typescript-eslint/parser": "^4.14.0",
"eslint": "^7.18.0",
"mocha": "^8.2.1",
"rimraf": "^3.0.2",
"typescript": "^4.1.3"
},
"dependencies": {
"jsonc-parser": "^3.0.0",
"vscode-languageserver-textdocument": "^1.0.1",
"vscode-languageserver-types": "^3.16.0",
"vscode-nls": "^5.0.0",
"vscode-uri": "^3.0.2"
},
"scripts": {
"prepublishOnly": "npm run clean && npm run compile-esm && npm run test && npm run remove-sourcemap-refs",
"postpublish": "node ./build/post-publish.js",
"compile": "tsc -p ./src",
"compile-esm": "tsc -p ./src/tsconfig.esm.json",
"clean": "rimraf lib",
"remove-sourcemap-refs": "node ./build/remove-sourcemap-refs.js",
"watch": "tsc -w -p ./src",
"pretest": "npm run compile",
"test": "mocha",
"posttest": "npm run lint",
"coverage": "npx nyc -r lcov npm run test",
"lint": "eslint src/**/*.ts",
"install-types-next": "npm install vscode-languageserver-types@next -f -S && npm install vscode-languageserver-textdocument@next -f -S",
"preversion": "npm test",
"postversion": "git push && git push --tags",
"sample": "npm run compile && node ./lib/umd/example/sample.js"
}
}