Son CV dans un terminal web en Javascript!
https://terminal-cv.gregandev.fr
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
240 lines
8.5 KiB
240 lines
8.5 KiB
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
exports.default = codeFrame;
|
|
|
|
function _chalk() {
|
|
const data = _interopRequireDefault(require("chalk"));
|
|
|
|
_chalk = function () {
|
|
return data;
|
|
};
|
|
|
|
return data;
|
|
}
|
|
|
|
function _emphasize() {
|
|
const data = _interopRequireDefault(require("emphasize"));
|
|
|
|
_emphasize = function () {
|
|
return data;
|
|
};
|
|
|
|
return data;
|
|
}
|
|
|
|
function _stringWidth() {
|
|
const data = _interopRequireDefault(require("string-width"));
|
|
|
|
_stringWidth = function () {
|
|
return data;
|
|
};
|
|
|
|
return data;
|
|
}
|
|
|
|
function _sliceAnsi() {
|
|
const data = _interopRequireDefault(require("slice-ansi"));
|
|
|
|
_sliceAnsi = function () {
|
|
return data;
|
|
};
|
|
|
|
return data;
|
|
}
|
|
|
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
|
|
const NEWLINE = /\r\n|[\n\r\u2028\u2029]/;
|
|
const TAB_REPLACE_REGEX = /\t/g;
|
|
const TAB_REPLACEMENT = ' ';
|
|
const DEFAULT_TERMINAL_WIDTH = 80;
|
|
|
|
const highlightSyntax = (txt, lang) => {
|
|
if (lang) {
|
|
try {
|
|
return _emphasize().default.highlight(lang, txt).value;
|
|
} catch (e) {// fallback for unknown languages...
|
|
}
|
|
}
|
|
|
|
return _emphasize().default.highlightAuto(txt).value;
|
|
};
|
|
|
|
function codeFrame(code, highlights, inputOpts = {}) {
|
|
var _inputOpts$maxLines;
|
|
|
|
if (highlights.length < 1) return '';
|
|
let opts = {
|
|
useColor: !!inputOpts.useColor,
|
|
syntaxHighlighting: !!inputOpts.syntaxHighlighting,
|
|
language: inputOpts.language,
|
|
maxLines: (_inputOpts$maxLines = inputOpts.maxLines) !== null && _inputOpts$maxLines !== void 0 ? _inputOpts$maxLines : 12,
|
|
terminalWidth: inputOpts.terminalWidth || DEFAULT_TERMINAL_WIDTH,
|
|
padding: inputOpts.padding || {
|
|
before: 1,
|
|
after: 2
|
|
}
|
|
}; // Highlights messages and prefixes when colors are enabled
|
|
|
|
const highlighter = (s, bold) => {
|
|
if (opts.useColor) {
|
|
let redString = _chalk().default.red(s);
|
|
|
|
return bold ? _chalk().default.bold(redString) : redString;
|
|
}
|
|
|
|
return s;
|
|
}; // Prefix lines with the line number
|
|
|
|
|
|
const lineNumberPrefixer = params => {
|
|
let {
|
|
lineNumber,
|
|
lineNumberLength,
|
|
isHighlighted
|
|
} = params;
|
|
return `${isHighlighted ? highlighter('>') : ' '} ${lineNumber ? lineNumber.padStart(lineNumberLength, ' ') : ' '.repeat(lineNumberLength)} | `;
|
|
}; // Make columns/lines start at 1
|
|
|
|
|
|
highlights = highlights.map(h => {
|
|
return {
|
|
start: {
|
|
column: h.start.column - 1,
|
|
line: h.start.line - 1
|
|
},
|
|
end: {
|
|
column: h.end.column - 1,
|
|
line: h.end.line - 1
|
|
},
|
|
message: h.message
|
|
};
|
|
}); // Find first and last highlight
|
|
|
|
let firstHighlight = highlights.length > 1 ? highlights.sort((a, b) => a.start.line - b.start.line)[0] : highlights[0];
|
|
let lastHighlight = highlights.length > 1 ? highlights.sort((a, b) => b.end.line - a.end.line)[0] : highlights[0]; // Calculate first and last line index of codeframe
|
|
|
|
let startLine = firstHighlight.start.line - opts.padding.before;
|
|
startLine = startLine < 0 ? 0 : startLine;
|
|
let endLineIndex = lastHighlight.end.line + opts.padding.after;
|
|
endLineIndex = endLineIndex - startLine > opts.maxLines ? startLine + opts.maxLines - 1 : endLineIndex;
|
|
let lineNumberLength = (endLineIndex + 1).toString(10).length; // Split input into lines and highlight syntax
|
|
|
|
let lines = code.split(NEWLINE);
|
|
let syntaxHighlightedLines = (opts.syntaxHighlighting ? highlightSyntax(code, opts.language) : code).replace(TAB_REPLACE_REGEX, TAB_REPLACEMENT).split(NEWLINE); // Loop over all lines and create codeframe
|
|
|
|
let resultLines = [];
|
|
|
|
for (let currentLineIndex = startLine; currentLineIndex < syntaxHighlightedLines.length; currentLineIndex++) {
|
|
if (currentLineIndex > endLineIndex) break;
|
|
if (currentLineIndex > syntaxHighlightedLines.length - 1) break; // Find highlights that need to get rendered on the current line
|
|
|
|
let lineHighlights = highlights.filter(highlight => highlight.start.line <= currentLineIndex && highlight.end.line >= currentLineIndex).sort((a, b) => (a.start.line < currentLineIndex ? 0 : a.start.column) - (b.start.line < currentLineIndex ? 0 : b.start.column)); // Check if this line has a full line highlight
|
|
|
|
let isWholeLine = lineHighlights.length && !!lineHighlights.find(h => h.start.line < currentLineIndex && h.end.line > currentLineIndex);
|
|
let lineLengthLimit = opts.terminalWidth > lineNumberLength + 7 ? opts.terminalWidth - (lineNumberLength + 5) : 10; // Split the line into line parts that will fit the provided terminal width
|
|
|
|
let colOffset = 0;
|
|
let lineEndCol = lineLengthLimit;
|
|
let syntaxHighlightedLine = syntaxHighlightedLines[currentLineIndex];
|
|
|
|
if ((0, _stringWidth().default)(syntaxHighlightedLine) > lineLengthLimit) {
|
|
if (lineHighlights.length > 0) {
|
|
if (lineHighlights[0].start.line === currentLineIndex) {
|
|
colOffset = lineHighlights[0].start.column - 5;
|
|
} else if (lineHighlights[0].end.line === currentLineIndex) {
|
|
colOffset = lineHighlights[0].end.column - 5;
|
|
}
|
|
}
|
|
|
|
colOffset = colOffset > 0 ? colOffset : 0;
|
|
lineEndCol = colOffset + lineLengthLimit;
|
|
syntaxHighlightedLine = (0, _sliceAnsi().default)(syntaxHighlightedLine, colOffset, lineEndCol);
|
|
} // Write the syntax highlighted line part
|
|
|
|
|
|
resultLines.push(lineNumberPrefixer({
|
|
lineNumber: (currentLineIndex + 1).toString(10),
|
|
lineNumberLength,
|
|
isHighlighted: lineHighlights.length > 0
|
|
}) + syntaxHighlightedLine);
|
|
let lineWidth = (0, _stringWidth().default)(syntaxHighlightedLine);
|
|
let highlightLine = '';
|
|
|
|
if (isWholeLine) {
|
|
highlightLine = highlighter('^'.repeat(lineWidth));
|
|
} else if (lineHighlights.length > 0) {
|
|
let lastCol = 0;
|
|
let highlight = null;
|
|
let highlightHasEnded = false;
|
|
|
|
for (let highlightIndex = 0; highlightIndex < lineHighlights.length; highlightIndex++) {
|
|
// Set highlight to current highlight
|
|
highlight = lineHighlights[highlightIndex];
|
|
highlightHasEnded = false; // Calculate the startColumn and get the real width by doing a substring of the original
|
|
// line and replacing tabs with our tab replacement to support tab handling
|
|
|
|
let startCol = 0;
|
|
|
|
if (highlight.start.line === currentLineIndex && highlight.start.column > colOffset) {
|
|
startCol = lines[currentLineIndex].substring(colOffset, highlight.start.column).replace(TAB_REPLACE_REGEX, TAB_REPLACEMENT).length;
|
|
} // Calculate the endColumn and get the real width by doing a substring of the original
|
|
// line and replacing tabs with our tab replacement to support tab handling
|
|
|
|
|
|
let endCol = lineWidth - 1;
|
|
|
|
if (highlight.end.line === currentLineIndex) {
|
|
endCol = lines[currentLineIndex].substring(colOffset, highlight.end.column).replace(TAB_REPLACE_REGEX, TAB_REPLACEMENT).length; // If the endCol is too big for this line part, trim it so we can handle it in the next one
|
|
|
|
if (endCol > lineWidth) {
|
|
endCol = lineWidth - 1;
|
|
}
|
|
|
|
highlightHasEnded = true;
|
|
} // If endcol is smaller than lastCol it overlaps with another highlight and is no longer visible, we can skip those
|
|
|
|
|
|
if (endCol >= lastCol) {
|
|
let characters = endCol - startCol + 1;
|
|
|
|
if (startCol > lastCol) {
|
|
// startCol is before lastCol, so add spaces as padding before the highlight indicators
|
|
highlightLine += ' '.repeat(startCol - lastCol);
|
|
} else if (lastCol > startCol) {
|
|
// If last column is larger than the start, there's overlap in highlights
|
|
// This line adjusts the characters count to ensure we don't add too many characters
|
|
characters += startCol - lastCol;
|
|
} // Append the highlight indicators
|
|
|
|
|
|
highlightLine += highlighter('^'.repeat(characters)); // Set the lastCol equal to character count between start of line part and highlight end-column
|
|
|
|
lastCol = endCol + 1;
|
|
} // There's no point in processing more highlights if we reached the end of the line
|
|
|
|
|
|
if (endCol >= lineEndCol - 1) {
|
|
break;
|
|
}
|
|
} // Append the highlight message if the current highlights ends on this line part
|
|
|
|
|
|
if (highlight && highlight.message && highlightHasEnded) {
|
|
highlightLine += ' ' + highlighter(highlight.message, true);
|
|
}
|
|
}
|
|
|
|
if (highlightLine) {
|
|
resultLines.push(lineNumberPrefixer({
|
|
lineNumberLength,
|
|
isHighlighted: true
|
|
}) + highlightLine);
|
|
}
|
|
}
|
|
|
|
return resultLines.join('\n');
|
|
} |