From dfccc6fc095faeb5d07051b51f308478cdac70fd Mon Sep 17 00:00:00 2001 From: Skip Baney Date: Fri, 5 Jan 2024 00:35:22 -0600 Subject: [PATCH] fix: render examples as yaml on hover (#947) Co-authored-by: Muthurajan Sivasubramanian <93245779+msivasubramaniaan@users.noreply.github.com> --- src/languageservice/services/yamlHover.ts | 48 +++++++++++------------ test/hover.test.ts | 23 +++++++++-- 2 files changed, 43 insertions(+), 28 deletions(-) diff --git a/src/languageservice/services/yamlHover.ts b/src/languageservice/services/yamlHover.ts index eb163f6a..a0e72ce3 100644 --- a/src/languageservice/services/yamlHover.ts +++ b/src/languageservice/services/yamlHover.ts @@ -19,6 +19,7 @@ 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; @@ -87,11 +88,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, @@ -120,12 +116,12 @@ export class YAMLHover { 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) { if (s.schema.markdownEnumDescriptions) { markdownEnumDescriptions = s.schema.markdownEnumDescriptions; } else if (s.schema.enumDescriptions) { - markdownEnumDescriptions = s.schema.enumDescriptions.map(toMarkdown); + markdownEnumDescriptions = s.schema.enumDescriptions.map(this.toMarkdown, this); } else { markdownEnumDescriptions = []; } @@ -145,7 +141,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 += ' || '; @@ -156,7 +152,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)); }); } } @@ -164,7 +160,7 @@ export class YAMLHover { }); let result = ''; if (title) { - result = '#### ' + toMarkdown(title); + result = '#### ' + this.toMarkdown(title); } if (markdownDescription) { result = ensureLineBreak(result); @@ -182,10 +178,10 @@ export class YAMLHover { }); } if (markdownExamples.length !== 0) { - result = ensureLineBreak(result); - result += 'Examples:\n\n'; markdownExamples.forEach((example) => { - result += `* \`\`\`${example}\`\`\`\n`; + result = ensureLineBreak(result); + result += 'Example:\n\n'; + result += `\`\`\`yaml\n${example}\`\`\`\n`; }); } if (result.length > 0 && schema.schema.url) { @@ -197,6 +193,21 @@ export class YAMLHover { 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 { @@ -226,17 +237,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/test/hover.test.ts b/test/hover.test.ts index 83cf9932..404ed676 100644 --- a/test/hover.test.ts +++ b/test/hover.test.ts @@ -595,7 +595,14 @@ Source: [${SCHEMA_ID}](file:///${SCHEMA_ID})` type: 'string', description: 'should return this description', enum: ['cat', 'dog'], - examples: ['cat', 'dog'], + examples: [ + 'cat', + { + animal: { + type: 'dog', + }, + }, + ], }, }, }); @@ -613,10 +620,18 @@ Allowed Values: * \`cat\` * \`dog\` -Examples: +Example: + +\`\`\`yaml +cat +\`\`\` + +Example: -* \`\`\`"cat"\`\`\` -* \`\`\`"dog"\`\`\` +\`\`\`yaml +animal: + type: dog +\`\`\` Source: [${SCHEMA_ID}](file:///${SCHEMA_ID})` );