diff --git a/display/about.go b/display/about.go index f22c480..bdd3005 100644 --- a/display/about.go +++ b/display/about.go @@ -34,14 +34,13 @@ func aboutInit(version, commit, builtBy string) { } func createAboutPage(url string, content string) structs.Page { - renderContent, links, maxPreCols := renderer.RenderGemini(content, textWidth(), false) + renderContent, links := renderer.RenderGemini(content, textWidth(), false) return structs.Page{ - Raw: content, - Content: renderContent, - MaxPreCols: maxPreCols, - Links: links, - URL: url, - TermWidth: -1, // Force reformatting on first display - Mediatype: structs.TextGemini, + Raw: content, + Content: renderContent, + Links: links, + URL: url, + TermWidth: -1, // Force reformatting on first display + Mediatype: structs.TextGemini, } } diff --git a/display/bookmarks.go b/display/bookmarks.go index 6415f74..116bbe3 100644 --- a/display/bookmarks.go +++ b/display/bookmarks.go @@ -132,15 +132,14 @@ func Bookmarks(t *tab) { bkmkPageRaw += fmt.Sprintf("=> %s %s\r\n", keys[i], m[keys[i]]) } // Render and display - content, links, maxPreCols := renderer.RenderGemini(bkmkPageRaw, textWidth(), false) + content, links := renderer.RenderGemini(bkmkPageRaw, textWidth(), false) page := structs.Page{ - Raw: bkmkPageRaw, - Content: content, - MaxPreCols: maxPreCols, - Links: links, - URL: "about:bookmarks", - TermWidth: termW, - Mediatype: structs.TextGemini, + Raw: bkmkPageRaw, + Content: content, + Links: links, + URL: "about:bookmarks", + TermWidth: termW, + Mediatype: structs.TextGemini, } setPage(t, &page) t.applyBottomBar() diff --git a/display/display.go b/display/display.go index 0a658a7..268b5b4 100644 --- a/display/display.go +++ b/display/display.go @@ -249,15 +249,14 @@ func Init(version, commit, builtBy string) { // Render the default new tab content ONCE and store it for later // This code is repeated in Reload() newTabContent := getNewTabContent() - renderedNewTabContent, newTabLinks, maxPreCols := renderer.RenderGemini(newTabContent, textWidth(), false) + renderedNewTabContent, newTabLinks := renderer.RenderGemini(newTabContent, textWidth(), false) newTabPage = structs.Page{ - Raw: newTabContent, - Content: renderedNewTabContent, - MaxPreCols: maxPreCols, - Links: newTabLinks, - URL: "about:newtab", - TermWidth: -1, // Force reformatting on first display - Mediatype: structs.TextGemini, + Raw: newTabContent, + Content: renderedNewTabContent, + Links: newTabLinks, + URL: "about:newtab", + TermWidth: -1, // Force reformatting on first display + Mediatype: structs.TextGemini, } modalInit() @@ -532,15 +531,14 @@ func Reload() { // Re-render new tab, similar to Init() newTabContent := getNewTabContent() tmpTermW := termW - renderedNewTabContent, newTabLinks, maxPreCols := renderer.RenderGemini(newTabContent, textWidth(), false) + renderedNewTabContent, newTabLinks := renderer.RenderGemini(newTabContent, textWidth(), false) newTabPage = structs.Page{ - Raw: newTabContent, - Content: renderedNewTabContent, - MaxPreCols: maxPreCols, - Links: newTabLinks, - URL: "about:newtab", - TermWidth: tmpTermW, - Mediatype: structs.TextGemini, + Raw: newTabContent, + Content: renderedNewTabContent, + Links: newTabLinks, + URL: "about:newtab", + TermWidth: tmpTermW, + Mediatype: structs.TextGemini, } temp := newTabPage // Copy setPage(tabs[curTab], &temp) diff --git a/display/file.go b/display/file.go index 53e74bf..df9cb59 100644 --- a/display/file.go +++ b/display/file.go @@ -59,25 +59,23 @@ func handleFile(u string) (*structs.Page, bool) { } if mimetype == "text/gemini" { - rendered, links, maxPreCols := renderer.RenderGemini(string(content), textWidth(), false) + rendered, links := renderer.RenderGemini(string(content), textWidth(), false) page = &structs.Page{ - Mediatype: structs.TextGemini, - URL: u, - Raw: string(content), - Content: rendered, - MaxPreCols: maxPreCols, - Links: links, - TermWidth: termW, + Mediatype: structs.TextGemini, + URL: u, + Raw: string(content), + Content: rendered, + Links: links, + TermWidth: termW, } } else { page = &structs.Page{ - Mediatype: structs.TextPlain, - URL: u, - Raw: string(content), - Content: renderer.RenderPlainText(string(content)), - MaxPreCols: -1, - Links: []string{}, - TermWidth: termW, + Mediatype: structs.TextPlain, + URL: u, + Raw: string(content), + Content: renderer.RenderPlainText(string(content)), + Links: []string{}, + TermWidth: termW, } } } @@ -109,15 +107,14 @@ func createDirectoryListing(u string) (*structs.Page, bool) { content += fmt.Sprintf("=> %s%s %s%s\n", f.Name(), separator, f.Name(), separator) } - rendered, links, maxPreCols := renderer.RenderGemini(content, textWidth(), false) + rendered, links := renderer.RenderGemini(content, textWidth(), false) page = &structs.Page{ - Mediatype: structs.TextGemini, - URL: u, - Raw: content, - Content: rendered, - MaxPreCols: maxPreCols, - Links: links, - TermWidth: termW, + Mediatype: structs.TextGemini, + URL: u, + Raw: content, + Content: rendered, + Links: links, + TermWidth: termW, } return page, true } diff --git a/display/private.go b/display/private.go index 5f66a75..90cd8bd 100644 --- a/display/private.go +++ b/display/private.go @@ -64,7 +64,7 @@ func reformatPage(p *structs.Page) { strings.HasPrefix(p.URL, "file") { proxied = false } - rendered, _, _ = renderer.RenderGemini(p.Raw, textWidth(), proxied) + rendered, _ = renderer.RenderGemini(p.Raw, textWidth(), proxied) case structs.TextPlain: rendered = renderer.RenderPlainText(p.Raw) case structs.TextAnsi: diff --git a/display/subscriptions.go b/display/subscriptions.go index 50f00c4..b582dd9 100644 --- a/display/subscriptions.go +++ b/display/subscriptions.go @@ -149,15 +149,14 @@ func Subscriptions(t *tab, u string) string { } } - content, links, maxPreCols := renderer.RenderGemini(rawPage, textWidth(), false) + content, links := renderer.RenderGemini(rawPage, textWidth(), false) page := structs.Page{ - Raw: rawPage, - Content: content, - MaxPreCols: maxPreCols, - Links: links, - URL: u, - TermWidth: termW, - Mediatype: structs.TextGemini, + Raw: rawPage, + Content: content, + Links: links, + URL: u, + TermWidth: termW, + Mediatype: structs.TextGemini, } go cache.AddPage(&page) setPage(t, &page) @@ -192,15 +191,14 @@ func ManageSubscriptions(t *tab, u string) { ) } - content, links, maxPreCols := renderer.RenderGemini(rawPage, textWidth(), false) + content, links := renderer.RenderGemini(rawPage, textWidth(), false) page := structs.Page{ - Raw: rawPage, - Content: content, - MaxPreCols: maxPreCols, - Links: links, - URL: "about:manage-subscriptions", - TermWidth: termW, - Mediatype: structs.TextGemini, + Raw: rawPage, + Content: content, + Links: links, + URL: "about:manage-subscriptions", + TermWidth: termW, + Mediatype: structs.TextGemini, } go cache.AddPage(&page) setPage(t, &page) diff --git a/display/tab.go b/display/tab.go index 0bc6abf..733b845 100644 --- a/display/tab.go +++ b/display/tab.go @@ -122,8 +122,7 @@ func makeNewTab() *tab { } }) t.view.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { - // Capture left/right scrolling and change the left margin size accordingly - // See #197 + // Capture left/right scrolling and change the left margin size accordingly, see #197 // Up/down scrolling is saved in this func to keep them in sync, but the keys // are passed and no extra behaviour happens. @@ -131,12 +130,33 @@ func makeNewTab() *tab { mod := event.Modifiers() ru := event.Rune() - oldCol := t.page.Column + width, height := t.view.TextDimensions() + _, _, boxW, boxH := t.view.GetInnerRect() + + // Make boxW accurate by subtracting one if a scrollbar is covering the last + // column of text + if config.ScrollBar == cview.ScrollBarAlways || + (config.ScrollBar == cview.ScrollBarAuto && height > boxH) { + boxW-- + } if (key == tcell.KeyRight && mod == tcell.ModNone) || (key == tcell.KeyRune && mod == tcell.ModNone && ru == 'l') { // Scrolling to the right - // TODO check if already scrolled to the end + + if t.page.Column >= leftMargin() { + // Scrolled right far enought that no left margin is needed + if (t.page.Column-leftMargin())+boxW >= width { + // And scrolled as far as possible to the right + return nil + } + } else { + // Left margin still exists + if boxW-(leftMargin()-t.page.Column) >= width { + // But still scrolled as far as possible + return nil + } + } t.page.Column++ } else if (key == tcell.KeyLeft && mod == tcell.ModNone) || (key == tcell.KeyRune && mod == tcell.ModNone && ru == 'h') { @@ -156,19 +176,15 @@ func makeNewTab() *tab { } else if (key == tcell.KeyDown && mod == tcell.ModNone) || (key == tcell.KeyRune && mod == tcell.ModNone && ru == 'j') { // Scrolling down - // TODO need to check for max vertical scroll before doing this + if t.page.Row < height { + t.page.Row++ + } return event } else { // Some other key, stop processing it return event } - if t.page.MaxPreCols <= termW && t.page.MaxPreCols > -1 { - // No scrolling is actually necessary - t.page.Column = oldCol // Reset - return nil // Ignore keys - } - t.applyHorizontalScroll() App.Draw() return nil diff --git a/go.mod b/go.mod index 4d1f970..09f37bb 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,6 @@ require ( github.com/gdamore/tcell/v2 v2.1.1-0.20210125004847-19e17097d8fe github.com/google/go-cmp v0.5.0 // indirect github.com/makeworld-the-better-one/go-gemini v0.11.0 - github.com/mattn/go-runewidth v0.0.10 github.com/mitchellh/go-homedir v1.1.0 github.com/mitchellh/mapstructure v1.3.1 // indirect github.com/mmcdole/gofeed v1.1.0 @@ -31,3 +30,5 @@ require ( replace github.com/mmcdole/gofeed => github.com/makeworld-the-better-one/gofeed v1.1.1-0.20201123002655-c0c6354134fe replace github.com/schollz/progressbar/v3 => github.com/makeworld-the-better-one/progressbar/v3 v3.3.5-0.20201220005701-b036c4d38568 + +replace gitlab.com/tslocum/cview => gitlab.com/makeworld-the-better-one/cview v1.5.4-0.20210228021109-c74ebf14710b diff --git a/go.sum b/go.sum index 01e8df2..829d789 100644 --- a/go.sum +++ b/go.sum @@ -235,10 +235,10 @@ github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69 github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/urfave/cli v1.22.3/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +gitlab.com/makeworld-the-better-one/cview v1.5.4-0.20210228021109-c74ebf14710b h1:cLZ5jXiGeSdsp+zJPRsGSXMh6oUoHO+pNkQ3A82rTEA= +gitlab.com/makeworld-the-better-one/cview v1.5.4-0.20210228021109-c74ebf14710b/go.mod h1:lCEqP/zDhBihNbyiEn59LgOCk09ejefHaS7kNZ57Nmc= gitlab.com/tslocum/cbind v0.1.4 h1:cbZXPPcieXspk8cShoT6efz7HAT8yMNQcofYWNizis4= gitlab.com/tslocum/cbind v0.1.4/go.mod h1:RvwYE3auSjBNlCmWeGspzn+jdLUVQ8C2QGC+0nP9ChI= -gitlab.com/tslocum/cview v1.5.4-0.20210207045010-d776e728ef6d h1:ck3gAnCoraAI2doDfH2MZsz+DxVpvNwnaXa453jH5aI= -gitlab.com/tslocum/cview v1.5.4-0.20210207045010-d776e728ef6d/go.mod h1:lCEqP/zDhBihNbyiEn59LgOCk09ejefHaS7kNZ57Nmc= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= diff --git a/renderer/page.go b/renderer/page.go index 20ca083..940a322 100644 --- a/renderer/page.go +++ b/renderer/page.go @@ -118,14 +118,13 @@ func MakePage(url string, res *gemini.Response, width int, proxied bool) (*struc } if mediatype == "text/gemini" { - rendered, links, maxPreCols := RenderGemini(utfText, width, proxied) + rendered, links := RenderGemini(utfText, width, proxied) return &structs.Page{ Mediatype: structs.TextGemini, RawMediatype: mediatype, URL: url, Raw: utfText, Content: rendered, - MaxPreCols: maxPreCols, Links: links, MadeAt: time.Now(), }, nil @@ -138,7 +137,6 @@ func MakePage(url string, res *gemini.Response, width int, proxied bool) (*struc URL: url, Raw: utfText, Content: RenderANSI(utfText), - MaxPreCols: -1, Links: []string{}, MadeAt: time.Now(), }, nil @@ -151,7 +149,6 @@ func MakePage(url string, res *gemini.Response, width int, proxied bool) (*struc URL: url, Raw: utfText, Content: RenderPlainText(utfText), - MaxPreCols: -1, Links: []string{}, MadeAt: time.Now(), }, nil diff --git a/renderer/renderer.go b/renderer/renderer.go index 2e31b6d..0376fe8 100644 --- a/renderer/renderer.go +++ b/renderer/renderer.go @@ -12,7 +12,6 @@ import ( "strings" "github.com/makeworld-the-better-one/amfora/config" - "github.com/mattn/go-runewidth" "github.com/spf13/viper" "gitlab.com/tslocum/cview" ) @@ -268,19 +267,18 @@ func convertRegularGemini(s string, numLinks, width int, proxied bool) (string, } // RenderGemini converts text/gemini into a cview displayable format. -// It also returns a slice of link URLs, and the Page.MaxPreCols value. +// It also returns a slice of link URLs. // // width is the number of columns to wrap to. // leftMargin is the number of blank spaces to prepend to each line. // // proxied is whether the request is through the gemini:// scheme. // If it's not a gemini:// page, set this to true. -func RenderGemini(s string, width int, proxied bool) (string, []string, int) { +func RenderGemini(s string, width int, proxied bool) (string, []string) { s = cview.Escape(s) lines := strings.Split(s, "\n") links := make([]string, 0) - maxPreCols := 0 // Process and wrap non preformatted lines rendered := "" // Final result @@ -289,10 +287,6 @@ func RenderGemini(s string, width int, proxied bool) (string, []string, int) { // processPre is for rendering preformatted blocks processPre := func() { - lineWidth := runewidth.StringWidth(buf) - if lineWidth > maxPreCols { - maxPreCols = lineWidth - } // Support ANSI color codes in preformatted blocks - see #59 if viper.GetBool("a-general.color") && viper.GetBool("a-general.ansi") { @@ -354,5 +348,5 @@ func RenderGemini(s string, width int, proxied bool) (string, []string, int) { processRegular() } - return rendered, links, maxPreCols + return rendered, links } diff --git a/structs/structs.go b/structs/structs.go index d5f19e6..c2d36d9 100644 --- a/structs/structs.go +++ b/structs/structs.go @@ -25,7 +25,6 @@ type Page struct { RawMediatype string // The actual mediatype sent by the server Raw string // The raw response, as received over the network Content string // The processed content, NOT raw. Uses cview color tags. It will also have a left margin. - MaxPreCols int // The amount of the terminal columns the longest preformatted line in Raw takes up, used for #197. -1 means infinite length lines, AKA always allow scrolling. Links []string // URLs, for each region in the content. Row int // Vertical scroll position Column int // Horizontal scroll position - does not map exactly to a cview.TextView because it includes left margin size changes, see #197