Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions modules/feedreader/settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ type Settings struct {

feeds []string `help:"An array of RSS and Atom feed URLs"`
feedLimit int `help:"The maximum number of stories to display for each feed"`
maxHeight int `help:"Maximum number of lines to display for each item" optional:"true" default:"3"`
minHeight int `help:"Minimum number of lines to display for each item" optional:"true" default:"2"`
showSource bool `help:"Wether or not to show feed source in front of item titles." values:"true or false" optional:"true" default:"true"`
showPublishDate bool `help:"Wether or not to show publish date in front of item titles." values:"true or false" optional:"true" default:"false"`
dateFormat string `help:"Date format to use for publish dates" values:"Any valid Go time layout which is handled by Time.Format" optional:"true" default:"Jan 02"`
Expand All @@ -44,6 +46,8 @@ func NewSettingsFromYAML(name string, ymlConfig, globalConfig *config.Config) *S
Common: cfg.NewCommonSettingsFromModule(name, defaultTitle, defaultFocusable, ymlConfig, globalConfig),
feeds: utils.ToStrs(ymlConfig.UList("feeds")),
feedLimit: ymlConfig.UInt("feedLimit", -1),
maxHeight: ymlConfig.UInt("maxHeight", 3),
minHeight: ymlConfig.UInt("minHeight", 2),
showSource: ymlConfig.UBool("showSource", true),
showPublishDate: ymlConfig.UBool("showPublishDate", false),
dateFormat: ymlConfig.UString("dateFormat", "Jan 02"),
Expand Down
49 changes: 41 additions & 8 deletions modules/feedreader/widget.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,9 @@ func NewWidget(tviewApp *tview.Application, redrawChan chan bool, pages *tview.P
widget.SetRenderFunction(widget.Render)
widget.initializeKeyboardControls()

widget.View.SetWrap(true)
widget.View.SetWordWrap(true)

return widget
}

Expand Down Expand Up @@ -198,17 +201,47 @@ func (widget *Widget) content() (string, string, bool) {

displayText := widget.getShowText(feedItem, rowColor)

row := fmt.Sprintf(
"[%s]%2d. %s[white]",
rowColor,
idx+1,
displayText,
)
lines := strings.Split(displayText, "\n")

moveTitle := widget.settings.maxHeight == 0 || widget.settings.maxHeight >= 2
if moveTitle && widget.showType != SHOW_LINK && len(lines) > 0 {
space := regexp.MustCompile(`\s+`)

source := ""
publishDate := ""

if widget.settings.showSource && feedItem.sourceTitle != "" {
source = fmt.Sprintf("[%s]%s ", widget.settings.source, feedItem.sourceTitle)
}
if widget.settings.showPublishDate && feedItem.item.Published != "" {
publishDate = fmt.Sprintf("[%s]%s ", widget.settings.publishDate, feedItem.item.PublishedParsed.Format(widget.settings.dateFormat))
}

titleText := space.ReplaceAllString(feedItem.item.Title, " ")
prefixLine := html.UnescapeString(source + publishDate)
titleLine := html.UnescapeString(fmt.Sprintf("[%s]%s", rowColor, titleText))

lines = append([]string{prefixLine, titleLine}, lines[1:]...)
}

if widget.settings.maxHeight > 0 && len(lines) > widget.settings.maxHeight {
lines = lines[:widget.settings.maxHeight]
}
for len(lines) < widget.settings.minHeight {
lines = append(lines, "")
}

if len(lines) > 0 {
lines[0] = fmt.Sprintf("[%s]%2d. %s[white]", rowColor, idx+1, lines[0])
for i := 1; i < len(lines); i++ {
lines[i] = fmt.Sprintf("[%s]%s[white]", rowColor, lines[i])
}
}

str += utils.HighlightableHelper(widget.View, row, idx, len(feedItem.item.Title))
str += utils.HighlightableBlockHelper(widget.View, lines, idx)
}

return title, str, false
return title, str, true
}

func (widget *Widget) getShowText(feedItem *FeedItem, rowColor string) string {
Expand Down
99 changes: 99 additions & 0 deletions modules/feedreader/widget_test.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
package feedreader

import (
"fmt"
"html"
"regexp"
"strings"
"testing"

"github.com/mmcdole/gofeed"
"github.com/rivo/tview"
"github.com/wtfutil/wtf/cfg"
"github.com/wtfutil/wtf/utils"
"gotest.tools/assert"
)

Expand Down Expand Up @@ -82,3 +89,95 @@ func Test_getShowText(t *testing.T) {
})
}
}

