diff --git a/lib/format-pretty.js b/lib/format-pretty.js index 1f351cd..8eb0323 100644 --- a/lib/format-pretty.js +++ b/lib/format-pretty.js @@ -74,13 +74,18 @@ function formatMessage(msg) { const pad = utils.rightPad(`${l}:${col}`, MAX_LINE_COL_LEN) const line = chalk.grey(pad) const id = formatId(msg.id) - const m = msg.message const icon = msg.level === 'fail' ? utils.X : msg.level === 'warn' ? utils.WARN : utils.CHECK - return ` ${icon} ${line} ${utils.rightPad(m, 40)} ${id}` + const msg_lines = msg.message.split('\n') + let formatted = + [ ` ${icon} ${line} ${utils.rightPad(msg_lines[0], 40)} ${id}` ] + for (const msg_line of msg_lines.slice(1)) { + formatted.push(`${' '.repeat(17)}${msg_line}`) + } + return formatted.join('\n') } function formatId(id) { diff --git a/lib/rules/subsystem.js b/lib/rules/subsystem.js index f6d9c07..78a058a 100644 --- a/lib/rules/subsystem.js +++ b/lib/rules/subsystem.js @@ -1,5 +1,7 @@ 'use strict' +const FuzzySet = require('fuzzyset.js') + const id = 'subsystem' const validSubsystems = [ @@ -111,11 +113,16 @@ module.exports = { for (const sub of parsed.subsystems) { if (!~subs.indexOf(sub)) { failed = true + let suggestion = '' + const suggestions = new FuzzySet(subs).get(sub) + if (suggestions) { + suggestion = `\nDid you mean "${suggestions[0][1]}"?` + } // invalid subsystem const column = parsed.title.indexOf(sub) context.report({ id: id - , message: `Invalid subsystem: "${sub}"` + , message: `Invalid subsystem: "${sub}"${suggestion}` , string: parsed.title , line: 0 , column: column diff --git a/package-lock.json b/package-lock.json index a227a81..5a384fc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -937,6 +937,11 @@ "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", "dev": true }, + "fuzzyset.js": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/fuzzyset.js/-/fuzzyset.js-0.0.8.tgz", + "integrity": "sha512-wymI6DYJgCBDFUrIyA/M2gIjJPEWj40pnCf04+Dma0PaprQRrTRh9/Cmz1wl9jCJxA+iqrCqNrGYuq7lVOtzXQ==" + }, "get-stdin": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-5.0.1.tgz", diff --git a/package.json b/package.json index 7d76109..d702745 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ }, "dependencies": { "chalk": "~1.1.1", + "fuzzyset.js": "^0.0.8", "gitlint-parser-node": "^1.1.0", "help": "^2.1.3", "nopt": "^3.0.6" diff --git a/test/rules/subsystem.js b/test/rules/subsystem.js index 4c3d096..76077fa 100644 --- a/test/rules/subsystem.js +++ b/test/rules/subsystem.js @@ -88,5 +88,45 @@ test('rule: subsystem', (t) => { Rule.validate(context, {options: {subsystems: Rule.defaults.subsystems}}) }) + + const suggestionTests = [ + [ 'docs', 'doc' ] + , [ 'error', 'errors' ] + , [ 'napi', 'n-api' ] + , [ 'perfhooks', 'perf_hooks' ] + , [ 'worker_threads', 'worker' ] + , [ 'V8', 'v8' ] + ] + for (const [sub, suggestion] of suggestionTests) { + t.test(`suggestion "${sub}" -> "${suggestion}"`, (tt) => { + tt.plan(7) + const title = `${sub}: come on` + const v = new Validator() + const context = new Commit({ + sha: 'e7c077c610afa371430180fbd447bfef60ebc5ea' + , author: { + name: 'Evan Lucas' + , email: 'evanlucas@me.com' + , date: '2016-04-12T19:42:23Z' + } + , message: title + }, v) + + context.report = (opts) => { + tt.pass('called report') + tt.equal(opts.id, 'subsystem', 'id') + tt.equal(opts.message + , `Invalid subsystem: "${sub}"\nDid you mean "${suggestion}"?` + , 'message') + tt.equal(opts.string, title, 'string') + tt.equal(opts.line, 0, 'line') + tt.equal(opts.column, 0, 'column') + tt.equal(opts.level, 'fail', 'level') + tt.end() + } + + Rule.validate(context, {options: {subsystems: Rule.defaults.subsystems}}) + }) + } t.end() })