From 18e11877421e16aa248dc62d914cc420d48e108a Mon Sep 17 00:00:00 2001 From: Andreas Madsen Date: Fri, 12 Jan 2024 11:19:58 -0500 Subject: [PATCH] Add source and hover information to error output For the CLI, only the source (i.e. title property) is added. FOr the CI, the hover information (title, description, examples) are added and formatted according to YAMLHover. --- github-action/index.ts | 24 ++++-- github-action/webpack.config.js | 2 +- package.json | 3 + pnpm-lock.yaml | 136 ++++++++++++++++++++++++++++++++ src/lib.ts | 26 ++++-- 5 files changed, 177 insertions(+), 14 deletions(-) diff --git a/github-action/index.ts b/github-action/index.ts index f8e5448..f331d4a 100644 --- a/github-action/index.ts +++ b/github-action/index.ts @@ -2,8 +2,14 @@ import * as core from '@actions/core'; import * as path from 'path'; import { YamlVersion } from 'yaml-language-server/out/server/src/languageservice/parser/yamlParser07'; import { SchemaMapping, validateDirectory } from '../src'; +import { MarkupContent } from 'vscode-languageserver-types'; + +import { marked } from 'marked'; +import { markedTerminal } from 'marked-terminal'; async function run() { + marked.use(markedTerminal() as any); + let rootPath = process.env['GITHUB_WORKSPACE'] as string; let relativeToRoot = core.getInput('root', { trimWhitespace: true }); if (relativeToRoot) { @@ -29,17 +35,19 @@ async function run() { if (results && results.length > 0) { for (const result of results) { for (const error of result.error) { + const { diag, hover } = error; + const hoverMessage = (hover && MarkupContent.is(hover.contents)) ? `\n\n${marked(hover.contents.value)}` : '' + + core.error( - `${result.filePath}:${error.range.start.line + 1}:${error.range.start.character + 1}: ${ - error.message - }`, + diag.message + hoverMessage, { - title: error.message, + title: `${diag.message}${diag.source ? ' ' + diag.source + '.' : ''}`, file: result.filePath, - startLine: error.range.start.line + 1, - endLine: error.range.end.line + 1, - startColumn: error.range.start.character, - endColumn: error.range.end.character, + startLine: diag.range.start.line + 1, + endLine: diag.range.end.line + 1, + startColumn: diag.range.start.character, + endColumn: diag.range.end.character, }, ); } diff --git a/github-action/webpack.config.js b/github-action/webpack.config.js index 7a07cff..47ff7c9 100644 --- a/github-action/webpack.config.js +++ b/github-action/webpack.config.js @@ -24,7 +24,7 @@ module.exports = { ), ], resolve: { - extensions: [ '.ts', '.js' ], + extensions: [ '.ts', '.js', '.json' ], }, output: { filename: 'index.js', diff --git a/package.json b/package.json index c00e0d0..e01d260 100644 --- a/package.json +++ b/package.json @@ -42,8 +42,11 @@ "@actions/core": "^1.10.0", "@types/glob": "^7.1.4", "@types/jest": "^27.0.2", + "@types/marked-terminal": "^6.0.1", "@types/node": "^16.9.4", "jest": "^27.2.1", + "marked": "^11.1.1", + "marked-terminal": "^6.2.0", "npm-run-all": "^4.1.5", "prettier": "^2.4.1", "rimraf": "^3.0.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index dc69ebe..59c5b56 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,10 +4,13 @@ specifiers: '@actions/core': ^1.10.0 '@types/glob': ^7.1.4 '@types/jest': ^27.0.2 + '@types/marked-terminal': ^6.0.1 '@types/node': ^16.9.4 commander: ^8.2.0 glob: ^7.1.7 jest: ^27.2.1 + marked: ^11.1.1 + marked-terminal: ^6.2.0 npm-run-all: ^4.1.5 prettier: ^2.4.1 rimraf: ^3.0.2 @@ -33,8 +36,11 @@ devDependencies: '@actions/core': 1.10.0 '@types/glob': 7.2.0 '@types/jest': 27.5.2 + '@types/marked-terminal': 6.0.1 '@types/node': 16.18.14 jest: 27.5.1_ts-node@10.9.1 + marked: 11.1.1 + marked-terminal: 6.2.0_marked@11.1.1 npm-run-all: 4.1.5 prettier: 2.8.4 rimraf: 3.0.2 @@ -394,6 +400,13 @@ packages: resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} dev: true + /@colors/colors/1.5.0: + resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} + engines: {node: '>=0.1.90'} + requiresBuild: true + dev: true + optional: true + /@cspotcode/source-map-support/0.8.1: resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} engines: {node: '>=12'} @@ -665,6 +678,11 @@ packages: '@jridgewell/sourcemap-codec': 1.4.14 dev: true + /@sindresorhus/is/4.6.0: + resolution: {integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==} + engines: {node: '>=10'} + dev: true + /@sinonjs/commons/1.8.6: resolution: {integrity: sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==} dependencies: @@ -727,6 +745,10 @@ packages: '@babel/types': 7.21.2 dev: true + /@types/cardinal/2.1.1: + resolution: {integrity: sha512-/xCVwg8lWvahHsV2wXZt4i64H1sdL+sN1Uoq7fAc8/FA6uYHjuIveDwPwvGUYp4VZiv85dVl6J/Bum3NDAOm8g==} + dev: true + /@types/eslint-scope/3.7.4: resolution: {integrity: sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==} dependencies: @@ -785,6 +807,15 @@ packages: resolution: {integrity: sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==} dev: true + /@types/marked-terminal/6.0.1: + resolution: {integrity: sha512-HkOmxZoAV39+mymm/t4o4fQNWyxH6qY1Ph5VTj3/KP09bPf+WBQb3UViFR7TUJMFeHTieBtTismYAnMhJgUvbQ==} + dependencies: + '@types/cardinal': 2.1.1 + '@types/node': 16.18.14 + chalk: 5.3.0 + marked: 9.1.6 + dev: true + /@types/minimatch/5.1.2: resolution: {integrity: sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==} dev: true @@ -1043,6 +1074,13 @@ packages: type-fest: 0.21.3 dev: true + /ansi-escapes/6.2.0: + resolution: {integrity: sha512-kzRaCqXnpzWs+3z5ABPQiVke+iq0KXkHo8xiWV4RPTi5Yli0l97BEQuhXV1s7+aSU/fu1kUuxgS4MsQ0fRuygw==} + engines: {node: '>=14.16'} + dependencies: + type-fest: 3.13.1 + dev: true + /ansi-regex/5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} @@ -1067,6 +1105,10 @@ packages: engines: {node: '>=10'} dev: true + /ansicolors/0.3.2: + resolution: {integrity: sha512-QXu7BPrP29VllRxH8GwB7x5iX5qWKAAMLqKQGWTeLWVlNHNOpVMJ91dsxQAIWXpjuW5wqvxu3Jd/nRjrJ+0pqg==} + dev: true + /anymatch/3.1.3: resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} engines: {node: '>= 8'} @@ -1241,6 +1283,14 @@ packages: resolution: {integrity: sha512-oww27MtUmusatpRpCGSOneQk2/l5czXANDSFvsc7VuOQ86s3ANhZetpwXNf1zY/zdfP63Xvjz325DAdAoES13g==} dev: true + /cardinal/2.1.1: + resolution: {integrity: sha512-JSr5eOgoEymtYHBjNWyjrMqet9Am2miJhlfKNdqLp6zoeAh0KN5dRAcxlecj5mAJrmQomgiOBj35xHLrFjqBpw==} + hasBin: true + dependencies: + ansicolors: 0.3.2 + redeyed: 2.1.1 + dev: true + /chalk/2.4.2: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} engines: {node: '>=4'} @@ -1258,6 +1308,11 @@ packages: supports-color: 7.2.0 dev: true + /chalk/5.3.0: + resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + dev: true + /char-regex/1.0.2: resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} engines: {node: '>=10'} @@ -1277,6 +1332,15 @@ packages: resolution: {integrity: sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==} dev: true + /cli-table3/0.6.3: + resolution: {integrity: sha512-w5Jac5SykAeZJKntOxJCrm63Eg5/4dhMWIcuTbo9rpE+brgaSZo0RuNJZeOyMgsUdhDeojvgyQLmjI+K50ZGyg==} + engines: {node: 10.* || >= 12.*} + dependencies: + string-width: 4.2.3 + optionalDependencies: + '@colors/colors': 1.5.0 + dev: true + /cliui/7.0.4: resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} dependencies: @@ -1481,6 +1545,10 @@ packages: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} dev: true + /emojilib/2.4.0: + resolution: {integrity: sha512-5U0rVMU5Y2n2+ykNLQqMoqklN9ICBT/KsvC1Gz6vqHbz2AXXGkG+Pm5rMWk/8Vjrr/mY9985Hi8DYzn1F09Nyw==} + dev: true + /enhanced-resolve/5.12.0: resolution: {integrity: sha512-QHTXI/sZQmko1cbDoNAa3mJ5qhWUUNAq3vR0/YiD379fWQrcfuoX1+HW2S0MTt7XmoPLapdaDKUtelUSPic7hQ==} engines: {node: '>=10.13.0'} @@ -2780,6 +2848,33 @@ packages: tmpl: 1.0.5 dev: true + /marked-terminal/6.2.0_marked@11.1.1: + resolution: {integrity: sha512-ubWhwcBFHnXsjYNsu+Wndpg0zhY4CahSpPlA70PlO0rR9r2sZpkyU+rkCsOWH+KMEkx847UpALON+HWgxowFtw==} + engines: {node: '>=16.0.0'} + peerDependencies: + marked: '>=1 <12' + dependencies: + ansi-escapes: 6.2.0 + cardinal: 2.1.1 + chalk: 5.3.0 + cli-table3: 0.6.3 + marked: 11.1.1 + node-emoji: 2.1.3 + supports-hyperlinks: 3.0.0 + dev: true + + /marked/11.1.1: + resolution: {integrity: sha512-EgxRjgK9axsQuUa/oKMx5DEY8oXpKJfk61rT5iY3aRlgU6QJtUcxU5OAymdhCvWvhYcd9FKmO5eQoX8m9VGJXg==} + engines: {node: '>= 18'} + hasBin: true + dev: true + + /marked/9.1.6: + resolution: {integrity: sha512-jcByLnIFkd5gSXZmjNvS1TlmRhCXZjIzHYlaGkPlLIekG55JDR2Z4va9tZwCiP+/RDERiNhMOFu01xd6O5ct1Q==} + engines: {node: '>= 16'} + hasBin: true + dev: true + /memorystream/0.3.1: resolution: {integrity: sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==} engines: {node: '>= 0.10.0'} @@ -2835,6 +2930,16 @@ packages: resolution: {integrity: sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==} dev: true + /node-emoji/2.1.3: + resolution: {integrity: sha512-E2WEOVsgs7O16zsURJ/eH8BqhF029wGpEOnv7Urwdo2wmQanOACwJQh0devF9D9RhoZru0+9JXIS0dBXIAz+lA==} + engines: {node: '>=18'} + dependencies: + '@sindresorhus/is': 4.6.0 + char-regex: 1.0.2 + emojilib: 2.4.0 + skin-tone: 2.0.0 + dev: true + /node-int64/0.4.0: resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} dev: true @@ -3104,6 +3209,12 @@ packages: resolve: 1.22.1 dev: true + /redeyed/2.1.1: + resolution: {integrity: sha512-FNpGGo1DycYAdnrKFxCMmKYgo/mILAqtRYbkdQD8Ep/Hk2PQ5+aEAEx+IU713RTDmuBaH0c8P5ZozurNu5ObRQ==} + dependencies: + esprima: 4.0.1 + dev: true + /regexp.prototype.flags/1.4.3: resolution: {integrity: sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==} engines: {node: '>= 0.4'} @@ -3271,6 +3382,13 @@ packages: resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} dev: true + /skin-tone/2.0.0: + resolution: {integrity: sha512-kUMbT1oBJCpgrnKoSr0o6wPtvRWT9W9UKvGLwfJYO2WuahZRHOpEyL1ckyMGgMWh0UdpmaoFqKKD29WTomNEGA==} + engines: {node: '>=8'} + dependencies: + unicode-emoji-modifier-base: 1.0.0 + dev: true + /slash/3.0.0: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} @@ -3424,6 +3542,14 @@ packages: supports-color: 7.2.0 dev: true + /supports-hyperlinks/3.0.0: + resolution: {integrity: sha512-QBDPHyPQDRTy9ku4URNGY5Lah8PAaXs6tAAwp55sL5WCsSW7GIfdf6W5ixfziW+t7wh3GVvHyHHyQ1ESsoRvaA==} + engines: {node: '>=14.18'} + dependencies: + has-flag: 4.0.0 + supports-color: 7.2.0 + dev: true + /supports-preserve-symlinks-flag/1.0.0: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} @@ -3629,6 +3755,11 @@ packages: engines: {node: '>=10'} dev: true + /type-fest/3.13.1: + resolution: {integrity: sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==} + engines: {node: '>=14.16'} + dev: true + /typed-array-length/1.0.4: resolution: {integrity: sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==} dependencies: @@ -3658,6 +3789,11 @@ packages: which-boxed-primitive: 1.0.2 dev: true + /unicode-emoji-modifier-base/1.0.0: + resolution: {integrity: sha512-yLSH4py7oFH3oG/9K+XWrz1pSi3dfUrWEnInbxMfArOfc1+33BlGPQtLsOYwvdMy11AwUBetYuaRxSPqgkq+8g==} + engines: {node: '>=4'} + dev: true + /universalify/0.2.0: resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==} engines: {node: '>= 4.0.0'} diff --git a/src/lib.ts b/src/lib.ts index 0b15074..bd899f0 100644 --- a/src/lib.ts +++ b/src/lib.ts @@ -3,9 +3,10 @@ import * as path from 'path'; import { YAMLSchemaService } from 'yaml-language-server/out/server/src/languageservice/services/yamlSchemaService'; import { YAMLValidation } from 'yaml-language-server/out/server/src/languageservice/services/yamlValidation'; +import { YAMLHover } from 'yaml-language-server/out/server/src/languageservice/services/yamlHover'; import { WorkspaceContextService } from 'yaml-language-server/out/server/src/languageservice/yamlLanguageService'; import { TextDocument } from 'vscode-languageserver-textdocument'; -import { Diagnostic } from 'vscode-languageserver-types'; +import { Diagnostic, Hover } from 'vscode-languageserver-types'; import { readJson } from './util'; import { createSchemaRequestHandler } from './schema-handler'; @@ -56,7 +57,10 @@ function hasSchema(settings?: Settings): settings is SettingsWithSchema { interface FileValidationResult { filePath: string; - error: Diagnostic[]; + error: { + diag: Diagnostic; + hover: Hover | null; + }[] } /** @@ -110,20 +114,30 @@ export async function getValidationResults(files: string[], settings?: Settings) schemaService.registerExternalSchema(uri, patterns); } - const yamlValidation = new YAMLValidation(schemaService, new ConsoleTelemetry() as any); + const telemetry = new ConsoleTelemetry(); + const yamlValidation = new YAMLValidation(schemaService, telemetry); yamlValidation.configure({ validate: true, yamlVersion: settings?.yamlVersion ?? '1.2', disableAdditionalProperties: false, customTags: [], }); + const yamlHover = new YAMLHover(schemaService, telemetry) return await Promise.all( files.map(async (relativePath: string) => { const filePath = rootPath ? path.join(rootPath, relativePath) : relativePath; const doc = TextDocument.create(relativePath, 'yaml', 0, fs.readFileSync(filePath).toString()); - return await yamlValidation.doValidation(doc).then((error) => ({ filePath, error })); + const diagnostics = await yamlValidation.doValidation(doc); + const hovers = await Promise.all(diagnostics.map((diag) => yamlHover.doHover(doc, diag.range.start))); + return { + filePath, + error: diagnostics.map((diagnostics, index) => ({ + diag: diagnostics, + hover: hovers[index] + })) + }; }), ).then((rs) => rs.filter((r) => r.error.length > 0)); } @@ -146,8 +160,10 @@ async function validateAndOutput(files: string[], settings: Settings) { console.error('Found invalid files:'); for (const result of results) { for (const error of result.error) { + const { diag, hover } = error; + const sourceMessage = diag.source ? ` ${diag.source}` : ''; console.error( - `${result.filePath}:${error.range.start.line + 1}:${error.range.start.character + 1}: ${error.message}`, + `${result.filePath}:${diag.range.start.line + 1}:${diag.range.start.character + 1}: ${diag.message}${sourceMessage}`, ); } }