From 436a49b1b383877c0f2757bde6e9d0c154707f6d Mon Sep 17 00:00:00 2001 From: pablu Date: Sun, 29 Mar 2026 17:15:20 +0200 Subject: [PATCH] Working codeviewer implemented --- ui/codeviewer/messages.go | 14 ++++++++ ui/codeviewer/model.go | 72 +++++++++++++++++++-------------------- ui/model.go | 15 ++++---- ui/update.go | 33 ++++++++++-------- 4 files changed, 75 insertions(+), 59 deletions(-) create mode 100644 ui/codeviewer/messages.go diff --git a/ui/codeviewer/messages.go b/ui/codeviewer/messages.go new file mode 100644 index 0000000..5dc9061 --- /dev/null +++ b/ui/codeviewer/messages.go @@ -0,0 +1,14 @@ +package codeviewer + +type ChangeTextMsg struct { + Text string +} + +type BreakpointMsg struct { + Line int + Added bool +} + +type ExecutionStoppedMsg struct { + Line int +} diff --git a/ui/codeviewer/model.go b/ui/codeviewer/model.go index 58f3ac3..ce5eb66 100644 --- a/ui/codeviewer/model.go +++ b/ui/codeviewer/model.go @@ -15,16 +15,22 @@ type CodeViewer struct { lines []string Width, Height int - cursor int + Cursor int offset int + + breakpoints map[int]struct{} + + currStoppedLine int } func NewCodeViewer(text string) CodeViewer { cv := CodeViewer{ - Width: 0, - Height: 0, - cursor: 0, - offset: 0, + Width: 0, + Height: 0, + Cursor: 0, + offset: 0, + currStoppedLine: -1, + breakpoints: map[int]struct{}{}, } return cv.colorize(text) @@ -38,8 +44,16 @@ func (c CodeViewer) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { case tea.KeyMsg: return c.handleKeyMsg(msg) - // case tea.WindowSizeMsg: - // return c.UpdateWindowSize(msg) + case ChangeTextMsg: + c = c.colorize(msg.Text) + case BreakpointMsg: + if msg.Added { + c.breakpoints[msg.Line] = struct{}{} + } else { + delete(c.breakpoints, msg.Line) + } + case ExecutionStoppedMsg: + c.currStoppedLine = msg.Line } return c, nil @@ -48,40 +62,17 @@ func (c CodeViewer) Update(msg tea.Msg) (tea.Model, tea.Cmd) { func (c CodeViewer) handleKeyMsg(key tea.KeyMsg) (tea.Model, tea.Cmd) { switch key.String() { case "k": - c.cursor = max(0, c.cursor-1) + c.Cursor = max(0, c.Cursor-1) topThreshold := c.offset + int(float64(c.Height)*0.1) - if c.cursor < topThreshold && c.offset > 0 { + if c.Cursor < topThreshold && c.offset > 0 { c.offset -= 1 } case "j": - c.cursor = min(len(c.lines)-1, c.cursor+1) + c.Cursor = min(len(c.lines)-1, c.Cursor+1) bottomThreshold := c.offset + int(float64(c.Height)*0.9) - if c.cursor > bottomThreshold && c.offset < len(c.lines) { + if c.Cursor > bottomThreshold && c.offset < len(c.lines) { c.offset += 1 } - - // case "b": - // lineNumber := c.cursor + 1 - // - // c.bridge.Breakpoint(m.currentFile, lineNumber) - // if file, ok := m.breakpoints[m.currentFile]; ok { - // m.breakpoints[m.currentFile] = append(file, lineNumber) - // } else { - // m.breakpoints[m.currentFile] = []int{ - // lineNumber, - // } - // } - // case "s": - // m.bridge.Step() - // case "c": - // m.bridge.Continue() - // case "r": - // m.messages = make([]string, 0) - // m.stdoutOutput.SetContent("") - // err := m.bridge.Start() - // if err != nil { - // slog.Error("could not start brige", "error", err) - // } } return c, nil @@ -105,13 +96,22 @@ func (c CodeViewer) View() string { lines := c.lines[max(0, c.offset):min(c.offset+c.Height, len(c.lines))] for i, line := range lines { + lineNumber := i + c.offset + 1 breakpoint := " " cursor := " " executor := " " - if c.offset+i == c.cursor { + + if lineNumber == c.Cursor+1 { cursor = ">" } - fmt.Fprintf(&out, "%-4d%s%s%s %s\n", c.offset+i+1, breakpoint, executor, cursor, line) + if _, ok := c.breakpoints[lineNumber]; ok { + breakpoint = "O" + } + if lineNumber == c.currStoppedLine { + executor = "@" + } + + fmt.Fprintf(&out, "%-4d%s%s%s %s\n", lineNumber, breakpoint, executor, cursor, line) } if len(lines)-c.offset < c.Height { for range c.Height - len(lines) { diff --git a/ui/model.go b/ui/model.go index b6d0d8d..126f3e4 100644 --- a/ui/model.go +++ b/ui/model.go @@ -17,8 +17,7 @@ type Model struct { listenBridge chan string listenBridgeExecutionsStopped chan bridge.ExecutionPoint - currLocals map[string]any - currExecutionPoint bridge.ExecutionPoint + currLocals map[string]any messages []string stdoutOutput viewport.Model @@ -43,11 +42,7 @@ func NewModel(b *bridge.Bridge, file string, text string) Model { listenBridge: c, listenBridgeExecutionsStopped: c2, - currLocals: make(map[string]any), - currExecutionPoint: bridge.ExecutionPoint{ - File: file, - Line: 0, - }, + currLocals: make(map[string]any), breakpoints: make(map[string][]int), messages: make([]string, 0), @@ -67,12 +62,14 @@ func ListenBridge(ch <-chan string) tea.Cmd { func ListenBridgeExecutionsStopped(ch <-chan bridge.ExecutionPoint) tea.Cmd { return func() tea.Msg { msg := <-ch - return ExecutionStoppedMsg(msg) + return codeviewer.ExecutionStoppedMsg{ + Line: msg.Line, + // TODO, WE IGNORE FILE HERE + } } } type LocalsMsg map[string]any -type ExecutionStoppedMsg bridge.ExecutionPoint type StdoutMsg string func (m Model) Init() tea.Cmd { diff --git a/ui/update.go b/ui/update.go index 52cdc67..7359502 100644 --- a/ui/update.go +++ b/ui/update.go @@ -4,7 +4,6 @@ import ( "log/slog" "strings" - "git.pablu.de/pablu/pybug/internal/bridge" "git.pablu.de/pablu/pybug/ui/codeviewer" tea "github.com/charmbracelet/bubbletea" ) @@ -23,8 +22,7 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.stdoutOutput.SetContent(strings.Join(m.messages, "")) m.stdoutOutput.GotoBottom() return m, ListenBridge(m.listenBridge) - case ExecutionStoppedMsg: - m.currExecutionPoint = bridge.ExecutionPoint(msg) + case codeviewer.ExecutionStoppedMsg: return m, tea.Batch(ListenBridgeExecutionsStopped(m.listenBridgeExecutionsStopped), m.GetLocals()) case LocalsMsg: m.currLocals = map[string]any(msg) @@ -71,17 +69,24 @@ func (m Model) HandleKeyMsg(key tea.KeyMsg) (tea.Model, tea.Cmd) { switch key.String() { case "q", "ctrl+c": return m, tea.Quit - // case "b": - // lineNumber := m.cursor + 1 - // - // m.bridge.Breakpoint(m.currentFile, lineNumber) - // if file, ok := m.breakpoints[m.currentFile]; ok { - // m.breakpoints[m.currentFile] = append(file, lineNumber) - // } else { - // m.breakpoints[m.currentFile] = []int{ - // lineNumber, - // } - // } + case "b": + lineNumber := m.codeViewer.Cursor + 1 + + m.bridge.Breakpoint(m.currentFile, lineNumber) + if file, ok := m.breakpoints[m.currentFile]; ok { + m.breakpoints[m.currentFile] = append(file, lineNumber) + } else { + m.breakpoints[m.currentFile] = []int{ + lineNumber, + } + } + return m, func() tea.Msg { + // check if this is in currently viewed file + return codeviewer.BreakpointMsg{ + Added: true, + Line: lineNumber, + } + } case "s": m.bridge.Step() case "c":