mirror of
https://github.com/charmbracelet/glow
synced 2024-11-10 06:04:18 +00:00
When exiting pager or filter, stay in news if we were on news prior
This commit is contained in:
parent
4c63c62bfa
commit
cffa611dfb
4 changed files with 82 additions and 59 deletions
|
@ -58,10 +58,11 @@ func (d DocTypeSet) Difference(t ...DocType) DocTypeSet {
|
|||
|
||||
// Equals returns whether or not the two sets are equal.
|
||||
func (d DocTypeSet) Equals(other DocTypeSet) bool {
|
||||
return d.Contains(other.asSlice()...) && len(d) == len(other)
|
||||
return d.Contains(other.AsSlice()...) && len(d) == len(other)
|
||||
}
|
||||
|
||||
func (d DocTypeSet) asSlice() (agg []DocType) {
|
||||
// AsSlice returns the set as a slice of document types.
|
||||
func (d DocTypeSet) AsSlice() (agg []DocType) {
|
||||
for k := range d {
|
||||
agg = append(agg, k)
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ type markdown struct {
|
|||
charm.Markdown
|
||||
}
|
||||
|
||||
// Generate the value we're doing to filter against.
|
||||
func (m *markdown) buildFilterValue() {
|
||||
note, err := normalize(m.Note)
|
||||
if err != nil {
|
||||
|
@ -109,6 +110,7 @@ func wrapMarkdowns(t DocType, md []*charm.Markdown) (m []*markdown) {
|
|||
return m
|
||||
}
|
||||
|
||||
// Return the time in a human-readable format relative to the current time.
|
||||
func relativeTime(then time.Time) string {
|
||||
now := time.Now()
|
||||
ago := now.Sub(then)
|
||||
|
@ -120,6 +122,7 @@ func relativeTime(then time.Time) string {
|
|||
return then.Format("02 Jan 2006 15:04 MST")
|
||||
}
|
||||
|
||||
// Magnitudes for relative time.
|
||||
var magnitudes = []humanize.RelTimeMagnitude{
|
||||
{D: time.Second, Format: "now", DivBy: time.Second},
|
||||
{D: 2 * time.Second, Format: "1 second %s", DivBy: 1},
|
||||
|
|
105
ui/stash.go
105
ui/stash.go
|
@ -43,23 +43,37 @@ type filteredMarkdownMsg []*markdown
|
|||
|
||||
// MODEL
|
||||
|
||||
// High-level state of the application.
|
||||
type stashViewState int
|
||||
|
||||
const (
|
||||
stashStateReady stashViewState = iota
|
||||
stashStateLoadingDocument
|
||||
stashStateShowingError
|
||||
stashStateShowNews
|
||||
)
|
||||
|
||||
// Which types of documents we are showing. We use an int as the underlying
|
||||
// type both for easy equality testing, and because the default state can have
|
||||
// different types of docs depending on the user's preferences. For example,
|
||||
// if the local-only flag is passed, the default state contains only documents
|
||||
// of type local. Otherwise, it could contain stash, news, and converted types.
|
||||
type stashDocState int
|
||||
|
||||
const (
|
||||
stashShowDefaultDocs stashDocState = iota
|
||||
stashShowNewsDocs
|
||||
)
|
||||
|
||||
// The current filtering state.
|
||||
type filterState int
|
||||
|
||||
const (
|
||||
unfiltered filterState = iota
|
||||
unfiltered filterState = iota // no filter set
|
||||
filtering // user is actively setting a filter
|
||||
filterApplied // a filter is applied and user is not editing filter
|
||||
)
|
||||
|
||||
// The state of the currently selected document.
|
||||
type selectionState int
|
||||
|
||||
const (
|
||||
|
@ -70,15 +84,22 @@ const (
|
|||
|
||||
type stashModel struct {
|
||||
general *general
|
||||
state stashViewState
|
||||
filterState filterState
|
||||
selectionState selectionState
|
||||
err error
|
||||
spinner spinner.Model
|
||||
noteInput textinput.Model
|
||||
filterInput textinput.Model
|
||||
stashFullyLoaded bool // have we loaded all available stashed documents from the server?
|
||||
loadingFromNetwork bool // are we currently loading something from the network?
|
||||
viewState stashViewState
|
||||
filterState filterState
|
||||
selectionState selectionState
|
||||
|
||||
// The types of documents we are showing
|
||||
docState stashDocState
|
||||
|
||||
// Maps document states to document types, i.e. the news state contains a
|
||||
// set of type "news".
|
||||
docStateMap map[stashDocState]DocTypeSet
|
||||
|
||||
// Tracks what exactly is loaded between the stash, news and local files
|
||||
loaded DocTypeSet
|
||||
|
@ -124,7 +145,7 @@ func (m stashModel) stashedOnly() bool {
|
|||
}
|
||||
|
||||
func (m stashModel) loadingDone() bool {
|
||||
return m.loaded.Equals(m.general.cfg.DocumentTypes)
|
||||
return m.loaded.Equals(m.general.cfg.DocumentTypes.Difference(ConvertedDoc))
|
||||
}
|
||||
|
||||
// Returns whether or not we're online. That is, when "local-only" mode is
|
||||
|
@ -291,27 +312,19 @@ func (m stashModel) getVisibleMarkdowns() []*markdown {
|
|||
return m.filteredMarkdowns
|
||||
}
|
||||
|
||||
if m.state == stashStateShowNews {
|
||||
return m.getMarkdownByType(NewsDoc)
|
||||
}
|
||||
|
||||
return m.getMarkdownByType(LocalDoc, StashedDoc, ConvertedDoc)
|
||||
return m.getMarkdownByType(m.docStateMap[m.docState].AsSlice()...)
|
||||
}
|
||||
|
||||
// Return the markdowns eligible to be filtered.
|
||||
func (m stashModel) getFilterableMarkdowns() []*markdown {
|
||||
if m.state == stashStateShowNews {
|
||||
return m.getMarkdownByType(NewsDoc)
|
||||
}
|
||||
|
||||
return m.getMarkdownByType(LocalDoc, StashedDoc, ConvertedDoc)
|
||||
return m.getMarkdownByType(m.docStateMap[m.docState].AsSlice()...)
|
||||
}
|
||||
|
||||
// Command for opening a markdown document in the pager. Note that this also
|
||||
// alters the model.
|
||||
func (m *stashModel) openMarkdown(md *markdown) tea.Cmd {
|
||||
var cmd tea.Cmd
|
||||
m.state = stashStateLoadingDocument
|
||||
m.viewState = stashStateLoadingDocument
|
||||
|
||||
if md.markdownType == LocalDoc {
|
||||
cmd = loadLocalMarkdown(md)
|
||||
|
@ -406,6 +419,11 @@ func newStashModel(general *general) stashModel {
|
|||
loaded: NewDocTypeSet(),
|
||||
loadingFromNetwork: true,
|
||||
filesStashing: make(map[string]struct{}),
|
||||
docState: stashShowDefaultDocs,
|
||||
docStateMap: map[stashDocState]DocTypeSet{
|
||||
stashShowDefaultDocs: general.cfg.DocumentTypes.Difference(NewsDoc),
|
||||
stashShowNewsDocs: NewDocTypeSet(NewsDoc),
|
||||
},
|
||||
}
|
||||
|
||||
return m
|
||||
|
@ -482,7 +500,7 @@ func (m stashModel) update(msg tea.Msg) (stashModel, tea.Cmd) {
|
|||
case spinner.TickMsg:
|
||||
condition := !m.loadingDone() ||
|
||||
m.loadingFromNetwork ||
|
||||
m.state == stashStateLoadingDocument ||
|
||||
m.viewState == stashStateLoadingDocument ||
|
||||
len(m.filesStashing) > 0 ||
|
||||
m.spinner.Visible()
|
||||
|
||||
|
@ -537,13 +555,13 @@ func (m stashModel) update(msg tea.Msg) (stashModel, tea.Cmd) {
|
|||
}
|
||||
|
||||
// Updates per the current state
|
||||
switch m.state {
|
||||
case stashStateReady, stashStateShowNews:
|
||||
switch m.viewState {
|
||||
case stashStateReady:
|
||||
cmds = append(cmds, m.handleDocumentBrowsing(msg))
|
||||
case stashStateShowingError:
|
||||
// Any key exists the error view
|
||||
if _, ok := msg.(tea.KeyMsg); ok {
|
||||
m.state = stashStateReady
|
||||
m.viewState = stashStateReady
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -577,8 +595,11 @@ func (m *stashModel) handleDocumentBrowsing(msg tea.Msg) tea.Cmd {
|
|||
m.index = m.paginator.ItemsOnPage(pages) - 1
|
||||
|
||||
case "esc":
|
||||
if m.isFiltering() {
|
||||
m.resetFiltering()
|
||||
m.state = stashStateReady
|
||||
break
|
||||
}
|
||||
m.docState = stashShowDefaultDocs
|
||||
|
||||
// Open document
|
||||
case "enter":
|
||||
|
@ -637,16 +658,16 @@ func (m *stashModel) handleDocumentBrowsing(msg tea.Msg) tea.Cmd {
|
|||
// If we're offline disable the news section
|
||||
return nil
|
||||
}
|
||||
if m.state == stashStateShowNews {
|
||||
if m.docState == stashShowNewsDocs {
|
||||
// Exit news
|
||||
m.state = stashStateReady
|
||||
m.docState = stashShowDefaultDocs
|
||||
m.resetFiltering()
|
||||
} else {
|
||||
// Show news
|
||||
m.hideStatusMessage()
|
||||
m.paginator.Page = 0
|
||||
m.index = 0
|
||||
m.state = stashStateShowNews
|
||||
m.docState = stashShowNewsDocs
|
||||
m.setTotalPages()
|
||||
}
|
||||
|
||||
|
@ -686,7 +707,7 @@ func (m *stashModel) handleDocumentBrowsing(msg tea.Msg) tea.Cmd {
|
|||
case "x":
|
||||
m.hideStatusMessage()
|
||||
|
||||
validState := m.state == stashStateReady &&
|
||||
validState := m.viewState == stashStateReady &&
|
||||
m.selectionState == selectionIdle
|
||||
|
||||
if pages == 0 && !validState {
|
||||
|
@ -700,8 +721,8 @@ func (m *stashModel) handleDocumentBrowsing(msg tea.Msg) tea.Cmd {
|
|||
|
||||
// Show errors
|
||||
case "!":
|
||||
if m.err != nil && m.state == stashStateReady {
|
||||
m.state = stashStateShowingError
|
||||
if m.err != nil && m.viewState == stashStateReady {
|
||||
m.viewState = stashStateShowingError
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
@ -812,7 +833,7 @@ func (m *stashModel) handleFiltering(msg tea.Msg) tea.Cmd {
|
|||
|
||||
// If we've filtered down to nothing, clear the filter
|
||||
if len(h) == 0 {
|
||||
m.state = stashStateReady
|
||||
m.viewState = stashStateReady
|
||||
m.resetFiltering()
|
||||
break
|
||||
}
|
||||
|
@ -820,7 +841,7 @@ func (m *stashModel) handleFiltering(msg tea.Msg) tea.Cmd {
|
|||
// When there's only one filtered markdown left we can just
|
||||
// "open" it directly
|
||||
if len(h) == 1 {
|
||||
m.state = stashStateReady
|
||||
m.viewState = stashStateReady
|
||||
m.resetFiltering()
|
||||
cmds = append(cmds, m.openMarkdown(h[0]))
|
||||
break
|
||||
|
@ -890,12 +911,12 @@ func (m *stashModel) handleNoteInput(msg tea.Msg) tea.Cmd {
|
|||
|
||||
func (m stashModel) view() string {
|
||||
var s string
|
||||
switch m.state {
|
||||
switch m.viewState {
|
||||
case stashStateShowingError:
|
||||
return errorView(m.err, false)
|
||||
case stashStateLoadingDocument:
|
||||
s += " " + m.spinner.View() + " Loading document..."
|
||||
case stashStateReady, stashStateShowNews:
|
||||
case stashStateReady:
|
||||
|
||||
loadingIndicator := " "
|
||||
if !m.localOnly() && (!m.loadingDone() || m.loadingFromNetwork || m.spinner.Visible()) {
|
||||
|
@ -925,7 +946,7 @@ func (m stashModel) view() string {
|
|||
// Only draw the normal header if we're not using the header area for
|
||||
// something else (like a prompt or status message)
|
||||
if header == "" {
|
||||
header = stashHeaderView(m)
|
||||
header = m.headerView()
|
||||
}
|
||||
|
||||
logoOrFilter := glowLogoView(" Glow ")
|
||||
|
@ -933,7 +954,7 @@ func (m stashModel) view() string {
|
|||
// If we're filtering we replace the logo with the filter field
|
||||
if m.isFiltering() {
|
||||
logoOrFilter = m.filterInput.View()
|
||||
} else if m.state == stashStateShowNews {
|
||||
} else if m.docState == stashShowNewsDocs {
|
||||
logoOrFilter += newsTitleStyle(" News ")
|
||||
}
|
||||
|
||||
|
@ -958,10 +979,10 @@ func (m stashModel) view() string {
|
|||
loadingIndicator,
|
||||
logoOrFilter,
|
||||
header,
|
||||
stashPopulatedView(m),
|
||||
m.populatedView(),
|
||||
blankLines,
|
||||
pagination,
|
||||
stashHelpView(m),
|
||||
m.miniHelpView(),
|
||||
)
|
||||
}
|
||||
return "\n" + indent(s, stashIndent)
|
||||
|
@ -975,7 +996,7 @@ func glowLogoView(text string) string {
|
|||
String()
|
||||
}
|
||||
|
||||
func stashHeaderView(m stashModel) string {
|
||||
func (m stashModel) headerView() string {
|
||||
loading := !m.loadingDone()
|
||||
noMarkdowns := len(m.markdowns) == 0
|
||||
|
||||
|
@ -1035,7 +1056,7 @@ func stashHeaderView(m stashModel) string {
|
|||
return common.Subtle(s) + maybeOffline
|
||||
}
|
||||
|
||||
func stashPopulatedView(m stashModel) string {
|
||||
func (m stashModel) populatedView() string {
|
||||
var b strings.Builder
|
||||
|
||||
mds := m.getVisibleMarkdowns()
|
||||
|
@ -1068,7 +1089,7 @@ func stashPopulatedView(m stashModel) string {
|
|||
return b.String()
|
||||
}
|
||||
|
||||
func stashHelpView(m stashModel) string {
|
||||
func (m stashModel) miniHelpView() string {
|
||||
var (
|
||||
h []string
|
||||
isStashed bool
|
||||
|
@ -1092,7 +1113,7 @@ func stashHelpView(m stashModel) string {
|
|||
h = append(h, "enter/esc: cancel")
|
||||
} else if m.filterState == filtering {
|
||||
h = append(h, "enter: confirm", "esc: cancel", "ctrl+j/ctrl+k, ↑/↓: choose")
|
||||
} else if m.state == stashStateShowNews {
|
||||
} else if m.docState == stashShowNewsDocs {
|
||||
h = append(h, "enter: open", "esc: return", "j/k, ↑/↓: choose", "q: quit")
|
||||
} else {
|
||||
if len(m.markdowns) > 0 {
|
||||
|
@ -1118,12 +1139,12 @@ func stashHelpView(m stashModel) string {
|
|||
h = append(h, "/: filter")
|
||||
h = append(h, "q: quit")
|
||||
}
|
||||
return stashHelpViewBuilder(m.general.width, h...)
|
||||
return stashMiniHelpViewBuilder(m.general.width, h...)
|
||||
}
|
||||
|
||||
// builds the help view from various sections pieces, truncating it if the view
|
||||
// would otherwise wrap to two lines.
|
||||
func stashHelpViewBuilder(windowWidth int, sections ...string) string {
|
||||
func stashMiniHelpViewBuilder(windowWidth int, sections ...string) string {
|
||||
if len(sections) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
|
22
ui/ui.go
22
ui/ui.go
|
@ -164,7 +164,7 @@ type model struct {
|
|||
// method alters the model we also need to send along any commands returned.
|
||||
func (m *model) unloadDocument() []tea.Cmd {
|
||||
m.state = stateShowStash
|
||||
m.stash.state = stashStateReady
|
||||
m.stash.viewState = stashStateReady
|
||||
m.pager.unload()
|
||||
m.pager.showHelp = false
|
||||
|
||||
|
@ -181,8 +181,7 @@ func (m *model) unloadDocument() []tea.Cmd {
|
|||
|
||||
func newModel(cfg Config) tea.Model {
|
||||
if cfg.GlamourStyle == "auto" {
|
||||
dbg := te.HasDarkBackground()
|
||||
if dbg {
|
||||
if te.HasDarkBackground() {
|
||||
cfg.GlamourStyle = "dark"
|
||||
} else {
|
||||
cfg.GlamourStyle = "light"
|
||||
|
@ -190,7 +189,7 @@ func newModel(cfg Config) tea.Model {
|
|||
}
|
||||
|
||||
if len(cfg.DocumentTypes) == 0 {
|
||||
cfg.DocumentTypes.Add(LocalDoc, StashedDoc, NewsDoc)
|
||||
cfg.DocumentTypes.Add(LocalDoc, StashedDoc, ConvertedDoc, NewsDoc)
|
||||
}
|
||||
|
||||
general := general{
|
||||
|
@ -255,17 +254,21 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||
}
|
||||
|
||||
// Send q/esc through in these cases
|
||||
switch m.stash.state {
|
||||
case stashStateShowingError, stashStateShowNews:
|
||||
switch m.stash.viewState {
|
||||
case stashStateReady:
|
||||
|
||||
// Q also quits glow when displaying only newsitems. Esc
|
||||
// still passes through.
|
||||
if m.stash.state == stashStateShowNews && msg.String() == "q" {
|
||||
if msg.String() == "q" {
|
||||
return m, tea.Quit
|
||||
}
|
||||
|
||||
m.stash, cmd = m.stash.update(msg)
|
||||
return m, cmd
|
||||
|
||||
case stashStateShowingError:
|
||||
m.stash, cmd = m.stash.update(msg)
|
||||
return m, cmd
|
||||
}
|
||||
|
||||
// Special cases for the pager
|
||||
|
@ -304,11 +307,6 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||
// Ctrl+C always quits no matter where in the application you are.
|
||||
case "ctrl+c":
|
||||
return m, tea.Quit
|
||||
|
||||
// Repaint
|
||||
case "ctrl+l":
|
||||
// TODO
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// Window size is received when starting up and on every resize
|
||||
|
|
Loading…
Reference in a new issue