diff --git a/src/language/json/jsonMode.ts b/src/language/json/jsonMode.ts index 2133e84803..d1345b2dd1 100644 --- a/src/language/json/jsonMode.ts +++ b/src/language/json/jsonMode.ts @@ -10,6 +10,18 @@ import * as languageFeatures from '../common/lspLanguageFeatures'; import { createTokenizationSupport } from './tokenization'; import { Uri, IDisposable, languages, editor } from '../../fillers/monaco-editor-core'; +let worker: languageFeatures.WorkerAccessor; + +export function getWorker(): Promise<(...uris: Uri[]) => Promise> { + return new Promise((resolve, reject) => { + if (!worker) { + return reject('JSON not registered!'); + } + + resolve(worker); + }); +} + class JSONDiagnosticsAdapter extends languageFeatures.DiagnosticsAdapter { constructor( languageId: string, @@ -44,9 +56,7 @@ export function setupMode(defaults: LanguageServiceDefaults): IDisposable { const client = new WorkerManager(defaults); disposables.push(client); - const worker: languageFeatures.WorkerAccessor = ( - ...uris: Uri[] - ): Promise => { + worker = (...uris: Uri[]): Promise => { return client.getLanguageServiceWorker(...uris); }; diff --git a/src/language/json/jsonWorker.ts b/src/language/json/jsonWorker.ts index 02900d80c3..138f40b9e4 100644 --- a/src/language/json/jsonWorker.ts +++ b/src/language/json/jsonWorker.ts @@ -144,6 +144,22 @@ export class JSONWorker { let ranges = this._languageService.getSelectionRanges(document, positions, jsonDocument); return Promise.resolve(ranges); } + async parseJSONDocument(uri: string): Promise { + let document = this._getTextDocument(uri); + if (!document) { + return null; + } + let jsonDocument = this._languageService.parseJSONDocument(document); + return Promise.resolve(jsonDocument); + } + async getMatchingSchemas(uri: string): Promise { + let document = this._getTextDocument(uri); + if (!document) { + return []; + } + let jsonDocument = this._languageService.parseJSONDocument(document); + return Promise.resolve(this._languageService.getMatchingSchemas(document, jsonDocument)); + } private _getTextDocument(uri: string): jsonService.TextDocument | null { let models = this._ctx.getMirrorModels(); for (let model of models) { diff --git a/src/language/json/monaco.contribution.ts b/src/language/json/monaco.contribution.ts index 3f9544c614..ae44000f72 100644 --- a/src/language/json/monaco.contribution.ts +++ b/src/language/json/monaco.contribution.ts @@ -4,10 +4,147 @@ *--------------------------------------------------------------------------------------------*/ import * as mode from './jsonMode'; -import { Emitter, IEvent, languages } from '../../fillers/monaco-editor-core'; +import { Emitter, IEvent, languages, Uri } from 'monaco-editor-core'; + +// ---- JSON service types ---- +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; +} -// --- JSON configuration and defaults --------- +export type ASTNode = + | ObjectASTNode + | PropertyASTNode + | ArrayASTNode + | StringASTNode + | NumberASTNode + | BooleanASTNode + | NullASTNode; + +export type JSONDocument = { + root: ASTNode | undefined; + getNodeFromOffset(offset: number, includeRightBound?: boolean): ASTNode | undefined; +}; + +export type JSONSchemaRef = JSONSchema | boolean; +export interface JSONSchemaMap { + [name: string]: JSONSchemaRef; +} + +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 MatchingSchema { + node: ASTNode; + schema: JSONSchema; +} + +// --- JSON configuration and defaults --------- export interface DiagnosticsOptions { /** * If set, the validator will be enabled and perform syntax and schema based validation, @@ -197,8 +334,16 @@ export const jsonDefaults: LanguageServiceDefaults = new LanguageServiceDefaults modeConfigurationDefault ); +export interface IJSONWorker { + parseJSONDocument(uri: string): Promise; + getMatchingSchemas(uri: string): Promise; +} + +export const getWorker = (): Promise<(...uris: Uri[]) => Promise> => + getMode().then((mode) => mode.getWorker()); + // export to the global based API -(languages).json = { jsonDefaults }; +(languages).json = { jsonDefaults, getWorker }; // --- Registration to monaco editor ---