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(`` + body + ``)) } 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", }