7d76a60dce427a4cdcd845a1d1adbd9efe1aebda

Author
Ayman Bagabas <ayman.bagabas@gmail.com>
Committer
Ayman Bagabas <ayman.bagabas@gmail.com>
Date

Message

feat: render markdown files using glamour

Fixes: https://github.com/charmbracelet/soft-serve/issues/87

Diff

  1diff --git a/internal/tui/bubbles/git/about/bubble.go b/internal/tui/bubbles/git/about/bubble.go
  2index b92135cc9f841348ccc76bac956cc6fc8dc7bb84..9b3e84f86012b05acb569299a0665a0aeb4a9340 100644
  3--- a/internal/tui/bubbles/git/about/bubble.go
  4+++ b/internal/tui/bubbles/git/about/bubble.go
  5@@ -3,11 +3,9 @@ package about
  6 import (
  7 	"github.com/charmbracelet/bubbles/viewport"
  8 	tea "github.com/charmbracelet/bubbletea"
  9-	"github.com/charmbracelet/glamour"
 10 	"github.com/charmbracelet/soft-serve/internal/tui/bubbles/git/types"
 11 	vp "github.com/charmbracelet/soft-serve/internal/tui/bubbles/git/viewport"
 12 	"github.com/charmbracelet/soft-serve/internal/tui/style"
 13-	"github.com/muesli/reflow/wrap"
 14 )
 15 
 16 type Bubble struct {
 17@@ -45,7 +43,7 @@ func (b *Bubble) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 18 		// XXX: if we find that longer readmes take more than a few
 19 		// milliseconds to render we may need to move Glamour rendering into a
 20 		// command.
 21-		md, err := b.glamourize(b.repo.GetReadme())
 22+		md, err := b.glamourize()
 23 		if err != nil {
 24 			return b, nil
 25 		}
 26@@ -81,8 +79,13 @@ func (b *Bubble) Help() []types.HelpEntry {
 27 	return nil
 28 }
 29 
 30+func (b *Bubble) glamourize() (string, error) {
 31+	w := b.width - b.widthMargin - b.styles.RepoBody.GetHorizontalFrameSize()
 32+	return types.Glamourize(w, b.repo.GetReadme())
 33+}
 34+
 35 func (b *Bubble) setupCmd() tea.Msg {
 36-	md, err := b.glamourize(b.repo.GetReadme())
 37+	md, err := b.glamourize()
 38 	if err != nil {
 39 		return types.ErrMsg{err}
 40 	}
 41@@ -90,30 +93,3 @@ func (b *Bubble) setupCmd() tea.Msg {
 42 	b.GotoTop()
 43 	return nil
 44 }
 45-
 46-func (b *Bubble) glamourize(md string) (string, error) {
 47-	w := b.width - b.widthMargin - b.styles.RepoBody.GetHorizontalFrameSize()
 48-	if w > types.GlamourMaxWidth {
 49-		w = types.GlamourMaxWidth
 50-	}
 51-	tr, err := glamour.NewTermRenderer(
 52-		glamour.WithStyles(types.DefaultStyles()),
 53-		glamour.WithWordWrap(w),
 54-	)
 55-
 56-	if err != nil {
 57-		return "", err
 58-	}
 59-	mdt, err := tr.Render(md)
 60-	if err != nil {
 61-		return "", err
 62-	}
 63-	// For now, hard-wrap long lines in Glamour that would otherwise break the
 64-	// layout when wrapping. This may be due to #43 in Reflow, which has to do
 65-	// with a bug in the way lines longer than the given width are wrapped.
 66-	//
 67-	//     https://github.com/muesli/reflow/issues/43
 68-	//
 69-	// TODO: solve this upstream in Glamour/Reflow.
 70-	return wrap.String(mdt, w), nil
 71-}
 72diff --git a/internal/tui/bubbles/git/log/bubble.go b/internal/tui/bubbles/git/log/bubble.go
 73index 68250f8013925cd1bcfec0dc801e3a26834066d3..3299ca211d9a0f226028f08895cbbeb91505cc9a 100644
 74--- a/internal/tui/bubbles/git/log/bubble.go
 75+++ b/internal/tui/bubbles/git/log/bubble.go
 76@@ -99,8 +99,8 @@ type Bubble struct {
 77 	error          types.ErrMsg
 78 }
 79 
 80-func NewBubble(repo types.Repo, style *style.Styles, width, widthMargin, height, heightMargin int) *Bubble {
 81-	l := list.New([]list.Item{}, itemDelegate{style}, width-widthMargin, height-heightMargin)
 82+func NewBubble(repo types.Repo, styles *style.Styles, width, widthMargin, height, heightMargin int) *Bubble {
 83+	l := list.New([]list.Item{}, itemDelegate{styles}, width-widthMargin, height-heightMargin)
 84 	l.SetShowFilter(false)
 85 	l.SetShowHelp(false)
 86 	l.SetShowPagination(false)
 87@@ -115,7 +115,7 @@ func NewBubble(repo types.Repo, style *style.Styles, width, widthMargin, height,
 88 			Viewport: &viewport.Model{},
 89 		},
 90 		repo:         repo,
 91-		style:        style,
 92+		style:        styles,
 93 		state:        logState,
 94 		width:        width,
 95 		widthMargin:  widthMargin,
 96diff --git a/internal/tui/bubbles/git/refs/bubble.go b/internal/tui/bubbles/git/refs/bubble.go
 97index bb77d6ee65e71cbfb1cf2836a2fff5722d41f255..bd4b2f8e83fd713a8d2dae394b325ef547a8e249 100644
 98--- a/internal/tui/bubbles/git/refs/bubble.go
 99+++ b/internal/tui/bubbles/git/refs/bubble.go
100@@ -75,8 +75,8 @@ type Bubble struct {
101 	heightMargin int
102 }
103 
104-func NewBubble(repo types.Repo, style *style.Styles, width, widthMargin, height, heightMargin int) *Bubble {
105-	l := list.NewModel([]list.Item{}, itemDelegate{style}, width-widthMargin, height-heightMargin)
106+func NewBubble(repo types.Repo, styles *style.Styles, width, widthMargin, height, heightMargin int) *Bubble {
107+	l := list.NewModel([]list.Item{}, itemDelegate{styles}, width-widthMargin, height-heightMargin)
108 	l.SetShowFilter(false)
109 	l.SetShowHelp(false)
110 	l.SetShowPagination(false)
111@@ -86,7 +86,7 @@ func NewBubble(repo types.Repo, style *style.Styles, width, widthMargin, height,
112 	l.DisableQuitKeybindings()
113 	b := &Bubble{
114 		repo:         repo,
115-		style:        style,
116+		style:        styles,
117 		width:        width,
118 		height:       height,
119 		widthMargin:  widthMargin,
120diff --git a/internal/tui/bubbles/git/tree/bubble.go b/internal/tui/bubbles/git/tree/bubble.go
121index 91974f34a83e76e66d60ba77ddf4ab8d23b0d03c..926ff77cc9c4057136fa42b7ec9908f6e7a4edd4 100644
122--- a/internal/tui/bubbles/git/tree/bubble.go
123+++ b/internal/tui/bubbles/git/tree/bubble.go
124@@ -130,8 +130,8 @@ type Bubble struct {
125 	lastSelected []int
126 }
127 
128-func NewBubble(repo types.Repo, style *style.Styles, width, widthMargin, height, heightMargin int) *Bubble {
129-	l := list.New([]list.Item{}, itemDelegate{style}, width-widthMargin, height-heightMargin)
130+func NewBubble(repo types.Repo, styles *style.Styles, width, widthMargin, height, heightMargin int) *Bubble {
131+	l := list.New([]list.Item{}, itemDelegate{styles}, width-widthMargin, height-heightMargin)
132 	l.SetShowFilter(false)
133 	l.SetShowHelp(false)
134 	l.SetShowPagination(false)
135@@ -146,7 +146,7 @@ func NewBubble(repo types.Repo, style *style.Styles, width, widthMargin, height,
136 			Viewport: &viewport.Model{},
137 		},
138 		repo:         repo,
139-		style:        style,
140+		style:        styles,
141 		width:        width,
142 		height:       height,
143 		widthMargin:  widthMargin,
144@@ -330,12 +330,22 @@ func (b *Bubble) renderFile(m fileMsg) string {
145 			Code:     c,
146 			Language: lang,
147 		}
148-		r := strings.Builder{}
149-		err := formatter.Render(&r, types.RenderCtx)
150-		if err != nil {
151-			s.WriteString(err.Error())
152+		if lang == "markdown" {
153+			w := b.width - b.widthMargin - b.style.RepoBody.GetHorizontalFrameSize()
154+			md, err := types.Glamourize(w, c)
155+			if err != nil {
156+				s.WriteString(err.Error())
157+			} else {
158+				s.WriteString(md)
159+			}
160 		} else {
161-			s.WriteString(r.String())
162+			r := strings.Builder{}
163+			err := formatter.Render(&r, types.RenderCtx)
164+			if err != nil {
165+				s.WriteString(err.Error())
166+			} else {
167+				s.WriteString(r.String())
168+			}
169 		}
170 	}
171 	return b.style.TreeFileContent.Copy().Width(b.width - b.widthMargin).Render(s.String())
172diff --git a/internal/tui/bubbles/git/types/formatter.go b/internal/tui/bubbles/git/types/formatter.go
173index 7356581d19a61ada2c184b25d364e8c7c8b85ccb..ea3197a22e08a7c687b6720e6efcf8817efb480d 100644
174--- a/internal/tui/bubbles/git/types/formatter.go
175+++ b/internal/tui/bubbles/git/types/formatter.go
176@@ -3,6 +3,7 @@ package types
177 import (
178 	"github.com/charmbracelet/glamour"
179 	gansi "github.com/charmbracelet/glamour/ansi"
180+	"github.com/muesli/reflow/wrap"
181 	"github.com/muesli/termenv"
182 )
183 
184@@ -34,3 +35,29 @@ func NewRenderCtx(worldwrap int) gansi.RenderContext {
185 		WordWrap:     worldwrap,
186 	})
187 }
188+
189+func Glamourize(w int, md string) (string, error) {
190+	if w > GlamourMaxWidth {
191+		w = GlamourMaxWidth
192+	}
193+	tr, err := glamour.NewTermRenderer(
194+		glamour.WithStyles(DefaultStyles()),
195+		glamour.WithWordWrap(w),
196+	)
197+
198+	if err != nil {
199+		return "", err
200+	}
201+	mdt, err := tr.Render(md)
202+	if err != nil {
203+		return "", err
204+	}
205+	// For now, hard-wrap long lines in Glamour that would otherwise break the
206+	// layout when wrapping. This may be due to #43 in Reflow, which has to do
207+	// with a bug in the way lines longer than the given width are wrapped.
208+	//
209+	//     https://github.com/muesli/reflow/issues/43
210+	//
211+	// TODO: solve this upstream in Glamour/Reflow.
212+	return wrap.String(mdt, w), nil
213+}