mirror of
https://github.com/charmbracelet/glow.git
synced 2026-08-23 00:24:17 +02:00
Render with glamour asyncronously
This commit is contained in:
parent
c622816a43
commit
f5df688860
2 changed files with 90 additions and 15 deletions
22
ui/stash.go
22
ui/stash.go
|
|
@ -34,8 +34,8 @@ type stashState int
|
|||
|
||||
const (
|
||||
stashStateInit stashState = iota
|
||||
stashStateLoaded
|
||||
stashStateLoadingItem
|
||||
stashStateStashLoaded
|
||||
stashStateLoadingDocument
|
||||
)
|
||||
|
||||
type stashModel struct {
|
||||
|
|
@ -147,7 +147,9 @@ func stashUpdate(msg boba.Msg, m stashModel) (stashModel, boba.Cmd) {
|
|||
return m, nil
|
||||
|
||||
case "enter":
|
||||
m.state = stashStateLoadingItem
|
||||
// Load the document from the server. We'll handle the message
|
||||
// that comes back in the main update function.
|
||||
m.state = stashStateLoadingDocument
|
||||
indexToLoad := m.paginator.Page*m.paginator.PerPage + m.index
|
||||
return m, boba.Batch(
|
||||
loadStashedItem(m.cc, m.documents[indexToLoad].ID),
|
||||
|
|
@ -162,17 +164,17 @@ func stashUpdate(msg boba.Msg, m stashModel) (stashModel, boba.Cmd) {
|
|||
case gotStashMsg:
|
||||
sort.Sort(charm.MarkdownsByCreatedAt(msg)) // sort by date
|
||||
m.documents = append(m.documents, msg...)
|
||||
m.state = stashStateLoaded
|
||||
m.state = stashStateStashLoaded
|
||||
m.paginator.SetTotalPages(len(m.documents))
|
||||
|
||||
case stashSpinnerTickMsg:
|
||||
if m.state == stashStateInit || m.state == stashStateLoadingItem {
|
||||
if m.state == stashStateInit || m.state == stashStateLoadingDocument {
|
||||
m.spinner, cmd = spinner.Update(msg, m.spinner)
|
||||
return m, cmd
|
||||
}
|
||||
}
|
||||
|
||||
if m.state == stashStateLoaded {
|
||||
if m.state == stashStateStashLoaded {
|
||||
m.paginator, cmd = paginator.Update(msg, m.paginator)
|
||||
cmds = append(cmds, cmd)
|
||||
}
|
||||
|
|
@ -183,13 +185,17 @@ func stashUpdate(msg boba.Msg, m stashModel) (stashModel, boba.Cmd) {
|
|||
// VIEW
|
||||
|
||||
func stashView(m stashModel) string {
|
||||
if m.err != nil {
|
||||
return "\n" + m.err.Error()
|
||||
}
|
||||
|
||||
var s string
|
||||
switch m.state {
|
||||
case stashStateInit:
|
||||
s += spinner.View(m.spinner) + " Loading stash..."
|
||||
case stashStateLoadingItem:
|
||||
case stashStateLoadingDocument:
|
||||
s += spinner.View(m.spinner) + " Loading document..."
|
||||
case stashStateLoaded:
|
||||
case stashStateStashLoaded:
|
||||
if len(m.documents) == 0 {
|
||||
s += stashEmtpyView(m)
|
||||
break
|
||||
|
|
|
|||
83
ui/ui.go
83
ui/ui.go
|
|
@ -12,6 +12,7 @@ import (
|
|||
"github.com/charmbracelet/charm"
|
||||
"github.com/charmbracelet/charm/ui/common"
|
||||
"github.com/charmbracelet/charm/ui/keygen"
|
||||
"github.com/charmbracelet/glamour"
|
||||
"github.com/muesli/reflow/indent"
|
||||
te "github.com/muesli/termenv"
|
||||
"golang.org/x/crypto/ssh/terminal"
|
||||
|
|
@ -38,6 +39,7 @@ type fatalErrMsg error
|
|||
type errMsg error
|
||||
type newCharmClientMsg *charm.Client
|
||||
type sshAuthErrMsg struct{}
|
||||
type contentRenderedMsg string
|
||||
|
||||
type terminalSizeMsg struct {
|
||||
width int
|
||||
|
|
@ -78,7 +80,7 @@ type model struct {
|
|||
func (m *model) unloadDocument() {
|
||||
m.pager = pager.Model{}
|
||||
m.state = stateShowStash
|
||||
m.stash.state = stashStateLoaded
|
||||
m.stash.state = stashStateStashLoaded
|
||||
}
|
||||
|
||||
// INIT
|
||||
|
|
@ -196,18 +198,34 @@ func update(msg boba.Msg, mdl boba.Model) (boba.Model, boba.Cmd) {
|
|||
return m, cmd
|
||||
|
||||
case gotStashedItemMsg:
|
||||
// TODO: there's a (unlikely) potential race condition that could
|
||||
// happen here where we start the pager before we have received the
|
||||
// terminal size. We could solve this in a few ways, including by
|
||||
// getting the terminal size imperatively and synchronously
|
||||
m.state = stateShowDocument
|
||||
// We've received stashed item data. Render with Glamour and send to
|
||||
// the pager.
|
||||
//m.state = stateShowDocument
|
||||
m.pager = pager.NewModel(
|
||||
m.terminalWidth,
|
||||
m.terminalHeight-statusBarHeight,
|
||||
)
|
||||
m.docNote = msg.Note
|
||||
m.pager.Content(msg.Body)
|
||||
|
||||
// This could happen asyncronously with a Cmd since there's techincally
|
||||
// IO happening, but since rendering is fast and Go is an imperative
|
||||
// language I guess it's fine to just render synchronously here.
|
||||
/*
|
||||
md, err := glamourRender(m, msg.Body)
|
||||
if err != nil {
|
||||
m.err = err
|
||||
return m, nil
|
||||
}
|
||||
*/
|
||||
|
||||
//m.pager.SetContent(md)
|
||||
return m, renderWithGlamour(m, msg.Body)
|
||||
|
||||
case contentRenderedMsg:
|
||||
m.state = stateShowDocument
|
||||
m.pager.SetContent(string(msg))
|
||||
return m, nil
|
||||
|
||||
}
|
||||
|
||||
switch m.state {
|
||||
|
|
@ -227,6 +245,7 @@ func update(msg boba.Msg, mdl boba.Model) (boba.Model, boba.Cmd) {
|
|||
return m, cmd
|
||||
|
||||
case stateShowDocument:
|
||||
// Process keys (and eventually mouse) with pager.Update
|
||||
newPagerModel, cmd := pager.Update(msg, boba.Model(m.pager))
|
||||
newPagerModel_, ok := newPagerModel.(pager.Model)
|
||||
if !ok {
|
||||
|
|
@ -329,8 +348,58 @@ func newCharmClient() boba.Msg {
|
|||
return newCharmClientMsg(cc)
|
||||
}
|
||||
|
||||
func renderWithGlamour(m model, md string) boba.Cmd {
|
||||
return func() boba.Msg {
|
||||
s, err := glamourRender(m, md)
|
||||
if err != nil {
|
||||
return errMsg(err)
|
||||
}
|
||||
return contentRenderedMsg(s)
|
||||
}
|
||||
}
|
||||
|
||||
// ETC
|
||||
|
||||
// This is where the magic happens
|
||||
func glamourRender(m model, markdown string) (string, error) {
|
||||
|
||||
// initialize glamour
|
||||
var gs glamour.TermRendererOption
|
||||
if m.style == "auto" {
|
||||
gs = glamour.WithAutoStyle()
|
||||
} else {
|
||||
gs = glamour.WithStylePath(m.style)
|
||||
}
|
||||
|
||||
r, err := glamour.NewTermRenderer(
|
||||
gs,
|
||||
glamour.WithWordWrap(m.terminalWidth),
|
||||
)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
out, err := r.Render(markdown)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// trim lines
|
||||
lines := strings.Split(string(out), "\n")
|
||||
|
||||
var content string
|
||||
for i, s := range lines {
|
||||
content += strings.TrimSpace(s)
|
||||
|
||||
// don't add an artificial newline after the last split
|
||||
if i+1 < len(lines) {
|
||||
content += "\n"
|
||||
}
|
||||
}
|
||||
|
||||
return content, nil
|
||||
}
|
||||
|
||||
func min(a, b int) int {
|
||||
if a < b {
|
||||
return a
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue