-
Notifications
You must be signed in to change notification settings - Fork 5
/
code.go
141 lines (114 loc) · 3.13 KB
/
code.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
// Package code implements a code bubble which renders syntax highlighted
// source code based on a filename.
package code
import (
"bytes"
"fmt"
"path/filepath"
"github.com/alecthomas/chroma/quick"
"github.com/charmbracelet/bubbles/viewport"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
"github.com/mistakenelf/teacup/filesystem"
)
type syntaxMsg string
type errorMsg error
// Highlight returns a syntax highlighted string of text.
func Highlight(content, extension, syntaxTheme string) (string, error) {
buf := new(bytes.Buffer)
if err := quick.Highlight(buf, content, extension, "terminal256", syntaxTheme); err != nil {
return "", fmt.Errorf("%w", err)
}
return buf.String(), nil
}
// readFileContentCmd reads the content of the file.
func readFileContentCmd(fileName, syntaxTheme string) tea.Cmd {
return func() tea.Msg {
content, err := filesystem.ReadFileContent(fileName)
if err != nil {
return errorMsg(err)
}
highlightedContent, err := Highlight(content, filepath.Ext(fileName), syntaxTheme)
if err != nil {
return errorMsg(err)
}
return syntaxMsg(highlightedContent)
}
}
// Model represents the properties of a code bubble.
type Model struct {
Viewport viewport.Model
Active bool
Filename string
HighlightedContent string
SyntaxTheme string
}
// New creates a new instance of code.
func New(active bool) Model {
viewPort := viewport.New(0, 0)
return Model{
Viewport: viewPort,
Active: active,
SyntaxTheme: "dracula",
}
}
// Init initializes the code bubble.
func (m Model) Init() tea.Cmd {
return nil
}
// SetFileName sets current file to highlight.
func (m *Model) SetFileName(filename string) tea.Cmd {
m.Filename = filename
return readFileContentCmd(filename, m.SyntaxTheme)
}
// SetIsActive sets if the bubble is currently active.
func (m *Model) SetIsActive(active bool) {
m.Active = active
}
// SetSyntaxTheme sets the syntax theme of the rendered code.
func (m *Model) SetSyntaxTheme(theme string) {
m.SyntaxTheme = theme
}
// SetSize sets the size of the bubble.
func (m *Model) SetSize(w, h int) {
m.Viewport.Width = w
m.Viewport.Height = h
}
// GotoTop jumps to the top of the viewport.
func (m *Model) GotoTop() {
m.Viewport.GotoTop()
}
// Update handles updating the UI of a code bubble.
func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) {
var (
cmd tea.Cmd
cmds []tea.Cmd
)
switch msg := msg.(type) {
case syntaxMsg:
m.Filename = ""
m.HighlightedContent = lipgloss.NewStyle().
Width(m.Viewport.Width).
Height(m.Viewport.Height).
Render(string(msg))
m.Viewport.SetContent(m.HighlightedContent)
return m, nil
case errorMsg:
m.Filename = ""
m.HighlightedContent = lipgloss.NewStyle().
Width(m.Viewport.Width).
Height(m.Viewport.Height).
Render("Error: " + msg.Error())
m.Viewport.SetContent(m.HighlightedContent)
return m, nil
}
if m.Active {
m.Viewport, cmd = m.Viewport.Update(msg)
cmds = append(cmds, cmd)
}
return m, tea.Batch(cmds...)
}
// View returns a string representation of the code bubble.
func (m Model) View() string {
return m.Viewport.View()
}