Skip to content

Commit

Permalink
feat: watch for file changes and reload automatically
Browse files Browse the repository at this point in the history
  • Loading branch information
andreynering committed Feb 10, 2025
1 parent e1902ae commit 1adc31c
Showing 1 changed file with 68 additions and 1 deletion.
69 changes: 68 additions & 1 deletion ui/pager.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package ui
import (
"fmt"
"math"
"os"
"path/filepath"
"strings"
"time"
Expand All @@ -14,6 +15,7 @@ import (
"github.com/charmbracelet/glow/v2/utils"
"github.com/charmbracelet/lipgloss"
"github.com/charmbracelet/log"
"github.com/fsnotify/fsnotify"
runewidth "github.com/mattn/go-runewidth"
"github.com/muesli/reflow/ansi"
"github.com/muesli/reflow/truncate"
Expand Down Expand Up @@ -78,6 +80,7 @@ var (

type (
contentRenderedMsg string
reloadMsg struct{}
)

type pagerState int
Expand All @@ -99,6 +102,8 @@ type pagerModel struct {
// Current document being rendered, sans-glamour rendering. We cache
// it here so we can re-render it on resize.
currentDocument markdown

watcher *fsnotify.Watcher
}

func newPagerModel(common *commonModel) pagerModel {
Expand All @@ -107,11 +112,13 @@ func newPagerModel(common *commonModel) pagerModel {
vp.YPosition = 0
vp.HighPerformanceRendering = config.HighPerformancePager

return pagerModel{
m := pagerModel{
common: common,
state: pagerStateBrowse,
viewport: vp,
}
m.initWatcher()
return m
}

func (m *pagerModel) setSize(w, h int) {
Expand Down Expand Up @@ -159,6 +166,7 @@ func (m *pagerModel) showStatusMessage(msg pagerStatusMessage) tea.Cmd {
}

func (m *pagerModel) unload() {
log.Debug("unload")
if m.showHelp {
m.toggleHelp()
}
Expand All @@ -168,6 +176,7 @@ func (m *pagerModel) unload() {
m.state = pagerStateBrowse
m.viewport.SetContent("")
m.viewport.YOffset = 0
m.unwatchFile()
}

func (m pagerModel) update(msg tea.Msg) (pagerModel, tea.Cmd) {
Expand Down Expand Up @@ -226,10 +235,17 @@ func (m pagerModel) update(msg tea.Msg) (pagerModel, tea.Cmd) {

// Glow has rendered the content
case contentRenderedMsg:
log.Info("content rendered", "state", m.state)

m.setContent(string(msg))
if m.viewport.HighPerformanceRendering {
cmds = append(cmds, viewport.Sync(m.viewport))
}
cmds = append(cmds, m.watchFile)

// The file was changed on disk and we're reloading it
case reloadMsg:
return m, loadLocalMarkdown(&m.currentDocument)

// We've finished editing the document, potentially making changes. Let's
// retrieve the latest version of the document so that we display
Expand Down Expand Up @@ -451,3 +467,54 @@ func glamourRender(m pagerModel, markdown string) (string, error) {

return content.String(), nil
}

func (m *pagerModel) initWatcher() {
var err error
m.watcher, err = fsnotify.NewWatcher()
if err != nil {
log.Error("error creating fsnotify watcher", "error", err)
}
}

func (m *pagerModel) watchFile() tea.Msg {
path := m.relFilePath()

if err := m.watcher.Add(path); err != nil {
log.Error("error adding file to fsnotify watcher", "error", err)
return nil
}

log.Info("fsnotify watching file", "file", path)

for {
select {
case event, ok := <-m.watcher.Events:
if !ok || !event.Has(fsnotify.Write) {
continue
}
log.Debug("fsnotify event", "file", path, "event", event.Op)
return reloadMsg{}
case err, ok := <-m.watcher.Errors:
if !ok {
continue
}
log.Debug("fsnotify error", "file", path, "error", err)
}
}
}

func (m *pagerModel) unwatchFile() {
path := m.relFilePath()

err := m.watcher.Remove(path)
if err == nil {
log.Debug("fsnotify file unwatched", "file", path)
} else {
log.Error("fsnotify fail to unwatch file", "file", path, "error", err)
}
}

func (m *pagerModel) relFilePath() string {
wd, _ := os.Getwd()
return stripAbsolutePath(m.currentDocument.localPath, wd)
}

0 comments on commit 1adc31c

Please sign in to comment.