UNPKG

@yuebai008/cli

Version:

Command line interface for rapid qg-minigame development

90 lines 19.5 kB
import*as Common from"../../../core/common/common.js";import*as Host from"../../../core/host/host.js";import*as i18n from"../../../core/i18n/i18n.js";import*as Platform from"../../../core/platform/platform.js";import{assertNotNullOrUndefined}from"../../../core/platform/platform.js";import*as SDK from"../../../core/sdk/sdk.js";import*as Bindings from"../../../models/bindings/bindings.js";import*as Breakpoints from"../../../models/breakpoints/breakpoints.js";import*as TextUtils from"../../../models/text_utils/text_utils.js";import*as Workspace from"../../../models/workspace/workspace.js";import*as ComponentHelpers from"../../../ui/components/helpers/helpers.js";import*as IconButton from"../../../ui/components/icon_button/icon_button.js";import*as Input from"../../../ui/components/input/input.js";import*as LegacyWrapper from"../../../ui/components/legacy_wrapper/legacy_wrapper.js";import*as Coordinator from"../../../ui/components/render_coordinator/render_coordinator.js";import*as UI from"../../../ui/legacy/legacy.js";import*as LitHtml from"../../../ui/lit-html/lit-html.js";import breakpointsViewStyles from"./breakpointsView.css.js";import{findNextNodeForKeyboardNavigation,getDifferentiatingPathMap}from"./BreakpointsViewUtils.js";const UIStrings={pauseOnUncaughtExceptions:"Pause on uncaught exceptions",pauseOnCaughtExceptions:"Pause on caught exceptions",checked:"checked",unchecked:"unchecked",indeterminate:"mixed",breakpointHit:"{PH1} breakpoint hit",removeAllBreakpointsInFile:"Remove all breakpoints in file",disableAllBreakpointsInFile:"Disable all breakpoints in file",enableAllBreakpointsInFile:"Enable all breakpoints in file",editCondition:"Edit condition",editLogpoint:"Edit logpoint",removeBreakpoint:"Remove breakpoint",removeAllBreakpoints:"Remove all breakpoints",removeOtherBreakpoints:"Remove other breakpoints",revealLocation:"Reveal location",conditionCode:"Condition: {PH1}",logpointCode:"Logpoint: {PH1}"},str_=i18n.i18n.registerUIStrings("panels/sources/components/BreakpointsView.ts",UIStrings),i18nString=i18n.i18n.getLocalizedString.bind(void 0,str_),coordinator=Coordinator.RenderCoordinator.RenderCoordinator.instance(),MAX_SNIPPET_LENGTH=200;let breakpointsViewInstance,breakpointsViewControllerInstance;export class BreakpointsSidebarController{#e;#t=new WeakMap;#n;#i;#o;#a;#s;#r;#c=!1;#p=!1;constructor(e,t){this.#a=Common.Settings.Settings.instance().createSetting("collapsedFiles",[]),this.#s=new Set(this.#a.get()),this.#e=e,this.#e.addEventListener(Breakpoints.BreakpointManager.Events.BreakpointAdded,this.#l,this),this.#e.addEventListener(Breakpoints.BreakpointManager.Events.BreakpointRemoved,this.#d,this),this.#n=t.moduleSetting("breakpointsActive"),this.#n.addChangeListener(this.update,this),this.#i=t.moduleSetting("pauseOnUncaughtException"),this.#i.addChangeListener(this.update,this),this.#o=t.moduleSetting("pauseOnCaughtException"),this.#o.addChangeListener(this.update,this)}static instance({forceNew:e,breakpointManager:t,settings:n}={forceNew:null,breakpointManager:Breakpoints.BreakpointManager.BreakpointManager.instance(),settings:Common.Settings.Settings.instance()}){return breakpointsViewControllerInstance&&!e||(breakpointsViewControllerInstance=new BreakpointsSidebarController(t,n)),breakpointsViewControllerInstance}static removeInstance(){breakpointsViewControllerInstance=null}static targetSupportsIndependentPauseOnExceptionToggles(){return!SDK.TargetManager.TargetManager.instance().targets().some((e=>e.type()===SDK.Target.Type.Node))}flavorChanged(e){this.update()}breakpointEditFinished(e,t){this.#r&&this.#r===e&&(t&&Host.userMetrics.actionTaken(Host.UserMetrics.Action.BreakpointConditionEditedFromSidebar),this.#r=void 0)}breakpointStateChanged(e,t){this.#u(e).forEach((e=>{e.breakpoint.setEnabled(t)}))}async breakpointEdited(e,t){const n=this.#u(e);let i;for(const e of n)(!i||e.uiLocation.compareTo(i.uiLocation)<0)&&(i=e);i&&(t&&(this.#r=i.breakpoint),await Common.Revealer.reveal(i))}breakpointsRemoved(e){e.flatMap((e=>this.#u(e))).forEach((e=>e?.breakpoint.remove(!1)))}expandedStateChanged(e,t){t?this.#s.delete(e):this.#s.add(e),this.#h()}async jumpToSource(e){const t=this.#u(e).map((e=>e.uiLocation));let n;for(const e of t)(!n||e.compareTo(n)<0)&&(n=e);n&&await Common.Revealer.reveal(n)}setPauseOnUncaughtExceptions(e){this.#i.set(e)}setPauseOnCaughtExceptions(e){this.#o.set(e)}async update(){if(this.#c=!0,!this.#p){for(this.#p=!0;this.#c;){this.#c=!1;const e=await this.getUpdatedBreakpointViewData();BreakpointsView.instance().data=e}this.#p=!1}}async getUpdatedBreakpointViewData(){const e=this.#n.get(),t=BreakpointsSidebarController.targetSupportsIndependentPauseOnExceptionToggles(),n=this.#i.get(),i=this.#o.get(),o=this.#g();if(!o.length)return{breakpointsActive:e,pauseOnCaughtExceptions:i,pauseOnUncaughtExceptions:n,independentPauseToggles:t,groups:[]};const a=this.#k(o),s=this.#m(o),[r,c]=await Promise.all([this.#b(a),this.#S()]),p=new Map;for(let e=0;e<a.length;e++){const t=a[e],n=t[0],i=n.uiLocation.uiSourceCode.url(),o=n.uiLocation.uiSourceCode.canononicalScriptId(),l=n.uiLocation,d=null!==c&&t.some((e=>e.uiLocation.id()===c.id())),u=s.get(l.lineId()).size>1,h=l.lineAndColumnText(u),g=r[e],k=g instanceof TextUtils.Text.Text?g.lineAt(l.lineNumber):g.lines[g.bytecodeOffsetToLineNumber(l.columnNumber??0)]??"";d&&this.#s.has(i)&&(this.#s.delete(i),this.#h());const m=!this.#s.has(i),b=this.#I(t),{type:S,hoverText:I}=this.#f(t),f={id:n.breakpoint.breakpointStorageId(),location:h,codeSnippet:k,isHit:d,status:b,type:S,hoverText:I};this.#t.set(f,t);let B=p.get(o);if(B)B.breakpointItems.push(f),B.expanded||=m;else{const e=this.#e.supportsConditionalBreakpoints(l.uiSourceCode);B={url:i,name:l.uiSourceCode.displayName(),editable:e,expanded:m,breakpointItems:[f]},p.set(o,B)}}return{breakpointsActive:e,pauseOnCaughtExceptions:i,pauseOnUncaughtExceptions:n,independentPauseToggles:t,groups:Array.from(p.values())}}#l(e){const t=e.data.breakpoint;return"USER_ACTION"===t.origin&&this.#s.has(t.url())&&(this.#s.delete(t.url()),this.#h()),this.update()}#d(e){const t=e.data.breakpoint;if(this.#s.has(t.url())){Breakpoints.BreakpointManager.BreakpointManager.instance().allBreakpointLocations().some((e=>e.breakpoint.url()===t.url()))||(this.#s.delete(t.url()),this.#h())}return this.update()}#h(){this.#a.set(Array.from(this.#s.values()))}#f(e){const t=e.find((e=>Boolean(e.breakpoint.condition()))),n=t?.breakpoint;if(!n||!n.condition())return{type:"REGULAR_BREAKPOINT"};const i=n.condition();return n.isLogpoint()?{type:"LOGPOINT",hoverText:i}:{type:"CONDITIONAL_BREAKPOINT",hoverText:i}}#u(e){const t=this.#t.get(e);return assertNotNullOrUndefined(t),t}async#S(){const e=UI.Context.Context.instance().flavor(SDK.DebuggerModel.DebuggerPausedDetails);return e&&e.callFrames.length?await Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding.instance().rawLocationToUILocation(e.callFrames[0].location()):null}#g(){const e=this.#e.allBreakpointLocations().filter((e=>e.uiLocation.uiSourceCode.project().type()!==Workspace.Workspace.projectTypes.Debugger));e.sort(((e,t)=>e.uiLocation.compareTo(t.uiLocation)));const t=[];let n=null,i=null;for(const o of e)(o.breakpoint!==n||i&&o.uiLocation.compareTo(i))&&(t.push(o),n=o.breakpoint,i=o.uiLocation);return t}#k(e){const t=new Platform.MapUtilities.Multimap;for(const n of e){const e=n.uiLocation;t.set(e.id(),n)}const n=[];for(const e of t.keysArray()){const i=Array.from(t.get(e));i.length&&n.push(i)}return n}#m(e){const t=new Platform.MapUtilities.Multimap;for(const n of e){const e=n.uiLocation;t.set(e.lineId(),e.id())}return t}#I(e){const t=e.some((e=>e.breakpoint.enabled())),n=e.some((e=>!e.breakpoint.enabled()));let i;return i=t?n?"INDETERMINATE":"ENABLED":"DISABLED",i}#b(e){const t=new Map;return Promise.all(e.map((async([{uiLocation:{uiSourceCode:e}}])=>{const n=await e.requestContent({cachedWasmOnly:!0});if("wasmDisassemblyInfo"in n&&n.wasmDisassemblyInfo)return n.wasmDisassemblyInfo;const i=n.content||"";if(t.has(i))return t.get(i);const o=new TextUtils.Text.Text(i);return t.set(i,o),o})))}}export class BreakpointsView extends LegacyWrapper.LegacyWrapper.WrappableComponent{#B;static instance({forceNew:e}={forceNew:!1}){return breakpointsViewInstance&&!e||(breakpointsViewInstance=LegacyWrapper.LegacyWrapper.legacyWrapper(UI.Widget.Widget,new BreakpointsView)),breakpointsViewInstance.getComponent()}constructor(){super(),this.#B=BreakpointsSidebarController.instance(),this.#B.update()}static litTagName=LitHtml.literal`devtools-breakpoint-view`;#x=this.attachShadow({mode:"open"});#E=!1;#v=!1;#y=!1;#C=!0;#w=[];#L=new Map;set data(e){this.#E=e.pauseOnUncaughtExceptions,this.#v=e.pauseOnCaughtExceptions,this.#y=e.independentPauseToggles,this.#C=e.breakpointsActive,this.#w=e.groups;const t=[];for(const n of e.groups)t.push({name:n.name,url:n.url});this.#L=getDifferentiatingPathMap(t),this.render()}connectedCallback(){this.#x.adoptedStyleSheets=[Input.checkboxStyles,breakpointsViewStyles]}async render(){await coordinator.write("BreakpointsView render",(()=>{const e=async e=>{const t=e.currentTarget;await this.#T(t),e.consume()},t=(this.#y||this.#E)&&this.#v,n=!this.#y&&!this.#E,i=LitHtml.html` <div class='pause-on-uncaught-exceptions' tabindex='0' @click=${e} @keydown=${this.#U} data-first-pause> <label class='checkbox-label'> <input type='checkbox' tabindex=-1 ?checked=${this.#E} @change=${this.#O.bind(this)}> <span>${i18nString(UIStrings.pauseOnUncaughtExceptions)}</span> </label> </div> <div class='pause-on-caught-exceptions' tabindex='-1' @click=${e} @keydown=${this.#U} data-last-pause> <label class='checkbox-label'> <input data-pause-on-caught-checkbox type='checkbox' tabindex=-1 ?checked=${t} ?disabled=${n} @change=${this.#A.bind(this)}> <span>${i18nString(UIStrings.pauseOnCaughtExceptions)}</span> </label> </div> <div role=tree> ${LitHtml.Directives.repeat(this.#w,(e=>e.url),((e,t)=>LitHtml.html`${this.#M(e,t)}`))} </div>`;LitHtml.render(i,this.#x,{host:this})})),await coordinator.write("BreakpointsView make pause-on-exceptions focusable",(()=>{if(null===this.#x.querySelector('[tabindex="0"]')){const e=this.#x.querySelector("[data-first-pause]");e?.setAttribute("tabindex","0")}}))}async#U(e){if(e.target&&e.target instanceof HTMLElement){if("Home"===e.key||"End"===e.key)return e.consume(!0),this.#N(e.key);if(Platform.KeyboardUtilities.keyIsArrowKey(e.key))return e.consume(!0),this.#H(e.key,e.target);if(Platform.KeyboardUtilities.isEnterOrSpaceKey(e)){const t=e.currentTarget;await this.#T(t);const n=t.getElementsByTagName("input");1===n.length&&(n[0].checked=!n[0].checked),e.consume()}}}async#T(e){e&&coordinator.write("BreakpointsView focus on selected element",(()=>{const t=this.#x.querySelector('[tabindex="0"]');t?.setAttribute("tabindex","-1"),e.setAttribute("tabindex","0"),e.focus()}))}async#H(e,t){const n=await findNextNodeForKeyboardNavigation(t,e,((e,t)=>t?coordinator.write("BreakpointsView expand",(()=>{e.setAttribute("open","")})):coordinator.write("BreakpointsView expand",(()=>{e.removeAttribute("open")}))));return this.#T(n)}async#N(e){if("Home"===e){const e=this.#x.querySelector("[data-first-pause]");return this.#T(e)}if("End"===e){const e=this.#w.length;if(0===e){const e=this.#x.querySelector("[data-last-pause]");return this.#T(e)}const t=e-1;if(this.#w[t].expanded){const e=this.#x.querySelector("[data-last-group] > [data-last-breakpoint]");return this.#T(e)}const n=this.#x.querySelector("[data-last-group] > summary");return this.#T(n)}}#$(e){const t="LOGPOINT"===e.type?i18nString(UIStrings.editLogpoint):i18nString(UIStrings.editCondition);return LitHtml.html` <button data-edit-breakpoint @click=${t=>{Host.userMetrics.breakpointEditDialogRevealedFrom(1),this.#B.breakpointEdited(e,!0),t.consume()}} title=${t}> <${IconButton.Icon.Icon.litTagName} .data=${{iconName:"edit",width:"16px",height:"16px",color:"var(--icon-default)"}} > </${IconButton.Icon.Icon.litTagName}> </button> `}#D(e,t,n){return LitHtml.html` <button data-remove-breakpoint @click=${t=>{Host.userMetrics.actionTaken(n),this.#B.breakpointsRemoved(e),t.consume()}} title=${t} aria-label=${t}> <${IconButton.Icon.Icon.litTagName} .data=${{iconName:"cross",width:"20px",height:"20px",color:"var(--icon-default)"}} }> </${IconButton.Icon.Icon.litTagName}> </button> `}#P(e,t){const{breakpointItems:n}=t,i=new UI.ContextMenu.ContextMenu(e);i.defaultSection().appendItem(i18nString(UIStrings.removeAllBreakpointsInFile),(()=>{Host.userMetrics.actionTaken(Host.UserMetrics.Action.BreakpointsInFileRemovedFromContextMenu),this.#B.breakpointsRemoved(n)}));const o=this.#w.filter((e=>e!==t));i.defaultSection().appendItem(i18nString(UIStrings.removeOtherBreakpoints),(()=>{const e=o.map((({breakpointItems:e})=>e)).flat();this.#B.breakpointsRemoved(e)}),0===o.length),i.defaultSection().appendItem(i18nString(UIStrings.removeAllBreakpoints),(()=>{const e=this.#w.map((({breakpointItems:e})=>e)).flat();this.#B.breakpointsRemoved(e)}));const a=n.filter((e=>"ENABLED"!==e.status));i.debugSection().appendItem(i18nString(UIStrings.enableAllBreakpointsInFile),(()=>{Host.userMetrics.actionTaken(Host.UserMetrics.Action.BreakpointsInFileEnabledDisabledFromContextMenu);for(const e of a)this.#B.breakpointStateChanged(e,!0)}),0===a.length);const s=n.filter((e=>"DISABLED"!==e.status));i.debugSection().appendItem(i18nString(UIStrings.disableAllBreakpointsInFile),(()=>{Host.userMetrics.actionTaken(Host.UserMetrics.Action.BreakpointsInFileEnabledDisabledFromContextMenu);for(const e of s)this.#B.breakpointStateChanged(e,!1)}),0===s.length),i.show()}#M(e,t){const n={active:this.#C};return LitHtml.html` <details class=${LitHtml.Directives.classMap(n)} ?data-first-group=${0===t} ?data-last-group=${t===this.#w.length-1} role=group aria-label='${e.name}' aria-description='${e.url}' ?open=${LitHtml.Directives.live(e.expanded)} @toggle=${t=>{const n=t.target;e.expanded=n.open,this.#B.expandedStateChanged(e.url,e.expanded)}}> <summary @contextmenu=${t=>{this.#P(t,e),t.consume()}} tabindex='-1' @keydown=${this.#U} @click=${async e=>{const t=e.currentTarget;await this.#T(t),Host.userMetrics.actionTaken(Host.UserMetrics.Action.BreakpointGroupExpandedStateChanged),e.consume()}}> <span class='group-header' aria-hidden=true><span class='group-icon-or-disable'>${this.#F()}${this.#R(e)}</span><span class='group-header-title' title='${e.url}'>${e.name}<span class='group-header-differentiator'>${this.#L.get(e.url)}</span></span></span> <span class='group-hover-actions'> ${this.#D(e.breakpointItems,i18nString(UIStrings.removeAllBreakpointsInFile),Host.UserMetrics.Action.BreakpointsInFileRemovedFromRemoveButton)} </span> </summary> ${LitHtml.Directives.repeat(e.breakpointItems,(e=>e.id),((n,i)=>this.#G(n,e.editable,t,i)))} </details> `}#R(e){const t=e.breakpointItems.some((e=>"ENABLED"===e.status));return LitHtml.html` <input class='group-checkbox' type='checkbox' aria-label='' .checked=${t} @change=${t=>{Host.userMetrics.actionTaken(Host.UserMetrics.Action.BreakpointsInFileCheckboxToggled);const n=t.target,i=n.checked?"ENABLED":"DISABLED";e.breakpointItems.filter((e=>e.status!==i)).forEach((e=>{this.#B.breakpointStateChanged(e,n.checked)})),t.consume()}} tabindex=-1> `}#F(){return LitHtml.html` <${IconButton.Icon.Icon.litTagName} class='file-icon' .data=${{iconName:"file-script",color:"var(--icon-file-script)",width:"18px",height:"18px"}}></${IconButton.Icon.Icon.litTagName}> `}#V(e,t,n){const i=new UI.ContextMenu.ContextMenu(e),o="LOGPOINT"===t.type?i18nString(UIStrings.editLogpoint):i18nString(UIStrings.editCondition);i.revealSection().appendItem(o,(()=>{Host.userMetrics.breakpointEditDialogRevealedFrom(0),this.#B.breakpointEdited(t,!1)}),!n),i.defaultSection().appendItem(i18nString(UIStrings.removeBreakpoint),(()=>{Host.userMetrics.actionTaken(Host.UserMetrics.Action.BreakpointRemovedFromContextMenu),this.#B.breakpointsRemoved([t])}));const a=this.#w.map((({breakpointItems:e})=>e)).flat().filter((e=>e!==t));i.defaultSection().appendItem(i18nString(UIStrings.removeOtherBreakpoints),(()=>{this.#B.breakpointsRemoved(a)}),0===a.length),i.defaultSection().appendItem(i18nString(UIStrings.removeAllBreakpoints),(()=>{const e=this.#w.map((({breakpointItems:e})=>e)).flat();this.#B.breakpointsRemoved(e)})),i.editSection().appendItem(i18nString(UIStrings.revealLocation),(()=>{this.#B.jumpToSource(t)})),i.show()}#G(e,t,n,i){const o={"breakpoint-item":!0,hit:e.isHit,"conditional-breakpoint":"CONDITIONAL_BREAKPOINT"===e.type,logpoint:"LOGPOINT"===e.type},a=this.#j(e),s=Platform.StringUtilities.trimEndWithMaxLength(e.codeSnippet,200),r=this.#K(e.type,e.hoverText),c=this.#w[n].breakpointItems;return LitHtml.html` <div class=${LitHtml.Directives.classMap(o)} ?data-first-breakpoint=${0===i} ?data-last-breakpoint=${i===c.length-1} aria-label=${a} role=treeitem tabindex='-1' @contextmenu=${n=>{this.#V(n,e,t),n.consume()}} @click=${async e=>{const t=e.currentTarget;await this.#T(t),e.consume()}} @keydown=${this.#U}> <label class='checkbox-label'> <span class='type-indicator'></span> <input type='checkbox' aria-label=${e.location} ?indeterminate=${"INDETERMINATE"===e.status} .checked=${"ENABLED"===e.status} @change=${t=>this.#_(t,e)} tabindex=-1> </label> <span class='code-snippet' @click=${t=>{this.#B.jumpToSource(e),t.consume()}} title=${r}>${s}</span> <span class='breakpoint-item-location-or-actions'> ${t?this.#$(e):LitHtml.nothing} ${this.#D([e],i18nString(UIStrings.removeBreakpoint),Host.UserMetrics.Action.BreakpointRemovedFromRemoveButton)} <span class='location'>${e.location}</span> </span> </div> `}#K(e,t){switch(e){case"REGULAR_BREAKPOINT":return;case"CONDITIONAL_BREAKPOINT":return assertNotNullOrUndefined(t),i18nString(UIStrings.conditionCode,{PH1:t});case"LOGPOINT":return assertNotNullOrUndefined(t),i18nString(UIStrings.logpointCode,{PH1:t})}}#j(e){let t;switch(e.status){case"ENABLED":t=i18nString(UIStrings.checked);break;case"DISABLED":t=i18nString(UIStrings.unchecked);break;case"INDETERMINATE":t=i18nString(UIStrings.indeterminate)}return e.isHit?i18nString(UIStrings.breakpointHit,{PH1:t}):t}#_(e,t){const n=e.target;this.#B.breakpointStateChanged(t,n.checked)}#A(e){const{checked:t}=e.target;this.#B.setPauseOnCaughtExceptions(t)}#O(e){const{checked:t}=e.target;if(!this.#y){const e=this.#x.querySelector("[data-pause-on-caught-checkbox]");assertNotNullOrUndefined(e),!t&&e.checked&&e.click(),coordinator.write("BreakpointsView update pause-on-uncaught-exception",(()=>{e.disabled=!t}))}this.#B.setPauseOnUncaughtExceptions(t)}}ComponentHelpers.CustomElements.defineComponent("devtools-breakpoint-view",BreakpointsView);