diff --git a/package.json b/package.json index 9b2478e93d..5efeae1b61 100644 --- a/package.json +++ b/package.json @@ -4,9 +4,6 @@ "description": " Completion for entity-id's in Home Assistant Configurations", "version": "0.3.7", "preview": true, - "extensionDependencies": [ - "redhat.vscode-yaml" - ], "engines": { "vscode": "^1.32.0" }, @@ -77,10 +74,26 @@ "path": "./snippets/homeassistant_sensor.json" } ], - "yamlValidation": [ + "languages": [ { - "fileMatch": "ui-lovelace.yaml", - "url": "./src/lovelace-schema/ui-lovelace.json" + "id": "home-assistant", + "configuration": "./yaml-language/language-configuration.json", + "filenamePatterns": [ + "ui-lovelace.yml", + "ui-lovelace.yaml", + "configuration.yml", + "configuration.yaml" + ], + "aliases": [ + "Home Assistant" + ] + } + ], + "grammars": [ + { + "language": "yaml", + "scopeName": "source.yaml", + "path": "./yaml-language/yaml.tmLanguage.json" } ] }, diff --git a/src/extension.ts b/src/extension.ts index 78454506b4..1d646cc60f 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -7,8 +7,8 @@ import { Config } from './configuration'; import { ServiceCompletionProvider } from './service-completion-provider'; const documentSelector = [ - { language: 'yaml', scheme: 'file' }, - { language: 'yaml', scheme: 'untitled' } + { language: 'home-assistant', scheme: 'file' }, + { language: 'home-assistant', scheme: 'untitled' } ]; export function activate(context: vscode.ExtensionContext) { @@ -56,9 +56,10 @@ export function activate(context: vscode.ExtensionContext) { }; var client = new LanguageClient('home-assistant', 'Home Assistant Language Server', serverOptions, clientOptions); - client.onReady().then(() =>{ - client.onRequest('ha/openTextDocument', vscode.workspace.openTextDocument); - }) + + // is this really needed? + vscode.languages.setLanguageConfiguration('home-assistant', { wordPattern: /("(?:[^\\\"]*(?:\\.)?)*"?)|[^\s{}\[\],:]+/ }); + context.subscriptions.push(client.start()); } diff --git a/src/server/server.ts b/src/server/server.ts index 849edeb4b4..5ce074cfac 100644 --- a/src/server/server.ts +++ b/src/server/server.ts @@ -1,81 +1,119 @@ -import { createConnection, TextDocuments, ProposedFeatures, TextDocumentSyncKind, RequestType} from 'vscode-languageserver'; -import { getLanguageService } from './yamlLanguageService'; -import * as path from 'path'; -import { parse as parseYAML } from 'yaml-language-server/out/server/src/languageservice/parser/yamlParser'; -// import * as vscode from 'vscode'; -import { NestedYamlParser, VsCodeFileAccessor } from './yamlDiscovery'; +import * as path from "path"; +import { parse as parseYAML } from "yaml-language-server/out/server/src/languageservice/parser/yamlParser"; +import { YAMLValidation } from "yaml-language-server/out/server/src/languageservice/services/yamlValidation"; +import { JSONSchemaService } from "yaml-language-server/out/server/src/languageservice/services/jsonSchemaService"; +import { + createConnection, + TextDocuments, + ProposedFeatures +} from "vscode-languageserver"; + +import { + NestedYamlParser, + VsCodeFileAccessor, + SchemaServiceForIncludes, + Includetype +} from "./yamlDiscovery"; let connection = createConnection(ProposedFeatures.all); let documents = new TextDocuments(); let workspaceFolder: string | null; +let parser: NestedYamlParser | null; +let schemaServiceForIncludes: SchemaServiceForIncludes | null; +let yamlvalidation: YAMLValidation | null; + documents.listen(connection); - -connection.onInitialize((params) => { - workspaceFolder = params.rootUri; - connection.console.log(`[Server(${process.pid}) ${workspaceFolder}] Started and initialize received`); - return { - capabilities: { - textDocumentSync: { - openClose: true, - change: documents.syncKind - } - } - }; -}); - -let workspaceContext = { + +connection.onInitialize(params => { + workspaceFolder = params.rootUri; + + var vsCodeFileAccessor = new VsCodeFileAccessor(workspaceFolder, connection); + parser = new NestedYamlParser(vsCodeFileAccessor); + + let workspaceContext = { resolveRelativePath: (relativePath: string, resource: string) => { - return path.resolve(resource, relativePath); + return path.resolve(resource, relativePath); } -}; + }; + let jsonSchemaService = new JSONSchemaService(null, workspaceContext, null); + schemaServiceForIncludes = new SchemaServiceForIncludes(jsonSchemaService); -export let languageService = getLanguageService( - workspaceContext, - { - validation: true - } -); -documents.onDidChangeContent(async (textDocumentChangeEvent) =>{ - if (!textDocumentChangeEvent.document) { - return; - } + yamlvalidation = new YAMLValidation(jsonSchemaService); + yamlvalidation.configure({ + validate: true + }); - if (textDocumentChangeEvent.document.getText().length === 0) { - return; + connection.console.log( + `[Server(${ + process.pid + }) ${workspaceFolder}] Started and initialize received` + ); + return { + capabilities: { + textDocumentSync: { + openClose: true, + change: documents.syncKind + } } - - - var vsCodeFileAccessor = new VsCodeFileAccessor(workspaceFolder,connection); - var parser = new NestedYamlParser(vsCodeFileAccessor); - var parseResult = await parser.parse([ - path.join(workspaceFolder,"configuration.yaml"), - path.join(workspaceFolder,"ui-lovelace.yaml") - ]); - - let yamlDocument = parseYAML(textDocumentChangeEvent.document.getText(), []); - if (!yamlDocument) { - return; - } - languageService.doValidation(textDocumentChangeEvent.document, yamlDocument).then((diagnosticResults) => { - - if (!diagnosticResults) { - return; - } - let diagnostics = []; - for (let diagnosticItem in diagnosticResults) { - diagnosticResults[diagnosticItem].severity = 1; //Convert all warnings to errors - diagnostics.push(diagnosticResults[diagnosticItem]); - } - connection.sendDiagnostics({ uri: textDocumentChangeEvent.document.uri, diagnostics: diagnostics }); - }, (error) => { - connection.window.showErrorMessage(`oops: ${error}`); - }); - }) - -documents.onDidOpen((event) => { - connection.console.log(`[Server(${process.pid}) ${workspaceFolder}] Document opened: ${event.document.uri}`); -}); + }; +}); +documents.onDidChangeContent(async textDocumentChangeEvent => { + if (!textDocumentChangeEvent.document) { + return; + } + + var parseResult = await parser.parse([ + path.join(workspaceFolder, "configuration.yaml"), + path.join(workspaceFolder, "ui-lovelace.yaml") + ]); + + schemaServiceForIncludes.onUpdate(parseResult.filePathMappings); + + if (textDocumentChangeEvent.document.getText().length === 0) { + return; + } + + let yamlDocument = parseYAML(textDocumentChangeEvent.document.getText(), getValidYamlTags()); + if (!yamlDocument) { + return; + } + var diagnosticResults = await yamlvalidation.doValidation( + textDocumentChangeEvent.document, + yamlDocument + ); + + if (!diagnosticResults) { + return; + } + let diagnostics = []; + for (let diagnosticItem in diagnosticResults) { + diagnosticResults[diagnosticItem].severity = 1; //Convert all warnings to errors + diagnostics.push(diagnosticResults[diagnosticItem]); + } + connection.sendDiagnostics({ + uri: textDocumentChangeEvent.document.uri, + diagnostics: diagnostics + }); +}); + +documents.onDidOpen(event => { + connection.console.log( + `[Server(${process.pid}) ${workspaceFolder}] Document opened: ${ + event.document.uri + }` + ); +}); + +function getValidYamlTags() : string[] { + var validTags: string[] = []; + for (let item in Includetype) { + if (isNaN(Number(item))) { + validTags.push(`!${item} scalar`); + } + } + validTags.push("!secret scalar"); + return validTags; +} connection.listen(); - \ No newline at end of file diff --git a/src/server/yamlDiscovery.ts b/src/server/yamlDiscovery.ts index 07d80d184f..2ce9f056b0 100644 --- a/src/server/yamlDiscovery.ts +++ b/src/server/yamlDiscovery.ts @@ -1,22 +1,15 @@ -import * as YAML from "yaml"; -// @ts-ignore -import * as p from "yaml/dist/schema/parseMap"; - +import * as YAML from "yaml"; import { Tag } from "yaml"; import URI from "vscode-uri"; import * as path from "path"; import * as fs from "fs"; -import { - IConnection, - RequestType, - VersionedTextDocumentIdentifier -} from "vscode-languageserver"; +import { IConnection } from "vscode-languageserver"; export interface NestedYamlParseResult { filename: string; path: string; contents: string; - referencedFiles: NestedYamlParseResult[]; + includedFiles: NestedYamlParseResult[]; } export interface FileAccessor { @@ -71,11 +64,16 @@ export interface ParseResult { } export interface FilePathMapping { - [filename: string]: string; + [filename: string]: FilePathMappingEntry; +} + +export interface FilePathMappingEntry { + path: string; + includeType: Includetype; } export class NestedYamlParser { - private includes: YamlIncludeReferences = new YamlIncludeReferences(); + private includes: YamlIncludes | null; constructor(private fileAccessor: FileAccessor) {} @@ -83,6 +81,8 @@ export class NestedYamlParser { fileNames: string[], currentPath: string = "" ): Promise { + this.includes = new YamlIncludes(); + for (var index in fileNames) { var fileContents = await this.fileAccessor.getFileContents( fileNames[index] @@ -94,7 +94,7 @@ export class NestedYamlParser { await this.updatePathsViaTraversal(yaml, currentPath); } - // parse all referenced files (this causes recursion) + // parse all included files (this causes recursion) // this.parse(Object.keys(this.includes), currentPath); await this.replaceFolderBasedIncludes(); @@ -110,10 +110,13 @@ export class NestedYamlParser { var result: FilePathMapping = {}; for (var toFileOrFolder in this.includes) { - for (var fromFile in this.includes[toFileOrFolder].referencedFrom) { - var mapping = this.includes[toFileOrFolder].referencedFrom[fromFile]; + for (var fromFile in this.includes[toFileOrFolder].includedFrom) { + var mapping = this.includes[toFileOrFolder].includedFrom[fromFile]; if (!result[toFileOrFolder]) { - result[toFileOrFolder] = mapping.path; + result[toFileOrFolder] = { + path: mapping.path, + includeType: mapping.includeType + }; } else { // assuming it's the same path // todo: if multiple files point to this same file, and the path is different, throw an exception @@ -125,19 +128,16 @@ export class NestedYamlParser { private async replaceFolderBasedIncludes() { for (var toFileOrFolder in this.includes) { - for (var fromFile in this.includes[toFileOrFolder].referencedFrom) { - var mapping = this.includes[toFileOrFolder].referencedFrom[fromFile]; - //@ts-ignore - if(mapping.tag.suffix === "include"){ + for (var fromFile in this.includes[toFileOrFolder].includedFrom) { + var mapping = this.includes[toFileOrFolder].includedFrom[fromFile]; + if (mapping.includeType === Includetype.include) { continue; } - var files = await this.fileAccessor.getFilesInFolder( - toFileOrFolder - ); + var files = await this.fileAccessor.getFilesInFolder(toFileOrFolder); files.map(x => { - this.includes[x] = new YamlIncludeReference(); - this.includes[x].referencedFrom[fromFile] = { ...mapping }; + this.includes[x] = new YamlInclude(); + this.includes[x].includedFrom[fromFile] = { ...mapping }; }); delete this.includes[toFileOrFolder]; } @@ -153,9 +153,9 @@ export class NestedYamlParser { } else if (typeof obj === "object" && obj !== null) { // objects if (obj.isInclude) { - // parsed reference - var theDetails = ( - this.includes[obj.toFileOrFolder].referencedFrom[obj.fromFile] + // parsed include + var theDetails = ( + this.includes[obj.toFileOrFolder].includedFrom[obj.fromFile] ); theDetails.path = `${currentPath}`; @@ -172,23 +172,23 @@ export class NestedYamlParser { private getCustomTags(filename: string): Tag[] { return [ { - tag: "!include", + tag: `!${Includetype[Includetype.include]}`, resolve: (doc, cstNode) => this.includeResolver(filename, doc, cstNode) }, { - tag: "!include_dir_list", + tag: `!${Includetype[Includetype.include_dir_list]}`, resolve: (doc, cstNode) => this.includeResolver(filename, doc, cstNode) }, { - tag: "!include_dir_named", + tag: `!${Includetype[Includetype.include_dir_named]}`, resolve: (doc, cstNode) => this.includeResolver(filename, doc, cstNode) }, { - tag: "!include_dir_merge_list", + tag: `!${Includetype[Includetype.include_dir_merge_list]}`, resolve: (doc, cstNode) => this.includeResolver(filename, doc, cstNode) }, { - tag: "!include_dir_merge_named", + tag: `!${Includetype[Includetype.include_dir_merge_named]}`, resolve: (doc, cstNode) => this.includeResolver(filename, doc, cstNode) } ]; @@ -202,20 +202,41 @@ export class NestedYamlParser { var fromFile = this.fileAccessor.getUnifiedUri(filename); var toFileOrFolder = this.fileAccessor.getUnifiedUri(cstNode.rawValue); - var reference = this.includes[toFileOrFolder]; - if (!reference) { - reference = new YamlIncludeReference(); + var include = this.includes[toFileOrFolder]; + if (!include) { + include = new YamlInclude(); } - var referencedFrom = reference.referencedFrom[fromFile]; - if (!referencedFrom) { - referencedFrom = new ReferencedFromEntry(); + var includedFrom = include.includedFrom[fromFile]; + if (!includedFrom) { + includedFrom = new IncludedFromEntry(); } - referencedFrom.tag = cstNode.tag; - referencedFrom.start = cstNode.range.start; - referencedFrom.end = cstNode.range.end; + var includeType: Includetype; + // @ts-ignore + switch (cstNode.tag.suffix) { + case "include": + includeType = Includetype.include; + break; + case "include_dir_list": + includeType = Includetype.include_dir_list; + break; + case "include_dir_merge_list": + includeType = Includetype.include_dir_merge_list; + break; + case "include_dir_merge_named": + includeType = Includetype.include_dir_merge_named; + break; + case "include_dir_named": + includeType = Includetype.include_dir_named; + break; + default: + throw new Error("Unknown include tag"); + } + includedFrom.includeType = includeType; + includedFrom.start = cstNode.range.start; + includedFrom.end = cstNode.range.end; - reference.referencedFrom[fromFile] = referencedFrom; - this.includes[toFileOrFolder] = reference; + include.includedFrom[fromFile] = includedFrom; + this.includes[toFileOrFolder] = include; return YAML.createNode({ isInclude: true, @@ -225,26 +246,61 @@ export class NestedYamlParser { }; } -namespace OpenTextDocumentRequest { - export const type: RequestType<{}, {}, {}, {}> = new RequestType( - "ha/openTextDocument" - ); -} - -export class YamlIncludeReferences { - [filename: string]: YamlIncludeReference; +export class YamlIncludes { + [filename: string]: YamlInclude; } -export class YamlIncludeReference { - referencedFrom: ReferencedFrom = new ReferencedFrom(); +export class YamlInclude { + includedFrom: IncludedFrom = new IncludedFrom(); } -export class ReferencedFrom { - [filename: string]: ReferencedFromEntry; +export class IncludedFrom { + [filename: string]: IncludedFromEntry; } -export class ReferencedFromEntry { +export class IncludedFromEntry { path: string | null; - tag: null | { verbatim: string } | { handle: string; suffix: string }; + includeType: Includetype; start: number; end: number; } + +export class SchemaServiceForIncludes { + private schemaContributions: any; + + constructor(private jsonSchemaService: any) {} + + public onUpdate(fileMappings: FilePathMapping) { + if (!this.schemaContributions) { + this.schemaContributions = this.getSchemaContributions(); + } + this.jsonSchemaService.setSchemaContributions(this.schemaContributions); + } + + private getSchemaContributions() { + var jsonPath = path.join( + __dirname, + "..", + "lovelace-schema", + "ui-lovelace.json" + ); + var sc = fs.readFileSync(jsonPath, "utf-8"); + var schema = JSON.parse(sc); + + return { + schemas: { + "http://schema.ha.com/lovelace": schema + }, + schemaAssociations: { + "**/ui-lovelace.yaml": ["http://schema.ha.com/lovelace"] + } + }; + } +} + +export enum Includetype { + include, + include_dir_list, + include_dir_named, + include_dir_merge_list, + include_dir_merge_named +} diff --git a/src/server/yamlLanguageService.ts b/src/server/yamlLanguageService.ts deleted file mode 100644 index b62d039e4a..0000000000 --- a/src/server/yamlLanguageService.ts +++ /dev/null @@ -1,44 +0,0 @@ - -import * as path from 'path'; -import { SchemaRequestService, YAMLDocument, LanguageSettings } from 'yaml-language-server/out/server/src/languageservice/yamlLanguageService'; -import { YAMLValidation } from 'yaml-language-server/out/server/src/languageservice/services/yamlValidation'; -import { JSONSchemaService } from 'yaml-language-server/out/server/src/languageservice/services/jsonSchemaService'; -import * as fs from "fs" - -import { TextDocument, Diagnostic } from 'vscode-languageserver'; - -export function getLanguageService(workspaceContext, promiseConstructor?): LanguageService { - let promise = promiseConstructor || Promise; - - let jsonSchemaService = new JSONSchemaService(null, workspaceContext, null); - - var jsonPath = path.join(__dirname, '..', 'lovelace-schema', 'ui-lovelace.json'); - var sc = fs.readFileSync(jsonPath,"utf-8"); - var schema = JSON.parse(sc); - - jsonSchemaService.setSchemaContributions({ - schemas: { - "http://schema.ha.com/lovelace": schema - }, - schemaAssociations:{ - "**/ui-lovelace.yaml": ["http://schema.ha.com/lovelace"] - } - }); - - let yamlvalidation = new YAMLValidation(jsonSchemaService, promise); - yamlvalidation.configure({ - validate: true , - customTags: [ - "!include scalar", - "!include_dir_list scalar" - ] - }); - - return { - doValidation: yamlvalidation.doValidation.bind(yamlvalidation) - }; -} - -export interface LanguageService { - doValidation(document: TextDocument, yamlDocument: YAMLDocument): Thenable; -} \ No newline at end of file diff --git a/yaml-language/language-configuration.json b/yaml-language/language-configuration.json new file mode 100644 index 0000000000..a66c63b02c --- /dev/null +++ b/yaml-language/language-configuration.json @@ -0,0 +1,31 @@ +{ + "comments": { + "lineComment": "#" + }, + "brackets": [ + ["{", "}"], + ["[", "]"], + ["(", ")"] + ], + "autoClosingPairs": [ + ["{", "}"], + ["[", "]"], + ["(", ")"], + ["\"", "\""], + ["'", "'"] + ], + "surroundingPairs": [ + ["{", "}"], + ["[", "]"], + ["(", ")"], + ["\"", "\""], + ["'", "'"] + ], + "folding": { + "offSide": true + }, + "indentationRules": { + "increaseIndentPattern": "^\\s*.*(:|-) ?(&\\w+)?(\\{[^}\"']*|\\([^)\"']*)?$", + "decreaseIndentPattern": "^\\s+\\}$" + } +} \ No newline at end of file diff --git a/yaml-language/yaml.tmLanguage.json b/yaml-language/yaml.tmLanguage.json new file mode 100644 index 0000000000..90f562a3ef --- /dev/null +++ b/yaml-language/yaml.tmLanguage.json @@ -0,0 +1,621 @@ +{ + "information_for_contributors": [ + "This file has been converted from https://github.com/textmate/yaml.tmbundle/blob/master/Syntaxes/YAML.tmLanguage", + "If you want to provide a fix or improvement, please create a pull request against the original repository.", + "Once accepted there, we are happy to receive an update request." + ], + "version": "https://github.com/textmate/yaml.tmbundle/commit/e54ceae3b719506dba7e481a77cea4a8b576ae46", + "name": "YAML", + "scopeName": "source.yaml", + "patterns": [ + { + "include": "#comment" + }, + { + "include": "#property" + }, + { + "include": "#directive" + }, + { + "match": "^---", + "name": "entity.other.document.begin.yaml" + }, + { + "match": "^\\.{3}", + "name": "entity.other.document.end.yaml" + }, + { + "include": "#node" + } + ], + "repository": { + "block-collection": { + "patterns": [ + { + "include": "#block-sequence" + }, + { + "include": "#block-mapping" + } + ] + }, + "block-mapping": { + "patterns": [ + { + "include": "#block-pair" + } + ] + }, + "block-node": { + "patterns": [ + { + "include": "#prototype" + }, + { + "include": "#block-scalar" + }, + { + "include": "#block-collection" + }, + { + "include": "#flow-scalar-plain-out" + }, + { + "include": "#flow-node" + } + ] + }, + "block-pair": { + "patterns": [ + { + "begin": "\\?", + "beginCaptures": { + "1": { + "name": "punctuation.definition.key-value.begin.yaml" + } + }, + "end": "(?=\\?)|^ *(:)|(:)", + "endCaptures": { + "1": { + "name": "punctuation.separator.key-value.mapping.yaml" + }, + "2": { + "name": "invalid.illegal.expected-newline.yaml" + } + }, + "name": "meta.block-mapping.yaml", + "patterns": [ + { + "include": "#block-node" + } + ] + }, + { + "begin": "(?x)\n (?=\n (?x:\n [^\\s[-?:,\\[\\]{}#&*!|>'\"%@`]]\n | [?:-] \\S\n )\n (\n [^\\s:]\n | : \\S\n | \\s+ (?![#\\s])\n )*\n \\s*\n :\n\t\t\t\t\t\t\t(\\s|$)\n )\n ", + "end": "(?x)\n (?=\n \\s* $\n | \\s+ \\#\n | \\s* : (\\s|$)\n )\n ", + "patterns": [ + { + "include": "#flow-scalar-plain-out-implicit-type" + }, + { + "begin": "(?x)\n [^\\s[-?:,\\[\\]{}#&*!|>'\"%@`]]\n | [?:-] \\S\n ", + "beginCaptures": { + "0": { + "name": "entity.name.tag.yaml" + } + }, + "contentName": "entity.name.tag.yaml", + "end": "(?x)\n (?=\n \\s* $\n | \\s+ \\#\n | \\s* : (\\s|$)\n )\n ", + "name": "string.unquoted.plain.out.yaml" + } + ] + }, + { + "match": ":(?=\\s|$)", + "name": "punctuation.separator.key-value.mapping.yaml" + } + ] + }, + "block-scalar": { + "begin": "(?:(\\|)|(>))([1-9])?([-+])?(.*\\n?)", + "beginCaptures": { + "1": { + "name": "keyword.control.flow.block-scalar.literal.yaml" + }, + "2": { + "name": "keyword.control.flow.block-scalar.folded.yaml" + }, + "3": { + "name": "constant.numeric.indentation-indicator.yaml" + }, + "4": { + "name": "storage.modifier.chomping-indicator.yaml" + }, + "5": { + "patterns": [ + { + "include": "#comment" + }, + { + "match": ".+", + "name": "invalid.illegal.expected-comment-or-newline.yaml" + } + ] + } + }, + "end": "^(?=\\S)|(?!\\G)", + "patterns": [ + { + "begin": "^([ ]+)(?! )", + "end": "^(?!\\1|\\s*$)", + "name": "string.unquoted.block.yaml" + } + ] + }, + "block-sequence": { + "match": "(-)(?!\\S)", + "name": "punctuation.definition.block.sequence.item.yaml" + }, + "comment": { + "begin": "(?:(^[ \\t]*)|[ \\t]+)(?=#\\p{Print}*$)", + "beginCaptures": { + "1": { + "name": "punctuation.whitespace.comment.leading.yaml" + } + }, + "end": "(?!\\G)", + "patterns": [ + { + "begin": "#", + "beginCaptures": { + "0": { + "name": "punctuation.definition.comment.yaml" + } + }, + "end": "\\n", + "name": "comment.line.number-sign.yaml" + } + ] + }, + "directive": { + "begin": "^%", + "beginCaptures": { + "0": { + "name": "punctuation.definition.directive.begin.yaml" + } + }, + "end": "(?=$|[ \\t]+($|#))", + "name": "meta.directive.yaml", + "patterns": [ + { + "captures": { + "1": { + "name": "keyword.other.directive.yaml.yaml" + }, + "2": { + "name": "constant.numeric.yaml-version.yaml" + } + }, + "match": "\\G(YAML)[ \\t]+(\\d+\\.\\d+)" + }, + { + "captures": { + "1": { + "name": "keyword.other.directive.tag.yaml" + }, + "2": { + "name": "storage.type.tag-handle.yaml" + }, + "3": { + "name": "support.type.tag-prefix.yaml" + } + }, + "match": "(?x)\n \\G\n (TAG)\n (?:[ \\t]+\n ((?:!(?:[0-9A-Za-z\\-]*!)?))\n (?:[ \\t]+ (\n ! (?x: %[0-9A-Fa-f]{2} | [0-9A-Za-z\\-#;\/?:@&=+$,_.!~*'()\\[\\]] )*\n | (?![,!\\[\\]{}]) (?x: %[0-9A-Fa-f]{2} | [0-9A-Za-z\\-#;\/?:@&=+$,_.!~*'()\\[\\]] )+\n )\n )?\n )?\n " + }, + { + "captures": { + "1": { + "name": "support.other.directive.reserved.yaml" + }, + "2": { + "name": "string.unquoted.directive-name.yaml" + }, + "3": { + "name": "string.unquoted.directive-parameter.yaml" + } + }, + "match": "(?x) \\G (\\w+) (?:[ \\t]+ (\\w+) (?:[ \\t]+ (\\w+))? )?" + }, + { + "match": "\\S+", + "name": "invalid.illegal.unrecognized.yaml" + } + ] + }, + "flow-alias": { + "captures": { + "1": { + "name": "keyword.control.flow.alias.yaml" + }, + "2": { + "name": "punctuation.definition.alias.yaml" + }, + "3": { + "name": "variable.other.alias.yaml" + }, + "4": { + "name": "invalid.illegal.character.anchor.yaml" + } + }, + "match": "((\\*))([^\\s\\[\\]\/{\/},]+)([^\\s\\]},]\\S*)?" + }, + "flow-collection": { + "patterns": [ + { + "include": "#flow-sequence" + }, + { + "include": "#flow-mapping" + } + ] + }, + "flow-mapping": { + "begin": "\\{", + "beginCaptures": { + "0": { + "name": "punctuation.definition.mapping.begin.yaml" + } + }, + "end": "\\}", + "endCaptures": { + "0": { + "name": "punctuation.definition.mapping.end.yaml" + } + }, + "name": "meta.flow-mapping.yaml", + "patterns": [ + { + "include": "#prototype" + }, + { + "match": ",", + "name": "punctuation.separator.mapping.yaml" + }, + { + "include": "#flow-pair" + } + ] + }, + "flow-node": { + "patterns": [ + { + "include": "#prototype" + }, + { + "include": "#flow-alias" + }, + { + "include": "#flow-collection" + }, + { + "include": "#flow-scalar" + } + ] + }, + "flow-pair": { + "patterns": [ + { + "begin": "\\?", + "beginCaptures": { + "0": { + "name": "punctuation.definition.key-value.begin.yaml" + } + }, + "end": "(?=[},\\]])", + "name": "meta.flow-pair.explicit.yaml", + "patterns": [ + { + "include": "#prototype" + }, + { + "include": "#flow-pair" + }, + { + "include": "#flow-node" + }, + { + "begin": ":(?=\\s|$|[\\[\\]{},])", + "beginCaptures": { + "0": { + "name": "punctuation.separator.key-value.mapping.yaml" + } + }, + "end": "(?=[},\\]])", + "patterns": [ + { + "include": "#flow-value" + } + ] + } + ] + }, + { + "begin": "(?x)\n (?=\n (?:\n [^\\s[-?:,\\[\\]{}#&*!|>'\"%@`]]\n | [?:-] [^\\s[\\[\\]{},]]\n )\n (\n [^\\s:[\\[\\]{},]]\n | : [^\\s[\\[\\]{},]]\n | \\s+ (?![#\\s])\n )*\n \\s*\n :\n\t\t\t\t\t\t\t(\\s|$)\n )\n ", + "end": "(?x)\n (?=\n \\s* $\n | \\s+ \\#\n | \\s* : (\\s|$)\n | \\s* : [\\[\\]{},]\n | \\s* [\\[\\]{},]\n )\n ", + "name": "meta.flow-pair.key.yaml", + "patterns": [ + { + "include": "#flow-scalar-plain-in-implicit-type" + }, + { + "begin": "(?x)\n [^\\s[-?:,\\[\\]{}#&*!|>'\"%@`]]\n | [?:-] [^\\s[\\[\\]{},]]\n ", + "beginCaptures": { + "0": { + "name": "entity.name.tag.yaml" + } + }, + "contentName": "entity.name.tag.yaml", + "end": "(?x)\n (?=\n \\s* $\n | \\s+ \\#\n | \\s* : (\\s|$)\n | \\s* : [\\[\\]{},]\n | \\s* [\\[\\]{},]\n )\n ", + "name": "string.unquoted.plain.in.yaml" + } + ] + }, + { + "include": "#flow-node" + }, + { + "begin": ":(?=\\s|$|[\\[\\]{},])", + "captures": { + "0": { + "name": "punctuation.separator.key-value.mapping.yaml" + } + }, + "end": "(?=[},\\]])", + "name": "meta.flow-pair.yaml", + "patterns": [ + { + "include": "#flow-value" + } + ] + } + ] + }, + "flow-scalar": { + "patterns": [ + { + "include": "#flow-scalar-double-quoted" + }, + { + "include": "#flow-scalar-single-quoted" + }, + { + "include": "#flow-scalar-plain-in" + } + ] + }, + "flow-scalar-double-quoted": { + "begin": "\"", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.yaml" + } + }, + "end": "\"", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.yaml" + } + }, + "name": "string.quoted.double.yaml", + "patterns": [ + { + "match": "\\\\([0abtnvfre \"\/\\\\N_Lp]|x\\d\\d|u\\d{4}|U\\d{8})", + "name": "constant.character.escape.yaml" + }, + { + "match": "\\\\\\n", + "name": "constant.character.escape.double-quoted.newline.yaml" + } + ] + }, + "flow-scalar-plain-in": { + "patterns": [ + { + "include": "#flow-scalar-plain-in-implicit-type" + }, + { + "begin": "(?x)\n [^\\s[-?:,\\[\\]{}#&*!|>'\"%@`]]\n | [?:-] [^\\s[\\[\\]{},]]\n ", + "end": "(?x)\n (?=\n \\s* $\n | \\s+ \\#\n | \\s* : (\\s|$)\n | \\s* : [\\[\\]{},]\n | \\s* [\\[\\]{},]\n )\n ", + "name": "string.unquoted.plain.in.yaml" + } + ] + }, + "flow-scalar-plain-in-implicit-type": { + "patterns": [ + { + "captures": { + "1": { + "name": "constant.language.null.yaml" + }, + "2": { + "name": "constant.language.boolean.yaml" + }, + "3": { + "name": "constant.numeric.integer.yaml" + }, + "4": { + "name": "constant.numeric.float.yaml" + }, + "5": { + "name": "constant.other.timestamp.yaml" + }, + "6": { + "name": "constant.language.value.yaml" + }, + "7": { + "name": "constant.language.merge.yaml" + } + }, + "match": "(?x)\n (?x:\n (null|Null|NULL|~)\n | (y|Y|yes|Yes|YES|n|N|no|No|NO|true|True|TRUE|false|False|FALSE|on|On|ON|off|Off|OFF)\n | (\n (?:\n [-+]? 0b [0-1_]+ # (base 2)\n | [-+]? 0 [0-7_]+ # (base 8)\n | [-+]? (?: 0|[1-9][0-9_]*) # (base 10)\n | [-+]? 0x [0-9a-fA-F_]+ # (base 16)\n | [-+]? [1-9] [0-9_]* (?: :[0-5]?[0-9])+ # (base 60)\n )\n )\n | (\n (?x:\n [-+]? (?: [0-9] [0-9_]*)? \\. [0-9.]* (?: [eE] [-+] [0-9]+)? # (base 10)\n | [-+]? [0-9] [0-9_]* (?: :[0-5]?[0-9])+ \\. [0-9_]* # (base 60)\n | [-+]? \\. (?: inf|Inf|INF) # (infinity)\n | \\. (?: nan|NaN|NAN) # (not a number)\n )\n )\n | (\n (?x:\n \\d{4} - \\d{2} - \\d{2} # (y-m-d)\n | \\d{4} # (year)\n - \\d{1,2} # (month)\n - \\d{1,2} # (day)\n (?: [Tt] | [ \\t]+) \\d{1,2} # (hour)\n : \\d{2} # (minute)\n : \\d{2} # (second)\n (?: \\.\\d*)? # (fraction)\n (?:\n (?:[ \\t]*) Z\n | [-+] \\d{1,2} (?: :\\d{1,2})?\n )? # (time zone)\n )\n )\n | (=)\n | (<<)\n )\n (?:\n (?=\n \\s* $\n | \\s+ \\#\n | \\s* : (\\s|$)\n | \\s* : [\\[\\]{},]\n | \\s* [\\[\\]{},]\n )\n )\n " + } + ] + }, + "flow-scalar-plain-out": { + "patterns": [ + { + "include": "#flow-scalar-plain-out-implicit-type" + }, + { + "begin": "(?x)\n [^\\s[-?:,\\[\\]{}#&*!|>'\"%@`]]\n | [?:-] \\S\n ", + "end": "(?x)\n (?=\n \\s* $\n | \\s+ \\#\n | \\s* : (\\s|$)\n )\n ", + "name": "string.unquoted.plain.out.yaml" + } + ] + }, + "flow-scalar-plain-out-implicit-type": { + "patterns": [ + { + "captures": { + "1": { + "name": "constant.language.null.yaml" + }, + "2": { + "name": "constant.language.boolean.yaml" + }, + "3": { + "name": "constant.numeric.integer.yaml" + }, + "4": { + "name": "constant.numeric.float.yaml" + }, + "5": { + "name": "constant.other.timestamp.yaml" + }, + "6": { + "name": "constant.language.value.yaml" + }, + "7": { + "name": "constant.language.merge.yaml" + } + }, + "match": "(?x)\n (?x:\n (null|Null|NULL|~)\n | (y|Y|yes|Yes|YES|n|N|no|No|NO|true|True|TRUE|false|False|FALSE|on|On|ON|off|Off|OFF)\n | (\n (?:\n [-+]? 0b [0-1_]+ # (base 2)\n | [-+]? 0 [0-7_]+ # (base 8)\n | [-+]? (?: 0|[1-9][0-9_]*) # (base 10)\n | [-+]? 0x [0-9a-fA-F_]+ # (base 16)\n | [-+]? [1-9] [0-9_]* (?: :[0-5]?[0-9])+ # (base 60)\n )\n )\n | (\n (?x:\n [-+]? (?: [0-9] [0-9_]*)? \\. [0-9.]* (?: [eE] [-+] [0-9]+)? # (base 10)\n | [-+]? [0-9] [0-9_]* (?: :[0-5]?[0-9])+ \\. [0-9_]* # (base 60)\n | [-+]? \\. (?: inf|Inf|INF) # (infinity)\n | \\. (?: nan|NaN|NAN) # (not a number)\n )\n )\n | (\n (?x:\n \\d{4} - \\d{2} - \\d{2} # (y-m-d)\n | \\d{4} # (year)\n - \\d{1,2} # (month)\n - \\d{1,2} # (day)\n (?: [Tt] | [ \\t]+) \\d{1,2} # (hour)\n : \\d{2} # (minute)\n : \\d{2} # (second)\n (?: \\.\\d*)? # (fraction)\n (?:\n (?:[ \\t]*) Z\n | [-+] \\d{1,2} (?: :\\d{1,2})?\n )? # (time zone)\n )\n )\n | (=)\n | (<<)\n )\n (?x:\n (?=\n \\s* $\n | \\s+ \\#\n | \\s* : (\\s|$)\n )\n )\n " + } + ] + }, + "flow-scalar-single-quoted": { + "begin": "'", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.yaml" + } + }, + "end": "'(?!')", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.yaml" + } + }, + "name": "string.quoted.single.yaml", + "patterns": [ + { + "match": "''", + "name": "constant.character.escape.single-quoted.yaml" + } + ] + }, + "flow-sequence": { + "begin": "\\[", + "beginCaptures": { + "0": { + "name": "punctuation.definition.sequence.begin.yaml" + } + }, + "end": "\\]", + "endCaptures": { + "0": { + "name": "punctuation.definition.sequence.end.yaml" + } + }, + "name": "meta.flow-sequence.yaml", + "patterns": [ + { + "include": "#prototype" + }, + { + "match": ",", + "name": "punctuation.separator.sequence.yaml" + }, + { + "include": "#flow-pair" + }, + { + "include": "#flow-node" + } + ] + }, + "flow-value": { + "patterns": [ + { + "begin": "\\G(?![},\\]])", + "end": "(?=[},\\]])", + "name": "meta.flow-pair.value.yaml", + "patterns": [ + { + "include": "#flow-node" + } + ] + } + ] + }, + "node": { + "patterns": [ + { + "include": "#block-node" + } + ] + }, + "property": { + "begin": "(?=!|&)", + "end": "(?!\\G)", + "name": "meta.property.yaml", + "patterns": [ + { + "captures": { + "1": { + "name": "keyword.control.property.anchor.yaml" + }, + "2": { + "name": "punctuation.definition.anchor.yaml" + }, + "3": { + "name": "entity.name.type.anchor.yaml" + }, + "4": { + "name": "invalid.illegal.character.anchor.yaml" + } + }, + "match": "\\G((&))([^\\s\\[\\]\/{\/},]+)(\\S+)?" + }, + { + "match": "(?x)\n \\G\n (?:\n ! < (?: %[0-9A-Fa-f]{2} | [0-9A-Za-z\\-#;\/?:@&=+$,_.!~*'()\\[\\]] )+ >\n | (?:!(?:[0-9A-Za-z\\-]*!)?) (?: %[0-9A-Fa-f]{2} | [0-9A-Za-z\\-#;\/?:@&=+$_.~*'()] )+\n | !\n )\n (?=\\ |\\t|$)\n ", + "name": "storage.type.tag-handle.yaml" + }, + { + "match": "\\S+", + "name": "invalid.illegal.tag-handle.yaml" + } + ] + }, + "prototype": { + "patterns": [ + { + "include": "#comment" + }, + { + "include": "#property" + } + ] + } + } +} \ No newline at end of file