vscode-chrome-debug-core
Version:
A library for building VS Code debug adapters for targets that support the Chrome Remote Debug Protocol
232 lines (230 loc) • 8.85 kB
JavaScript
;
/*---------------------------------------------------------
* Copyright (C) Microsoft Corporation. All rights reserved.
*--------------------------------------------------------*/
Object.defineProperty(exports, "__esModule", { value: true });
const Color = require("color");
const variables = require("./variables");
function formatExceptionDetails(e) {
if (!e.exception) {
return `${e.text || 'Uncaught Error'}\n${stackTraceToString(e.stackTrace)}`;
}
return (e.exception.className && e.exception.className.endsWith('Error') && e.exception.description) ||
(`Error: ${variables.getRemoteObjectPreview(e.exception)}\n${stackTraceToString(e.stackTrace)}`);
}
exports.formatExceptionDetails = formatExceptionDetails;
exports.clearConsoleCode = '\u001b[2J';
function formatConsoleArguments(type, args, stackTrace) {
switch (type) {
case 'log':
case 'debug':
case 'info':
case 'error':
case 'warning':
case 'dir':
case 'timeEnd':
case 'count':
args = resolveParams(args);
break;
case 'assert':
const formattedParams = args.length ?
// 'assert' doesn't support format specifiers
resolveParams(args, /*skipFormatSpecifiers=*/ true) :
[];
const assertMsg = (formattedParams[0] && formattedParams[0].type === 'string') ?
formattedParams.shift().value :
'';
let outputText = `Assertion failed: ${assertMsg}\n` + stackTraceToString(stackTrace);
args = [{ type: 'string', value: outputText }, ...formattedParams];
break;
case 'startGroup':
case 'startGroupCollapsed':
let startMsg = '‹Start group›';
const formattedGroupParams = resolveParams(args);
const previewMessage = formattedGroupParams.find(x => x && x.type === 'string');
if (previewMessage) {
startMsg += ': ' + previewMessage.value;
}
args = [{ type: 'string', value: startMsg }, ...formattedGroupParams];
break;
case 'endGroup':
args = [{ type: 'string', value: '‹End group›' }];
break;
case 'trace':
args = [{ type: 'string', value: 'console.trace()\n' + stackTraceToString(stackTrace) }];
break;
case 'clear':
args = [{ type: 'string', value: exports.clearConsoleCode }];
break;
default:
// Some types we have to ignore
return null;
}
const isError = type === 'assert' || type === 'error';
return { args, isError };
}
exports.formatConsoleArguments = formatConsoleArguments;
/**
* Collapse non-object arguments, and apply format specifiers (%s, %d, etc). Return a reduced a formatted list of RemoteObjects.
*/
function resolveParams(args, skipFormatSpecifiers) {
if (!args.length || args[0].objectId) {
// If the first arg is not text, nothing is going to happen here
return args;
}
// Find all %s, %i, etc in the first argument, which is always the main text. Strip %
let formatSpecifiers;
const firstTextArg = args.shift();
// currentCollapsedStringArg is the accumulated text
let currentCollapsedStringArg = variables.getRemoteObjectPreview(firstTextArg, /*stringify=*/ false) + '';
if (firstTextArg.type === 'string' && !skipFormatSpecifiers) {
formatSpecifiers = (currentCollapsedStringArg.match(/\%[sidfoOc]/g) || [])
.map(spec => spec[1]);
}
else {
formatSpecifiers = [];
}
const processedArgs = [];
const pushStringArg = (strArg) => {
if (typeof strArg === 'string') {
processedArgs.push({ type: 'string', value: strArg });
}
};
// Collapse all text parameters, formatting properly if there's a format specifier
for (let argIdx = 0; argIdx < args.length; argIdx++) {
const arg = args[argIdx];
const formatSpec = formatSpecifiers.shift();
const formatted = formatArg(formatSpec, arg);
currentCollapsedStringArg = currentCollapsedStringArg || '';
if (typeof formatted === 'string') {
if (formatSpec) {
// If this param had a format specifier, search and replace it with the formatted param.
currentCollapsedStringArg = currentCollapsedStringArg.replace('%' + formatSpec, formatted);
}
else {
currentCollapsedStringArg += (currentCollapsedStringArg ? ' ' + formatted : formatted);
}
}
else if (formatSpec) {
// `formatted` is an object - split currentCollapsedStringArg around the current formatSpec and add the object
const curSpecIdx = currentCollapsedStringArg.indexOf('%' + formatSpec);
const processedPart = currentCollapsedStringArg.slice(0, curSpecIdx);
if (processedPart) {
pushStringArg(processedPart);
}
currentCollapsedStringArg = currentCollapsedStringArg.slice(curSpecIdx + 2);
processedArgs.push(formatted);
}
else {
pushStringArg(currentCollapsedStringArg);
currentCollapsedStringArg = null;
processedArgs.push(formatted);
}
}
pushStringArg(currentCollapsedStringArg);
return processedArgs;
}
function formatArg(formatSpec, arg) {
const paramValue = String(typeof arg.value !== 'undefined' ? arg.value : arg.description);
if (formatSpec === 's') {
return paramValue;
}
else if (['i', 'd'].indexOf(formatSpec) >= 0) {
return Math.floor(+paramValue) + '';
}
else if (formatSpec === 'f') {
return +paramValue + '';
}
else if (formatSpec === 'c') {
return formatColorArg(arg);
}
else if (formatSpec === 'O') {
if (arg.objectId) {
return arg;
}
else {
return paramValue;
}
}
else {
// No formatSpec, or unsupported formatSpec:
// %o - expandable DOM element
if (arg.objectId) {
return arg;
}
else {
return paramValue;
}
}
}
function formatColorArg(arg) {
const cssRegex = /\s*(.*?)\s*:\s*(.*?)\s*(?:;|$)/g;
let escapedSequence;
let match = cssRegex.exec(arg.value);
while (match != null) {
if (match.length === 3) {
if (escapedSequence === undefined) {
// Some valid pattern appeared, initialize escapedSequence.
// If the pattern has no value like `color:`, then this should remain an empty string.
escapedSequence = '';
}
if (match[2]) {
switch (match[1]) {
case 'color':
const color = getAnsi16Color(match[2]);
if (color) {
escapedSequence += `;${color}`;
}
break;
case 'background':
const background = getAnsi16Color(match[2]);
if (background) {
escapedSequence += `;${background + 10}`;
}
break;
case 'font-weight':
if (match[2] === 'bold') {
escapedSequence += ';1';
}
break;
case 'text-decoration':
if (match[2] === 'underline') {
escapedSequence += ';4';
}
break;
default:
}
}
}
match = cssRegex.exec(arg.value);
}
if (typeof escapedSequence === 'string') {
escapedSequence = `\x1b[0${escapedSequence}m`;
}
return escapedSequence;
}
function stackTraceToString(stackTrace) {
if (!stackTrace) {
return '';
}
return stackTrace.callFrames
.map(frame => {
const fnName = frame.functionName || (frame.url ? '(anonymous)' : '(eval)');
const fileName = frame.url ? frame.url : 'eval';
return ` at ${fnName} (${fileName}:${frame.lineNumber + 1}:${frame.columnNumber})`;
})
.join('\n');
}
function getAnsi16Color(colorString) {
try {
// Color can parse hex and color names
const color = new Color(colorString);
return color.ansi16().object().ansi16;
}
catch (ex) {
// Unable to parse Color
// For instance, "inherit" color will throw
}
return undefined;
}
//# sourceMappingURL=consoleHelper.js.map