Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,14 @@ yaml.schemas: {

`yaml.schemas` allows you to specify json schemas that you want to validate against the yaml that you write. Kubernetes is an optional field. It does not require a url as the language server will provide that. You just need the keyword kubernetes and a glob pattern.

### Using `$schema` key to specify schema

Like [VSCode](https://code.visualstudio.com/Docs/languages/json#_mapping-in-the-json), specifying schema via `$schema` key at root is supported.

```
$schema: <urlToTheSchema>
```

### Using inlined schema

It is possible to specify a yaml schema using a modeline.
Expand Down
30 changes: 22 additions & 8 deletions src/languageservice/services/yamlSchemaService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { URI } from 'vscode-uri';
import * as nls from 'vscode-nls';
import { convertSimple2RegExpPattern } from '../utils/strings';
import { SingleYAMLDocument } from '../parser/yamlParser07';
import { ObjectASTNode, StringASTNode } from '../jsonASTTypes';
import { JSONDocument } from '../parser/jsonParser07';
import * as yaml from 'js-yaml';

Expand Down Expand Up @@ -268,10 +269,10 @@ export class YAMLSchemaService extends JSONSchemaService {
const seen: { [schemaId: string]: boolean } = Object.create(null);
const schemas: string[] = [];

const schemaFromModeline = this.getSchemaFromModeline(doc);
if (schemaFromModeline !== undefined) {
schemas.push(schemaFromModeline);
seen[schemaFromModeline] = true;
const schemaFromFileContent = this.getSchemaFromFileContent(doc);
if (schemaFromFileContent !== undefined) {
schemas.push(schemaFromFileContent);
seen[schemaFromFileContent] = true;
}

for (const entry of this.filePatternAssociations) {
Expand Down Expand Up @@ -364,12 +365,25 @@ export class YAMLSchemaService extends JSONSchemaService {
}

/**
* Retrieve schema if declared as modeline.
* Retrieve schema if declared in file content.
* Public for testing purpose, not part of the API.
* @param doc
*/
public getSchemaFromModeline(doc: SingleYAMLDocument | JSONDocument): string {
public getSchemaFromFileContent(doc: SingleYAMLDocument | JSONDocument): string {
let schema = undefined;
if (doc instanceof SingleYAMLDocument) {
const properties = (doc.root as ObjectASTNode)?.properties;
if (Array.isArray(properties)) {
for (const prop of properties) {
if (prop.keyNode.value == '$schema') {
schema = (prop.valueNode as StringASTNode).value;
}
}
}
if (schema !== undefined) {
return schema;
}

const yamlLanguageServerModeline = doc.lineComments.find((lineComment) => {
const matchModeline = lineComment.match(/^#\s+yaml-language-server\s*:/g);
return matchModeline !== null && matchModeline.length === 1;
Expand All @@ -382,11 +396,11 @@ export class YAMLSchemaService extends JSONSchemaService {
'Several $schema attributes have been found on the yaml-language-server modeline. The first one will be picked.'
);
}
return schemaMatchs[0].substring('$schema='.length);
schema = schemaMatchs[0].substring('$schema='.length);
}
}
}
return undefined;
return schema;
}

private async resolveCustomSchema(schemaUri, doc): ResolvedSchema {
Expand Down
21 changes: 16 additions & 5 deletions test/schema.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -538,7 +538,7 @@ suite('JSON Schema', () => {
assert.equal(hello_world_schema, null);
});

describe('Test getSchemaFromModeline', function () {
describe('Test getSchemaFromFileContent', function () {
test('simple case', async () => {
checkReturnSchemaUrl('# yaml-language-server: $schema=expectedUrl', 'expectedUrl');
});
Expand Down Expand Up @@ -586,11 +586,22 @@ suite('JSON Schema', () => {
checkReturnSchemaUrl('# yaml-language-server: $notschema=url1', undefined);
});

function checkReturnSchemaUrl(modeline: string, expectedResult: string): void {
test('schema key', async () => {
checkReturnSchemaUrl('$schema: expectedUrl', 'expectedUrl');
});

test('schema key and simple case', async () => {
checkReturnSchemaUrl('# yaml-language-server: $schema=expectedUrl1\n$schema: expectedUrl2', 'expectedUrl2');
});

test('schema key with wrong type', async () => {
checkReturnSchemaUrl('# yaml-language-server: $schema=expectedUrl1\n$schema: [expectedUrl2,expectedUrl3]', 'expectedUrl1');
});

function checkReturnSchemaUrl(fileContent: string, expectedResult: string): void {
const service = new SchemaService.YAMLSchemaService(schemaRequestServiceForURL, workspaceContext);
const yamlDoc = new parser.SingleYAMLDocument([]);
yamlDoc.lineComments = [modeline];
assert.equal(service.getSchemaFromModeline(yamlDoc), expectedResult);
const yamlDoc = parser.parse(fileContent).documents[0];
assert.equal(service.getSchemaFromFileContent(yamlDoc), expectedResult);
}
});
});