diff --git a/CHANGELOG.md b/CHANGELOG.md index c1c381fd..06b89ee4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,20 @@ +### 1.15.0 +- Fix: some small type issues [#918](https://github.com/redhat-developer/yaml-language-server/pull/918) +- Add: volar-service-yaml to clients [#920](https://github.com/redhat-developer/yaml-language-server/pull/920) +- Fix: Remove ide-yaml from known clients [#921](https://github.com/redhat-developer/yaml-language-server/pull/921) +- Fix: schema loading performance [#923](https://github.com/redhat-developer/yaml-language-server/pull/923) +- Fix: undefined error in mergingResult.problemArgs [#924](https://github.com/redhat-developer/yaml-language-server/pull/924) +- Add: unify string insert text for array and property [#934](https://github.com/redhat-developer/yaml-language-server/pull/934) +- Add: Improve some special cases for selection ranges [#939](https://github.com/redhat-developer/yaml-language-server/pull/939) +- Fix: show all enums on hover [#942](https://github.com/redhat-developer/yaml-language-server/pull/942) +- Fix: update README syntax highlighting [#945](https://github.com/redhat-developer/yaml-language-server/pull/945) +- Fix: render examples as yaml on hover [#947](https://github.com/redhat-developer/yaml-language-server/pull/947) +- Fix: snippets in additionalProperties [#951](https://github.com/redhat-developer/yaml-language-server/pull/951) +- Fix: crash when url is undefined [#954](https://github.com/redhat-developer/yaml-language-server/pull/954) +- Fix: Add null check for customTags [#955](https://github.com/redhat-developer/yaml-language-server/pull/955) + +Thanks to [Remco Haszing](https://github.com/remcohaszing), [Petr Spacek](https://github.com/p-spacek), [Tony](https://github.com/Legend-Master), [Gustav Eikaas](https://github.com/GustavEikaas), [Skip Baney](https://github.com/twelvelabs) and [Pierre Prinetti](https://github.com/pierreprinetti) for your contributions. + ### 1.14.0 - Fix: Request textDocument/hover failed with message: Invalid regular expression: /(?s).*/: Invalid group [#874](https://github.com/redhat-developer/yaml-language-server/issues/874) - Fix: nested anyof const [#888](https://github.com/redhat-developer/yaml-language-server/pull/888) diff --git a/README.md b/README.md index d31a0c0b..6d6c052a 100755 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ The following settings are supported: In order to use the custom tags in your YAML file you need to first specify the custom tags in the setting of your code editor. For example, we can have the following custom tags: -```YAML +```yaml "yaml.customTags": [ "!Scalar-example scalar", "!Seq-example sequence", @@ -70,7 +70,7 @@ The !Scalar-example would map to a scalar custom tag, the !Seq-example would map We can then use the newly defined custom tags inside our YAML file: -```YAML +```yaml some_key: !Scalar-example some_value some_sequence: !Seq-example - some_seq_key_1: some_seq_value_1 @@ -93,7 +93,7 @@ myProject you can do -``` +```yaml yaml.schemas: { "https://json.schemastore.org/composer": "/myYamlFile.yaml" } @@ -109,7 +109,7 @@ and that will associate the composer schema with myYamlFile.yaml. When associating a schema it should follow the format below -```json +```yaml yaml.schemas: { "url": "globPattern", "Kubernetes": "globPattern" @@ -118,7 +118,7 @@ yaml.schemas: { e.g. -```json +```yaml yaml.schemas: { "https://json.schemastore.org/composer": "/*" } @@ -126,7 +126,7 @@ yaml.schemas: { e.g. -```json +```yaml yaml.schemas: { "kubernetes": "/myYamlFile.yaml" } @@ -134,7 +134,7 @@ yaml.schemas: { e.g. -```json +```yaml yaml.schemas: { "https://json.schemastore.org/composer": "/*", "kubernetes": "/myYamlFile.yaml" @@ -143,7 +143,7 @@ yaml.schemas: { On Windows with full path: -```json +```yaml yaml.schemas: { "C:\\Users\\user\\Documents\\custom_schema.json": "someFilePattern.yaml", } @@ -151,7 +151,7 @@ yaml.schemas: { On Mac/Linux with full path: -```json +```yaml yaml.schemas: { "/home/user/custom_schema.json": "someFilePattern.yaml", } @@ -159,13 +159,13 @@ yaml.schemas: { Since `0.11.0` YAML Schemas can be used for validation: -```json +```yaml "/home/user/custom_schema.yaml": "someFilePattern.yaml" ``` A schema can be associated with multiple globs using a json array, e.g. -```json +```yaml yaml.schemas: { "kubernetes": ["filePattern1.yaml", "filePattern2.yaml"] } @@ -173,7 +173,7 @@ yaml.schemas: { e.g. -```json +```yaml "yaml.schemas": { "http://json.schemastore.org/composer": ["/*"], "file:///home/johnd/some-schema.json": ["some.yaml"], @@ -184,7 +184,7 @@ e.g. e.g. -```json +```yaml "yaml.schemas": { "kubernetes": ["/myYamlFile.yaml"] } @@ -192,7 +192,7 @@ e.g. e.g. -```json +```yaml "yaml.schemas": { "http://json.schemastore.org/composer": ["/*"], "kubernetes": ["/myYamlFile.yaml"] @@ -205,7 +205,7 @@ You can also use relative paths when working with multi root workspaces. Suppose you have a multi root workspace that is laid out like: -``` +```yaml My_first_project: test.yaml my_schema.json @@ -216,7 +216,7 @@ My_second_project: You must then associate schemas relative to the root of the multi root workspace project. -``` +```yaml yaml.schemas: { "My_first_project/my_schema.json": "test.yaml", "My_second_project/my_schema2.json": "test2.yaml" @@ -229,7 +229,7 @@ yaml.schemas: { Suppose a file is meant to be a component of an existing schema (like a `job.yaml` file in a circleci orb), but there isn't a standalone schema that you can reference. If there is a nested schema definition for this subcomponent, you can reference it using a url fragment, e.g.: -``` +```yaml yaml.schemas: { "https://json.schemastore.org/circleciconfig#/definitions/jobs/additionalProperties": "/src/jobs/*.yaml", } @@ -275,7 +275,7 @@ The image is located at `quay.io/redhat-developer/yaml-language-server` To run the image you can use: -``` +```sh docker run -it quay.io/redhat-developer/yaml-language-server:latest ``` @@ -379,7 +379,6 @@ This repository only contains the server implementation. Here are some known cli - [Eclipse Che](https://www.eclipse.org/che/) - [vscode-yaml](https://marketplace.visualstudio.com/items?itemName=redhat.vscode-yaml) for VSCode -- [ide-yaml](https://atom.io/packages/ide-yaml) for Atom editor - [coc-yaml](https://github.com/neoclide/coc-yaml) for [coc.nvim](https://github.com/neoclide/coc.nvim) - [Eclipse Wild Web Developer](https://marketplace.eclipse.org/content/eclipse-wild-web-developer-web-development-eclipse-ide) for Eclipse IDE - [lsp-mode](https://github.com/emacs-lsp/lsp-mode) for Emacs @@ -388,6 +387,7 @@ This repository only contains the server implementation. Here are some known cli - [monaco-yaml](https://monaco-yaml.js.org) for Monaco editor - [Vim-EasyComplete](https://github.com/jayli/vim-easycomplete) for Vim/NeoVim - [nova-yaml](https://github.com/robb-j/nova-yaml/) for Nova +- [volar-service-yaml](https://github.com/volarjs/services/tree/master/packages/yaml) for Volar ## Developer Support diff --git a/package.json b/package.json index 62be0e23..a367cafd 100644 --- a/package.json +++ b/package.json @@ -29,15 +29,13 @@ "type": "git", "url": "https://github.com/redhat-developer/yaml-language-server.git" }, - "optionalDependencies": { - "prettier": "2.8.7" - }, "dependencies": { "ajv": "^8.11.0", "lodash": "4.17.21", + "prettier": "^3.0.0", "request-light": "^0.5.7", "vscode-json-languageservice": "4.1.8", - "vscode-languageserver": "^7.0.0", + "vscode-languageserver": "^9.0.0", "vscode-languageserver-textdocument": "^1.0.1", "vscode-languageserver-types": "^3.16.0", "vscode-nls": "^5.0.0", @@ -49,7 +47,6 @@ "@types/chai": "^4.2.12", "@types/mocha": "8.2.2", "@types/node": "16.x", - "@types/prettier": "2.7.2", "@types/sinon": "^9.0.5", "@types/sinon-chai": "^3.2.5", "@typescript-eslint/eslint-plugin": "^5.38.0", @@ -57,9 +54,9 @@ "chai": "^4.2.0", "coveralls": "3.1.1", "eslint": "^8.24.0", - "eslint-config-prettier": "^8.5.0", + "eslint-config-prettier": "^9.0.0", "eslint-plugin-import": "^2.26.0", - "eslint-plugin-prettier": "^4.2.1", + "eslint-plugin-prettier": "^5.0.0", "http-proxy-agent": "^5.0.0", "https-proxy-agent": "^5.0.0", "mocha": "9.2.2", diff --git a/src/languageserver/handlers/languageHandlers.ts b/src/languageserver/handlers/languageHandlers.ts index 7820f06e..7f971167 100644 --- a/src/languageserver/handlers/languageHandlers.ts +++ b/src/languageserver/handlers/languageHandlers.ts @@ -114,7 +114,7 @@ export class LanguageHandlers { * Called when the formatter is invoked * Returns the formatted document content using prettier */ - formatterHandler(formatParams: DocumentFormattingParams): TextEdit[] { + formatterHandler(formatParams: DocumentFormattingParams): Promise { const document = this.yamlSettings.documents.get(formatParams.textDocument.uri); if (!document) { @@ -229,7 +229,7 @@ export class LanguageHandlers { return this.languageService.getCodeAction(textDocument, params); } - codeLensHandler(params: CodeLensParams): Thenable | CodeLens[] | undefined { + codeLensHandler(params: CodeLensParams): PromiseLike | CodeLens[] | undefined { const textDocument = this.yamlSettings.documents.get(params.textDocument.uri); if (!textDocument) { return; @@ -237,7 +237,7 @@ export class LanguageHandlers { return this.languageService.getCodeLens(textDocument); } - codeLensResolveHandler(param: CodeLens): Thenable | CodeLens { + codeLensResolveHandler(param: CodeLens): PromiseLike | CodeLens { return this.languageService.resolveCodeLens(param); } diff --git a/src/languageserver/handlers/requestHandlers.ts b/src/languageserver/handlers/requestHandlers.ts index 120dcb12..50d21f00 100644 --- a/src/languageserver/handlers/requestHandlers.ts +++ b/src/languageserver/handlers/requestHandlers.ts @@ -14,7 +14,10 @@ import { SchemaModificationNotification } from '../../requestTypes'; export class RequestHandlers { private languageService: LanguageService; - constructor(private readonly connection: Connection, languageService: LanguageService) { + constructor( + private readonly connection: Connection, + languageService: LanguageService + ) { this.languageService = languageService; } diff --git a/src/languageserver/handlers/settingsHandlers.ts b/src/languageserver/handlers/settingsHandlers.ts index 95ddba77..e2351ea7 100644 --- a/src/languageserver/handlers/settingsHandlers.ts +++ b/src/languageserver/handlers/settingsHandlers.ts @@ -81,7 +81,7 @@ export class SettingsHandler { if (settings.yaml.schemaStore) { this.yamlSettings.schemaStoreEnabled = settings.yaml.schemaStore.enable; - if (settings.yaml.schemaStore.url.length !== 0) { + if (settings.yaml.schemaStore.url?.length !== 0) { this.yamlSettings.schemaStoreUrl = settings.yaml.schemaStore.url; } } @@ -180,7 +180,7 @@ export class SettingsHandler { private async setSchemaStoreSettingsIfNotSet(): Promise { const schemaStoreIsSet = this.yamlSettings.schemaStoreSettings.length !== 0; let schemaStoreUrl = ''; - if (this.yamlSettings.schemaStoreUrl.length !== 0) { + if (this.yamlSettings.schemaStoreUrl?.length !== 0) { schemaStoreUrl = this.yamlSettings.schemaStoreUrl; } else { schemaStoreUrl = JSON_SCHEMASTORE_URL; diff --git a/src/languageserver/handlers/validationHandlers.ts b/src/languageserver/handlers/validationHandlers.ts index b0dd75f5..e0db58d7 100644 --- a/src/languageserver/handlers/validationHandlers.ts +++ b/src/languageserver/handlers/validationHandlers.ts @@ -14,7 +14,11 @@ export class ValidationHandler { private languageService: LanguageService; private yamlSettings: SettingsState; - constructor(private readonly connection: Connection, languageService: LanguageService, yamlSettings: SettingsState) { + constructor( + private readonly connection: Connection, + languageService: LanguageService, + yamlSettings: SettingsState + ) { this.languageService = languageService; this.yamlSettings = yamlSettings; diff --git a/src/languageserver/handlers/workspaceHandlers.ts b/src/languageserver/handlers/workspaceHandlers.ts index 448933ff..05b808b4 100644 --- a/src/languageserver/handlers/workspaceHandlers.ts +++ b/src/languageserver/handlers/workspaceHandlers.ts @@ -7,7 +7,10 @@ import { ExecuteCommandParams, Connection } from 'vscode-languageserver'; import { CommandExecutor } from '../commandExecutor'; export class WorkspaceHandlers { - constructor(private readonly connection: Connection, private readonly commandExecutor: CommandExecutor) {} + constructor( + private readonly connection: Connection, + private readonly commandExecutor: CommandExecutor + ) {} registerHandlers(): void { this.connection.onExecuteCommand((params) => this.executeCommand(params)); diff --git a/src/languageservice/parser/jsonParser07.ts b/src/languageservice/parser/jsonParser07.ts index a9bb2d48..537a9beb 100644 --- a/src/languageservice/parser/jsonParser07.ts +++ b/src/languageservice/parser/jsonParser07.ts @@ -286,7 +286,10 @@ export interface ISchemaCollector { class SchemaCollector implements ISchemaCollector { schemas: IApplicableSchema[] = []; - constructor(private focusOffset = -1, private exclude: ASTNode = null) {} + constructor( + private focusOffset = -1, + private exclude: ASTNode = null + ) {} add(schema: IApplicableSchema): void { this.schemas.push(schema); } @@ -400,7 +403,7 @@ export class ValidationResult { (problemType !== ProblemType.missingRequiredPropWarning || isArrayEqual(p.problemArgs, bestResult.problemArgs)) // missingProp is merged only with same problemArg ); if (mergingResult) { - if (mergingResult.problemArgs.length) { + if (mergingResult.problemArgs?.length) { mergingResult.problemArgs .filter((p) => !bestResult.problemArgs.includes(p)) .forEach((p) => bestResult.problemArgs.push(p)); @@ -888,6 +891,7 @@ function validate( ), source: getSchemaSource(schema, originalSchema), schemaUri: getSchemaUri(schema, originalSchema), + data: { values: schema.enum }, }); } } @@ -907,6 +911,7 @@ function validate( source: getSchemaSource(schema, originalSchema), schemaUri: getSchemaUri(schema, originalSchema), problemArgs: [JSON.stringify(schema.const)], + data: { values: [schema.const] }, }); validationResult.enumValueMatch = false; } else { @@ -1385,6 +1390,7 @@ function validate( length: propertyNode.keyNode.length, }, severity: DiagnosticSeverity.Warning, + code: ErrorCode.PropertyExpected, message: schema.errorMessage || localize('DisallowedExtraPropWarning', MSG_PROPERTY_NOT_ALLOWED, propertyName), source: getSchemaSource(schema, originalSchema), schemaUri: getSchemaUri(schema, originalSchema), diff --git a/src/languageservice/parser/yamlParser07.ts b/src/languageservice/parser/yamlParser07.ts index bd44891d..1f39a822 100644 --- a/src/languageservice/parser/yamlParser07.ts +++ b/src/languageservice/parser/yamlParser07.ts @@ -3,7 +3,6 @@ * Copyright (c) Adam Voss. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; import { Parser, Composer, Document, LineCounter, ParseOptions, DocumentOptions, SchemaOptions } from 'yaml'; import { YAMLDocument, SingleYAMLDocument } from './yaml-documents'; diff --git a/src/languageservice/services/documentSymbols.ts b/src/languageservice/services/documentSymbols.ts index 6aa631d6..e50c992c 100644 --- a/src/languageservice/services/documentSymbols.ts +++ b/src/languageservice/services/documentSymbols.ts @@ -3,7 +3,6 @@ * 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 { SymbolInformation, DocumentSymbol } from 'vscode-languageserver-types'; import { YAMLSchemaService } from './yamlSchemaService'; @@ -18,7 +17,10 @@ import { convertErrorToTelemetryMsg } from '../utils/objects'; export class YAMLDocumentSymbols { private jsonDocumentSymbols; - constructor(schemaService: YAMLSchemaService, private readonly telemetry?: Telemetry) { + constructor( + schemaService: YAMLSchemaService, + private readonly telemetry?: Telemetry + ) { this.jsonDocumentSymbols = new JSONDocumentSymbols(schemaService); // override 'getKeyLabel' to handle complex mapping diff --git a/src/languageservice/services/modelineUtil.ts b/src/languageservice/services/modelineUtil.ts index 76aa1bdd..46badd63 100644 --- a/src/languageservice/services/modelineUtil.ts +++ b/src/languageservice/services/modelineUtil.ts @@ -2,7 +2,7 @@ * Copyright (c) Red Hat, Inc. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; + import { SingleYAMLDocument } from '../parser/yamlParser07'; import { JSONDocument } from '../parser/jsonParser07'; diff --git a/src/languageservice/services/yamlCodeActions.ts b/src/languageservice/services/yamlCodeActions.ts index c0ef20b5..da0c7eea 100644 --- a/src/languageservice/services/yamlCodeActions.ts +++ b/src/languageservice/services/yamlCodeActions.ts @@ -28,9 +28,12 @@ import { FlowStyleRewriter } from '../utils/flow-style-rewriter'; import { ASTNode } from '../jsonASTTypes'; import * as _ from 'lodash'; import { SourceToken } from 'yaml/dist/parse/cst'; +import { ErrorCode } from 'vscode-json-languageservice'; interface YamlDiagnosticData { schemaUri: string[]; + values?: string[]; + properties?: string[]; } export class YamlCodeActions { private indentation = ' '; @@ -54,6 +57,7 @@ export class YamlCodeActions { result.push(...this.getUnusedAnchorsDelete(params.context.diagnostics, document)); result.push(...this.getConvertToBlockStyleActions(params.context.diagnostics, document)); result.push(...this.getKeyOrderActions(params.context.diagnostics, document)); + result.push(...this.getQuickFixForPropertyOrValueMismatch(params.context.diagnostics, document)); return result; } @@ -221,7 +225,7 @@ export class YamlCodeActions { const results: CodeAction[] = []; for (const diagnostic of diagnostics) { if (diagnostic.code === 'flowMap' || diagnostic.code === 'flowSeq') { - const node = getNodeforDiagnostic(document, diagnostic); + const node = getNodeForDiagnostic(document, diagnostic); if (isMap(node.internalNode) || isSeq(node.internalNode)) { const blockTypeDescription = isMap(node.internalNode) ? 'map' : 'sequence'; const rewriter = new FlowStyleRewriter(this.indentation); @@ -242,7 +246,7 @@ export class YamlCodeActions { const results: CodeAction[] = []; for (const diagnostic of diagnostics) { if (diagnostic?.code === 'mapKeyOrder') { - let node = getNodeforDiagnostic(document, diagnostic); + let node = getNodeForDiagnostic(document, diagnostic); while (node && node.type !== 'object') { node = node.parent; } @@ -292,8 +296,8 @@ export class YamlCodeActions { item.value.end.splice(newLineIndex, 1); } } else if (item.value?.type === 'block-scalar') { - const nwline = item.value.props.find((p) => p.type === 'newline'); - if (!nwline) { + const newline = item.value.props.find((p) => p.type === 'newline'); + if (!newline) { item.value.props.push({ type: 'newline', indent: 0, offset: item.value.offset, source: '\n' } as SourceToken); } } @@ -312,9 +316,52 @@ export class YamlCodeActions { } return results; } + + /** + * Check if diagnostic contains info for quick fix + * Supports Enum/Const/Property mismatch + */ + private getPossibleQuickFixValues(diagnostic: Diagnostic): string[] | undefined { + if (typeof diagnostic.data !== 'object') { + return; + } + if ( + diagnostic.code === ErrorCode.EnumValueMismatch && + 'values' in diagnostic.data && + Array.isArray((diagnostic.data as YamlDiagnosticData).values) + ) { + return (diagnostic.data as YamlDiagnosticData).values; + } else if ( + diagnostic.code === ErrorCode.PropertyExpected && + 'properties' in diagnostic.data && + Array.isArray((diagnostic.data as YamlDiagnosticData).properties) + ) { + return (diagnostic.data as YamlDiagnosticData).properties; + } + } + + private getQuickFixForPropertyOrValueMismatch(diagnostics: Diagnostic[], document: TextDocument): CodeAction[] { + const results: CodeAction[] = []; + for (const diagnostic of diagnostics) { + const values = this.getPossibleQuickFixValues(diagnostic); + if (!values?.length) { + continue; + } + for (const value of values) { + results.push( + CodeAction.create( + value, + createWorkspaceEdit(document.uri, [TextEdit.replace(diagnostic.range, value)]), + CodeActionKind.QuickFix + ) + ); + } + } + return results; + } } -function getNodeforDiagnostic(document: TextDocument, diagnostic: Diagnostic): ASTNode { +function getNodeForDiagnostic(document: TextDocument, diagnostic: Diagnostic): ASTNode { const yamlDocuments = yamlDocumentsCache.getYamlDocument(document); const startOffset = document.offsetAt(diagnostic.range.start); const yamlDoc = matchOffsetToDocument(startOffset, yamlDocuments); diff --git a/src/languageservice/services/yamlCodeLens.ts b/src/languageservice/services/yamlCodeLens.ts index 7157ccf8..fdfde4fa 100644 --- a/src/languageservice/services/yamlCodeLens.ts +++ b/src/languageservice/services/yamlCodeLens.ts @@ -15,7 +15,10 @@ import { convertErrorToTelemetryMsg } from '../utils/objects'; import { getSchemaTitle } from '../utils/schemaUtils'; export class YamlCodeLens { - constructor(private schemaService: YAMLSchemaService, private readonly telemetry?: Telemetry) {} + constructor( + private schemaService: YAMLSchemaService, + private readonly telemetry?: Telemetry + ) {} async getCodeLens(document: TextDocument): Promise { const result = []; @@ -44,7 +47,7 @@ export class YamlCodeLens { return result; } - resolveCodeLens(param: CodeLens): Thenable | CodeLens { + resolveCodeLens(param: CodeLens): PromiseLike | CodeLens { return param; } } diff --git a/src/languageservice/services/yamlCompletion.ts b/src/languageservice/services/yamlCompletion.ts index 99e061f0..513a23d6 100644 --- a/src/languageservice/services/yamlCompletion.ts +++ b/src/languageservice/services/yamlCompletion.ts @@ -293,7 +293,7 @@ export class YamlCompletion { proposed, }; - if (this.customTags.length > 0) { + if (this.customTags && this.customTags.length > 0) { this.getCustomTagValueCompletions(collector); } @@ -925,7 +925,8 @@ export class YamlCompletion { if (propertySchema) { this.addSchemaValueCompletions(propertySchema, separatorAfter, collector, types, 'value'); } - } else if (s.schema.additionalProperties) { + } + if (s.schema.additionalProperties) { this.addSchemaValueCompletions(s.schema.additionalProperties, separatorAfter, collector, types, 'value'); } } @@ -1205,7 +1206,7 @@ export class YamlCompletion { insertText = `\${${insertIndex++}:0}`; break; case 'string': - insertText = `\${${insertIndex++}:""}`; + insertText = `\${${insertIndex++}}`; break; case 'object': { diff --git a/src/languageservice/services/yamlFormatter.ts b/src/languageservice/services/yamlFormatter.ts index 203cf749..2d9bcaf6 100644 --- a/src/languageservice/services/yamlFormatter.ts +++ b/src/languageservice/services/yamlFormatter.ts @@ -3,13 +3,11 @@ * Copyright (c) Adam Voss. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; import { Range, Position, TextEdit, FormattingOptions } from 'vscode-languageserver-types'; import { CustomFormatterOptions, LanguageSettings } from '../yamlLanguageService'; -import * as prettier from 'prettier'; -import { Options } from 'prettier'; -import * as parser from 'prettier/parser-yaml'; +import { format, Options } from 'prettier'; +import * as parser from 'prettier/plugins/yaml'; import { TextDocument } from 'vscode-languageserver-textdocument'; export class YAMLFormatter { @@ -21,7 +19,10 @@ export class YAMLFormatter { } } - public format(document: TextDocument, options: FormattingOptions & CustomFormatterOptions): TextEdit[] { + public async format( + document: TextDocument, + options: Partial & CustomFormatterOptions = {} + ): Promise { if (!this.formatterEnabled) { return []; } @@ -44,7 +45,7 @@ export class YAMLFormatter { printWidth: options.printWidth, }; - const formatted = prettier.format(text, prettierOptions); + const formatted = await format(text, prettierOptions); return [TextEdit.replace(Range.create(Position.create(0, 0), document.positionAt(text.length)), formatted)]; } catch (error) { diff --git a/src/languageservice/services/yamlHover.ts b/src/languageservice/services/yamlHover.ts index 3e7cc8a5..f02c29d1 100644 --- a/src/languageservice/services/yamlHover.ts +++ b/src/languageservice/services/yamlHover.ts @@ -3,7 +3,6 @@ * 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 { Hover, MarkupContent, MarkupKind, Position, Range } from 'vscode-languageserver-types'; import { matchOffsetToDocument } from '../utils/arrUtils'; @@ -13,20 +12,24 @@ import { setKubernetesParserOption } from '../parser/isKubernetes'; import { TextDocument } from 'vscode-languageserver-textdocument'; import { yamlDocumentsCache } from '../parser/yaml-documents'; import { SingleYAMLDocument } from '../parser/yamlParser07'; -import { getNodeValue, IApplicableSchema } from '../parser/jsonParser07'; +import { IApplicableSchema } from '../parser/jsonParser07'; import { JSONSchema } from '../jsonSchema'; import { URI } from 'vscode-uri'; import * as path from 'path'; import { Telemetry } from '../telemetry'; import { convertErrorToTelemetryMsg } from '../utils/objects'; import { ASTNode } from 'vscode-json-languageservice'; +import { stringify as stringifyYAML } from 'yaml'; export class YAMLHover { private shouldHover: boolean; private indentation: string; private schemaService: YAMLSchemaService; - constructor(schemaService: YAMLSchemaService, private readonly telemetry?: Telemetry) { + constructor( + schemaService: YAMLSchemaService, + private readonly telemetry?: Telemetry + ) { this.shouldHover = true; this.schemaService = schemaService; } @@ -88,11 +91,6 @@ export class YAMLHover { ); const createHover = (contents: string): Hover => { - if (this.indentation !== undefined) { - const indentationMatchRegex = new RegExp(` {${this.indentation.length}}`, 'g'); - contents = contents.replace(indentationMatchRegex, ' '); - } - const markupContent: MarkupContent = { kind: MarkupKind.Markdown, value: contents, @@ -114,27 +112,31 @@ export class YAMLHover { let title: string | undefined = undefined; let markdownDescription: string | undefined = undefined; - let markdownEnumValueDescription: string | undefined = undefined; - let enumValue: string | undefined = undefined; + let markdownEnumDescriptions: string[] = []; const markdownExamples: string[] = []; + const markdownEnums: markdownEnum[] = []; matchingSchemas.every((s) => { if ((s.node === node || (node.type === 'property' && node.valueNode === s.node)) && !s.inverted && s.schema) { title = title || s.schema.title || s.schema.closestTitle; - markdownDescription = markdownDescription || s.schema.markdownDescription || toMarkdown(s.schema.description); + markdownDescription = markdownDescription || s.schema.markdownDescription || this.toMarkdown(s.schema.description); if (s.schema.enum) { - const idx = s.schema.enum.indexOf(getNodeValue(node)); if (s.schema.markdownEnumDescriptions) { - markdownEnumValueDescription = s.schema.markdownEnumDescriptions[idx]; + markdownEnumDescriptions = s.schema.markdownEnumDescriptions; } else if (s.schema.enumDescriptions) { - markdownEnumValueDescription = toMarkdown(s.schema.enumDescriptions[idx]); + markdownEnumDescriptions = s.schema.enumDescriptions.map(this.toMarkdown, this); + } else { + markdownEnumDescriptions = []; } - if (markdownEnumValueDescription) { - enumValue = s.schema.enum[idx]; + s.schema.enum.forEach((enumValue, idx) => { if (typeof enumValue !== 'string') { enumValue = JSON.stringify(enumValue); } - } + markdownEnums.push({ + value: enumValue, + description: markdownEnumDescriptions[idx], + }); + }); } if (s.schema.anyOf && isAllSchemasMatched(node, matchingSchemas, s.schema)) { //if append title and description of all matched schemas on hover @@ -142,7 +144,7 @@ export class YAMLHover { markdownDescription = ''; s.schema.anyOf.forEach((childSchema: JSONSchema, index: number) => { title += childSchema.title || s.schema.closestTitle || ''; - markdownDescription += childSchema.markdownDescription || toMarkdown(childSchema.description) || ''; + markdownDescription += childSchema.markdownDescription || this.toMarkdown(childSchema.description) || ''; if (index !== s.schema.anyOf.length - 1) { title += ' || '; markdownDescription += ' || '; @@ -153,7 +155,7 @@ export class YAMLHover { } if (s.schema.examples) { s.schema.examples.forEach((example) => { - markdownExamples.push(JSON.stringify(example, null, 2)); + markdownExamples.push(stringifyYAML(example, null, 2)); }); } } @@ -161,37 +163,69 @@ export class YAMLHover { }); let result = ''; if (title) { - result = '#### ' + toMarkdown(title); + result = '#### ' + this.toMarkdown(title); } if (markdownDescription) { - if (result.length > 0) { - result += '\n\n'; - } + result = ensureLineBreak(result); result += markdownDescription; } - if (markdownEnumValueDescription) { - if (result.length > 0) { - result += '\n\n'; - } - result += `\`${toMarkdownCodeBlock(enumValue)}\`: ${markdownEnumValueDescription}`; + if (markdownEnums.length !== 0) { + result = ensureLineBreak(result); + result += 'Allowed Values:\n\n'; + markdownEnums.forEach((me) => { + if (me.description) { + result += `* \`${toMarkdownCodeBlock(me.value)}\`: ${me.description}\n`; + } else { + result += `* \`${toMarkdownCodeBlock(me.value)}\`\n`; + } + }); } if (markdownExamples.length !== 0) { - if (result.length > 0) { - result += '\n\n'; - } - result += 'Examples:'; markdownExamples.forEach((example) => { - result += `\n\n\`\`\`${example}\`\`\``; + result = ensureLineBreak(result); + result += 'Example:\n\n'; + result += `\`\`\`yaml\n${example}\`\`\`\n`; }); } if (result.length > 0 && schema.schema.url) { - result += `\n\nSource: [${getSchemaName(schema.schema)}](${schema.schema.url})`; + result = ensureLineBreak(result); + result += `Source: [${getSchemaName(schema.schema)}](${schema.schema.url})`; } return createHover(result); } return null; }); } + + // copied from https://github.com/microsoft/vscode-json-languageservice/blob/2ea5ad3d2ffbbe40dea11cfe764a502becf113ce/src/services/jsonHover.ts#L112 + private toMarkdown(plain: string | undefined): string | undefined { + if (plain) { + let escaped = plain.replace(/([^\n\r])(\r?\n)([^\n\r])/gm, '$1\n\n$3'); // single new lines to \n\n (Markdown paragraph) + escaped = escaped.replace(/[\\`*_{}[\]()#+\-.!]/g, '\\$&'); // escape markdown syntax tokens: http://daringfireball.net/projects/markdown/syntax#backslash + if (this.indentation !== undefined) { + // escape indentation whitespace to prevent it from being converted to markdown code blocks. + const indentationMatchRegex = new RegExp(` {${this.indentation.length}}`, 'g'); + escaped = escaped.replace(indentationMatchRegex, ' '); + } + return escaped; + } + return undefined; + } +} + +interface markdownEnum { + value: string; + description: string; +} + +function ensureLineBreak(content: string): string { + if (content.length === 0) { + return content; + } + if (!content.endsWith('\n')) { + content += '\n'; + } + return content + '\n'; } function getSchemaName(schema: JSONSchema): string { @@ -206,17 +240,6 @@ function getSchemaName(schema: JSONSchema): string { return result; } -// copied from https://github.com/microsoft/vscode-json-languageservice/blob/2ea5ad3d2ffbbe40dea11cfe764a502becf113ce/src/services/jsonHover.ts#L112 -function toMarkdown(plain: string): string; -function toMarkdown(plain: string | undefined): string | undefined; -function toMarkdown(plain: string | undefined): string | undefined { - if (plain) { - const 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; -} - // copied from https://github.com/microsoft/vscode-json-languageservice/blob/2ea5ad3d2ffbbe40dea11cfe764a502becf113ce/src/services/jsonHover.ts#L122 function toMarkdownCodeBlock(content: string): string { // see https://daringfireball.net/projects/markdown/syntax#precode diff --git a/src/languageservice/services/yamlSchemaService.ts b/src/languageservice/services/yamlSchemaService.ts index 08c03229..453b74eb 100644 --- a/src/languageservice/services/yamlSchemaService.ts +++ b/src/languageservice/services/yamlSchemaService.ts @@ -3,7 +3,6 @@ * 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 { JSONSchema, JSONSchemaMap, JSONSchemaRef } from '../jsonSchema'; import { SchemaPriority, SchemaRequestService, WorkspaceContextService } from '../yamlLanguageService'; @@ -242,7 +241,7 @@ export class YAMLSchemaService extends JSONSchemaService { } const toWalk: JSONSchema[] = [node]; - const seen: JSONSchema[] = []; + const seen: Set = new Set(); // eslint-disable-next-line @typescript-eslint/no-explicit-any const openPromises: Promise[] = []; @@ -278,7 +277,7 @@ export class YAMLSchemaService extends JSONSchemaService { } }; const handleRef = (next: JSONSchema): void => { - const seenRefs = []; + const seenRefs = new Set(); while (next.$ref) { const ref = next.$ref; const segments = ref.split('#', 2); @@ -289,9 +288,9 @@ export class YAMLSchemaService extends JSONSchemaService { openPromises.push(resolveExternalLink(next, segments[0], segments[1], parentSchemaURL, parentSchemaDependencies)); return; } else { - if (seenRefs.indexOf(ref) === -1) { + if (!seenRefs.has(ref)) { merge(next, parentSchema, parentSchemaURL, segments[1]); // can set next.$ref again, use seenRefs to avoid circle - seenRefs.push(ref); + seenRefs.add(ref); } } } @@ -330,10 +329,10 @@ export class YAMLSchemaService extends JSONSchemaService { while (toWalk.length) { const next = toWalk.pop(); - if (seen.indexOf(next) >= 0) { + if (seen.has(next)) { continue; } - seen.push(next); + seen.add(next); handleRef(next); } return Promise.all(openPromises); diff --git a/src/languageservice/services/yamlSelectionRanges.ts b/src/languageservice/services/yamlSelectionRanges.ts index b361ba8d..f60dcc91 100644 --- a/src/languageservice/services/yamlSelectionRanges.ts +++ b/src/languageservice/services/yamlSelectionRanges.ts @@ -3,81 +3,72 @@ import { yamlDocumentsCache } from '../parser/yaml-documents'; import { TextDocument } from 'vscode-languageserver-textdocument'; import { ASTNode } from 'vscode-json-languageservice'; -export function getSelectionRanges(document: TextDocument, positions: Position[]): SelectionRange[] | undefined { - if (!document) { - return; - } +export function getSelectionRanges(document: TextDocument, positions: Position[]): SelectionRange[] { const doc = yamlDocumentsCache.getYamlDocument(document); return positions.map((position) => { const ranges = getRanges(position); - let current: SelectionRange; + let current: SelectionRange | undefined; for (const range of ranges) { current = SelectionRange.create(range, current); } - if (!current) { - current = SelectionRange.create({ - start: position, - end: position, - }); - } - return current; + return current ?? SelectionRange.create({ start: position, end: position }); }); function getRanges(position: Position): Range[] { const offset = document.offsetAt(position); const result: Range[] = []; for (const ymlDoc of doc.documents) { - let currentNode: ASTNode; - let firstNodeOffset: number; - let isFirstNode = true; + let currentNode: ASTNode | undefined; + let overrideStartOffset: number | undefined; ymlDoc.visit((node) => { const endOffset = node.offset + node.length; // Skip if end offset doesn't even reach cursor position if (endOffset < offset) { return true; } - let startOffset = node.offset; - // Recheck start offset with the trimmed one in case of this - // key: - // - value - // ↑ - if (startOffset > offset) { - const nodePosition = document.positionAt(startOffset); - if (nodePosition.line !== position.line) { - return true; - } - const lineBeginning = { line: nodePosition.line, character: 0 }; - const text = document.getText({ - start: lineBeginning, - end: nodePosition, - }); - if (text.trim().length !== 0) { + // Skip if we're ending at new line + // times: + // - second: 1 + // millisecond: 10 + // | - second: 2 + // ↑ millisecond: 0 + // (| is actually part of { second: 1, millisecond: 10 }) + // \r\n doesn't matter here + if (getTextFromOffsets(endOffset - 1, endOffset) === '\n') { + if (endOffset - 1 < offset) { return true; } - startOffset = document.offsetAt(lineBeginning); - if (startOffset > offset) { + } + + let startOffset = node.offset; + if (startOffset > offset) { + // Recheck start offset for some special cases + const newOffset = getStartOffsetForSpecialCases(node, position); + if (!newOffset || newOffset > offset) { return true; } + startOffset = newOffset; } + // Allow equal for children to override if (!currentNode || startOffset >= currentNode.offset) { currentNode = node; - firstNodeOffset = startOffset; + overrideStartOffset = startOffset; } return true; }); while (currentNode) { - const startOffset = isFirstNode ? firstNodeOffset : currentNode.offset; + const startOffset = overrideStartOffset ?? currentNode.offset; const endOffset = currentNode.offset + currentNode.length; const range = { start: document.positionAt(startOffset), end: document.positionAt(endOffset), }; const text = document.getText(range); - const trimmedText = text.trimEnd(); - const trimmedLength = text.length - trimmedText.length; - if (trimmedLength > 0) { - range.end = document.positionAt(endOffset - trimmedLength); + const trimmedText = trimEndNewLine(text); + const trimmedEndOffset = startOffset + trimmedText.length; + if (trimmedEndOffset >= offset) { + range.end = document.positionAt(trimmedEndOffset); } // Add a jump between '' "" {} [] const isSurroundedBy = (startCharacter: string, endCharacter?: string): boolean => { @@ -95,7 +86,7 @@ export function getSelectionRanges(document: TextDocument, positions: Position[] } result.push(range); currentNode = currentNode.parent; - isFirstNode = false; + overrideStartOffset = undefined; } // A position can't be in multiple documents if (result.length > 0) { @@ -104,4 +95,48 @@ export function getSelectionRanges(document: TextDocument, positions: Position[] } return result.reverse(); } + + function getStartOffsetForSpecialCases(node: ASTNode, position: Position): number | undefined { + const nodeStartPosition = document.positionAt(node.offset); + if (nodeStartPosition.line !== position.line) { + return; + } + + if (node.parent?.type === 'array') { + // array: + // - value + // ↑ + if (getTextFromOffsets(node.offset - 2, node.offset) === '- ') { + return node.offset - 2; + } + } + + if (node.type === 'array' || node.type === 'object') { + // array: + // - value + // ↑ + const lineBeginning = { line: nodeStartPosition.line, character: 0 }; + const text = document.getText({ start: lineBeginning, end: nodeStartPosition }); + if (text.trim().length === 0) { + return document.offsetAt(lineBeginning); + } + } + } + + function getTextFromOffsets(startOffset: number, endOffset: number): string { + return document.getText({ + start: document.positionAt(startOffset), + end: document.positionAt(endOffset), + }); + } +} + +function trimEndNewLine(str: string): string { + if (str.endsWith('\r\n')) { + return str.substring(0, str.length - 2); + } + if (str.endsWith('\n')) { + return str.substring(0, str.length - 1); + } + return str; } diff --git a/src/languageservice/services/yamlValidation.ts b/src/languageservice/services/yamlValidation.ts index 8ba3f023..04c58edf 100644 --- a/src/languageservice/services/yamlValidation.ts +++ b/src/languageservice/services/yamlValidation.ts @@ -3,7 +3,6 @@ * 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 { Diagnostic, Position } from 'vscode-languageserver-types'; import { LanguageSettings } from '../yamlLanguageService'; @@ -49,7 +48,10 @@ export class YAMLValidation { private MATCHES_MULTIPLE = 'Matches multiple schemas when only one must validate.'; - constructor(schemaService: YAMLSchemaService, private readonly telemetry?: Telemetry) { + constructor( + schemaService: YAMLSchemaService, + private readonly telemetry?: Telemetry + ) { this.validationEnabled = true; this.jsonValidation = new JSONValidation(schemaService, Promise); } diff --git a/src/languageservice/utils/documentPositionCalculator.ts b/src/languageservice/utils/documentPositionCalculator.ts index 0abbf098..a1b2d0f7 100644 --- a/src/languageservice/utils/documentPositionCalculator.ts +++ b/src/languageservice/utils/documentPositionCalculator.ts @@ -3,7 +3,6 @@ * 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 { Position } from 'vscode-languageserver-types'; diff --git a/src/languageservice/utils/objects.ts b/src/languageservice/utils/objects.ts index 7337cf94..8b45199b 100644 --- a/src/languageservice/utils/objects.ts +++ b/src/languageservice/utils/objects.ts @@ -2,7 +2,6 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types export function equals(one: any, other: any): boolean { diff --git a/src/languageservice/utils/schemaUtils.ts b/src/languageservice/utils/schemaUtils.ts index d36166ed..bc763fb4 100644 --- a/src/languageservice/utils/schemaUtils.ts +++ b/src/languageservice/utils/schemaUtils.ts @@ -16,8 +16,8 @@ export function getSchemaTypeName(schema: JSONSchema): string { return Array.isArray(schema.type) ? schema.type.join(' | ') : closestTitleWithType - ? schema.type.concat('(', schema.closestTitle, ')') - : schema.type || schema.closestTitle; //object + ? schema.type.concat('(', schema.closestTitle, ')') + : schema.type || schema.closestTitle; //object } /** diff --git a/src/languageservice/utils/strings.ts b/src/languageservice/utils/strings.ts index 726b260d..6171411d 100644 --- a/src/languageservice/utils/strings.ts +++ b/src/languageservice/utils/strings.ts @@ -2,7 +2,6 @@ * 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 { CharCode } from './charCode'; diff --git a/src/languageservice/yamlLanguageService.ts b/src/languageservice/yamlLanguageService.ts index 6fd1153d..457a9d09 100644 --- a/src/languageservice/yamlLanguageService.ts +++ b/src/languageservice/yamlLanguageService.ts @@ -157,28 +157,28 @@ export interface CustomFormatterOptions { } export interface LanguageService { - configure(settings: LanguageSettings): void; - registerCustomSchemaProvider(schemaProvider: CustomSchemaProvider): void; - doComplete(document: TextDocument, position: Position, isKubernetes: boolean): Promise; - doValidation(document: TextDocument, isKubernetes: boolean): Promise; - doHover(document: TextDocument, position: Position): Promise; - findDocumentSymbols(document: TextDocument, context: DocumentSymbolsContext): SymbolInformation[]; - findDocumentSymbols2(document: TextDocument, context: DocumentSymbolsContext): DocumentSymbol[]; - findLinks(document: TextDocument): Promise; - resetSchema(uri: string): boolean; - doFormat(document: TextDocument, options: CustomFormatterOptions): TextEdit[]; - doDefinition(document: TextDocument, params: DefinitionParams): DefinitionLink[] | undefined; - doDocumentOnTypeFormatting(document: TextDocument, params: DocumentOnTypeFormattingParams): TextEdit[] | undefined; - addSchema(schemaID: string, schema: JSONSchema): void; - deleteSchema(schemaID: string): void; - modifySchemaContent(schemaAdditions: SchemaAdditions): void; - deleteSchemaContent(schemaDeletions: SchemaDeletions): void; - deleteSchemasWhole(schemaDeletions: SchemaDeletionsAll): void; - getFoldingRanges(document: TextDocument, context: FoldingRangesContext): FoldingRange[] | null; - getSelectionRanges(document: TextDocument, positions: Position[]): SelectionRange[] | undefined; - getCodeAction(document: TextDocument, params: CodeActionParams): CodeAction[] | undefined; - getCodeLens(document: TextDocument): Thenable | CodeLens[] | undefined; - resolveCodeLens(param: CodeLens): Thenable | CodeLens; + configure: (settings: LanguageSettings) => void; + registerCustomSchemaProvider: (schemaProvider: CustomSchemaProvider) => void; + doComplete: (document: TextDocument, position: Position, isKubernetes: boolean) => Promise; + doValidation: (document: TextDocument, isKubernetes: boolean) => Promise; + doHover: (document: TextDocument, position: Position) => Promise; + findDocumentSymbols: (document: TextDocument, context?: DocumentSymbolsContext) => SymbolInformation[]; + findDocumentSymbols2: (document: TextDocument, context?: DocumentSymbolsContext) => DocumentSymbol[]; + findLinks: (document: TextDocument) => Promise; + resetSchema: (uri: string) => boolean; + doFormat: (document: TextDocument, options?: CustomFormatterOptions) => Promise; + doDefinition: (document: TextDocument, params: DefinitionParams) => DefinitionLink[] | undefined; + doDocumentOnTypeFormatting: (document: TextDocument, params: DocumentOnTypeFormattingParams) => TextEdit[] | undefined; + addSchema: (schemaID: string, schema: JSONSchema) => void; + deleteSchema: (schemaID: string) => void; + modifySchemaContent: (schemaAdditions: SchemaAdditions) => void; + deleteSchemaContent: (schemaDeletions: SchemaDeletions) => void; + deleteSchemasWhole: (schemaDeletions: SchemaDeletionsAll) => void; + getFoldingRanges: (document: TextDocument, context: FoldingRangesContext) => FoldingRange[] | null; + getSelectionRanges: (document: TextDocument, positions: Position[]) => SelectionRange[]; + getCodeAction: (document: TextDocument, params: CodeActionParams) => CodeAction[] | undefined; + getCodeLens: (document: TextDocument) => PromiseLike | CodeLens[] | undefined; + resolveCodeLens: (param: CodeLens) => PromiseLike | CodeLens; } export function getLanguageService(params: { diff --git a/src/server.ts b/src/server.ts index ad8128dc..0e91671e 100644 --- a/src/server.ts +++ b/src/server.ts @@ -4,7 +4,6 @@ * 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 { createConnection, Connection, ProposedFeatures } from 'vscode-languageserver/node'; import * as nls from 'vscode-nls'; diff --git a/src/yamlSettings.ts b/src/yamlSettings.ts index 011b0b8b..fc026034 100644 --- a/src/yamlSettings.ts +++ b/src/yamlSettings.ts @@ -60,7 +60,7 @@ export interface JSONSchemaSettings { export class SettingsState { yamlConfigurationSettings: JSONSchemaSettings[] = undefined; schemaAssociations: ISchemaAssociations | SchemaConfiguration[] | undefined = undefined; - formatterRegistration: Thenable = null; + formatterRegistration: PromiseLike = null; specificValidatorPaths = []; schemaConfigurationSettings = []; yamlShouldValidate = true; diff --git a/test/autoCompletion.test.ts b/test/autoCompletion.test.ts index 78b14996..eba8fa6d 100644 --- a/test/autoCompletion.test.ts +++ b/test/autoCompletion.test.ts @@ -1047,7 +1047,7 @@ describe('Auto Completion Tests', () => { const completion = parseSetup(content, content.lastIndexOf('Ba') + 2); // pos: 3+2 completion .then(function (result) { - assert.strictEqual('fooBar:\n - ${1:""}', result.items[0].insertText); + assert.strictEqual('fooBar:\n - ${1}', result.items[0].insertText); }) .then(done, done); }); @@ -3126,5 +3126,10 @@ describe('Auto Completion Tests', () => { expect(result.items.map((i) => i.label)).to.have.members(['fruit', 'vegetable']); }); }); + it('Should function when settings are undefined', async () => { + languageService.configure({ completion: true }); + const content = ''; + await parseSetup(content, 0); + }); }); }); diff --git a/test/autoCompletionFix.test.ts b/test/autoCompletionFix.test.ts index 81053315..c720ce76 100644 --- a/test/autoCompletionFix.test.ts +++ b/test/autoCompletionFix.test.ts @@ -482,7 +482,7 @@ objB: expect(completion.items.length).equal(1); expect(completion.items[0]).to.be.deep.equal( - createExpectedCompletion('objectWithArray', 'objectWithArray:\n - ${1:""}', 1, 4, 1, 4, 10, 2, { + createExpectedCompletion('objectWithArray', 'objectWithArray:\n - ${1}', 1, 4, 1, 4, 10, 2, { documentation: '', }) ); @@ -1183,6 +1183,30 @@ objB: expect(completion.items[0].insertText).to.be.equal('test1'); }); + it('should suggest defaultSnippets from additionalProperties', async () => { + const schema: JSONSchema = { + type: 'object', + properties: { + id: { + type: 'string', + }, + }, + additionalProperties: { + anyOf: [ + { + type: 'string', + defaultSnippets: [{ label: 'snippet', body: 'snippetBody' }], + }, + ], + }, + }; + schemaProvider.addSchema(SCHEMA_ID, schema); + const content = 'value: |\n|'; + const completion = await parseCaret(content); + + expect(completion.items.map((i) => i.insertText)).to.be.deep.equal(['snippetBody']); + }); + describe('should suggest prop of the object (based on not completed prop name)', () => { const schema: JSONSchema = { definitions: { diff --git a/test/formatter.test.ts b/test/formatter.test.ts index ed970a00..5ab02ae4 100644 --- a/test/formatter.test.ts +++ b/test/formatter.test.ts @@ -24,7 +24,7 @@ describe('Formatter Tests', () => { describe('Formatter', function () { describe('Test that formatter works with custom tags', function () { // eslint-disable-next-line @typescript-eslint/no-explicit-any - function parseSetup(content: string, options: any = {}): TextEdit[] { + function parseSetup(content: string, options: any = {}): Promise { const testTextDocument = setupTextDocument(content); yamlSettings.documents = new TextDocumentTestManager(); (yamlSettings.documents as TextDocumentTestManager).set(testTextDocument); @@ -35,31 +35,32 @@ describe('Formatter Tests', () => { }); } - it('Formatting works without custom tags', () => { + it('Formatting works without custom tags', async () => { const content = 'cwd: test'; - const edits = parseSetup(content); + const edits = await parseSetup(content); + console.dir({ edits }); assert.notEqual(edits.length, 0); assert.equal(edits[0].newText, 'cwd: test\n'); }); - it('Formatting works with custom tags', () => { + it('Formatting works with custom tags', async () => { const content = 'cwd: !Test test'; - const edits = parseSetup(content); + const edits = await parseSetup(content); assert.notEqual(edits.length, 0); assert.equal(edits[0].newText, 'cwd: !Test test\n'); }); - it('Formatting wraps text', () => { + it('Formatting wraps text', async () => { const content = `comments: > test test test test test test test test test test test test`; - const edits = parseSetup(content, { + const edits = await parseSetup(content, { printWidth: 20, proseWrap: 'always', }); assert.equal(edits[0].newText, 'comments: >\n test test test\n test test test\n test test test\n test test test\n'); }); - it('Formatting uses tabSize', () => { + it('Formatting uses tabSize', async () => { const content = `map: k1: v1 k2: v2 @@ -68,7 +69,7 @@ list: - item2 `; - const edits = parseSetup(content, { + const edits = await parseSetup(content, { tabSize: 5, }); @@ -82,7 +83,7 @@ list: assert.equal(edits[0].newText, expected); }); - it('Formatting uses tabWidth', () => { + it('Formatting uses tabWidth', async () => { const content = `map: k1: v1 k2: v2 @@ -91,7 +92,7 @@ list: - item2 `; - const edits = parseSetup(content, { + const edits = await parseSetup(content, { tabWidth: 5, }); @@ -105,7 +106,7 @@ list: assert.equal(edits[0].newText, expected); }); - it('Formatting uses tabWidth over tabSize', () => { + it('Formatting uses tabWidth over tabSize', async () => { const content = `map: k1: v1 k2: v2 @@ -114,7 +115,7 @@ list: - item2 `; - const edits = parseSetup(content, { + const edits = await parseSetup(content, { tabSize: 3, tabWidth: 5, }); diff --git a/test/hover.test.ts b/test/hover.test.ts index afd28690..404ed676 100644 --- a/test/hover.test.ts +++ b/test/hover.test.ts @@ -556,6 +556,37 @@ users: ); }); + it('Hover displays enum descriptions if present', async () => { + schemaProvider.addSchema(SCHEMA_ID, { + type: 'object', + properties: { + animal: { + type: 'string', + description: 'should return this description', + enum: ['cat', 'dog', 'non'], + enumDescriptions: ['', 'Canis familiaris'], + }, + }, + }); + const content = 'animal:\n ca|t|'; // len: 13, pos: 12 + const result = await parseSetup(content); + + assert.strictEqual(MarkupContent.is(result.contents), true); + assert.strictEqual((result.contents as MarkupContent).kind, 'markdown'); + assert.strictEqual( + (result.contents as MarkupContent).value, + `should return this description + +Allowed Values: + +* \`cat\` +* \`dog\`: Canis familiaris +* \`non\` + +Source: [${SCHEMA_ID}](file:///${SCHEMA_ID})` + ); + }); + it('Hover works on examples', async () => { schemaProvider.addSchema(SCHEMA_ID, { type: 'object', @@ -564,7 +595,14 @@ users: type: 'string', description: 'should return this description', enum: ['cat', 'dog'], - examples: ['cat', 'dog'], + examples: [ + 'cat', + { + animal: { + type: 'dog', + }, + }, + ], }, }, }); @@ -577,11 +615,23 @@ users: (result.contents as MarkupContent).value, `should return this description -Examples: +Allowed Values: + +* \`cat\` +* \`dog\` + +Example: + +\`\`\`yaml +cat +\`\`\` -\`\`\`"cat"\`\`\` +Example: -\`\`\`"dog"\`\`\` +\`\`\`yaml +animal: + type: dog +\`\`\` Source: [${SCHEMA_ID}](file:///${SCHEMA_ID})` ); diff --git a/test/schema.test.ts b/test/schema.test.ts index b4ddd1fd..1747b1c4 100644 --- a/test/schema.test.ts +++ b/test/schema.test.ts @@ -1,5 +1,3 @@ -'use strict'; - import * as assert from 'assert'; import * as parser from '../src/languageservice/parser/yamlParser07'; import * as SchemaService from '../src/languageservice/services/yamlSchemaService'; diff --git a/test/schemaValidation.test.ts b/test/schemaValidation.test.ts index 71268db7..829310c1 100644 --- a/test/schemaValidation.test.ts +++ b/test/schemaValidation.test.ts @@ -26,6 +26,7 @@ import { KUBERNETES_SCHEMA_URL } from '../src/languageservice/utils/schemaUrls'; import { IProblem } from '../src/languageservice/parser/jsonParser07'; import { JSONSchema } from '../src/languageservice/jsonSchema'; import { TestTelemetry } from './utils/testsTypes'; +import { ErrorCode } from 'vscode-json-languageservice'; describe('Validation Tests', () => { let languageSettingsSetup: ServiceSetup; @@ -396,7 +397,8 @@ describe('Validation Tests', () => { 4, DiagnosticSeverity.Error, `yaml-schema: file:///${SCHEMA_ID}`, - `file:///${SCHEMA_ID}` + `file:///${SCHEMA_ID}`, + ErrorCode.PropertyExpected ) ); }) @@ -1289,7 +1291,7 @@ obj: 4, 18, DiagnosticSeverity.Error, - 'yaml-schema: Package', + 'yaml-schema: Composer Package', 'https://raw.githubusercontent.com/composer/composer/master/res/composer-schema.json' ) ); @@ -1312,6 +1314,7 @@ obj: DiagnosticSeverity.Error, 'yaml-schema: Drone CI configuration file', 'https://json.schemastore.org/drone', + ErrorCode.PropertyExpected, { properties: [ 'type', diff --git a/test/utils/testsTypes.ts b/test/utils/testsTypes.ts index 6dbd78bd..96761a10 100644 --- a/test/utils/testsTypes.ts +++ b/test/utils/testsTypes.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Event, NotificationHandler, RequestHandler } from 'vscode-jsonrpc'; +import { Disposable, Event, NotificationHandler, RequestHandler } from 'vscode-jsonrpc'; import { ApplyWorkspaceEditParams, WorkspaceEdit, @@ -14,6 +14,8 @@ import { CreateFilesParams, RenameFilesParams, DeleteFilesParams, + ClientCapabilities, + ServerCapabilities, } from 'vscode-languageserver-protocol'; import { Connection, RemoteWorkspace } from 'vscode-languageserver'; import { TelemetryImpl } from '../../src/languageserver/telemetry'; @@ -27,6 +29,9 @@ export class TestWorkspace implements RemoteWorkspace { applyEdit(paramOrEdit: ApplyWorkspaceEditParams | WorkspaceEdit): Promise { throw new Error('Method not implemented.'); } + fillServerCapabilities(capabilities: ServerCapabilities): void { + throw new Error('Method not implemented.'); + } getConfiguration(): Promise; getConfiguration(section: string): Promise; getConfiguration(item: ConfigurationItem): Promise; @@ -37,23 +42,26 @@ export class TestWorkspace implements RemoteWorkspace { getWorkspaceFolders(): Promise { throw new Error('Method not implemented.'); } + initialize(capabilities: ClientCapabilities): void { + throw new Error('Method not implemented.'); + } onDidChangeWorkspaceFolders: Event; - onDidCreateFiles(handler: NotificationHandler): void { + onDidCreateFiles(handler: NotificationHandler): Disposable { throw new Error('Method not implemented.'); } - onDidRenameFiles(handler: NotificationHandler): void { + onDidRenameFiles(handler: NotificationHandler): Disposable { throw new Error('Method not implemented.'); } - onDidDeleteFiles(handler: NotificationHandler): void { + onDidDeleteFiles(handler: NotificationHandler): Disposable { throw new Error('Method not implemented.'); } - onWillCreateFiles(handler: RequestHandler): void { + onWillCreateFiles(handler: RequestHandler): Disposable { throw new Error('Method not implemented.'); } - onWillRenameFiles(handler: RequestHandler): void { + onWillRenameFiles(handler: RequestHandler): Disposable { throw new Error('Method not implemented.'); } - onWillDeleteFiles(handler: RequestHandler): void { + onWillDeleteFiles(handler: RequestHandler): Disposable { throw new Error('Method not implemented.'); } } diff --git a/test/utils/verifyError.ts b/test/utils/verifyError.ts index ea7f17c5..d2769c74 100644 --- a/test/utils/verifyError.ts +++ b/test/utils/verifyError.ts @@ -39,9 +39,19 @@ export function createDiagnosticWithData( severity: DiagnosticSeverity = 1, source = 'YAML', schemaUri: string | string[], + code: string | number = ErrorCode.Undefined, data: Record = {} ): Diagnostic { - const diagnostic: Diagnostic = createExpectedError(message, startLine, startCharacter, endLine, endCharacter, severity, source); + const diagnostic: Diagnostic = createExpectedError( + message, + startLine, + startCharacter, + endLine, + endCharacter, + severity, + source, + code + ); diagnostic.data = { schemaUri: typeof schemaUri === 'string' ? [schemaUri] : schemaUri, ...data }; return diagnostic; } diff --git a/test/yamlCodeActions.test.ts b/test/yamlCodeActions.test.ts index aa2b72ce..447cfdcb 100644 --- a/test/yamlCodeActions.test.ts +++ b/test/yamlCodeActions.test.ts @@ -22,6 +22,7 @@ import { setupTextDocument, TEST_URI } from './utils/testHelper'; import { createDiagnosticWithData, createExpectedError, createUnusedAnchorDiagnostic } from './utils/verifyError'; import { YamlCommands } from '../src/commands'; import { LanguageSettings } from '../src'; +import { ErrorCode } from 'vscode-json-languageservice'; const expect = chai.expect; chai.use(sinonChai); @@ -377,4 +378,58 @@ animals: [dog , cat , mouse] `; ]); }); }); + + describe('Enum value or property mismatch quick fix', () => { + it('should generate proper action for enum mismatch', () => { + const doc = setupTextDocument('foo: value1'); + const diagnostic = createDiagnosticWithData( + 'message', + 0, + 5, + 0, + 11, + DiagnosticSeverity.Hint, + 'YAML', + 'schemaUri', + ErrorCode.EnumValueMismatch, + { values: ['valueX', 'valueY'] } + ); + const params: CodeActionParams = { + context: CodeActionContext.create([diagnostic]), + range: undefined, + textDocument: TextDocumentIdentifier.create(TEST_URI), + }; + const actions = new YamlCodeActions(clientCapabilities); + const result = actions.getCodeAction(doc, params); + expect(result.map((r) => r.title)).deep.equal(['valueX', 'valueY']); + expect(result[0].edit.changes[TEST_URI]).deep.equal([TextEdit.replace(Range.create(0, 5, 0, 11), 'valueX')]); + }); + + it('should generate proper action for wrong property', () => { + const doc = setupTextDocument('foo: value1'); + const diagnostic = createDiagnosticWithData( + 'message', + 0, + 0, + 0, + 3, + DiagnosticSeverity.Hint, + 'YAML', + 'schemaUri', + ErrorCode.PropertyExpected, + { + properties: ['fooX', 'fooY'], + } + ); + const params: CodeActionParams = { + context: CodeActionContext.create([diagnostic]), + range: undefined, + textDocument: TextDocumentIdentifier.create(TEST_URI), + }; + const actions = new YamlCodeActions(clientCapabilities); + const result = actions.getCodeAction(doc, params); + expect(result.map((r) => r.title)).deep.equal(['fooX', 'fooY']); + expect(result[0].edit.changes[TEST_URI]).deep.equal([TextEdit.replace(Range.create(0, 0, 0, 3), 'fooX')]); + }); + }); }); diff --git a/test/yamlSelectionRanges.test.ts b/test/yamlSelectionRanges.test.ts index 3ac78ab4..93fd67e7 100644 --- a/test/yamlSelectionRanges.test.ts +++ b/test/yamlSelectionRanges.test.ts @@ -12,14 +12,16 @@ function isRangesEqual(range1: Range, range2: Range): boolean { ); } -function expectSelections(selectionRange: SelectionRange, ranges: Range[]): void { +function expectSelections(selectionRange: SelectionRange | undefined, ranges: Range[]): void { for (const range of ranges) { - expect(selectionRange.range).eql(range); + expect(selectionRange?.range).eql(range); + // Deduplicate ranges - while (selectionRange.parent && isRangesEqual(selectionRange.range, selectionRange.parent.range)) { + while (selectionRange?.parent && isRangesEqual(selectionRange.range, selectionRange.parent.range)) { selectionRange = selectionRange.parent; } - selectionRange = selectionRange.parent; + + selectionRange = selectionRange?.parent; } } @@ -62,6 +64,20 @@ key: { start: { line: 1, character: 0 }, end: { line: 3, character: 8 } }, ]); + positions = [ + { + line: 3, + character: 3, + }, + ]; + ranges = getSelectionRanges(document, positions); + expect(ranges.length).equal(positions.length); + expectSelections(ranges[0], [ + { start: { line: 3, character: 2 }, end: { line: 3, character: 8 } }, + { start: { line: 2, character: 2 }, end: { line: 3, character: 8 } }, + { start: { line: 1, character: 0 }, end: { line: 3, character: 8 } }, + ]); + positions = [ { line: 2, @@ -76,6 +92,64 @@ key: ]); }); + it('selection ranges for array of objects', () => { + const yaml = ` +times: + - second: 1 + millisecond: 10 + - second: 2 + millisecond: 0 + `; + let positions: Position[] = [ + { + line: 4, + character: 0, + }, + ]; + const document = setupTextDocument(yaml); + let ranges = getSelectionRanges(document, positions); + expect(ranges.length).equal(positions.length); + expectSelections(ranges[0], [ + { start: { line: 2, character: 2 }, end: { line: 5, character: 18 } }, + { start: { line: 1, character: 0 }, end: { line: 5, character: 18 } }, + ]); + + positions = [ + { + line: 5, + character: 2, + }, + ]; + ranges = getSelectionRanges(document, positions); + expect(ranges.length).equal(positions.length); + expectSelections(ranges[0], [ + { start: { line: 4, character: 4 }, end: { line: 5, character: 18 } }, + { start: { line: 2, character: 2 }, end: { line: 5, character: 18 } }, + { start: { line: 1, character: 0 }, end: { line: 5, character: 18 } }, + ]); + }); + + it('selection ranges for trailing spaces', () => { + const yaml = ` +key: + - 1 + - 2 \t + `; + const positions: Position[] = [ + { + line: 2, + character: 9, + }, + ]; + const document = setupTextDocument(yaml); + const ranges = getSelectionRanges(document, positions); + expect(ranges.length).equal(positions.length); + expectSelections(ranges[0], [ + { start: { line: 2, character: 2 }, end: { line: 3, character: 9 } }, + { start: { line: 1, character: 0 }, end: { line: 3, character: 9 } }, + ]); + }); + it('selection ranges jump for "" \'\'', () => { const yaml = ` - "word" diff --git a/tsconfig.json b/tsconfig.json index 5294b266..fa23d249 100755 --- a/tsconfig.json +++ b/tsconfig.json @@ -9,7 +9,7 @@ "outDir": "./out/server", "sourceMap": true, "target": "es2020", - "allowSyntheticDefaultImports": true, + "allowSyntheticDefaultImports": true, "skipLibCheck": true }, "include": [ "src", "test" ], diff --git a/yarn.lock b/yarn.lock index 6050c41f..96048a16 100644 --- a/yarn.lock +++ b/yarn.lock @@ -347,6 +347,11 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" +"@pkgr/core@^0.1.0": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@pkgr/core/-/core-0.1.1.tgz#1ec17e2edbec25c8306d424ecfbf13c7de1aaa31" + integrity sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA== + "@sinonjs/commons@^1.6.0", "@sinonjs/commons@^1.7.0", "@sinonjs/commons@^1.8.1": version "1.8.3" resolved "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz" @@ -425,11 +430,6 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.60.tgz#a1fbca80c18dd80c8783557304cdb7d55ac3aff5" integrity sha512-kYIYa1D1L+HDv5M5RXQeEu1o0FKA6yedZIoyugm/MBPROkLpX4L7HRxMrPVyo8bnvjpW/wDlqFNGzXNMb7AdRw== -"@types/prettier@2.7.2": - version "2.7.2" - resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.7.2.tgz#6c2324641cc4ba050a8c710b2b251b377581fbf0" - integrity sha512-KufADq8uQqo1pYKVIYzfKbJfBAc0sOeXqGbFaSpv8MRmC/zXgowNZmFcbngndGk922QDmOASEXUZCaY48gs4cg== - "@types/sinon-chai@^3.2.5": version "3.2.5" resolved "https://registry.npmjs.org/@types/sinon-chai/-/sinon-chai-3.2.5.tgz" @@ -1153,10 +1153,10 @@ escape-string-regexp@^1.0.5: resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= -eslint-config-prettier@^8.5.0: - version "8.5.0" - resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz#5a81680ec934beca02c7b1a61cf8ca34b66feab1" - integrity sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q== +eslint-config-prettier@^9.0.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz#31af3d94578645966c082fcb71a5846d3c94867f" + integrity sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw== eslint-import-resolver-node@^0.3.6: version "0.3.6" @@ -1193,12 +1193,13 @@ eslint-plugin-import@^2.26.0: resolve "^1.22.0" tsconfig-paths "^3.14.1" -eslint-plugin-prettier@^4.2.1: - version "4.2.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz#651cbb88b1dab98bfd42f017a12fa6b2d993f94b" - integrity sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ== +eslint-plugin-prettier@^5.0.0: + version "5.1.3" + resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-5.1.3.tgz#17cfade9e732cef32b5f5be53bd4e07afd8e67e1" + integrity sha512-C9GCVAs4Eq7ZC/XFQHITLiHJxQngdtraXaM+LoUFoFp/lHNl2Zn8f3WQbe9HvTBBQ9YnKFB0/2Ajdqwo5D1EAw== dependencies: prettier-linter-helpers "^1.0.0" + synckit "^0.8.6" eslint-scope@^5.1.1: version "5.1.1" @@ -2570,10 +2571,10 @@ prettier-linter-helpers@^1.0.0: dependencies: fast-diff "^1.1.2" -prettier@2.8.7: - version "2.8.7" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.7.tgz#bb79fc8729308549d28fe3a98fce73d2c0656450" - integrity sha512-yPngTo3aXUUmyuTjeTUT75txrf+aMh9FiD7q9ZE/i6r0bPb22g4FsE6Y338PQX1bmfy08i9QQCB7/rcUAVntfw== +prettier@^3.0.0: + version "3.3.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.3.1.tgz#e68935518dd90bb7ec4821ba970e68f8de16e1ac" + integrity sha512-7CAwy5dRsxs8PHXT3twixW9/OEll8MLE0VRPCJyl7CkS6VHGPSlsVaWTiASPTyGyYRyApxlaWTzwUxVNrhcwDg== process-on-spawn@^1.0.0: version "1.0.0" @@ -2783,7 +2784,7 @@ side-channel@^1.0.4: signal-exit@^3.0.2: version "3.0.7" - resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== sinon-chai@^3.5.0: @@ -2928,6 +2929,14 @@ supports-preserve-symlinks-flag@^1.0.0: resolved "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== +synckit@^0.8.6: + version "0.8.8" + resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.8.8.tgz#fe7fe446518e3d3d49f5e429f443cf08b6edfcd7" + integrity sha512-HwOKAP7Wc5aRGYdKH+dw0PRRpbO841v2DENBtjnR5HFWoiNByAl7vrx3p0G/rCyYXQsrxqtX48TImFtPcIHSpQ== + dependencies: + "@pkgr/core" "^0.1.0" + tslib "^2.6.2" + test-exclude@^6.0.0: version "6.0.0" resolved "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz" @@ -2996,6 +3005,11 @@ tslib@^1.8.1: resolved "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== +tslib@^2.6.2: + version "2.6.2" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" + integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== + tsutils@^3.21.0: version "3.21.0" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" @@ -3101,45 +3115,45 @@ vscode-json-languageservice@4.1.8: vscode-nls "^5.0.0" vscode-uri "^3.0.2" -vscode-jsonrpc@6.0.0: - version "6.0.0" - resolved "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-6.0.0.tgz" - integrity sha512-wnJA4BnEjOSyFMvjZdpiOwhSq9uDoK8e/kpRJDTaMYzwlkrhG1fwDIZI94CLsLzlCK5cIbMMtFlJlfR57Lavmg== +vscode-jsonrpc@8.2.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz#f43dfa35fb51e763d17cd94dcca0c9458f35abf9" + integrity sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA== -vscode-languageserver-protocol@3.16.0: - version "3.16.0" - resolved "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.16.0.tgz" - integrity sha512-sdeUoAawceQdgIfTI+sdcwkiK2KU+2cbEYA0agzM2uqaUy2UpnnGHtWTHVEtS0ES4zHU0eMFRGN+oQgDxlD66A== +vscode-languageserver-protocol@3.17.5: + version "3.17.5" + resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz#864a8b8f390835572f4e13bd9f8313d0e3ac4bea" + integrity sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg== dependencies: - vscode-jsonrpc "6.0.0" - vscode-languageserver-types "3.16.0" + vscode-jsonrpc "8.2.0" + vscode-languageserver-types "3.17.5" vscode-languageserver-textdocument@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.1.tgz" - integrity sha512-UIcJDjX7IFkck7cSkNNyzIz5FyvpQfY7sdzVy+wkKN/BLaD4DQ0ppXQrKePomCxTS7RrolK1I0pey0bG9eh8dA== + version "1.0.11" + resolved "https://registry.yarnpkg.com/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.11.tgz#0822a000e7d4dc083312580d7575fe9e3ba2e2bf" + integrity sha512-X+8T3GoiwTVlJbicx/sIAF+yuJAqz8VvwJyoMVhwEMoEKE/fkDmrqUgDMyBECcM2A2frVZIUj5HI/ErRXCfOeA== -vscode-languageserver-types@3.16.0, vscode-languageserver-types@^3.16.0: - version "3.16.0" - resolved "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0.tgz" - integrity sha512-k8luDIWJWyenLc5ToFQQMaSrqCHiLwyKPHKPQZ5zz21vM+vIVUSvsRpcbiECH4WR88K2XZqc4ScRcZ7nk/jbeA== +vscode-languageserver-types@3.17.5, vscode-languageserver-types@^3.16.0: + version "3.17.5" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz#3273676f0cf2eab40b3f44d085acbb7f08a39d8a" + integrity sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg== -vscode-languageserver@^7.0.0: - version "7.0.0" - resolved "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-7.0.0.tgz" - integrity sha512-60HTx5ID+fLRcgdHfmz0LDZAXYEV68fzwG0JWwEPBode9NuMYTIxuYXPg4ngO8i8+Ou0lM7y6GzaYWbiDL0drw== +vscode-languageserver@^9.0.0: + version "9.0.1" + resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-9.0.1.tgz#500aef82097eb94df90d008678b0b6b5f474015b" + integrity sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g== dependencies: - vscode-languageserver-protocol "3.16.0" + vscode-languageserver-protocol "3.17.5" vscode-nls@^5.0.0: - version "5.0.1" - resolved "https://registry.npmjs.org/vscode-nls/-/vscode-nls-5.0.1.tgz" - integrity sha512-hHQV6iig+M21lTdItKPkJAaWrxALQb/nqpVffakO4knJOh3DrU2SXOMzUzNgo1eADPzu3qSsJY1weCzvR52q9A== + version "5.2.0" + resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-5.2.0.tgz#3cb6893dd9bd695244d8a024bdf746eea665cc3f" + integrity sha512-RAaHx7B14ZU04EU31pT+rKz2/zSl7xMsfIZuo8pd+KZO6PXtQmpevpq3vxvWNcrGbdmhM/rr5Uw5Mz+NBfhVng== vscode-uri@^3.0.2: - version "3.0.3" - resolved "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.3.tgz" - integrity sha512-EcswR2S8bpR7fD0YPeS7r2xXExrScVMxg4MedACaWHEtx9ftCF/qHG1xGkolzTPcEmjTavCQgbVzHUIdTMzFGA== + version "3.0.8" + resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-3.0.8.tgz#1770938d3e72588659a172d0fd4642780083ff9f" + integrity sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw== which-boxed-primitive@^1.0.2: version "1.0.2"