@sussudio/base
Version:
Internal APIs for VS Code's utilities and user interface building blocks.
246 lines (245 loc) • 6.34 kB
JavaScript
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
let globalObservableLogger;
export function setLogger(logger) {
globalObservableLogger = logger;
}
export function getLogger() {
return globalObservableLogger;
}
export class ConsoleObservableLogger {
indentation = 0;
textToConsoleArgs(text) {
return consoleTextToArgs([normalText(repeat('| ', this.indentation)), text]);
}
formatInfo(info) {
return info.didChange
? [
normalText(` `),
styled(formatValue(info.oldValue, 70), {
color: 'red',
strikeThrough: true,
}),
normalText(` `),
styled(formatValue(info.newValue, 60), {
color: 'green',
}),
]
: [normalText(` (unchanged)`)];
}
handleObservableChanged(observable, info) {
console.log(
...this.textToConsoleArgs([
formatKind('observable value changed'),
styled(observable.debugName, { color: 'BlueViolet' }),
...this.formatInfo(info),
]),
);
}
changedObservablesSets = new WeakMap();
formatChanges(changes) {
if (changes.size === 0) {
return undefined;
}
return styled(' (changed deps: ' + [...changes].map((o) => o.debugName).join(', ') + ')', { color: 'gray' });
}
handleDerivedCreated(derived) {
const existingHandleChange = derived.handleChange;
this.changedObservablesSets.set(derived, new Set());
derived.handleChange = (observable, change) => {
this.changedObservablesSets.get(derived).add(observable);
return existingHandleChange.apply(derived, [observable, change]);
};
}
handleDerivedRecomputed(derived, info) {
const changedObservables = this.changedObservablesSets.get(derived);
console.log(
...this.textToConsoleArgs([
formatKind('derived recomputed'),
styled(derived.debugName, { color: 'BlueViolet' }),
...this.formatInfo(info),
this.formatChanges(changedObservables),
]),
);
changedObservables.clear();
}
handleFromEventObservableTriggered(observable, info) {
console.log(
...this.textToConsoleArgs([
formatKind('observable from event triggered'),
styled(observable.debugName, { color: 'BlueViolet' }),
...this.formatInfo(info),
]),
);
}
handleAutorunCreated(autorun) {
const existingHandleChange = autorun.handleChange;
this.changedObservablesSets.set(autorun, new Set());
autorun.handleChange = (observable, change) => {
this.changedObservablesSets.get(autorun).add(observable);
return existingHandleChange.apply(autorun, [observable, change]);
};
}
handleAutorunTriggered(autorun) {
const changedObservables = this.changedObservablesSets.get(autorun);
console.log(
...this.textToConsoleArgs([
formatKind('autorun'),
styled(autorun.debugName, { color: 'BlueViolet' }),
this.formatChanges(changedObservables),
]),
);
changedObservables.clear();
}
handleBeginTransaction(transaction) {
let transactionName = transaction.getDebugName();
if (transactionName === undefined) {
transactionName = '';
}
console.log(
...this.textToConsoleArgs([formatKind('transaction'), styled(transactionName, { color: 'BlueViolet' })]),
);
this.indentation++;
}
handleEndTransaction() {
this.indentation--;
}
}
function consoleTextToArgs(text) {
const styles = new Array();
const initial = {};
const data = initial;
let firstArg = '';
function process(t) {
if ('length' in t) {
for (const item of t) {
if (item) {
process(item);
}
}
} else if ('text' in t) {
firstArg += `%c${t.text}`;
styles.push(t.style);
if (t.data) {
Object.assign(data, t.data);
}
} else if ('data' in t) {
Object.assign(data, t.data);
}
}
process(text);
const result = [firstArg, ...styles];
if (Object.keys(data).length > 0) {
result.push(data);
}
return result;
}
function normalText(text) {
return styled(text, { color: 'black' });
}
function formatKind(kind) {
return styled(padStr(`${kind}: `, 10), { color: 'black', bold: true });
}
function styled(
text,
options = {
color: 'black',
},
) {
function objToCss(styleObj) {
return Object.entries(styleObj).reduce((styleString, [propName, propValue]) => {
return `${styleString}${propName}:${propValue};`;
}, '');
}
const style = {
color: options.color,
};
if (options.strikeThrough) {
style['text-decoration'] = 'line-through';
}
if (options.bold) {
style['font-weight'] = 'bold';
}
return {
text,
style: objToCss(style),
};
}
function formatValue(value, availableLen) {
switch (typeof value) {
case 'number':
return '' + value;
case 'string':
if (value.length + 2 <= availableLen) {
return `"${value}"`;
}
return `"${value.substr(0, availableLen - 7)}"+...`;
case 'boolean':
return value ? 'true' : 'false';
case 'undefined':
return 'undefined';
case 'object':
if (value === null) {
return 'null';
}
if (Array.isArray(value)) {
return formatArray(value, availableLen);
}
return formatObject(value, availableLen);
case 'symbol':
return value.toString();
case 'function':
return `[[Function${value.name ? ' ' + value.name : ''}]]`;
default:
return '' + value;
}
}
function formatArray(value, availableLen) {
let result = '[ ';
let first = true;
for (const val of value) {
if (!first) {
result += ', ';
}
if (result.length - 5 > availableLen) {
result += '...';
break;
}
first = false;
result += `${formatValue(val, availableLen - result.length)}`;
}
result += ' ]';
return result;
}
function formatObject(value, availableLen) {
let result = '{ ';
let first = true;
for (const [key, val] of Object.entries(value)) {
if (!first) {
result += ', ';
}
if (result.length - 5 > availableLen) {
result += '...';
break;
}
first = false;
result += `${key}: ${formatValue(val, availableLen - result.length)}`;
}
result += ' }';
return result;
}
function repeat(str, count) {
let result = '';
for (let i = 1; i <= count; i++) {
result += str;
}
return result;
}
function padStr(str, length) {
while (str.length < length) {
str += ' ';
}
return str;
}