Compare commits
4 Commits
d254d5cfd0
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 060ab01648 | |||
| bb829286d1 | |||
| 436a49b1b3 | |||
| 387804dbd0 |
@@ -8,6 +8,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"slices"
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -159,13 +160,19 @@ func (b *Bridge) Step() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Bridge) Breakpoint(file string, line int) error {
|
func (b *Bridge) Breakpoint(file string, line int) (set bool, err error) {
|
||||||
// Check if breakpoint already exists here
|
|
||||||
if !b.running {
|
if !b.running {
|
||||||
return ErrNotRunning
|
return false, ErrNotRunning
|
||||||
}
|
}
|
||||||
|
|
||||||
requestId, cmd := makeCommand(BreakCommand, map[string]any{
|
var command CommandType
|
||||||
|
if _, ok := b.breakpoints[file]; ok && slices.Contains(b.breakpoints[file], line) {
|
||||||
|
command = UnbreakCommand
|
||||||
|
} else {
|
||||||
|
command = BreakCommand
|
||||||
|
}
|
||||||
|
|
||||||
|
requestId, cmd := makeCommand(command, map[string]any{
|
||||||
"file": file,
|
"file": file,
|
||||||
"line": line,
|
"line": line,
|
||||||
})
|
})
|
||||||
@@ -175,18 +182,26 @@ func (b *Bridge) Breakpoint(file string, line int) error {
|
|||||||
obj := <-c
|
obj := <-c
|
||||||
|
|
||||||
var m map[string]any
|
var m map[string]any
|
||||||
err := json.Unmarshal([]byte(obj), &m)
|
err = json.Unmarshal([]byte(obj), &m)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if m["status"] != "ok" {
|
if m["status"] != "ok" {
|
||||||
return fmt.Errorf("error occured on break, err: %s", m["error"])
|
return false, fmt.Errorf("error occured on break, err: %s", m["error"])
|
||||||
}
|
}
|
||||||
|
|
||||||
b.breakpoints[file] = append(b.breakpoints[file], line)
|
if command == BreakCommand {
|
||||||
|
b.breakpoints[file] = append(b.breakpoints[file], line)
|
||||||
|
return true, nil
|
||||||
|
} else {
|
||||||
|
breakpointsLen := len(b.breakpoints[file])
|
||||||
|
index := slices.Index(b.breakpoints[file], line)
|
||||||
|
b.breakpoints[file][index] = b.breakpoints[file][breakpointsLen-1]
|
||||||
|
|
||||||
return nil
|
b.breakpoints[file] = b.breakpoints[file][0 : breakpointsLen-1]
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Bridge) Continue() error {
|
func (b *Bridge) Continue() error {
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ type CommandType string
|
|||||||
const (
|
const (
|
||||||
ContinueCommand CommandType = "continue"
|
ContinueCommand CommandType = "continue"
|
||||||
BreakCommand CommandType = "break"
|
BreakCommand CommandType = "break"
|
||||||
|
UnbreakCommand CommandType = "unbreak"
|
||||||
LocalsCommand CommandType = "locals"
|
LocalsCommand CommandType = "locals"
|
||||||
StepCommand CommandType = "step"
|
StepCommand CommandType = "step"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -92,6 +92,25 @@ class PyBugBridgeDebugger(bdb.Bdb):
|
|||||||
"status": "ok",
|
"status": "ok",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
case "unbreak":
|
||||||
|
err = self.clear_break(cmd["file"], cmd["line"])
|
||||||
|
if err:
|
||||||
|
self.send(
|
||||||
|
{
|
||||||
|
"request_id": cmd["request_id"],
|
||||||
|
"event": "unbreak",
|
||||||
|
"status": "error",
|
||||||
|
"error": err,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.send(
|
||||||
|
{
|
||||||
|
"request_id": cmd["request_id"],
|
||||||
|
"event": "unbreak",
|
||||||
|
"status": "ok",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
|||||||
14
ui/codeviewer/messages.go
Normal file
14
ui/codeviewer/messages.go
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
package codeviewer
|
||||||
|
|
||||||
|
type ChangeTextMsg struct {
|
||||||
|
Text string
|
||||||
|
}
|
||||||
|
|
||||||
|
type BreakpointMsg struct {
|
||||||
|
Line int
|
||||||
|
Added bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type ExecutionStoppedMsg struct {
|
||||||
|
Line int
|
||||||
|
}
|
||||||
124
ui/codeviewer/model.go
Normal file
124
ui/codeviewer/model.go
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
package codeviewer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/alecthomas/chroma/v2/formatters"
|
||||||
|
"github.com/alecthomas/chroma/v2/lexers"
|
||||||
|
"github.com/alecthomas/chroma/v2/styles"
|
||||||
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CodeViewer struct {
|
||||||
|
lines []string
|
||||||
|
|
||||||
|
Width, Height 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,
|
||||||
|
currStoppedLine: -1,
|
||||||
|
breakpoints: map[int]struct{}{},
|
||||||
|
}
|
||||||
|
|
||||||
|
return cv.colorize(text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c CodeViewer) Init() tea.Cmd {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c CodeViewer) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
|
switch msg := msg.(type) {
|
||||||
|
case tea.KeyMsg:
|
||||||
|
return c.handleKeyMsg(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
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c CodeViewer) handleKeyMsg(key tea.KeyMsg) (tea.Model, tea.Cmd) {
|
||||||
|
switch key.String() {
|
||||||
|
case "k":
|
||||||
|
c.Cursor = max(0, c.Cursor-1)
|
||||||
|
topThreshold := c.offset + int(float64(c.Height)*0.1)
|
||||||
|
if c.Cursor < topThreshold && c.offset > 0 {
|
||||||
|
c.offset -= 1
|
||||||
|
}
|
||||||
|
case "j":
|
||||||
|
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) {
|
||||||
|
c.offset += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c CodeViewer) colorize(text string) CodeViewer {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
lexer := lexers.Get("python")
|
||||||
|
style := styles.Get("monokai")
|
||||||
|
formatter := formatters.Get("terminal16m")
|
||||||
|
|
||||||
|
iterator, _ := lexer.Tokenise(nil, text)
|
||||||
|
formatter.Format(&buf, style, iterator)
|
||||||
|
|
||||||
|
c.lines = strings.Split(buf.String(), "\n")
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c CodeViewer) View() string {
|
||||||
|
var out strings.Builder
|
||||||
|
|
||||||
|
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 lineNumber == c.Cursor+1 {
|
||||||
|
cursor = ">"
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
// Count long lines, and add here if they are out of view, this is a bit tricky I guess
|
||||||
|
if len(lines)-c.offset < c.Height {
|
||||||
|
for range c.Height - len(lines) {
|
||||||
|
fmt.Fprintf(&out, "\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return out.String()
|
||||||
|
}
|
||||||
33
ui/model.go
33
ui/model.go
@@ -1,17 +1,15 @@
|
|||||||
package ui
|
package ui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
|
||||||
|
|
||||||
"git.pablu.de/pablu/pybug/internal/bridge"
|
"git.pablu.de/pablu/pybug/internal/bridge"
|
||||||
|
"git.pablu.de/pablu/pybug/ui/codeviewer"
|
||||||
|
"git.pablu.de/pablu/pybug/ui/variablesviewer"
|
||||||
"github.com/charmbracelet/bubbles/viewport"
|
"github.com/charmbracelet/bubbles/viewport"
|
||||||
tea "github.com/charmbracelet/bubbletea"
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Model struct {
|
type Model struct {
|
||||||
currentFile string
|
currentFile string
|
||||||
text string
|
|
||||||
textLines int
|
|
||||||
|
|
||||||
width int
|
width int
|
||||||
height int
|
height int
|
||||||
@@ -20,14 +18,12 @@ 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
|
||||||
codeViewer viewport.Model
|
codeViewer codeviewer.CodeViewer
|
||||||
localsViewer viewport.Model
|
localsViewer variablesviewer.VariableViewer
|
||||||
cursor int
|
|
||||||
|
|
||||||
breakpoints map[string][]int
|
breakpoints map[string][]int
|
||||||
}
|
}
|
||||||
@@ -37,27 +33,20 @@ func NewModel(b *bridge.Bridge, file string, text string) Model {
|
|||||||
c2 := b.SubscribeStopped()
|
c2 := b.SubscribeStopped()
|
||||||
|
|
||||||
stdoutOutput := viewport.New(0, 0)
|
stdoutOutput := viewport.New(0, 0)
|
||||||
codeViewer := viewport.New(0, 0)
|
codeViewer := codeviewer.NewCodeViewer(text)
|
||||||
localsViewer := viewport.New(0, 0)
|
localsViewer := variablesviewer.NewVariableViewer(map[string]any{})
|
||||||
|
|
||||||
return Model{
|
return Model{
|
||||||
currentFile: file,
|
currentFile: file,
|
||||||
text: text,
|
|
||||||
textLines: len(strings.Split(text, "\n")),
|
|
||||||
|
|
||||||
bridge: b,
|
bridge: b,
|
||||||
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),
|
||||||
|
|
||||||
cursor: 0,
|
|
||||||
codeViewer: codeViewer,
|
codeViewer: codeViewer,
|
||||||
stdoutOutput: stdoutOutput,
|
stdoutOutput: stdoutOutput,
|
||||||
localsViewer: localsViewer,
|
localsViewer: localsViewer,
|
||||||
@@ -74,12 +63,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 {
|
||||||
|
|||||||
34
ui/update.go
34
ui/update.go
@@ -4,11 +4,14 @@ import (
|
|||||||
"log/slog"
|
"log/slog"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"git.pablu.de/pablu/pybug/internal/bridge"
|
"git.pablu.de/pablu/pybug/ui/codeviewer"
|
||||||
tea "github.com/charmbracelet/bubbletea"
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
|
updatedCv, cmd := m.codeViewer.Update(msg)
|
||||||
|
m.codeViewer = updatedCv.(codeviewer.CodeViewer)
|
||||||
|
|
||||||
switch msg := msg.(type) {
|
switch msg := msg.(type) {
|
||||||
case tea.KeyMsg:
|
case tea.KeyMsg:
|
||||||
return m.HandleKeyMsg(msg)
|
return m.HandleKeyMsg(msg)
|
||||||
@@ -19,15 +22,14 @@ 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)
|
||||||
m.localsViewer.SetContent(strings.Join(flattenDict(m.currLocals, 0), "\n"))
|
m.localsViewer.SetNewVariables(m.currLocals)
|
||||||
}
|
}
|
||||||
|
|
||||||
return m, nil
|
return m, cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m Model) GetLocals() tea.Cmd {
|
func (m Model) GetLocals() tea.Cmd {
|
||||||
@@ -50,7 +52,7 @@ func (m Model) UpdateWindowSize(msg tea.WindowSizeMsg) (tea.Model, tea.Cmd) {
|
|||||||
outputHeight := msg.Height - editorHeight - 4
|
outputHeight := msg.Height - editorHeight - 4
|
||||||
|
|
||||||
m.codeViewer.Width = msg.Width
|
m.codeViewer.Width = msg.Width
|
||||||
m.codeViewer.Height = editorHeight
|
m.codeViewer.Height = editorHeight - 2
|
||||||
|
|
||||||
m.stdoutOutput.Width = msg.Width / 2
|
m.stdoutOutput.Width = msg.Width / 2
|
||||||
m.stdoutOutput.Height = outputHeight
|
m.stdoutOutput.Height = outputHeight
|
||||||
@@ -67,16 +69,13 @@ 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 "k":
|
|
||||||
m.codeViewer.ScrollUp(1)
|
|
||||||
m.cursor = max(0, m.cursor-1)
|
|
||||||
case "j":
|
|
||||||
m.codeViewer.ScrollDown(1)
|
|
||||||
m.cursor = min(m.textLines-1, m.cursor+1)
|
|
||||||
case "b":
|
case "b":
|
||||||
lineNumber := m.cursor + 1
|
lineNumber := m.codeViewer.Cursor + 1
|
||||||
|
|
||||||
m.bridge.Breakpoint(m.currentFile, lineNumber)
|
set, err := m.bridge.Breakpoint(m.currentFile, lineNumber)
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("could not set or unset breakpoint", "error", err)
|
||||||
|
}
|
||||||
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 {
|
||||||
@@ -84,6 +83,13 @@ func (m Model) HandleKeyMsg(key tea.KeyMsg) (tea.Model, tea.Cmd) {
|
|||||||
lineNumber,
|
lineNumber,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return m, func() tea.Msg {
|
||||||
|
// check if this is in currently viewed file
|
||||||
|
return codeviewer.BreakpointMsg{
|
||||||
|
Added: set,
|
||||||
|
Line: lineNumber,
|
||||||
|
}
|
||||||
|
}
|
||||||
case "s":
|
case "s":
|
||||||
m.bridge.Step()
|
m.bridge.Step()
|
||||||
case "c":
|
case "c":
|
||||||
|
|||||||
53
ui/variablesviewer/model.go
Normal file
53
ui/variablesviewer/model.go
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
package variablesviewer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"slices"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
|
)
|
||||||
|
|
||||||
|
type VariableViewer struct {
|
||||||
|
variables map[string]any
|
||||||
|
|
||||||
|
Width, Height int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewVariableViewer(variables map[string]any) VariableViewer {
|
||||||
|
return VariableViewer{
|
||||||
|
variables: variables,
|
||||||
|
Width: 0,
|
||||||
|
Height: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *VariableViewer) SetNewVariables(variables map[string]any) {
|
||||||
|
v.variables = variables
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v VariableViewer) Init() tea.Cmd {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v VariableViewer) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v VariableViewer) View() string {
|
||||||
|
out := strings.Builder{}
|
||||||
|
|
||||||
|
keys := make([]string, 0, len(v.variables))
|
||||||
|
for k := range v.variables {
|
||||||
|
keys = append(keys, k)
|
||||||
|
}
|
||||||
|
slices.Sort(keys)
|
||||||
|
|
||||||
|
// TODO make this listen to Height correctly
|
||||||
|
for _, key := range keys {
|
||||||
|
value := v.variables[key]
|
||||||
|
fmt.Fprintf(&out, "> %s: %s\n", key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
return out.String()
|
||||||
|
}
|
||||||
40
ui/view.go
40
ui/view.go
@@ -1,14 +1,9 @@
|
|||||||
package ui
|
package ui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"slices"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/alecthomas/chroma/v2/formatters"
|
|
||||||
"github.com/alecthomas/chroma/v2/lexers"
|
|
||||||
"github.com/alecthomas/chroma/v2/styles"
|
|
||||||
"github.com/charmbracelet/lipgloss"
|
"github.com/charmbracelet/lipgloss"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -19,7 +14,7 @@ func flattenDict(m map[string]interface{}, indent int) []string {
|
|||||||
prefix := strings.Repeat(" ", indent)
|
prefix := strings.Repeat(" ", indent)
|
||||||
for k, v := range m {
|
for k, v := range m {
|
||||||
switch val := v.(type) {
|
switch val := v.(type) {
|
||||||
case map[string]interface{}:
|
case map[string]any:
|
||||||
lines = append(lines, fmt.Sprintf("%s%s:", prefix, k))
|
lines = append(lines, fmt.Sprintf("%s%s:", prefix, k))
|
||||||
lines = append(lines, flattenDict(val, indent+1)...)
|
lines = append(lines, flattenDict(val, indent+1)...)
|
||||||
default:
|
default:
|
||||||
@@ -30,39 +25,6 @@ func flattenDict(m map[string]interface{}, indent int) []string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m Model) View() string {
|
func (m Model) View() string {
|
||||||
var buf bytes.Buffer
|
|
||||||
lexer := lexers.Get("python")
|
|
||||||
style := styles.Get("monokai")
|
|
||||||
formatter := formatters.Get("terminal16m")
|
|
||||||
|
|
||||||
iterator, _ := lexer.Tokenise(nil, m.text)
|
|
||||||
formatter.Format(&buf, style, iterator)
|
|
||||||
|
|
||||||
var out strings.Builder
|
|
||||||
|
|
||||||
lines := strings.Split(buf.String(), "\n")
|
|
||||||
breakpoints := m.breakpoints[m.currentFile]
|
|
||||||
for i, line := range lines {
|
|
||||||
breakpoint := " "
|
|
||||||
cursor := " "
|
|
||||||
executor := " "
|
|
||||||
|
|
||||||
if slices.Contains(breakpoints, i+1) {
|
|
||||||
breakpoint = "O"
|
|
||||||
}
|
|
||||||
|
|
||||||
if i+1 == m.currExecutionPoint.Line {
|
|
||||||
executor = "!"
|
|
||||||
}
|
|
||||||
|
|
||||||
if i == m.cursor {
|
|
||||||
cursor = ">"
|
|
||||||
}
|
|
||||||
fmt.Fprintf(&out, "%-4d%s%s%s %s\n", i+1, breakpoint, executor, cursor, line)
|
|
||||||
}
|
|
||||||
|
|
||||||
m.codeViewer.SetContent(out.String())
|
|
||||||
|
|
||||||
hFrame, wFrame := panelStyle.GetFrameSize()
|
hFrame, wFrame := panelStyle.GetFrameSize()
|
||||||
topPanel := panelStyle.
|
topPanel := panelStyle.
|
||||||
Height(m.codeViewer.Height - hFrame).
|
Height(m.codeViewer.Height - hFrame).
|
||||||
|
|||||||
Reference in New Issue
Block a user