func Test_widget_content_block(t *testing.T) {
app := tview.NewApplication()
w := NewWidget(app, make(chan bool), tview.NewPages(), &Settings{Common: &cfg.Common{}, maxHeight: 3})
w.showType = SHOW_CONTENT
w.stories = []*FeedItem{
{
item: &gofeed.Item{
Title: "Cats",
Content: "<pre>one\ntwo\nthree\nfour</pre>",
},
},
}

title, content, wrap := w.content()

rowColor := w.RowColor(0)
display := w.getShowText(w.stories[0], rowColor)
lines := strings.Split(display, "\n")
moveTitle := w.settings.maxHeight == 0 || w.settings.maxHeight >= 2
if moveTitle && w.showType != SHOW_LINK && len(lines) > 0 {
space := regexp.MustCompile(`\s+`)
titleText := space.ReplaceAllString(w.stories[0].item.Title, " ")
prefixLine := ""
titleLine := html.UnescapeString(fmt.Sprintf("[%s]%s", rowColor, titleText))
lines = append([]string{prefixLine, titleLine}, lines[1:]...)
}
if w.settings.maxHeight > 0 && len(lines) > w.settings.maxHeight {
lines = lines[:w.settings.maxHeight]
}
for len(lines) < w.settings.minHeight {
lines = append(lines, "")
}
if len(lines) > 0 {
lines[0] = fmt.Sprintf("[%s]%2d. %s[white]", rowColor, 1, lines[0])
for i := 1; i < len(lines); i++ {
lines[i] = fmt.Sprintf("[%s]%s[white]", rowColor, lines[i])
}
}
expected := utils.HighlightableBlockHelper(w.View, lines, 0)

assert.Equal(t, w.CommonSettings().Title, title)
assert.Equal(t, expected, content)
assert.Equal(t, true, wrap)
}

func Test_widget_content_min_height(t *testing.T) {
app := tview.NewApplication()
w := NewWidget(app, make(chan bool), tview.NewPages(), &Settings{Common: &cfg.Common{}, minHeight: 2})
w.showType = SHOW_TITLE
w.stories = []*FeedItem{
{
item: &gofeed.Item{
Title: "Cats",
},
},
}

_, content, _ := w.content()

expectedLines := 2
actualLines := strings.Count(content, "\n")
assert.Equal(t, expectedLines, actualLines)
}

func Test_widget_title_second_line(t *testing.T) {
app := tview.NewApplication()
w := NewWidget(app, make(chan bool), tview.NewPages(), &Settings{Common: &cfg.Common{}, maxHeight: 3})
w.showType = SHOW_CONTENT
w.stories = []*FeedItem{
{
item: &gofeed.Item{
Title: "Cats",
Content: "<pre>meow</pre>",
},
},
}

_, content, _ := w.content()

// strip highlight regions and padding for easier comparison
cleaned := utils.StripColorTags(strings.ReplaceAll(content, "[\"0\"][\"\"]", ""))
lines := strings.Split(cleaned, "\n")

if len(lines) < 2 {
t.Fatalf("expected at least 2 lines, got %d", len(lines))
}

expectedTitle := "Cats "
assert.Equal(t, expectedTitle, lines[1])
assert.Assert(t, strings.TrimSpace(lines[0]) != "", "first line should not be blank")
}
22 changes: 22 additions & 0 deletions utils/text.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,28 @@ func HighlightableHelper(view *tview.TextView, input string, idx, offset int) st
return fmtStr
}

// HighlightableBlockHelper pads each line of a multiline block with spaces so
// that the block spans the full width of the view. It wraps the entire block in
// a region so that all lines are highlighted together when selected.
func HighlightableBlockHelper(view *tview.TextView, lines []string, idx int) string {
_, _, w, _ := view.GetInnerRect()

fmtStr := fmt.Sprintf(`["%d"][""]`, idx)

for i, line := range lines {
visibleWidth := len([]rune(StripColorTags(line)))
fmtStr += line
fmtStr += RowPadding(visibleWidth, w)
if i < len(lines)-1 {
fmtStr += "\n"
}
}

fmtStr += `[""]` + "\n"

return fmtStr
}

// RowPadding returns a padding for a row to make it the full width of the containing widget.
// Useful for ensuring row highlighting spans the full width (I suspect tcell has a better
// way to do this, but I haven't yet found it)
Expand Down
10 changes: 10 additions & 0 deletions utils/text_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,16 @@ func Test_HighlightableHelper(t *testing.T) {
assert.Equal(t, "[\"0\"][\"\"]cats [\"\"]\n", actual)
}

func Test_HighlightableBlockHelper(t *testing.T) {
view := tview.NewTextView()
lines := []string{"cats", "dogs"}

actual := HighlightableBlockHelper(view, lines, 0)

expected := "[\"0\"][\"\"]cats \ndogs [\"\"]\n"
assert.Equal(t, expected, actual)
}

func Test_RowPadding(t *testing.T) {
assert.Equal(t, "", RowPadding(0, 0))
assert.Equal(t, "", RowPadding(5, 2))
Expand Down
Loading