archieve-projects/微信机器人/node_modules/cmd-ts/dist/deno_cjs/errorBox.ts

137 lines
3.6 KiB
TypeScript

import { ParsingError } from './argparser.ts';
import chalk from 'https://deno.land/std@0.85.0/fmt/colors.ts';
import { AstNode } from './newparser/parser.ts';
import { padNoAnsi, enumerate } from './utils.ts';
import { stripAnsi } from './stripAnsi.ts';
type HighlightResult = { colorized: string; errorIndex: number };
/**
* Get the input as highlighted keywords to show to the user
* with the error that was generated from parsing the input.
*
* @param nodes AST nodes
* @param error A parsing error
*/
function highlight(
nodes: AstNode[],
error: ParsingError
): HighlightResult | undefined {
const strings: string[] = [];
let errorIndex: undefined | number = undefined;
function foundError() {
if (errorIndex !== undefined) return;
errorIndex = stripAnsi(strings.join(' ')).length;
}
if (error.nodes.length === 0) return;
nodes.forEach((node) => {
if (error.nodes.includes(node)) {
foundError();
return strings.push(chalk.red(node.raw));
} else {
if (node.type === 'shortOptions') {
let failed = false;
let s = '';
for (const option of node.options) {
if (error.nodes.includes(option)) {
s += chalk.red(option.raw);
failed = true;
} else {
s += chalk.dim(option.raw);
}
}
const prefix = failed ? chalk.red(`-`) : chalk.dim('-');
if (failed) {
foundError();
}
return strings.push(prefix + s);
}
return strings.push(chalk.dim(node.raw));
}
});
return { colorized: strings.join(' '), errorIndex: errorIndex ?? 0 };
}
/**
* An error UI
*
* @param breadcrumbs The command breadcrumbs to print with the error
*/
export function errorBox(
nodes: AstNode[],
errors: ParsingError[],
breadcrumbs: string[]
): string {
let withHighlight: { message: string; highlighted?: HighlightResult }[] = [];
let errorMessages: string[] = [];
for (const error of errors) {
const highlighted = highlight(nodes, error);
withHighlight.push({ message: error.message, highlighted });
}
let number = 1;
const maxNumberWidth = String(withHighlight.length).length;
errorMessages.push(
chalk.red.bold('error: ') +
'found ' +
chalk.yellow(withHighlight.length) +
' error' +
(withHighlight.length > 1 ? 's' : '')
);
errorMessages.push('');
withHighlight
.filter((x) => x.highlighted)
.forEach((x) => {
if (!x.highlighted) {
throw new Error('WELP');
}
const pad = ''.padStart(x.highlighted.errorIndex);
errorMessages.push(` ${x.highlighted.colorized}`);
for (const [index, line] of enumerate(x.message.split('\n'))) {
const prefix = index === 0 ? chalk.bold('^') : ' ';
const msg = chalk.red(` ${pad} ${prefix} ${line}`);
errorMessages.push(msg);
}
errorMessages.push('');
number++;
});
const withNoHighlight = withHighlight.filter((x) => !x.highlighted);
if (number > 1) {
if (withNoHighlight.length === 1) {
errorMessages.push('Along the following error:');
} else if (withNoHighlight.length > 1) {
errorMessages.push('Along the following errors:');
}
}
withNoHighlight.forEach(({ message }) => {
const num = chalk.red.bold(
`${padNoAnsi(number.toString(), maxNumberWidth, 'start')}.`
);
errorMessages.push(` ${num} ${chalk.red(message)}`);
number++;
});
const helpCmd = chalk.yellow(breadcrumbs.join(' ') + ' --help');
errorMessages.push('');
errorMessages.push(
chalk.red.bold('hint: ') + `for more information, try '${helpCmd}'`
);
return errorMessages.join('\n');
}