Working codeviewer implemented

This commit is contained in:
2026-03-29 17:15:20 +02:00
parent 387804dbd0
commit 436a49b1b3
4 changed files with 75 additions and 59 deletions

14
ui/codeviewer/messages.go Normal file
View File

@@ -0,0 +1,14 @@
package codeviewer
type ChangeTextMsg struct {
Text string
}
type BreakpointMsg struct {
Line int
Added bool
}
type ExecutionStoppedMsg struct {
Line int
}

View File

@@ -15,16 +15,22 @@ type CodeViewer struct {
lines []string lines []string
Width, Height int Width, Height int
cursor int Cursor int
offset int offset int
breakpoints map[int]struct{}
currStoppedLine int
} }
func NewCodeViewer(text string) CodeViewer { func NewCodeViewer(text string) CodeViewer {
cv := CodeViewer{ cv := CodeViewer{
Width: 0, Width: 0,
Height: 0, Height: 0,
cursor: 0, Cursor: 0,
offset: 0, offset: 0,
currStoppedLine: -1,
breakpoints: map[int]struct{}{},
} }
return cv.colorize(text) return cv.colorize(text)
@@ -38,8 +44,16 @@ func (c CodeViewer) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) { switch msg := msg.(type) {
case tea.KeyMsg: case tea.KeyMsg:
return c.handleKeyMsg(msg) return c.handleKeyMsg(msg)
// case tea.WindowSizeMsg: case ChangeTextMsg:
// return c.UpdateWindowSize(msg) 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 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) { func (c CodeViewer) handleKeyMsg(key tea.KeyMsg) (tea.Model, tea.Cmd) {
switch key.String() { switch key.String() {
case "k": 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) 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 c.offset -= 1
} }
case "j": 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) 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 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 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))] lines := c.lines[max(0, c.offset):min(c.offset+c.Height, len(c.lines))]
for i, line := range lines { for i, line := range lines {
lineNumber := i + c.offset + 1
breakpoint := " " breakpoint := " "
cursor := " " cursor := " "
executor := " " executor := " "
if c.offset+i == c.cursor {
if lineNumber == c.Cursor+1 {
cursor = ">" 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 { if len(lines)-c.offset < c.Height {
for range c.Height - len(lines) { for range c.Height - len(lines) {

View File

@@ -17,8 +17,7 @@ type Model struct {
listenBridge chan string listenBridge chan string
listenBridgeExecutionsStopped chan bridge.ExecutionPoint listenBridgeExecutionsStopped chan bridge.ExecutionPoint
currLocals map[string]any currLocals map[string]any
currExecutionPoint bridge.ExecutionPoint
messages []string messages []string
stdoutOutput viewport.Model stdoutOutput viewport.Model
@@ -43,11 +42,7 @@ func NewModel(b *bridge.Bridge, file string, text string) Model {
listenBridge: c, listenBridge: c,
listenBridgeExecutionsStopped: c2, listenBridgeExecutionsStopped: c2,
currLocals: make(map[string]any), currLocals: make(map[string]any),
currExecutionPoint: bridge.ExecutionPoint{
File: file,
Line: 0,
},
breakpoints: make(map[string][]int), breakpoints: make(map[string][]int),
messages: make([]string, 0), messages: make([]string, 0),
@@ -67,12 +62,14 @@ func ListenBridge(ch <-chan string) tea.Cmd {
func ListenBridgeExecutionsStopped(ch <-chan bridge.ExecutionPoint) tea.Cmd { func ListenBridgeExecutionsStopped(ch <-chan bridge.ExecutionPoint) tea.Cmd {
return func() tea.Msg { return func() tea.Msg {
msg := <-ch msg := <-ch
return ExecutionStoppedMsg(msg) return codeviewer.ExecutionStoppedMsg{
Line: msg.Line,
// TODO, WE IGNORE FILE HERE
}
} }
} }
type LocalsMsg map[string]any type LocalsMsg map[string]any
type ExecutionStoppedMsg bridge.ExecutionPoint
type StdoutMsg string type StdoutMsg string
func (m Model) Init() tea.Cmd { func (m Model) Init() tea.Cmd {

View File

@@ -4,7 +4,6 @@ import (
"log/slog" "log/slog"
"strings" "strings"
"git.pablu.de/pablu/pybug/internal/bridge"
"git.pablu.de/pablu/pybug/ui/codeviewer" "git.pablu.de/pablu/pybug/ui/codeviewer"
tea "github.com/charmbracelet/bubbletea" 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.SetContent(strings.Join(m.messages, ""))
m.stdoutOutput.GotoBottom() m.stdoutOutput.GotoBottom()
return m, ListenBridge(m.listenBridge) return m, ListenBridge(m.listenBridge)
case ExecutionStoppedMsg: case codeviewer.ExecutionStoppedMsg:
m.currExecutionPoint = bridge.ExecutionPoint(msg)
return m, tea.Batch(ListenBridgeExecutionsStopped(m.listenBridgeExecutionsStopped), m.GetLocals()) return m, tea.Batch(ListenBridgeExecutionsStopped(m.listenBridgeExecutionsStopped), m.GetLocals())
case LocalsMsg: case LocalsMsg:
m.currLocals = map[string]any(msg) m.currLocals = map[string]any(msg)
@@ -71,17 +69,24 @@ func (m Model) HandleKeyMsg(key tea.KeyMsg) (tea.Model, tea.Cmd) {
switch key.String() { switch key.String() {
case "q", "ctrl+c": case "q", "ctrl+c":
return m, tea.Quit return m, tea.Quit
// case "b": case "b":
// lineNumber := m.cursor + 1 lineNumber := m.codeViewer.Cursor + 1
//
// m.bridge.Breakpoint(m.currentFile, lineNumber) m.bridge.Breakpoint(m.currentFile, lineNumber)
// if file, ok := m.breakpoints[m.currentFile]; ok { if file, ok := m.breakpoints[m.currentFile]; ok {
// m.breakpoints[m.currentFile] = append(file, lineNumber) m.breakpoints[m.currentFile] = append(file, lineNumber)
// } else { } else {
// m.breakpoints[m.currentFile] = []int{ m.breakpoints[m.currentFile] = []int{
// lineNumber, lineNumber,
// } }
// } }
return m, func() tea.Msg {
// check if this is in currently viewed file
return codeviewer.BreakpointMsg{
Added: true,
Line: lineNumber,
}
}
case "s": case "s":
m.bridge.Step() m.bridge.Step()
case "c": case "c":