package code
import (
"fmt"
"log/slog"
"runtime"
"github.com/egoist/mygo/ui"
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/core"
"git.zacharyzhang.com/ZacharyZhang-NY/MujicaUI/navigation"
)
// DebugState is where a debug session is.
type DebugState int
const (
DebugStopped DebugState = iota
DebugRunning
DebugPaused
)
// DebugAction is what the user asked the debugger to do.
type DebugAction int
const (
DebugNone DebugAction = iota
// DebugContinue resumes a paused session or starts a stopped one.
DebugContinue
DebugPause
DebugStepOver
DebugStepInto
DebugStepOut
DebugRestart
DebugStop
)
// DebugToolbarOptions configure a DebugToolbar.
type DebugToolbarOptions struct {
Disabled bool
}
// DebugToolbarResult carries the requested action.
type DebugToolbarResult struct {
Element ui.Element
Action DebugAction
}
// debugIcon draws a toolbar glyph in a 24-unit box.
func debugIcon(body string) *ui.SVG {
return ui.MustParseSVG([]byte(``))
}
var debugIcons = map[DebugAction]*ui.SVG{
DebugContinue: debugIcon(``),
DebugPause: debugIcon(``),
DebugStepOver: debugIcon(``),
DebugStepInto: debugIcon(``),
DebugStepOut: debugIcon(``),
DebugRestart: debugIcon(``),
DebugStop: debugIcon(``),
}
// debugShortcut is an action's key, as in common IDEs.
type debugShortcut struct {
mods ui.Modifiers
key ui.Key
name string
}
func debugShortcuts() map[DebugAction]debugShortcut {
cmd := "Ctrl"
if runtime.GOOS == "darwin" {
cmd = "⌘"
}
return map[DebugAction]debugShortcut{
DebugContinue: {0, ui.KeyF5, "F5"},
DebugPause: {0, ui.KeyF6, "F6"},
DebugStepOver: {0, ui.KeyF10, "F10"},
DebugStepInto: {0, ui.KeyF11, "F11"},
DebugStepOut: {ui.Shift, ui.KeyF11, "Shift+F11"},
DebugRestart: {ui.Cmd | ui.Shift, ui.KeyF5, cmd + "+Shift+F5"},
DebugStop: {ui.Shift, ui.KeyF5, "Shift+F5"},
}
}
// DebugEnabled reports whether action applies in state.
func DebugEnabled(state DebugState, a DebugAction) bool {
switch a {
case DebugContinue:
return state != DebugRunning
case DebugPause:
return state == DebugRunning
case DebugStepOver, DebugStepInto, DebugStepOut:
return state == DebugPaused
case DebugRestart, DebugStop:
return state != DebugStopped
}
return false
}
// DebugToolbar shows debugger actions enabled by state.
func DebugToolbar(c *ui.Context, state DebugState, opts DebugToolbarOptions) DebugToolbarResult {
if state < DebugStopped || state > DebugPaused {
panic("mujica: DebugState out of range")
}
keys := debugShortcuts()
var res DebugToolbarResult
res.Element = navigation.Toolbar(c, navigation.ToolbarOptions{Label: core.Msg(c, "debug_toolbar.label")}, func() {
first := DebugContinue
if state == DebugRunning {
first = DebugPause
}
for _, a := range []DebugAction{first, DebugStepOver, DebugStepInto, DebugStepOut, DebugRestart, DebugStop} {
name := core.Msg(c, debugKeys[a])
if a == DebugContinue && state == DebugStopped {
name = core.Msg(c, "debug_toolbar.start")
}
on := DebugEnabled(state, a) && !opts.Disabled
b := navigation.ToolbarButton(c, name, navigation.ToolbarButtonOptions{Icon: debugIcons[a], Disabled: !on})
b.Tooltip(fmt.Sprintf("%s (%s)", name, keys[a].name))
if b.Clicked() && on && !b.IsDisabled() {
res.Action = a
}
}
}).Disabled(opts.Disabled)
if res.Action == DebugNone && !res.Element.IsDisabled() {
for _, a := range []DebugAction{DebugContinue, DebugPause, DebugStepOver, DebugStepInto, DebugStepOut, DebugRestart, DebugStop} {
if k := keys[a]; DebugEnabled(state, a) && c.Shortcut(k.mods, k.key) {
res.Action = a
break
}
}
}
if res.Action != DebugNone {
slog.Debug("mujica: debug_toolbar action", "action", res.Action, "state", state)
}
return res
}
var debugKeys = map[DebugAction]string{
DebugContinue: "debug_toolbar.continue", DebugPause: "debug_toolbar.pause", DebugStepOver: "debug_toolbar.stepOver",
DebugStepInto: "debug_toolbar.stepInto", DebugStepOut: "debug_toolbar.stepOut", DebugRestart: "debug_toolbar.restart", DebugStop: "debug_toolbar.stop",
}