UNPKG

@dcloudio/uni-debugger

Version:

uni-app debugger

310 lines (281 loc) 11.3 kB
// Copyright (c) 2015 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. /** * @unrestricted */ ObjectUI.RemoteObjectPreviewFormatter = class { /** * @param {!Protocol.Runtime.PropertyPreview} a * @param {!Protocol.Runtime.PropertyPreview} b * @return {number} */ static _objectPropertyComparator(a, b) { return sortValue(a) - sortValue(b); /** * @param {!Protocol.Runtime.PropertyPreview} property * @return {number} */ function sortValue(property) { const internalName = ObjectUI.RemoteObjectPreviewFormatter._internalName; if (property.name === internalName.PromiseStatus) return 1; else if (property.name === internalName.PromiseValue) return 2; else if (property.name === internalName.GeneratorStatus || internalName.PrimitiveValue) return 3; else if (property.type !== 'function') return 4; return 5; } } /** * @param {!Element} parentElement * @param {!Protocol.Runtime.ObjectPreview} preview * @param {boolean} isEntry */ appendObjectPreview(parentElement, preview, isEntry) { const description = preview.description; const subTypesWithoutValuePreview = new Set(['null', 'regexp', 'error', 'internal#entry']); if (preview.type !== 'object' || subTypesWithoutValuePreview.has(preview.subtype) || isEntry) { parentElement.appendChild(this.renderPropertyPreview(preview.type, preview.subtype, description)); return; } const isArrayOrTypedArray = preview.subtype === 'array' || preview.subtype === 'typedarray'; if (description) { let text; if (isArrayOrTypedArray) { const arrayLength = SDK.RemoteObject.arrayLength(preview); const arrayLengthText = arrayLength > 1 ? ('(' + arrayLength + ')') : ''; const arrayName = SDK.RemoteObject.arrayNameFromDescription(description); text = arrayName === 'Array' ? arrayLengthText : (arrayName + arrayLengthText); } else { const hideDescription = description === 'Object'; text = hideDescription ? '' : description; } if (text.length > 0) parentElement.createChild('span', 'object-description').textContent = text + '\u00a0'; } const propertiesElement = parentElement.createChild('span', 'object-properties-preview'); propertiesElement.createTextChild(isArrayOrTypedArray ? '[' : '{'); if (preview.entries) this._appendEntriesPreview(propertiesElement, preview); else if (isArrayOrTypedArray) this._appendArrayPropertiesPreview(propertiesElement, preview); else this._appendObjectPropertiesPreview(propertiesElement, preview); if (preview.overflow) { const ellipsisText = propertiesElement.textContent.length > 1 ? ',\u00a0\u2026' : '\u2026'; propertiesElement.createChild('span').textContent = ellipsisText; } propertiesElement.createTextChild(isArrayOrTypedArray ? ']' : '}'); } /** * @param {string} description * @return {string} */ _abbreviateFullQualifiedClassName(description) { const abbreviatedDescription = description.split('.'); for (let i = 0; i < abbreviatedDescription.length - 1; ++i) abbreviatedDescription[i] = abbreviatedDescription[i].trimMiddle(3); return abbreviatedDescription.join('.'); } /** * @param {!Element} parentElement * @param {!Protocol.Runtime.ObjectPreview} preview */ _appendObjectPropertiesPreview(parentElement, preview) { const internalName = ObjectUI.RemoteObjectPreviewFormatter._internalName; const properties = preview.properties.filter(p => p.type !== 'accessor') .stableSort(ObjectUI.RemoteObjectPreviewFormatter._objectPropertyComparator); for (let i = 0; i < properties.length; ++i) { if (i > 0) parentElement.createTextChild(', '); const property = properties[i]; const name = property.name; // Internal properties are given special formatting, e.g. Promises `<rejected>: 123`. if (preview.subtype === 'promise' && name === internalName.PromiseStatus) { parentElement.appendChild(this._renderDisplayName('<' + property.value + '>')); const nextProperty = i + 1 < properties.length ? properties[i + 1] : null; if (nextProperty && nextProperty.name === internalName.PromiseValue) { if (property.value !== 'pending') { parentElement.createTextChild(': '); parentElement.appendChild(this._renderPropertyPreviewOrAccessor([nextProperty])); } i++; } } else if (preview.subtype === 'generator' && name === internalName.GeneratorStatus) { parentElement.appendChild(this._renderDisplayName('<' + property.value + '>')); } else if (name === internalName.PrimitiveValue) { parentElement.appendChild(this._renderPropertyPreviewOrAccessor([property])); } else { parentElement.appendChild(this._renderDisplayName(name)); parentElement.createTextChild(': '); parentElement.appendChild(this._renderPropertyPreviewOrAccessor([property])); } } } /** * @param {!Element} parentElement * @param {!Protocol.Runtime.ObjectPreview} preview */ _appendArrayPropertiesPreview(parentElement, preview) { const arrayLength = SDK.RemoteObject.arrayLength(preview); const indexProperties = preview.properties.filter(p => toArrayIndex(p.name) !== -1).stableSort(arrayEntryComparator); const otherProperties = preview.properties.filter(p => toArrayIndex(p.name) === -1) .stableSort(ObjectUI.RemoteObjectPreviewFormatter._objectPropertyComparator); /** * @param {!Protocol.Runtime.PropertyPreview} a * @param {!Protocol.Runtime.PropertyPreview} b * @return {number} */ function arrayEntryComparator(a, b) { return toArrayIndex(a.name) - toArrayIndex(b.name); } /** * @param {string} name * @return {number} */ function toArrayIndex(name) { const index = name >>> 0; if (String(index) === name && index < arrayLength) return index; return -1; } // Gaps can be shown when all properties are guaranteed to be in the preview. const canShowGaps = !preview.overflow; let lastNonEmptyArrayIndex = -1; let elementsAdded = false; for (let i = 0; i < indexProperties.length; ++i) { if (elementsAdded) parentElement.createTextChild(', '); const property = indexProperties[i]; const index = toArrayIndex(property.name); if (canShowGaps && index - lastNonEmptyArrayIndex > 1) { appendUndefined(index); parentElement.createTextChild(', '); } if (!canShowGaps && i !== index) { parentElement.appendChild(this._renderDisplayName(property.name)); parentElement.createTextChild(': '); } parentElement.appendChild(this._renderPropertyPreviewOrAccessor([property])); lastNonEmptyArrayIndex = index; elementsAdded = true; } if (canShowGaps && arrayLength - lastNonEmptyArrayIndex > 1) { if (elementsAdded) parentElement.createTextChild(', '); appendUndefined(arrayLength); } for (let i = 0; i < otherProperties.length; ++i) { if (elementsAdded) parentElement.createTextChild(', '); const property = otherProperties[i]; parentElement.appendChild(this._renderDisplayName(property.name)); parentElement.createTextChild(': '); parentElement.appendChild(this._renderPropertyPreviewOrAccessor([property])); elementsAdded = true; } /** * @param {number} index */ function appendUndefined(index) { const span = parentElement.createChild('span', 'object-value-undefined'); const count = index - lastNonEmptyArrayIndex - 1; span.textContent = count !== 1 ? Common.UIString('empty × %d', count) : Common.UIString('empty'); elementsAdded = true; } } /** * @param {!Element} parentElement * @param {!Protocol.Runtime.ObjectPreview} preview */ _appendEntriesPreview(parentElement, preview) { for (let i = 0; i < preview.entries.length; ++i) { if (i > 0) parentElement.createTextChild(', '); const entry = preview.entries[i]; if (entry.key) { this.appendObjectPreview(parentElement, entry.key, true /* isEntry */); parentElement.createTextChild(' => '); } this.appendObjectPreview(parentElement, entry.value, true /* isEntry */); } } /** * @param {string} name * @return {!Element} */ _renderDisplayName(name) { const result = createElementWithClass('span', 'name'); const needsQuotes = /^\s|\s$|^$|\n/.test(name); result.textContent = needsQuotes ? '"' + name.replace(/\n/g, '\u21B5') + '"' : name; return result; } /** * @param {!Array.<!Protocol.Runtime.PropertyPreview>} propertyPath * @return {!Element} */ _renderPropertyPreviewOrAccessor(propertyPath) { const property = propertyPath.peekLast(); return this.renderPropertyPreview(property.type, /** @type {string} */ (property.subtype), property.value); } /** * @param {string} type * @param {string=} subtype * @param {string=} description * @return {!Element} */ renderPropertyPreview(type, subtype, description) { const span = createElementWithClass('span', 'object-value-' + (subtype || type)); description = description || ''; if (type === 'accessor') { span.textContent = '(...)'; span.title = Common.UIString('The property is computed with a getter'); return span; } if (type === 'function') { span.textContent = '\u0192'; return span; } if (type === 'object' && subtype === 'node' && description) { ObjectUI.RemoteObjectPreviewFormatter.createSpansForNodeTitle(span, description); return span; } if (type === 'string') { span.createTextChildren('"', description.replace(/\n/g, '\u21B5'), '"'); return span; } if (type === 'object' && !subtype) { let preview = this._abbreviateFullQualifiedClassName(description); if (preview === 'Object') preview = '{\u2026}'; span.textContent = preview; span.title = description; return span; } span.textContent = description; return span; } }; /** @enum {string} */ ObjectUI.RemoteObjectPreviewFormatter._internalName = { GeneratorStatus: '[[GeneratorStatus]]', PrimitiveValue: '[[PrimitiveValue]]', PromiseStatus: '[[PromiseStatus]]', PromiseValue: '[[PromiseValue]]' }; /** * @param {!Element} container * @param {string} nodeTitle */ ObjectUI.RemoteObjectPreviewFormatter.createSpansForNodeTitle = function(container, nodeTitle) { const match = nodeTitle.match(/([^#.]+)(#[^.]+)?(\..*)?/); container.createChild('span', 'webkit-html-tag-name').textContent = match[1]; if (match[2]) container.createChild('span', 'webkit-html-attribute-value').textContent = match[2]; if (match[3]) container.createChild('span', 'webkit-html-attribute-name').textContent = match[3]; };