Skip to content

Commit

Permalink
fix: show all enums on hover (#942)
Browse files Browse the repository at this point in the history
  • Loading branch information
twelvelabs authored Dec 19, 2023
1 parent ed03cbf commit d354c58
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 26 deletions.
67 changes: 44 additions & 23 deletions src/languageservice/services/yamlHover.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ 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';
Expand Down Expand Up @@ -113,27 +113,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);
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(toMarkdown);
} 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
Expand Down Expand Up @@ -163,28 +167,30 @@ export class YAMLHover {
result = '#### ' + 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:';
result = ensureLineBreak(result);
result += 'Examples:\n\n';
markdownExamples.forEach((example) => {
result += `\n\n\`\`\`${example}\`\`\``;
result += `* \`\`\`${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);
}
Expand All @@ -193,6 +199,21 @@ export class YAMLHover {
}
}

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 {
let result = 'JSON Schema';
const urlString = schema.url;
Expand Down
41 changes: 38 additions & 3 deletions test/hover.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand All @@ -577,11 +608,15 @@ users:
(result.contents as MarkupContent).value,
`should return this description
Examples:
Allowed Values:
* \`cat\`
* \`dog\`
\`\`\`"cat"\`\`\`
Examples:
\`\`\`"dog"\`\`\`
* \`\`\`"cat"\`\`\`
* \`\`\`"dog"\`\`\`
Source: [${SCHEMA_ID}](file:///${SCHEMA_ID})`
);
Expand Down

0 comments on commit d354c58

Please sign in to comment.