UNPKG

@jupyterlab/debugger

Version:
216 lines 9.59 kB
// Copyright (c) Jupyter Development Team. // Distributed under the terms of the Modified BSD License. import { nullTranslator } from '@jupyterlab/translation'; import { getTreeItemElement, ReactWidget, searchIcon } from '@jupyterlab/ui-components'; import { Button, TreeItem, TreeView } from '@jupyter/react-components'; import { ArrayExt } from '@lumino/algorithm'; import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { convertType } from '.'; import { Debugger } from '../../debugger'; /** * The body for tree of variables. */ export class VariablesBodyTree extends ReactWidget { /** * Instantiate a new Body for the tree of variables. * * @param options The instantiation options for a VariablesBodyTree. */ constructor(options) { super(); this._scope = ''; this._scopes = []; this._filter = new Set(); this._commands = options.commands; this._service = options.service; this._translator = options.translator; const model = (this.model = options.model); model.changed.connect(this._updateScopes, this); this.addClass('jp-DebuggerVariables-body'); } /** * Render the VariablesBodyTree. */ render() { var _a; const scope = (_a = this._scopes.find(scope => scope.name === this._scope)) !== null && _a !== void 0 ? _a : this._scopes[0]; const handleSelectVariable = (variable) => { this.model.selectedVariable = variable; }; if ((scope === null || scope === void 0 ? void 0 : scope.name) !== 'Globals') { this.addClass('jp-debuggerVariables-local'); } else { this.removeClass('jp-debuggerVariables-local'); } return scope ? (React.createElement(React.Fragment, null, React.createElement(TreeView, { className: "jp-TreeView" }, React.createElement(VariablesBranch, { key: scope.name, commands: this._commands, service: this._service, data: scope.variables, filter: this._filter, translator: this._translator, handleSelectVariable: handleSelectVariable })))) : (React.createElement("div", null)); } /** * Set the variable filter list. */ set filter(filter) { this._filter = filter; this.update(); } /** * Set the current scope */ set scope(scope) { this._scope = scope; this.update(); } /** * Update the scopes and the tree of variables. * * @param model The variables model. */ _updateScopes(model) { if (ArrayExt.shallowEqual(this._scopes, model.scopes)) { return; } this._scopes = model.scopes; this.update(); } } /** * A React component to display a list of variables. * * @param {object} props The component props. * @param props.data An array of variables. * @param props.service The debugger service. * @param props.filter Optional variable filter list. */ const VariablesBranch = (props) => { const { commands, data, service, filter, translator, handleSelectVariable } = props; const [variables, setVariables] = useState(data); useEffect(() => { setVariables(data); }, [data]); return (React.createElement(React.Fragment, null, variables .filter(variable => !(filter || new Set()).has(variable.evaluateName || '')) .map(variable => { const key = `${variable.name}-${variable.evaluateName}-${variable.type}-${variable.value}-${variable.variablesReference}`; return (React.createElement(VariableComponent, { key: key, commands: commands, data: variable, service: service, filter: filter, translator: translator, onSelect: handleSelectVariable })); }))); }; function _prepareDetail(variable) { if (variable.type === 'float' && (variable.value == 'inf' || variable.value == '-inf')) { return variable.value; } const detail = convertType(variable); if (variable.type === 'float' && isNaN(detail)) { // silence React warning: // `Received NaN for the `children` attribute. If this is expected, cast the value to a string` return 'NaN'; } return detail; } /** * A React component to display one node variable in tree. * * @param {object} props The component props. * @param props.data An array of variables. * @param props.service The debugger service. * @param props.filter Optional variable filter list. */ const VariableComponent = (props) => { var _a, _b; const { commands, data, service, filter, translator, onSelect } = props; const [variable] = useState(data); const [showDetailsButton, setShowDetailsButton] = useState(false); const [expanded, setExpanded] = useState(false); const [variables, setVariables] = useState(null); const trans = useMemo(() => (translator !== null && translator !== void 0 ? translator : nullTranslator).load('jupyterlab'), [translator]); const onSelection = onSelect !== null && onSelect !== void 0 ? onSelect : (() => void 0); const expandable = useMemo(() => variable.variablesReference !== 0 || variable.type === 'function', [variable.variablesReference, variable.type]); const details = useMemo(() => _prepareDetail(variable), [variable]); const hasMimeRenderer = useMemo(() => ![ 'special variables', 'protected variables', 'function variables', 'class variables' ].includes(variable.name), [variable.name]); const disableMimeRenderer = useMemo(() => { var _a; return !service.model.hasRichVariableRendering || !commands.isEnabled(Debugger.CommandIDs.renderMimeVariable, { name: variable.name, frameID: (_a = service.model.callstack.frame) === null || _a === void 0 ? void 0 : _a.id }); }, [ service.model.hasRichVariableRendering, variable.name, (_a = service.model.callstack.frame) === null || _a === void 0 ? void 0 : _a.id ]); const fetchChildren = useCallback(async () => { if (expandable && !variables) { setVariables(await service.inspectVariable(variable.variablesReference)); } }, [expandable, service, variable.variablesReference, variables]); const onVariableClicked = useCallback(async (event) => { const item = getTreeItemElement(event.target); if (event.currentTarget !== item) { return; } if (!expandable) { return; } setExpanded(!expanded); }, [expandable, expanded]); const onSelectChange = useCallback((event) => { if (event.currentTarget === event.detail && event.detail.selected) { onSelection(variable); } }, [variable]); const renderVariable = useCallback(() => { var _a; commands .execute(Debugger.CommandIDs.renderMimeVariable, { name: variable.name, frameID: (_a = service.model.callstack.frame) === null || _a === void 0 ? void 0 : _a.id }) .catch(reason => { console.error(`Failed to render variable ${variable === null || variable === void 0 ? void 0 : variable.name}`, reason); }); }, [commands, variable.name, (_b = service.model.callstack.frame) === null || _b === void 0 ? void 0 : _b.id]); const onContextMenu = useCallback((event) => { const item = getTreeItemElement(event.target); if (event.currentTarget !== item) { return; } onSelection(variable); }, [variable]); return (React.createElement(TreeItem, { className: "jp-TreeItem nested", expanded: expanded, onSelect: onSelectChange, onExpand: fetchChildren, onClick: (e) => onVariableClicked(e), onContextMenu: onContextMenu, onKeyDown: event => { if (event.key == 'Enter') { if (hasMimeRenderer && showDetailsButton) { onSelection(variable); renderVariable(); } } }, onFocus: event => { setShowDetailsButton(!event.defaultPrevented); event.preventDefault(); }, onBlur: event => { setShowDetailsButton(false); }, onMouseOver: (event) => { setShowDetailsButton(!event.defaultPrevented); event.preventDefault(); }, onMouseLeave: (event) => { setShowDetailsButton(false); } }, React.createElement("span", { className: "jp-DebuggerVariables-name" }, variable.name), details && (React.createElement("span", { className: "jp-DebuggerVariables-detail" }, details)), hasMimeRenderer && showDetailsButton && (React.createElement(Button, { className: "jp-DebuggerVariables-renderVariable", appearance: "stealth", slot: "end", disabled: disableMimeRenderer, onClick: e => { e.stopPropagation(); renderVariable(); }, title: trans.__('Render variable: %1', variable === null || variable === void 0 ? void 0 : variable.name) }, React.createElement(searchIcon.react, { tag: null }))), variables ? (React.createElement(VariablesBranch, { key: variable.name, commands: commands, data: variables, service: service, filter: filter, translator: translator, handleSelectVariable: onSelect })) : ( /* Trick to ensure collapse button is displayed when variables are not loaded yet */ expandable && React.createElement(TreeItem, null)))); }; //# sourceMappingURL=tree.js.map