1
0
mirror of https://github.com/makew0rld/amfora.git synced 2024-12-04 14:46:29 -05:00

Replace left margin spaces with an empty primitive

This commit introduced flashing issues in the tab row when reformatting,
and even can panic in certain reformatting situations. These bugs have
been logged in the inital comment of the PR.
This commit is contained in:
makeworld 2020-12-29 17:35:29 -05:00
parent 4dff2b0119
commit 90654cd2cf
11 changed files with 60 additions and 50 deletions

View File

@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- More reliable start, no more flash of unindented text, or text that stays unindented (#107) - More reliable start, no more flash of unindented text, or text that stays unindented (#107)
- Pages with ANSI resets don't use the terminal's default text and background colors (#107) - Pages with ANSI resets don't use the terminal's default text and background colors (#107)
- ANSI documents don't leak color into the left margin (#107) - ANSI documents don't leak color into the left margin (#107)
- Rendering very long documents is now ~96% faster, excluding gemtext parsing (#107)
## [1.7.2] - 2020-12-21 ## [1.7.2] - 2020-12-21

View File

@ -4,9 +4,6 @@
- URL for each tab should not be stored as a string - in the current code there's lots of reparsing the URL - URL for each tab should not be stored as a string - in the current code there's lots of reparsing the URL
## Upstream Bugs ## Upstream Bugs
- Text background not reset on ANSI pages
- Filed [issue 25](https://gitlab.com/tslocum/cview/-/issues/25)
- Add some bold back into modal text after this is fixed
- Bookmark keys aren't deleted, just set to `""` - Bookmark keys aren't deleted, just set to `""`
- Waiting on [this viper PR](https://github.com/spf13/viper/pull/519) to be merged - Waiting on [this viper PR](https://github.com/spf13/viper/pull/519) to be merged
- Help table cells aren't dynamically wrapped - Help table cells aren't dynamically wrapped

View File

@ -124,7 +124,7 @@ func Bookmarks(t *tab) {
bkmkPageRaw += fmt.Sprintf("=> %s %s\r\n", keys[i], m[keys[i]]) bkmkPageRaw += fmt.Sprintf("=> %s %s\r\n", keys[i], m[keys[i]])
} }
// Render and display // Render and display
content, links := renderer.RenderGemini(bkmkPageRaw, textWidth(), leftMargin(), false) content, links := renderer.RenderGemini(bkmkPageRaw, textWidth(), false)
page := structs.Page{ page := structs.Page{
Raw: bkmkPageRaw, Raw: bkmkPageRaw,
Content: content, Content: content,

View File

@ -6,6 +6,7 @@ import (
"regexp" "regexp"
"strconv" "strconv"
"strings" "strings"
"sync"
"github.com/gdamore/tcell/v2" "github.com/gdamore/tcell/v2"
"github.com/makeworld-the-better-one/amfora/cache" "github.com/makeworld-the-better-one/amfora/cache"
@ -52,6 +53,9 @@ var layout = cview.NewFlex()
var newTabPage structs.Page var newTabPage structs.Page
var versionPage structs.Page var versionPage structs.Page
// Global mutex for changing the size of the left margin on all tabs.
var reformatMu = sync.Mutex{}
var App = cview.NewApplication() var App = cview.NewApplication()
func Init(version, commit, builtBy string) { func Init(version, commit, builtBy string) {
@ -59,7 +63,7 @@ func Init(version, commit, builtBy string) {
"# Amfora Version Info\n\nAmfora: %s\nCommit: %s\nBuilt by: %s", "# Amfora Version Info\n\nAmfora: %s\nCommit: %s\nBuilt by: %s",
version, commit, builtBy, version, commit, builtBy,
) )
renderVersionContent, versionLinks := renderer.RenderGemini(versionContent, textWidth(), leftMargin(), false) renderVersionContent, versionLinks := renderer.RenderGemini(versionContent, textWidth(), false)
versionPage = structs.Page{ versionPage = structs.Page{
Raw: versionContent, Raw: versionContent,
Content: renderVersionContent, Content: renderVersionContent,
@ -78,10 +82,22 @@ func Init(version, commit, builtBy string) {
// Make sure the current tab content is reformatted when the terminal size changes // Make sure the current tab content is reformatted when the terminal size changes
go func(t *tab) { go func(t *tab) {
reformatMu.Lock()
// Lock the app to prevent screen updates until this is done, because calling
// browser.AddTab updates the display
for i := range tabs {
// Overwrite tabs with a new, differently sized, left margin
browser.AddTab(strconv.Itoa(i), makeTabLabel(strconv.Itoa(i+1)), makeContentLayout(tabs[i].view))
if tabs[i] == t {
// Reformat page ASAP, in the middle of loop
// TODO The per-tab mutext is unecessary if the global one is used
t.reformatMu.Lock() // Only one reformat job per tab t.reformatMu.Lock() // Only one reformat job per tab
defer t.reformatMu.Unlock()
// Use the current tab, but don't affect other tabs if the user switches tabs
reformatPageAndSetView(t, t.page) reformatPageAndSetView(t, t.page)
t.reformatMu.Unlock()
}
}
App.Draw()
reformatMu.Unlock()
}(tabs[curTab]) }(tabs[curTab])
}) })
@ -240,7 +256,7 @@ func Init(version, commit, builtBy string) {
// Render the default new tab content ONCE and store it for later // Render the default new tab content ONCE and store it for later
// This code is repeated in Reload() // This code is repeated in Reload()
newTabContent := getNewTabContent() newTabContent := getNewTabContent()
renderedNewTabContent, newTabLinks := renderer.RenderGemini(newTabContent, textWidth(), leftMargin(), false) renderedNewTabContent, newTabLinks := renderer.RenderGemini(newTabContent, textWidth(), false)
newTabPage = structs.Page{ newTabPage = structs.Page{
Raw: newTabContent, Raw: newTabContent,
Content: renderedNewTabContent, Content: renderedNewTabContent,
@ -436,7 +452,7 @@ func NewTab() {
tabs[curTab].addToHistory("about:newtab") tabs[curTab].addToHistory("about:newtab")
tabs[curTab].history.pos = 0 // Manually set as first page tabs[curTab].history.pos = 0 // Manually set as first page
browser.AddTab(strconv.Itoa(curTab), makeTabLabel(strconv.Itoa(curTab+1)), tabs[curTab].view) browser.AddTab(strconv.Itoa(curTab), makeTabLabel(strconv.Itoa(curTab+1)), makeContentLayout(tabs[curTab].view))
browser.SetCurrentTab(strconv.Itoa(curTab)) browser.SetCurrentTab(strconv.Itoa(curTab))
App.SetFocus(tabs[curTab].view) App.SetFocus(tabs[curTab].view)
@ -520,7 +536,7 @@ func Reload() {
// Re-render new tab, similar to Init() // Re-render new tab, similar to Init()
newTabContent := getNewTabContent() newTabContent := getNewTabContent()
tmpTermW := termW tmpTermW := termW
renderedNewTabContent, newTabLinks := renderer.RenderGemini(newTabContent, textWidth(), leftMargin(), false) renderedNewTabContent, newTabLinks := renderer.RenderGemini(newTabContent, textWidth(), false)
newTabPage = structs.Page{ newTabPage = structs.Page{
Raw: newTabContent, Raw: newTabContent,
Content: renderedNewTabContent, Content: renderedNewTabContent,

View File

@ -59,7 +59,7 @@ func handleFile(u string) (*structs.Page, bool) {
} }
if mimetype == "text/gemini" { if mimetype == "text/gemini" {
rendered, links := renderer.RenderGemini(string(content), textWidth(), leftMargin(), false) rendered, links := renderer.RenderGemini(string(content), textWidth(), false)
page = &structs.Page{ page = &structs.Page{
Mediatype: structs.TextGemini, Mediatype: structs.TextGemini,
URL: u, URL: u,
@ -73,7 +73,7 @@ func handleFile(u string) (*structs.Page, bool) {
Mediatype: structs.TextPlain, Mediatype: structs.TextPlain,
URL: u, URL: u,
Raw: string(content), Raw: string(content),
Content: renderer.RenderPlainText(string(content), leftMargin()), Content: renderer.RenderPlainText(string(content)),
Links: []string{}, Links: []string{},
Width: termW, Width: termW,
} }
@ -107,7 +107,7 @@ func createDirectoryListing(u string) (*structs.Page, bool) {
content += fmt.Sprintf("=> %s%s %s%s\n", f.Name(), separator, f.Name(), separator) content += fmt.Sprintf("=> %s%s %s%s\n", f.Name(), separator, f.Name(), separator)
} }
rendered, links := renderer.RenderGemini(content, textWidth(), leftMargin(), false) rendered, links := renderer.RenderGemini(content, textWidth(), false)
page = &structs.Page{ page = &structs.Page{
Mediatype: structs.TextGemini, Mediatype: structs.TextGemini,
URL: u, URL: u,

View File

@ -373,7 +373,7 @@ func handleURL(t *tab, u string, numRedirects int) (string, bool) {
res.Body = rr.NewRestartReader(res.Body) res.Body = rr.NewRestartReader(res.Body)
if renderer.CanDisplay(res) { if renderer.CanDisplay(res) {
page, err := renderer.MakePage(u, res, textWidth(), leftMargin(), usingProxy) page, err := renderer.MakePage(u, res, textWidth(), usingProxy)
// Rendering may have taken a while, make sure tab is still valid // Rendering may have taken a while, make sure tab is still valid
if !isValidTab(t) { if !isValidTab(t) {
return ret("", false) return ret("", false)

View File

@ -65,11 +65,11 @@ func reformatPage(p *structs.Page) {
strings.HasPrefix(p.URL, "file") { strings.HasPrefix(p.URL, "file") {
proxied = false proxied = false
} }
rendered, _ = renderer.RenderGemini(p.Raw, textWidth(), leftMargin(), proxied) rendered, _ = renderer.RenderGemini(p.Raw, textWidth(), proxied)
case structs.TextPlain: case structs.TextPlain:
rendered = renderer.RenderPlainText(p.Raw, leftMargin()) rendered = renderer.RenderPlainText(p.Raw)
case structs.TextAnsi: case structs.TextAnsi:
rendered = renderer.RenderANSI(p.Raw, leftMargin()) rendered = renderer.RenderANSI(p.Raw)
default: default:
// Rendering this type is not implemented // Rendering this type is not implemented
return return
@ -89,6 +89,8 @@ func reformatPageAndSetView(t *tab, p *structs.Page) {
reformatPage(p) reformatPage(p)
t.view.SetText(p.Content) t.view.SetText(p.Content)
t.applyScroll() // Go back to where you were, roughly t.applyScroll() // Go back to where you were, roughly
App.Draw()
} }
// setPage displays a Page on the passed tab number. // setPage displays a Page on the passed tab number.

View File

@ -149,7 +149,7 @@ func Subscriptions(t *tab, u string) string {
} }
} }
content, links := renderer.RenderGemini(rawPage, textWidth(), leftMargin(), false) content, links := renderer.RenderGemini(rawPage, textWidth(), false)
page := structs.Page{ page := structs.Page{
Raw: rawPage, Raw: rawPage,
Content: content, Content: content,
@ -191,7 +191,7 @@ func ManageSubscriptions(t *tab, u string) {
) )
} }
content, links := renderer.RenderGemini(rawPage, textWidth(), leftMargin(), false) content, links := renderer.RenderGemini(rawPage, textWidth(), false)
page := structs.Page{ page := structs.Page{
Raw: rawPage, Raw: rawPage,
Content: content, Content: content,

View File

@ -13,6 +13,17 @@ import (
// This file contains funcs that are small, self-contained utilities. // This file contains funcs that are small, self-contained utilities.
// makeContentLayout returns a flex that contains the given TextView
// along with the current correct left margin.
func makeContentLayout(tv *cview.TextView) *cview.Flex {
// Create horizontal flex with the left margin as an empty space
flex := cview.NewFlex()
flex.SetDirection(cview.FlexColumn)
flex.AddItem(nil, leftMargin(), 0, false)
flex.AddItem(tv, 0, 1, true)
return flex
}
// makeTabLabel takes a string and adds spacing to it, making it // makeTabLabel takes a string and adds spacing to it, making it
// suitable for display as a tab label. // suitable for display as a tab label.
func makeTabLabel(s string) string { func makeTabLabel(s string) string {

View File

@ -58,7 +58,7 @@ func CanDisplay(res *gemini.Response) bool {
// MakePage creates a formatted, rendered Page from the given network response and params. // MakePage creates a formatted, rendered Page from the given network response and params.
// You must set the Page.Width value yourself. // You must set the Page.Width value yourself.
func MakePage(url string, res *gemini.Response, width, leftMargin int, proxied bool) (*structs.Page, error) { func MakePage(url string, res *gemini.Response, width int, proxied bool) (*structs.Page, error) {
if !CanDisplay(res) { if !CanDisplay(res) {
return nil, ErrCantDisplay return nil, ErrCantDisplay
} }
@ -101,7 +101,7 @@ func MakePage(url string, res *gemini.Response, width, leftMargin int, proxied b
} }
if mediatype == "text/gemini" { if mediatype == "text/gemini" {
rendered, links := RenderGemini(utfText, width, leftMargin, proxied) rendered, links := RenderGemini(utfText, width, proxied)
return &structs.Page{ return &structs.Page{
Mediatype: structs.TextGemini, Mediatype: structs.TextGemini,
RawMediatype: mediatype, RawMediatype: mediatype,
@ -119,7 +119,7 @@ func MakePage(url string, res *gemini.Response, width, leftMargin int, proxied b
RawMediatype: mediatype, RawMediatype: mediatype,
URL: url, URL: url,
Raw: utfText, Raw: utfText,
Content: RenderANSI(utfText, leftMargin), Content: RenderANSI(utfText),
Links: []string{}, Links: []string{},
MadeAt: time.Now(), MadeAt: time.Now(),
}, nil }, nil
@ -131,7 +131,7 @@ func MakePage(url string, res *gemini.Response, width, leftMargin int, proxied b
RawMediatype: mediatype, RawMediatype: mediatype,
URL: url, URL: url,
Raw: utfText, Raw: utfText,
Content: RenderPlainText(utfText, leftMargin), Content: RenderPlainText(utfText),
Links: []string{}, Links: []string{},
MadeAt: time.Now(), MadeAt: time.Now(),
}, nil }, nil

View File

@ -21,7 +21,7 @@ var ansiRegex = regexp.MustCompile(`\x1b\[[0-9;]*m`)
// RenderANSI renders plain text pages containing ANSI codes. // RenderANSI renders plain text pages containing ANSI codes.
// Practically, it is used for the text/x-ansi. // Practically, it is used for the text/x-ansi.
func RenderANSI(s string, leftMargin int) string { func RenderANSI(s string) string {
s = cview.Escape(s) s = cview.Escape(s)
if viper.GetBool("a-general.color") && viper.GetBool("a-general.ansi") { if viper.GetBool("a-general.color") && viper.GetBool("a-general.ansi") {
s = cview.TranslateANSI(s) s = cview.TranslateANSI(s)
@ -33,24 +33,15 @@ func RenderANSI(s string, leftMargin int) string {
} else { } else {
s = ansiRegex.ReplaceAllString(s, "") s = ansiRegex.ReplaceAllString(s, "")
} }
var shifted string return s
lines := strings.Split(s, "\n")
for i := range lines {
shifted += fmt.Sprintf("[-:%s]", config.GetColorString("bg")) + strings.Repeat(" ", leftMargin) + lines[i] + "\n"
}
return shifted
} }
// RenderPlainText should be used to format plain text pages. // RenderPlainText should be used to format plain text pages.
func RenderPlainText(s string, leftMargin int) string { func RenderPlainText(s string) string {
var shifted string // It used to add a left margin, now this is done elsewhere.
lines := strings.Split(cview.Escape(s), "\n") // The function is kept for convenience and in case rendering
for i := range lines { // is needed in the future.
shifted += strings.Repeat(" ", leftMargin) + return s
"[" + config.GetColorString("regular_text") + "]" + lines[i] + "[-]" +
"\n"
}
return shifted
} }
// wrapLine wraps a line to the provided width, and adds the provided prefix and suffix to each wrapped line. // wrapLine wraps a line to the provided width, and adds the provided prefix and suffix to each wrapped line.
@ -283,7 +274,7 @@ func convertRegularGemini(s string, numLinks, width int, proxied bool) (string,
// //
// proxied is whether the request is through the gemini:// scheme. // proxied is whether the request is through the gemini:// scheme.
// If it's not a gemini:// page, set this to true. // If it's not a gemini:// page, set this to true.
func RenderGemini(s string, width, leftMargin int, proxied bool) (string, []string) { func RenderGemini(s string, width int, proxied bool) (string, []string) {
s = cview.Escape(s) s = cview.Escape(s)
lines := strings.Split(s, "\n") lines := strings.Split(s, "\n")
@ -357,13 +348,5 @@ func RenderGemini(s string, width, leftMargin int, proxied bool) (string, []stri
processRegular() processRegular()
} }
if leftMargin > 0 {
renLines := strings.Split(rendered, "\n")
for i := range renLines {
renLines[i] = strings.Repeat(" ", leftMargin) + renLines[i]
}
return strings.Join(renLines, "\n"), links
}
return rendered, links return rendered, links
} }