// ***************************************************************************** // Copyright (C) 2022 Ericsson and others. // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License v. 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0. // // This Source Code may also be made available under the following Secondary // Licenses when the conditions for such availability set forth in the Eclipse // Public License v. 2.0 are satisfied: GNU General Public License, version 2 // with the GNU Classpath Exception which is available at // https://www.gnu.org/software/classpath/license.html. // // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** import { append, $, addStandardDisposableListener } from '@theia/monaco-editor-core/esm/vs/base/browser/dom'; import { ITableRenderer } from '@theia/monaco-editor-core/esm/vs/base/browser/ui/table/table'; import { dispose } from '@theia/monaco-editor-core/esm/vs/base/common/lifecycle'; import { BreakpointManager } from '../breakpoint/breakpoint-manager'; import { BreakpointColumnTemplateData, DisassembledInstructionEntry, DisassemblyViewRendererReference } from './disassembly-view-utilities'; // This file is adapted from https://github.com/microsoft/vscode/blob/c061ce5c24fc480342fbc5f23244289d633c56eb/src/vs/workbench/contrib/debug/browser/disassemblyView.ts export class BreakpointRenderer implements ITableRenderer { static readonly TEMPLATE_ID = 'breakpoint'; templateId: string = BreakpointRenderer.TEMPLATE_ID; protected readonly _breakpointIcon = 'codicon-debug-breakpoint'; protected readonly _breakpointDisabledIcon = 'codicon-debug-breakpoint-disabled'; protected readonly _breakpointHintIcon = 'codicon-debug-hint'; protected readonly _debugStackframe = 'codicon-debug-stackframe'; protected readonly _debugStackframeFocused = 'codicon-debug-stackframe-focused'; constructor( protected readonly _disassemblyView: DisassemblyViewRendererReference, protected readonly _debugService: BreakpointManager, ) { } renderTemplate(container: HTMLElement): BreakpointColumnTemplateData { // align from the bottom so that it lines up with instruction when source code is present. container.style.alignSelf = 'flex-end'; const icon = append(container, $('.disassembly-view')); icon.classList.add('codicon'); icon.style.display = 'flex'; icon.style.alignItems = 'center'; icon.style.justifyContent = 'center'; icon.style.height = this._disassemblyView.fontInfo.lineHeight + 'px'; const currentElement: { element?: DisassembledInstructionEntry } = { element: undefined }; const disposables = [ this._disassemblyView.onDidChangeStackFrame(() => this.rerenderDebugStackframe(icon, currentElement.element)), addStandardDisposableListener(container, 'mouseover', () => { if (currentElement.element?.allowBreakpoint) { icon.classList.add(this._breakpointHintIcon); } }), addStandardDisposableListener(container, 'mouseout', () => { if (currentElement.element?.allowBreakpoint) { icon.classList.remove(this._breakpointHintIcon); } }), addStandardDisposableListener(container, 'click', () => { if (currentElement.element?.allowBreakpoint) { // click show hint while waiting for BP to resolve. icon.classList.add(this._breakpointHintIcon); if (currentElement.element.isBreakpointSet) { this._debugService.removeInstructionBreakpointAt(currentElement.element.instruction.address); } else if (currentElement.element.allowBreakpoint && !currentElement.element.isBreakpointSet) { this._debugService.addInstructionBreakpoint(currentElement.element.instruction.address, 0); } } }) ]; return { currentElement, icon, disposables }; } renderElement(element: DisassembledInstructionEntry, index: number, templateData: BreakpointColumnTemplateData): void { templateData.currentElement.element = element; this.rerenderDebugStackframe(templateData.icon, element); } disposeTemplate(templateData: BreakpointColumnTemplateData): void { dispose(templateData.disposables); templateData.disposables = []; } protected rerenderDebugStackframe(icon: HTMLElement, element?: DisassembledInstructionEntry): void { if (element?.instruction.address === this._disassemblyView.focusedCurrentInstructionAddress) { icon.classList.add(this._debugStackframe); } else if (element?.instruction.address === this._disassemblyView.focusedInstructionAddress) { icon.classList.add(this._debugStackframeFocused); } else { icon.classList.remove(this._debugStackframe); icon.classList.remove(this._debugStackframeFocused); } icon.classList.remove(this._breakpointHintIcon); if (element?.isBreakpointSet) { if (element.isBreakpointEnabled) { icon.classList.add(this._breakpointIcon); icon.classList.remove(this._breakpointDisabledIcon); } else { icon.classList.remove(this._breakpointIcon); icon.classList.add(this._breakpointDisabledIcon); } } else { icon.classList.remove(this._breakpointIcon); icon.classList.remove(this._breakpointDisabledIcon); } } }