enolib
Version:
The eno standard library
109 lines (86 loc) • 3.52 kB
JavaScript
const { COMMENT, HUMAN_INDEXING, UNPARSED } = require('../constants.js');
const { DISPLAY, EMPHASIZE, INDICATE, OMISSION, QUESTION, Reporter } = require('./reporter.js');
const RESET = '\x1b[0m';
const BOLD = '\x1b[1m';
const DIM = '\x1b[2m';
const BLACK = '\x1b[30m';
const BRIGHT_BLACK = '\x1b[90m';
const WHITE = '\x1b[37m';
const BRIGHT_WHITE = '\x1b[97m';
const BRIGHT_BLACK_BACKGROUND = '\x1b[40m';
const BRIGHT_RED_BACKGROUND = '\x1b[101m';
const WHITE_BACKGROUND = '\x1b[47m';
const INDICATORS = {
[DISPLAY]: ' ',
[EMPHASIZE]: '>',
[INDICATE]: '*',
[QUESTION]: '?'
};
const GUTTER_STYLE = {
[DISPLAY]: BRIGHT_BLACK_BACKGROUND,
[EMPHASIZE]: BLACK + BRIGHT_RED_BACKGROUND,
[INDICATE]: BLACK + WHITE_BACKGROUND,
[QUESTION]: BLACK + WHITE_BACKGROUND
};
const RANGE_STYLE = {
'elementOperator': WHITE,
'escapeBeginOperator': WHITE,
'escapeEndOperator': WHITE,
'itemOperator': WHITE,
'entryOperator': WHITE,
'sectionOperator': WHITE,
'copyOperator': WHITE,
'deepCopyOperator': WHITE,
'multilineFieldOperator': WHITE,
'directLineContinuationOperator': WHITE,
'spacedLineContinuationOperator': WHITE,
'key': BOLD + BRIGHT_WHITE,
'template': BOLD + BRIGHT_WHITE,
'value': DIM + WHITE
};
class TerminalReporter extends Reporter {
constructor(context) {
super(context);
let highestShownLineNumber = this._snippet.length;
for(let index = this._snippet.length; index >= 0; index--) {
if(this._snippet[index] !== undefined && this._snippet[index] !== OMISSION) {
highestShownLineNumber = index + 1;
break;
}
}
this._lineNumberPadding = Math.max(4, highestShownLineNumber.toString().length); // TODO: Pick this up in other reporters
this._header = '';
if(context.source) {
this._header += `${BLACK + BRIGHT_RED_BACKGROUND} ${INDICATORS[EMPHASIZE]} ${' '.padStart(this._lineNumberPadding)} ${RESET} ${BOLD}${context.source}${RESET}\n`;
}
}
_line(line, tag) {
if(tag === OMISSION)
return `${DIM + BRIGHT_BLACK_BACKGROUND}${'...'.padStart(this._lineNumberPadding + 2)} ${RESET}`;
const number = (line + HUMAN_INDEXING).toString();
const instruction = this._index[line];
let content = '';
if(instruction !== undefined) {
if(instruction.type === COMMENT || instruction.type === UNPARSED) {
content = BRIGHT_BLACK + this._context._input.substring(instruction.ranges.line[0], instruction.ranges.line[1]) + RESET;
} else {
content = this._context._input.substring(instruction.ranges.line[0], instruction.ranges.line[1]);
const ranges = Object.entries(instruction.ranges).filter(([name, _]) => name !== 'line');
ranges.sort((a,b) => a[1][0] < b[1][0] ? 1 : -1);
for(const [name, range] of ranges) {
const before = content.substring(0, range[0] - instruction.ranges.line[0]);
const after = content.substring(range[1] - instruction.ranges.line[0]);
content = before + RANGE_STYLE[name] + this._context._input.substring(range[0], range[1]) + RESET + after;
}
}
}
return `${GUTTER_STYLE[tag]} ${INDICATORS[tag]} ${number.padStart(this._lineNumberPadding)} ${RESET} ${content}`;
}
_print() {
const snippet = this._snippet.map((tag, line) => this._line(line, tag))
.filter(line => line !== undefined)
.join('\n');
return this._header + snippet;
}
}
exports.TerminalReporter = TerminalReporter;