Console.log objects
This commit is contained in:
Родитель
d5c2724a3b
Коммит
a2817c565d
|
@ -16,30 +16,32 @@ suite('ConsoleHelper', () => {
|
|||
}
|
||||
|
||||
suite('console.log()', () => {
|
||||
test('handles simple log', () => {
|
||||
test('simple log', () => {
|
||||
doAssert(Console.makeLog('Hello'), 'Hello');
|
||||
doAssert(Console.makeLog('Hello', 123, 'world!'), 'Hello 123 world!');
|
||||
});
|
||||
|
||||
test('handles basic format specifiers', () => {
|
||||
test('basic format specifiers', () => {
|
||||
doAssert(Console.makeLog('%s, %d', 'test', 123), 'test, 123');
|
||||
});
|
||||
|
||||
test('handles numeric format specifiers correctly', () => {
|
||||
test('numeric format specifiers correctly', () => {
|
||||
doAssert(Console.makeLog('%d %i %f', 1.9, 324, 9.4), '1 324 9.4');
|
||||
doAssert(Console.makeLog('%d %i %f', -19, -32.5, -9.4), '-19 -33 -9.4');
|
||||
doAssert(Console.makeLog('%d %i %f', 'not', 'a', 'number'), 'NaN NaN NaN');
|
||||
});
|
||||
|
||||
test('handles unmatched format specifiers', () => {
|
||||
test('unmatched format specifiers', () => {
|
||||
doAssert(Console.makeLog('%s %s %s', 'test'), 'test %s %s');
|
||||
doAssert(Console.makeLog('%s %s end', 'test1', 'test2', 'test3'), 'test1 test2 end test3');
|
||||
});
|
||||
|
||||
test('weird cases', () => {
|
||||
test('null/undefined cases', () => {
|
||||
doAssert(Console.makeLog('%s %s %s', null, undefined, 'test'), 'null undefined test');
|
||||
doAssert(Console.makeLog('test', null, undefined), 'test null undefined');
|
||||
});
|
||||
|
||||
test('objects- waiting on VS Code bug 20343');
|
||||
});
|
||||
|
||||
suite('console.assert()', () => {
|
||||
|
@ -71,7 +73,12 @@ namespace Console {
|
|||
url: 'file:///c:/page/script.js',
|
||||
executionContextId: 2,
|
||||
parameters: params.map(param => {
|
||||
return { type: typeof param, value: param }
|
||||
const remoteObj = { type: typeof param, value: param };
|
||||
if (param === null) {
|
||||
remoteObj['subtype'] = 'null';
|
||||
}
|
||||
|
||||
return remoteObj;
|
||||
})
|
||||
};
|
||||
|
||||
|
|
|
@ -59,6 +59,8 @@ function evalDebugger() {
|
|||
}
|
||||
|
||||
function consoleAPIs() {
|
||||
console.log({ a: 1, b: 'asdf', c: { d: 4 } });
|
||||
console.log({ a: 1}, {b: 2});
|
||||
console.time('timing');
|
||||
console.group('my group');
|
||||
console.log('hello', 'world!');
|
||||
|
@ -68,7 +70,6 @@ function consoleAPIs() {
|
|||
console.timeEnd('timing');
|
||||
console.trace();
|
||||
|
||||
console.dir({ a: 1, b: 2 });
|
||||
(<any>console).table([1, 2, 3]);
|
||||
console.assert(1 == 2, '1 is not 2');
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
*--------------------------------------------------------*/
|
||||
|
||||
import * as url from 'url';
|
||||
import * as Utilities from './utilities';
|
||||
|
||||
export function formatConsoleMessage(m: WebKitProtocol.Console.Message): { text: string, isError: boolean } {
|
||||
let outputText: string;
|
||||
|
@ -23,22 +24,29 @@ export function formatConsoleMessage(m: WebKitProtocol.Console.Message): { text:
|
|||
}
|
||||
} else if (m.type === 'endGroup') {
|
||||
outputText = '‹End group›'
|
||||
} else {
|
||||
// Some types we have to ignore
|
||||
outputText = 'Unimplemented console API: ' + m.type;
|
||||
}
|
||||
|
||||
return { text: outputText, isError: m.level === 'error' };
|
||||
}
|
||||
|
||||
function resolveParams(m: WebKitProtocol.Console.Message): string {
|
||||
let text = m.text;
|
||||
if (!m.parameters || m.parameters.length === 1) {
|
||||
return text;
|
||||
if (!m.parameters || !m.parameters.length) {
|
||||
return m.text;
|
||||
}
|
||||
|
||||
m.parameters.shift(); // The first param is 'text'
|
||||
const textParam = m.parameters[0];
|
||||
let text = remoteObjectToString(textParam);
|
||||
m.parameters.shift();
|
||||
|
||||
// Find all %s, %i, etc. Strip %
|
||||
let formatSpecifiers = text.match(/\%[sidfoOc]/g) || [];
|
||||
formatSpecifiers = formatSpecifiers.map(spec => spec[1]);
|
||||
// Find all %s, %i, etc in the first parameter, which is always the main text. Strip %
|
||||
let formatSpecifiers: string[] = [];
|
||||
if (textParam.type === 'string') {
|
||||
formatSpecifiers = textParam.value.match(/\%[sidfoOc]/g) || [];
|
||||
formatSpecifiers = formatSpecifiers.map(spec => spec[1]);
|
||||
}
|
||||
|
||||
// Append all parameters, formatting properly if there's a format specifier
|
||||
m.parameters.forEach((param, i) => {
|
||||
|
@ -59,13 +67,43 @@ function resolveParams(m: WebKitProtocol.Console.Message): string {
|
|||
if (formatSpecifiers[i]) {
|
||||
text = text.replace('%' + formatSpecifiers[i], formatted);
|
||||
} else {
|
||||
text += ' ' + param.value;
|
||||
text += ' ' + remoteObjectToString(param);
|
||||
}
|
||||
});
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
function remoteObjectToString(obj: WebKitProtocol.Runtime.RemoteObject): string {
|
||||
const result = Utilities.remoteObjectToValue(obj, /*stringify=*/false);
|
||||
if (result.variableHandleRef) {
|
||||
// The DebugProtocol console API doesn't support returning a variable reference, so do our best to
|
||||
// build a useful string out of this object.
|
||||
if (obj.preview && obj.preview.properties) {
|
||||
let props: string = obj.preview.properties
|
||||
.map(prop => {
|
||||
let propStr = prop.name + ': ';
|
||||
if (prop.type === 'string') {
|
||||
propStr += `"${prop.value}"`;
|
||||
} else {
|
||||
propStr += prop.value;
|
||||
}
|
||||
|
||||
return propStr;
|
||||
})
|
||||
.join(', ');
|
||||
|
||||
if (obj.preview.overflow) {
|
||||
props += '…';
|
||||
}
|
||||
|
||||
return `${obj.className} {${props}}`;
|
||||
}
|
||||
} else {
|
||||
return result.value;
|
||||
}
|
||||
}
|
||||
|
||||
function stackTraceToString(stackTrace: WebKitProtocol.Console.StackTrace): string {
|
||||
return stackTrace
|
||||
.map(frame => `${frame.functionName} @${url.parse(frame.url).pathname}:${frame.lineNumber}`)
|
||||
|
|
|
@ -39,7 +39,7 @@ export function getPlatform(): Platform {
|
|||
const platform = os.platform();
|
||||
return platform === 'darwin' ? Platform.OSX :
|
||||
platform === 'win32' ? Platform.Windows :
|
||||
Platform.Linux;
|
||||
Platform.Linux;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -69,7 +69,7 @@ export class DebounceHelper {
|
|||
this.waitToken = null;
|
||||
fn();
|
||||
},
|
||||
this.timeoutMs);
|
||||
this.timeoutMs);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -162,7 +162,7 @@ export class Logger {
|
|||
if (this._isServer && Logger.ALLOW_LOGGING) {
|
||||
if (timestamp) {
|
||||
const d = new Date();
|
||||
const timeStamp = `[${d.getMinutes()}:${d.getSeconds()}:${d.getMilliseconds()}]`;
|
||||
const timeStamp = `[${d.getMinutes() }:${d.getSeconds() }:${d.getMilliseconds() }]`;
|
||||
console.log(timeStamp + msg);
|
||||
} else {
|
||||
console.log(msg);
|
||||
|
@ -242,3 +242,41 @@ export function canonicalizeUrl(url: string): string {
|
|||
|
||||
return url;
|
||||
}
|
||||
|
||||
export function remoteObjectToValue(object: WebKitProtocol.Runtime.RemoteObject, stringify = true): { value: string, variableHandleRef: string } {
|
||||
let value = '';
|
||||
let variableHandleRef: string;
|
||||
|
||||
if (object) { // just paranoia?
|
||||
if (object && object.type === 'object') {
|
||||
if (object.subtype === 'null') {
|
||||
value = 'null';
|
||||
} else {
|
||||
// If it's a non-null object, create a variable reference so the client can ask for its props
|
||||
variableHandleRef = object.objectId;
|
||||
value = object.description;
|
||||
}
|
||||
} else if (object && object.type === 'undefined') {
|
||||
value = 'undefined';
|
||||
} else if (object.type === 'function') {
|
||||
const firstBraceIdx = object.description.indexOf('{');
|
||||
if (firstBraceIdx >= 0) {
|
||||
value = object.description.substring(0, firstBraceIdx) + '{ … }';
|
||||
} else {
|
||||
const firstArrowIdx = object.description.indexOf('=>');
|
||||
value = firstArrowIdx >= 0 ?
|
||||
object.description.substring(0, firstArrowIdx + 2) + ' …' :
|
||||
object.description;
|
||||
}
|
||||
} else {
|
||||
// The value is a primitive value, or something that has a description (not object, primitive, or undefined). And force to be string
|
||||
if (typeof object.value === 'undefined') {
|
||||
value = object.description;
|
||||
} else {
|
||||
value = stringify ? JSON.stringify(object.value) : object.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { value, variableHandleRef };
|
||||
}
|
||||
|
|
|
@ -215,9 +215,11 @@ export class WebKitDebugAdapter implements IDebugAdapter {
|
|||
|
||||
private onConsoleMessage(params: WebKitProtocol.Console.MessageAddedParams): void {
|
||||
const formattedMessage = formatConsoleMessage(params.message);
|
||||
this.fireEvent(new OutputEvent(
|
||||
formattedMessage.text + '\n',
|
||||
formattedMessage.isError ? 'stderr' : 'console'));
|
||||
if (formattedMessage) {
|
||||
this.fireEvent(new OutputEvent(
|
||||
formattedMessage.text + '\n',
|
||||
formattedMessage.isError ? 'stderr' : 'console'));
|
||||
}
|
||||
}
|
||||
|
||||
public disconnect(): Promise<void> {
|
||||
|
@ -485,38 +487,18 @@ export class WebKitDebugAdapter implements IDebugAdapter {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the object through Utilities.remoteObjectToValue, and if it returns a variableHandle reference,
|
||||
* use it with this instance's variableHandles to create a variable handle.
|
||||
*/
|
||||
private remoteObjectToValue(object: WebKitProtocol.Runtime.RemoteObject): { value: string, variablesReference: number } {
|
||||
let value = '';
|
||||
let variablesReference = 0;
|
||||
|
||||
if (object) { // just paranoia?
|
||||
if (object && object.type === 'object') {
|
||||
if (object.subtype === 'null') {
|
||||
value = 'null';
|
||||
} else {
|
||||
// If it's a non-null object, create a variable reference so the client can ask for its props
|
||||
variablesReference = this._variableHandles.create(object.objectId);
|
||||
value = object.description;
|
||||
}
|
||||
} else if (object && object.type === 'undefined') {
|
||||
value = 'undefined';
|
||||
} else if (object.type === 'function') {
|
||||
const firstBraceIdx = object.description.indexOf('{');
|
||||
if (firstBraceIdx >= 0) {
|
||||
value = object.description.substring(0, firstBraceIdx) + '{ … }';
|
||||
} else {
|
||||
const firstArrowIdx = object.description.indexOf('=>');
|
||||
value = firstArrowIdx >= 0 ?
|
||||
object.description.substring(0, firstArrowIdx + 2) + ' …' :
|
||||
object.description;
|
||||
}
|
||||
} else {
|
||||
// The value is a primitive value, or something that has a description (not object, primitive, or undefined). And force to be string
|
||||
value = typeof object.value === 'undefined' ? object.description : JSON.stringify(object.value);
|
||||
}
|
||||
const { value, variableHandleRef } = Utilities.remoteObjectToValue(object);
|
||||
const result = { value, variablesReference: 0 };
|
||||
if (variableHandleRef) {
|
||||
result.variablesReference = this._variableHandles.create(variableHandleRef);
|
||||
}
|
||||
|
||||
return { value, variablesReference };
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -167,6 +167,20 @@ declare namespace WebKitProtocol {
|
|||
subtype?: string;
|
||||
type: string;
|
||||
value?: any;
|
||||
preview?: {
|
||||
type: string;
|
||||
description: string;
|
||||
lossless: boolean;
|
||||
overflow: boolean;
|
||||
properties: PropertyPreview[];
|
||||
};
|
||||
}
|
||||
|
||||
interface PropertyPreview {
|
||||
name: string;
|
||||
type: string;
|
||||
subtype?: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
interface EvaluateParams {
|
||||
|
|
Загрузка…
Ссылка в новой задаче