Skip to content

Commit

Permalink
Merge 93aab43 into 65a22fe
Browse files Browse the repository at this point in the history
  • Loading branch information
JPinkney committed Jan 20, 2020
2 parents 65a22fe + 93aab43 commit ee9e218
Show file tree
Hide file tree
Showing 6 changed files with 298 additions and 12 deletions.
107 changes: 107 additions & 0 deletions src/languageservice/services/yamlSchemaService.ts
Expand Up @@ -16,6 +16,27 @@ const localize = nls.loadMessageBundle();

export declare type CustomSchemaProvider = (uri: string) => Thenable<string>;

export enum MODIFICATION_ACTIONS {
'delete',
'add'
}

export interface SchemaAdditions {
schema: string,
action: MODIFICATION_ACTIONS.add,
path: string,
key: string,
// tslint:disable-next-line: no-any
content: any
}

export interface SchemaDeletions {
schema: string,
action: MODIFICATION_ACTIONS.delete,
path: string,
key: string
}

export class FilePatternAssociation {

private schemas: string[];
Expand Down Expand Up @@ -45,6 +66,9 @@ export class FilePatternAssociation {
}

export class YAMLSchemaService extends JSONSchemaService {
// To allow to use schemasById from super.
// tslint:disable-next-line: no-any
[x: string]: any;

private customSchemaProvider: CustomSchemaProvider | undefined;
private filePatternAssociations: FilePatternAssociation[];
Expand Down Expand Up @@ -236,6 +260,89 @@ export class YAMLSchemaService extends JSONSchemaService {
}
}

/**
* Save a schema with schema ID and schema content.
* Overrides previous schemas set for that schema ID.
*/
public async saveSchema(schemaId: string, schemaContent: JSONSchema): Promise<void> {
const id = this.normalizeId(schemaId);
this.getOrAddSchemaHandle(id, schemaContent);
return Promise.resolve(undefined);
}

/**
* Delete a schema with schema ID.
*/
public async deleteSchema(schemaId: string): Promise<void> {
const id = this.normalizeId(schemaId);
if ( this.schemasById[id] ) {
delete this.schemasById[id];
}
return Promise.resolve(undefined);
}

/**
* Add content to a specified schema at a specified path
*/
public async addContent(additions: SchemaAdditions) {
const schema = await this.getResolvedSchema(additions.schema);
if (schema) {
const resolvedSchemaLocation = this.resolveJSONSchemaToSection(schema.schema, additions.path);

if (typeof resolvedSchemaLocation === 'object') {
resolvedSchemaLocation[additions.key] = additions.content;
}
await this.saveSchema(additions.schema, schema.schema);
}
}

/**
* Delete content in a specified schema at a specified path
*/
public async deleteContent(deletions: SchemaDeletions) {
const schema = await this.getResolvedSchema(deletions.schema);
if (schema) {
const resolvedSchemaLocation = this.resolveJSONSchemaToSection(schema.schema, deletions.path);

if (typeof resolvedSchemaLocation === 'object') {
delete resolvedSchemaLocation[deletions.key];
}
await this.saveSchema(deletions.schema, schema.schema);
}
}

/**
* Take a JSON Schema and the path that you would like to get to
* @returns the JSON Schema resolved at that specific path
*/
private resolveJSONSchemaToSection(schema: JSONSchema, paths: string): JSONSchema {
const splitPathway = paths.split('/');
let resolvedSchemaLocation = schema;
for (const path of splitPathway) {
if (path === '') {
continue;
}
this.resolveNext(resolvedSchemaLocation, path);
resolvedSchemaLocation = resolvedSchemaLocation[path];
}
return resolvedSchemaLocation;
}

/**
* Resolve the next Object if they have compatible types
* @param object a location in the JSON Schema
* @param token the next token that you want to search for
*/
// tslint:disable-next-line: no-any
private resolveNext(object: any, token: any) {
// tslint:disable-next-line: no-any
if (Array.isArray(object) && isNaN(token)) {
throw new Error('Expected a number after the array object');
} else if (typeof object === 'object' && typeof token !== 'string') {
throw new Error('Expected a string after the object');
}
}

/**
* Everything below here is needed because we're importing from vscode-json-languageservice umd and we need
* to provide a wrapper around the javascript methods we are calling since they have no type
Expand Down
2 changes: 2 additions & 0 deletions src/languageservice/utils/schemaUrls.ts
@@ -0,0 +1,2 @@
export const KUBERNETES_SCHEMA_URL = 'https://raw.githubusercontent.com/instrumenta/kubernetes-json-schema/master/v1.14.0-standalone-strict/all.json';
export const JSON_SCHEMASTORE_URL = 'http://schemastore.org/api/json/catalog.json';
14 changes: 11 additions & 3 deletions src/languageservice/yamlLanguageService.ts
Expand Up @@ -4,15 +4,15 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { YAMLSchemaService, CustomSchemaProvider } from './services/yamlSchemaService';
import { YAMLSchemaService, CustomSchemaProvider, SchemaAdditions, SchemaDeletions } from './services/yamlSchemaService';
import { TextDocument, Position, CompletionList, Diagnostic, Hover, SymbolInformation, DocumentSymbol, CompletionItem, TextEdit } from 'vscode-languageserver-types';
import { JSONSchema } from './jsonSchema';
import { YAMLDocumentSymbols } from './services/documentSymbols';
import { YAMLCompletion } from './services/yamlCompletion';
import { YAMLHover } from './services/yamlHover';
import { YAMLValidation } from './services/yamlValidation';
import { YAMLFormatter } from './services/yamlFormatter';
import { LanguageService as JSONLanguageService, getLanguageService as getJSONLanguageService, JSONWorkerContribution } from 'vscode-json-languageservice';
import { getLanguageService as getJSONLanguageService, JSONWorkerContribution } from 'vscode-json-languageservice';

export interface LanguageSettings {
validate?: boolean; //Setting for whether we want to validate the schema
Expand Down Expand Up @@ -118,6 +118,10 @@ export interface LanguageService {
doResolve(completionItem): Thenable<CompletionItem>;
resetSchema(uri: string): boolean;
doFormat(document: TextDocument, options: CustomFormatterOptions): TextEdit[];
addSchema(schemaID: string, schema: JSONSchema): void;
deleteSchema(schemaID: string): void;
modifySchemaContent(schemaAdditions: SchemaAdditions): void;
deleteSchemaContent(schemaDeletions: SchemaDeletions): void;
}

export function getLanguageService(schemaRequestService: SchemaRequestService,
Expand Down Expand Up @@ -157,6 +161,10 @@ export function getLanguageService(schemaRequestService: SchemaRequestService,
findDocumentSymbols: yamlDocumentSymbols.findDocumentSymbols.bind(yamlDocumentSymbols),
findDocumentSymbols2: yamlDocumentSymbols.findHierarchicalDocumentSymbols.bind(yamlDocumentSymbols),
resetSchema: (uri: string) => schemaService.onResourceChange(uri),
doFormat: formatter.format.bind(formatter)
doFormat: formatter.format.bind(formatter),
addSchema: (schemaID: string, schema: JSONSchema) => schemaService.saveSchema(schemaID, schema),
deleteSchema: (schemaID: string) => schemaService.deleteSchema(schemaID),
modifySchemaContent: (schemaAdditions: SchemaAdditions) => schemaService.addContent(schemaAdditions),
deleteSchemaContent: (schemaDeletions: SchemaDeletions) => schemaService.deleteContent(schemaDeletions)
};
}
5 changes: 5 additions & 0 deletions src/requestTypes.ts
@@ -1,4 +1,5 @@
import { NotificationType, RequestType } from 'vscode-languageserver';
import { SchemaAdditions, SchemaDeletions } from './languageservice/services/yamlSchemaService';

export namespace SchemaAssociationNotification {
export const type: NotificationType<{ }, { }> = new NotificationType('json/schemaAssociations');
Expand All @@ -23,3 +24,7 @@ export namespace CustomSchemaRequest {
export namespace ColorSymbolRequest {
export const type: RequestType<{ }, { }, { }, { }> = new RequestType('json/colorSymbols');
}

export namespace SchemaModificationNotification {
export const type: RequestType<SchemaAdditions | SchemaDeletions, void, { }, { }> = new RequestType('json/schema/modify');
}
22 changes: 13 additions & 9 deletions src/server.ts
Expand Up @@ -16,22 +16,16 @@ import * as URL from 'url';
import { removeDuplicatesObj } from './languageservice/utils/arrUtils';
import { getLanguageService as getCustomLanguageService, LanguageSettings, CustomFormatterOptions, WorkspaceContextService } from './languageservice/yamlLanguageService';
import * as nls from 'vscode-nls';
import { CustomSchemaProvider, FilePatternAssociation } from './languageservice/services/yamlSchemaService';
import { CustomSchemaProvider, FilePatternAssociation, SchemaDeletions, SchemaAdditions, MODIFICATION_ACTIONS } from './languageservice/services/yamlSchemaService';
import { JSONSchema } from './languageservice/jsonSchema';
import { SchemaAssociationNotification, DynamicCustomSchemaRequestRegistration, CustomSchemaRequest } from './requestTypes';
import { SchemaAssociationNotification, DynamicCustomSchemaRequestRegistration, CustomSchemaRequest, SchemaModificationNotification } from './requestTypes';
import { schemaRequestHandler } from './languageservice/services/schemaRequestHandler';
import { isRelativePath, relativeToAbsolutePath } from './languageservice/utils/paths';
import { URI } from 'vscode-uri';

import { KUBERNETES_SCHEMA_URL, JSON_SCHEMASTORE_URL } from './languageservice/utils/schemaUrls';
// tslint:disable-next-line: no-any
nls.config(process.env['VSCODE_NLS_CONFIG'] as any);

/****************
* Constants
****************/
const KUBERNETES_SCHEMA_URL = 'https://raw.githubusercontent.com/instrumenta/kubernetes-json-schema/master/v1.17.0-standalone-strict/all.json';
const JSON_SCHEMASTORE_URL = 'http://schemastore.org/api/json/catalog.json';

/**************************
* Generic helper functions
**************************/
Expand Down Expand Up @@ -237,6 +231,7 @@ function updateConfiguration() {
* @param languageSettings current server settings
*/
function configureSchemas(uri: string, fileMatch: string[], schema: any, languageSettings: LanguageSettings) {

uri = checkSchemaURI(uri);

if (schema === null) {
Expand Down Expand Up @@ -569,5 +564,14 @@ connection.onDocumentFormatting(formatParams => {
return customLanguageService.doFormat(document, customFormatterSettings);
});

connection.onRequest(SchemaModificationNotification.type, (modifications: SchemaAdditions | SchemaDeletions) => {
if (modifications.action === MODIFICATION_ACTIONS.add) {
customLanguageService.modifySchemaContent(modifications);
} else if (modifications.action === MODIFICATION_ACTIONS.delete) {
customLanguageService.deleteSchemaContent(modifications);
}
return Promise.resolve();
});

// Start listening for any messages from the client
connection.listen();

0 comments on commit ee9e218

Please sign in to comment.