From 9198572f342822582bc9eb4078597124bda98fde Mon Sep 17 00:00:00 2001 From: makeworld <25111343+makeworld-the-better-one@users.noreply.github.com> Date: Wed, 17 Feb 2021 14:17:13 -0500 Subject: [PATCH] cview update (#107) Co-authored-by: makeworld <25111343+makeworld-the-better-one@users.noreply.github.com> Co-authored-by: Stephen Robinson Co-authored-by: Trevor Slocum Co-authored-by: Stephen Robinson --- .github/workflows/golangci-lint.yml | 2 +- CHANGELOG.md | 10 ++ NOTES.md | 16 +- README.md | 2 +- amfora.go | 9 +- config/config.go | 17 +- config/default.go | 5 + config/keybindings.go | 2 +- config/theme.go | 7 +- default-config.toml | 5 + display/about.go | 2 +- display/bookmarks.go | 72 ++++---- display/display.go | 136 +++++++-------- display/download.go | 119 +++++++------ display/file.go | 6 +- display/handlers.go | 12 +- display/help.go | 131 ++++++-------- display/modals.go | 255 +++++++++++++++------------- display/private.go | 57 ++----- display/subscriptions.go | 36 ++-- display/tab.go | 51 +++--- display/util.go | 45 ++++- go.mod | 10 +- go.sum | 45 ++--- renderer/page.go | 8 +- renderer/renderer.go | 110 ++++++------ subscriptions/subscriptions.go | 4 +- 27 files changed, 625 insertions(+), 549 deletions(-) diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml index 617302d..cad7430 100644 --- a/.github/workflows/golangci-lint.yml +++ b/.github/workflows/golangci-lint.yml @@ -24,6 +24,6 @@ jobs: uses: golangci/golangci-lint-action@v2 with: # Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version. - version: v1.31 + version: v1.35 # Optional: show only new issues if it's a pull request. The default value is `false`. only-new-issues: true diff --git a/CHANGELOG.md b/CHANGELOG.md index 387d42f..3a1e867 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,8 +8,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - **Media type handlers** - open non-text files in another application (#121, #134) - Ability to set custom keybindings in config (#135) +- Added scrollbar, by default only appears on pages that go off-screen (#89, #107) - More internal about pages, see `about:about` (#160, 187) +### Changed +- Update cview to `d776e728ef6d2a9990a5cd86a70b31f0678613e2` for large performance and feature updates (#107) +- Update to tcell v2 (dependency of cview) + ### Fixed - Don't use cache when URL is typed in bottom bar (#159) - Fix downloading of pages that are too large or timed out @@ -18,6 +23,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Handle empty META string (#176) - Whitespace around the URL entered in the bottom bar is stripped (#184) - Don't break visiting IPv6 hosts when port 1965 is specified (#195) +- 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) +- ANSI documents don't leak color into the left margin (#107) +- Rendering very long documents is now ~96% faster, excluding gemtext parsing (#26, #107) +- Due to that same change, less memory is used per-page (#26, #107) ## [1.7.2] - 2020-12-21 diff --git a/NOTES.md b/NOTES.md index 783f1a9..7eaf9a0 100644 --- a/NOTES.md +++ b/NOTES.md @@ -4,17 +4,9 @@ - URL for each tab should not be stored as a string - in the current code there's lots of reparsing the URL ## Upstream Bugs -- Wrapping messes up on brackets - - Filed [issue 23](https://gitlab.com/tslocum/cview/-/issues/23) -- Wrapping panics on strings with brackets and Asian characters - - Filed cview [issue 27](https://gitlab.com/tslocum/cview/-/issues/27) - - The panicking was reported and fixed in Amfora [issue 20](https://github.com/makeworld-the-better-one/amfora/issues/20), but the lines are now just not wrapped -- Text background not reset on ANSI pages - - Filed [issue 25](https://gitlab.com/tslocum/cview/-/issues/25) -- Modal styling messed up when wrapped - example occurence is the error modal for a long unsupported scheme URL - - Filed [issue 26](https://gitlab.com/tslocum/cview/-/issues/26) - - Add some bold back into modal text after this is fixed - Bookmark keys aren't deleted, just set to `""` - Waiting on [this viper PR](https://github.com/spf13/viper/pull/519) to be merged -- Help table cells aren't dynamically wrapped - - Filed [issue 29](https://gitlab.com/tslocum/cview/-/issues/29) +- [cview.Styles not being used](https://gitlab.com/tslocum/cview/-/issues/47) - issue is circumvented in Amfora +- [ANSI conversion is messed up](https://gitlab.com/tslocum/cview/-/issues/48) +- [WordWrap is broken in some cases](https://gitlab.com/tslocum/cview/-/issues/27#note_475438483) - close #156 if this is fixed +- [Prevent panic when reformatting](https://gitlab.com/tslocum/cview/-/issues/50) - can't reliably reproduce or debug diff --git a/README.md b/README.md index 2338c83..6937001 100644 --- a/README.md +++ b/README.md @@ -178,7 +178,7 @@ You can also check out [all the issues with the bug label](https://github.com/ma ## Libraries Amfora ❤️ open source! -- [cview](https://gitlab.com/tslocum/cview/) for the TUI +- My [cview fork](https://gitlab.com/makeworld-the-better-one/cview/) for the TUI - pull request [here](https://gitlab.com/tslocum/cview/-/merge_requests/12) - It's a fork of [tview](https://github.com/rivo/tview) with PRs merged and active support - It uses [tcell](https://github.com/gdamore/tcell) for low level terminal operations - [Viper](https://github.com/spf13/viper) for configuration and TOFU storing diff --git a/amfora.go b/amfora.go index 09bbbc5..8c4f903 100644 --- a/amfora.go +++ b/amfora.go @@ -52,14 +52,19 @@ func main() { client.Init() + // Initialize lower-level cview app + if err = display.App.Init(); err != nil { + panic(err) + } + + // Initialize Amfora's settings display.Init(version, commit, builtBy) display.NewTab() - display.NewTab() // Open extra tab and close it to fully initialize the app and wrapping - display.CloseTab() if len(os.Args[1:]) > 0 { display.URL(os.Args[1]) } + // Start if err = display.App.Run(); err != nil { panic(err) } diff --git a/config/config.go b/config/config.go index 6e5aae4..37d9e6e 100644 --- a/config/config.go +++ b/config/config.go @@ -11,7 +11,7 @@ import ( "runtime" "strings" - "github.com/gdamore/tcell" + "github.com/gdamore/tcell/v2" "github.com/makeworld-the-better-one/amfora/cache" homedir "github.com/mitchellh/go-homedir" "github.com/rkoesters/xdg/basedir" @@ -55,6 +55,10 @@ type MediaHandler struct { var MediaHandlers = make(map[string]MediaHandler) +// Controlled by "a-general.scrollbar" in config +// Defaults to ScrollBarAuto on an invalid value +var ScrollBar cview.ScrollBarVisibility + func Init() error { // *** Set paths *** @@ -204,6 +208,7 @@ func Init() error { viper.SetDefault("a-general.page_max_size", 2097152) viper.SetDefault("a-general.page_max_time", 10) viper.SetDefault("a-general.emoji_favicons", false) + viper.SetDefault("a-general.scrollbar", "auto") viper.SetDefault("keybindings.bind_reload", []string{"R", "Ctrl-R"}) viper.SetDefault("keybindings.bind_home", "Backspace") viper.SetDefault("keybindings.bind_bookmarks", "Ctrl-B") @@ -392,5 +397,15 @@ func Init() error { } } + // Parse scrollbar options + switch viper.GetString("a-general.scrollbar") { + case "never": + ScrollBar = cview.ScrollBarNever + case "always": + ScrollBar = cview.ScrollBarAlways + default: + ScrollBar = cview.ScrollBarAuto + } + return nil } diff --git a/config/default.go b/config/default.go index 1dc3729..3ba8620 100644 --- a/config/default.go +++ b/config/default.go @@ -73,6 +73,10 @@ page_max_time = 10 # Whether to replace tab numbers with emoji favicons, which are cached. emoji_favicons = false +# When a scrollbar appears. "never", "auto", and "always" are the only valid values. +# "auto" means the scrollbar only appears when the page is longer than the window. +scrollbar = "auto" + [auth] # Authentication settings @@ -301,6 +305,7 @@ entries_per_page = 20 # bottombar_label: The color of the prompt that appears when you press space # bottombar_text: The color of the text you type # bottombar_bg +# scrollbar: The scrollbar that appears on the right for long pages # hdg_1 # hdg_2 diff --git a/config/keybindings.go b/config/keybindings.go index 8afd2e3..c01df35 100644 --- a/config/keybindings.go +++ b/config/keybindings.go @@ -3,7 +3,7 @@ package config import ( "strings" - "github.com/gdamore/tcell" + "github.com/gdamore/tcell/v2" "github.com/spf13/viper" ) diff --git a/config/theme.go b/config/theme.go index 9bb05d7..f17e4cb 100644 --- a/config/theme.go +++ b/config/theme.go @@ -4,7 +4,7 @@ import ( "fmt" "sync" - "github.com/gdamore/tcell" + "github.com/gdamore/tcell/v2" ) // Functions to allow themeing configuration. @@ -21,6 +21,7 @@ var theme = map[string]tcell.Color{ "bottombar_label": tcell.Color30, "bottombar_text": tcell.ColorBlack, "bottombar_bg": tcell.ColorWhite, + "scrollbar": tcell.ColorWhite, // Modals "btn_bg": tcell.ColorNavy, // All modal buttons @@ -74,7 +75,7 @@ func SetColor(key string, color tcell.Color) { func GetColor(key string) tcell.Color { themeMu.RLock() defer themeMu.RUnlock() - return theme[key] + return theme[key].TrueColor() } // GetColorString returns a string that can be used in a cview color tag, @@ -83,5 +84,5 @@ func GetColor(key string) tcell.Color { func GetColorString(key string) string { themeMu.RLock() defer themeMu.RUnlock() - return fmt.Sprintf("#%06x", theme[key].Hex()) + return fmt.Sprintf("#%06x", theme[key].TrueColor().Hex()) } diff --git a/default-config.toml b/default-config.toml index c45b587..fb65076 100644 --- a/default-config.toml +++ b/default-config.toml @@ -70,6 +70,10 @@ page_max_time = 10 # Whether to replace tab numbers with emoji favicons, which are cached. emoji_favicons = false +# When a scrollbar appears. "never", "auto", and "always" are the only valid values. +# "auto" means the scrollbar only appears when the page is longer than the window. +scrollbar = "auto" + [auth] # Authentication settings @@ -298,6 +302,7 @@ entries_per_page = 20 # bottombar_label: The color of the prompt that appears when you press space # bottombar_text: The color of the text you type # bottombar_bg +# scrollbar: The scrollbar that appears on the right for long pages # hdg_1 # hdg_2 diff --git a/display/about.go b/display/about.go index db34eac..4637751 100644 --- a/display/about.go +++ b/display/about.go @@ -34,7 +34,7 @@ func aboutInit(version, commit, builtBy string) { } func createAboutPage(url string, content string) structs.Page { - renderContent, links := renderer.RenderGemini(content, textWidth(), leftMargin(), false) + renderContent, links := renderer.RenderGemini(content, textWidth(), false) return structs.Page{ Raw: content, Content: renderContent, diff --git a/display/bookmarks.go b/display/bookmarks.go index f26fbb3..25ac460 100644 --- a/display/bookmarks.go +++ b/display/bookmarks.go @@ -2,9 +2,8 @@ package display import ( "fmt" - "strconv" - "github.com/gdamore/tcell" + "github.com/gdamore/tcell/v2" "github.com/makeworld-the-better-one/amfora/bookmarks" "github.com/makeworld-the-better-one/amfora/config" "github.com/makeworld-the-better-one/amfora/renderer" @@ -21,37 +20,44 @@ var bkmkCh = make(chan int) // 1, 0, -1 for add/update, cancel, and remove var bkmkModalText string // The current text of the input field in the modal func bkmkInit() { + panels.AddPanel("bkmk", bkmkModal, false, false) + + m := bkmkModal if viper.GetBool("a-general.color") { - bkmkModal.SetBackgroundColor(config.GetColor("bkmk_modal_bg")). - SetButtonBackgroundColor(config.GetColor("btn_bg")). - SetButtonTextColor(config.GetColor("btn_text")). - SetTextColor(config.GetColor("bkmk_modal_text")) - bkmkModal.GetForm(). - SetLabelColor(config.GetColor("bkmk_modal_label")). - SetFieldBackgroundColor(config.GetColor("bkmk_modal_field_bg")). - SetFieldTextColor(config.GetColor("bkmk_modal_field_text")) - bkmkModal.GetFrame(). - SetBorderColor(config.GetColor("bkmk_modal_text")). - SetTitleColor(config.GetColor("bkmk_modal_text")) + m.SetBackgroundColor(config.GetColor("bkmk_modal_bg")) + m.SetButtonBackgroundColor(config.GetColor("btn_bg")) + m.SetButtonTextColor(config.GetColor("btn_text")) + m.SetTextColor(config.GetColor("bkmk_modal_text")) + form := m.GetForm() + form.SetLabelColor(config.GetColor("bkmk_modal_label")) + form.SetFieldBackgroundColor(config.GetColor("bkmk_modal_field_bg")) + form.SetFieldTextColor(config.GetColor("bkmk_modal_field_text")) + form.SetButtonBackgroundColorFocused(config.GetColor("btn_text")) + form.SetButtonTextColorFocused(config.GetColor("btn_bg")) + frame := m.GetFrame() + frame.SetBorderColor(config.GetColor("bkmk_modal_text")) + frame.SetTitleColor(config.GetColor("bkmk_modal_text")) } else { - bkmkModal.SetBackgroundColor(tcell.ColorBlack). - SetButtonBackgroundColor(tcell.ColorWhite). - SetButtonTextColor(tcell.ColorBlack). - SetTextColor(tcell.ColorWhite) - bkmkModal.GetForm(). - SetLabelColor(tcell.ColorWhite). - SetFieldBackgroundColor(tcell.ColorWhite). - SetFieldTextColor(tcell.ColorBlack) - bkmkModal.GetFrame(). - SetBorderColor(tcell.ColorWhite). - SetTitleColor(tcell.ColorWhite) + m.SetBackgroundColor(tcell.ColorBlack) + m.SetButtonBackgroundColor(tcell.ColorWhite) + m.SetButtonTextColor(tcell.ColorBlack) + m.SetTextColor(tcell.ColorWhite) + form := m.GetForm() + form.SetLabelColor(tcell.ColorWhite) + form.SetFieldBackgroundColor(tcell.ColorWhite) + form.SetFieldTextColor(tcell.ColorBlack) + form.SetButtonBackgroundColorFocused(tcell.ColorBlack) + form.SetButtonTextColorFocused(tcell.ColorWhite) + frame := m.GetFrame() + frame.SetBorderColor(tcell.ColorWhite) + frame.SetTitleColor(tcell.ColorWhite) } - bkmkModal.SetBorder(true) - bkmkModal.GetFrame(). - SetTitleAlign(cview.AlignCenter). - SetTitle(" Add Bookmark ") - bkmkModal.SetDoneFunc(func(buttonIndex int, buttonLabel string) { + m.SetBorder(true) + frame := m.GetFrame() + frame.SetTitleAlign(cview.AlignCenter) + frame.SetTitle(" Add Bookmark ") + m.SetDoneFunc(func(buttonIndex int, buttonLabel string) { switch buttonLabel { case "Add": bkmkCh <- 1 @@ -97,13 +103,13 @@ func openBkmkModal(name string, exists bool, favicon string) (string, int) { bkmkModalText = text }) - tabPages.ShowPage("bkmk") - tabPages.SendToFront("bkmk") + panels.ShowPanel("bkmk") + panels.SendToFront("bkmk") App.SetFocus(bkmkModal) App.Draw() action := <-bkmkCh - tabPages.SwitchToPage(strconv.Itoa(curTab)) + panels.HidePanel("bkmk") App.SetFocus(tabs[curTab].view) App.Draw() @@ -120,7 +126,7 @@ func Bookmarks(t *tab) { bkmkPageRaw += fmt.Sprintf("=> %s %s\r\n", keys[i], m[keys[i]]) } // Render and display - content, links := renderer.RenderGemini(bkmkPageRaw, textWidth(), leftMargin(), false) + content, links := renderer.RenderGemini(bkmkPageRaw, textWidth(), false) page := structs.Page{ Raw: bkmkPageRaw, Content: content, diff --git a/display/display.go b/display/display.go index 8e0bd8d..cca444b 100644 --- a/display/display.go +++ b/display/display.go @@ -6,8 +6,9 @@ import ( "regexp" "strconv" "strings" + "sync" - "github.com/gdamore/tcell" + "github.com/gdamore/tcell/v2" "github.com/makeworld-the-better-one/amfora/cache" "github.com/makeworld-the-better-one/amfora/config" "github.com/makeworld-the-better-one/amfora/renderer" @@ -39,76 +40,92 @@ var hasSpaceisURL = regexp.MustCompile(`[^ ]+\.[^ ].*/.`) // The only pages that don't confine to this scheme are those named after modals, // which are used to draw modals on top the current tab. // Ex: "info", "error", "input", "yesno" -var tabPages = cview.NewPages() +var panels = cview.NewPanels() -// The tabs at the top with titles -var tabRow = cview.NewTextView(). - SetDynamicColors(true). - SetRegions(true). - SetScrollable(true). - SetWrap(false). - SetHighlightedFunc(func(added, removed, remaining []string) { - // There will always only be one string in added - never multiple highlights - // Remaining should always be empty - i, _ := strconv.Atoi(added[0]) - tabPages.SwitchToPage(strconv.Itoa(i)) // Tab names are just numbers, zero-indexed - }) +// Tabbed viewer for primitives +// Panels are named as strings of tab numbers - so the textview for the first tab +// is held in the page named "0". +var browser = cview.NewTabbedPanels() // Root layout -var layout = cview.NewFlex(). - SetDirection(cview.FlexRow) +var layout = cview.NewFlex() var newTabPage structs.Page -var App = cview.NewApplication(). - EnableMouse(false). - SetRoot(layout, true). - SetAfterResizeFunc(func(width int, height int) { +// Global mutex for changing the size of the left margin on all tabs. +var reformatMu = sync.Mutex{} + +var App = cview.NewApplication() + +func Init(version, commit, builtBy string) { + aboutInit(version, commit, builtBy) + + App.EnableMouse(false) + App.SetRoot(layout, true) + App.SetAfterResizeFunc(func(width int, height int) { // Store for calculations termW = width termH = height // Make sure the current tab content is reformatted when the terminal size changes go func(t *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) + reformatMu.Lock() // Only allow one reformat job at a time + for i := range tabs { + // Overwrite all 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 + reformatPageAndSetView(t, t.page) + } + } + App.Draw() + reformatMu.Unlock() }(tabs[curTab]) }) -func Init(version, commit, builtBy string) { - aboutInit(version, commit, builtBy) - - tabRow.SetChangedFunc(func() { - App.Draw() - }) + panels.AddPanel("browser", browser, true, true) helpInit() - layout. - AddItem(tabRow, 1, 1, false). - AddItem(nil, 1, 1, false). // One line of empty space above the page - AddItem(tabPages, 0, 1, true). - AddItem(nil, 1, 1, false). // One line of empty space before bottomBar - AddItem(bottomBar, 1, 1, false) + layout.SetDirection(cview.FlexRow) + layout.AddItem(panels, 0, 1, true) + layout.AddItem(bottomBar, 1, 1, false) if viper.GetBool("a-general.color") { layout.SetBackgroundColor(config.GetColor("bg")) - tabRow.SetBackgroundColor(config.GetColor("bg")) bottomBar.SetBackgroundColor(config.GetColor("bottombar_bg")) - bottomBar. - SetLabelColor(config.GetColor("bottombar_label")). - SetFieldBackgroundColor(config.GetColor("bottombar_bg")). - SetFieldTextColor(config.GetColor("bottombar_text")) + bottomBar.SetLabelColor(config.GetColor("bottombar_label")) + bottomBar.SetFieldBackgroundColor(config.GetColor("bottombar_bg")) + bottomBar.SetFieldTextColor(config.GetColor("bottombar_text")) + + browser.SetTabBackgroundColor(config.GetColor("bg")) + browser.SetTabBackgroundColorFocused(config.GetColor("tab_num")) + browser.SetTabTextColor(config.GetColor("tab_num")) + browser.SetTabTextColorFocused(config.GetColor("bg")) + browser.SetTabSwitcherDivider( + "", + fmt.Sprintf("[%s:%s]|[-]", config.GetColorString("tab_divider"), config.GetColorString("bg")), + fmt.Sprintf("[%s:%s]|[-]", config.GetColorString("tab_divider"), config.GetColorString("bg")), + ) + browser.Switcher.SetBackgroundColor(config.GetColor("bg")) } else { bottomBar.SetBackgroundColor(tcell.ColorWhite) - bottomBar. - SetLabelColor(tcell.ColorBlack). - SetFieldBackgroundColor(tcell.ColorWhite). - SetFieldTextColor(tcell.ColorBlack) + bottomBar.SetLabelColor(tcell.ColorBlack) + bottomBar.SetFieldBackgroundColor(tcell.ColorWhite) + bottomBar.SetFieldTextColor(tcell.ColorBlack) + + browser.SetTabBackgroundColor(tcell.ColorBlack) + browser.SetTabBackgroundColorFocused(tcell.ColorWhite) + browser.SetTabTextColor(tcell.ColorWhite) + browser.SetTabTextColorFocused(tcell.ColorBlack) + browser.SetTabSwitcherDivider( + "", + "[#ffffff:#000000]|[-]", + "[#ffffff:#000000]|[-]", + ) } + bottomBar.SetDoneFunc(func(key tcell.Key) { tab := curTab @@ -230,7 +247,7 @@ 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 := renderer.RenderGemini(newTabContent, textWidth(), leftMargin(), false) + renderedNewTabContent, newTabLinks := renderer.RenderGemini(newTabContent, textWidth(), false) newTabPage = structs.Page{ Raw: newTabContent, Content: renderedNewTabContent, @@ -426,23 +443,10 @@ func NewTab() { tabs[curTab].addToHistory("about:newtab") tabs[curTab].history.pos = 0 // Manually set as first page - tabPages.AddAndSwitchToPage(strconv.Itoa(curTab), tabs[curTab].view, true) + browser.AddTab(strconv.Itoa(curTab), makeTabLabel(strconv.Itoa(curTab+1)), makeContentLayout(tabs[curTab].view)) + browser.SetCurrentTab(strconv.Itoa(curTab)) App.SetFocus(tabs[curTab].view) - // Add tab number to the actual place where tabs are show on the screen - // Tab regions are 0-indexed but text displayed on the screen starts at 1 - if viper.GetBool("a-general.color") { - fmt.Fprintf(tabRow, `["%d"][%s] %d [%s][""]|`, - curTab, - config.GetColorString("tab_num"), - curTab+1, - config.GetColorString("tab_divider"), - ) - } else { - fmt.Fprintf(tabRow, `["%d"] %d [""]|`, curTab, curTab+1) - } - tabRow.Highlight(strconv.Itoa(curTab)).ScrollToHighlight() - bottomBar.SetLabel("") bottomBar.SetText("") tabs[curTab].saveBottomBar() @@ -469,7 +473,7 @@ func CloseTab() { } tabs = tabs[:len(tabs)-1] - tabPages.RemovePage(strconv.Itoa(curTab)) + browser.RemoveTab(strconv.Itoa(curTab)) if curTab <= 0 { curTab = NumTabs() - 1 @@ -477,8 +481,7 @@ func CloseTab() { curTab-- } - tabPages.SwitchToPage(strconv.Itoa(curTab)) // Go to previous page - rewriteTabRow() + browser.SetCurrentTab(strconv.Itoa(curTab)) // Go to previous page // Restore previous tab's state tabs[curTab].applyAll() @@ -510,8 +513,7 @@ func SwitchTab(tab int) { // Display tab reformatPageAndSetView(tabs[curTab], tabs[curTab].page) - tabPages.SwitchToPage(strconv.Itoa(curTab)) - tabRow.Highlight(strconv.Itoa(curTab)).ScrollToHighlight() + browser.SetCurrentTab(strconv.Itoa(curTab)) tabs[curTab].applyAll() App.SetFocus(tabs[curTab].view) @@ -525,7 +527,7 @@ func Reload() { // Re-render new tab, similar to Init() newTabContent := getNewTabContent() tmpTermW := termW - renderedNewTabContent, newTabLinks := renderer.RenderGemini(newTabContent, textWidth(), leftMargin(), false) + renderedNewTabContent, newTabLinks := renderer.RenderGemini(newTabContent, textWidth(), false) newTabPage = structs.Page{ Raw: newTabContent, Content: renderedNewTabContent, diff --git a/display/download.go b/display/download.go index 6cc6c84..5b8ff3c 100644 --- a/display/download.go +++ b/display/download.go @@ -14,7 +14,7 @@ import ( "strings" "time" - "github.com/gdamore/tcell" + "github.com/gdamore/tcell/v2" "github.com/makeworld-the-better-one/amfora/config" "github.com/makeworld-the-better-one/amfora/structs" "github.com/makeworld-the-better-one/amfora/sysopen" @@ -24,9 +24,8 @@ import ( "gitlab.com/tslocum/cview" ) -// For choosing between download and opening - copy of YesNo basically -var dlChoiceModal = cview.NewModal(). - AddButtons([]string{"Open", "Download", "Cancel"}) +// For choosing between download and the portal - copy of YesNo basically +var dlChoiceModal = cview.NewModal() // Channel to indicate what choice they made using the button text var dlChoiceCh = make(chan string) @@ -34,52 +33,70 @@ var dlChoiceCh = make(chan string) var dlModal = cview.NewModal() func dlInit() { + panels.AddPanel("dl", dlModal, false, false) + panels.AddPanel("dlChoice", dlChoiceModal, false, false) + + dlm := dlModal + chm := dlChoiceModal if viper.GetBool("a-general.color") { - dlChoiceModal.SetButtonBackgroundColor(config.GetColor("btn_bg")). - SetButtonTextColor(config.GetColor("btn_text")). - SetBackgroundColor(config.GetColor("dl_choice_modal_bg")). - SetTextColor(config.GetColor("dl_choice_modal_text")) - dlChoiceModal.GetFrame(). - SetBorderColor(config.GetColor("dl_choice_modal_text")). - SetTitleColor(config.GetColor("dl_choice_modal_text")) + chm.SetButtonBackgroundColor(config.GetColor("btn_bg")) + chm.SetButtonTextColor(config.GetColor("btn_text")) + chm.SetBackgroundColor(config.GetColor("dl_choice_modal_bg")) + chm.SetTextColor(config.GetColor("dl_choice_modal_text")) + form := chm.GetForm() + form.SetButtonBackgroundColorFocused(config.GetColor("btn_text")) + form.SetButtonTextColorFocused(config.GetColor("btn_bg")) + frame := chm.GetFrame() + frame.SetBorderColor(config.GetColor("dl_choice_modal_text")) + frame.SetTitleColor(config.GetColor("dl_choice_modal_text")) - dlModal.SetButtonBackgroundColor(config.GetColor("btn_bg")). - SetButtonTextColor(config.GetColor("btn_text")). - SetBackgroundColor(config.GetColor("dl_modal_bg")). - SetTextColor(config.GetColor("dl_modal_text")) - dlModal.GetFrame(). - SetBorderColor(config.GetColor("dl_modal_text")). - SetTitleColor(config.GetColor("dl_modal_text")) + dlm.SetButtonBackgroundColor(config.GetColor("btn_bg")) + dlm.SetButtonTextColor(config.GetColor("btn_text")) + dlm.SetBackgroundColor(config.GetColor("dl_modal_bg")) + dlm.SetTextColor(config.GetColor("dl_modal_text")) + form = dlm.GetForm() + form.SetButtonBackgroundColorFocused(config.GetColor("btn_text")) + form.SetButtonTextColorFocused(config.GetColor("btn_bg")) + frame = dlm.GetFrame() + frame.SetBorderColor(config.GetColor("dl_modal_text")) + frame.SetTitleColor(config.GetColor("dl_modal_text")) } else { - dlChoiceModal.SetButtonBackgroundColor(tcell.ColorWhite). - SetButtonTextColor(tcell.ColorBlack). - SetBackgroundColor(tcell.ColorBlack). - SetTextColor(tcell.ColorWhite) - dlChoiceModal.SetBorderColor(tcell.ColorWhite) - dlChoiceModal.GetFrame().SetTitleColor(tcell.ColorWhite) + chm.SetButtonBackgroundColor(tcell.ColorWhite) + chm.SetButtonTextColor(tcell.ColorBlack) + chm.SetBackgroundColor(tcell.ColorBlack) + chm.SetTextColor(tcell.ColorWhite) + chm.SetBorderColor(tcell.ColorWhite) + chm.GetFrame().SetTitleColor(tcell.ColorWhite) + form := chm.GetForm() + form.SetButtonBackgroundColorFocused(tcell.ColorBlack) + form.SetButtonTextColorFocused(tcell.ColorWhite) - dlModal.SetButtonBackgroundColor(tcell.ColorWhite). - SetButtonTextColor(tcell.ColorBlack). - SetBackgroundColor(tcell.ColorBlack). - SetTextColor(tcell.ColorWhite) - dlModal.GetFrame(). - SetBorderColor(tcell.ColorWhite). - SetTitleColor(tcell.ColorWhite) + dlm.SetButtonBackgroundColor(tcell.ColorWhite) + dlm.SetButtonTextColor(tcell.ColorBlack) + dlm.SetBackgroundColor(tcell.ColorBlack) + dlm.SetTextColor(tcell.ColorWhite) + form = dlm.GetForm() + form.SetButtonBackgroundColorFocused(tcell.ColorBlack) + form.SetButtonTextColorFocused(tcell.ColorWhite) + frame := dlm.GetFrame() + frame.SetBorderColor(tcell.ColorWhite) + frame.SetTitleColor(tcell.ColorWhite) } - dlChoiceModal.SetBorder(true) - dlChoiceModal.GetFrame().SetTitleAlign(cview.AlignCenter) - dlChoiceModal.SetDoneFunc(func(buttonIndex int, buttonLabel string) { + chm.AddButtons([]string{"Open", "Download", "Cancel"}) + chm.SetBorder(true) + chm.GetFrame().SetTitleAlign(cview.AlignCenter) + chm.SetDoneFunc(func(buttonIndex int, buttonLabel string) { dlChoiceCh <- buttonLabel }) - dlModal.SetBorder(true) - dlModal.GetFrame(). - SetTitleAlign(cview.AlignCenter). - SetTitle(" Download ") - dlModal.SetDoneFunc(func(buttonIndex int, buttonLabel string) { + dlm.SetBorder(true) + frame := dlm.GetFrame() + frame.SetTitleAlign(cview.AlignCenter) + frame.SetTitle(" Download ") + dlm.SetDoneFunc(func(buttonIndex int, buttonLabel string) { if buttonLabel == "Ok" { - tabPages.SwitchToPage(strconv.Itoa(curTab)) + panels.HidePanel("dl") App.SetFocus(tabs[curTab].view) App.Draw() } @@ -124,27 +141,29 @@ func dlChoice(text, u string, resp *gemini.Response) { choice = "Open" } else { dlChoiceModal.SetText(text) - tabPages.ShowPage("dlChoice") - tabPages.SendToFront("dlChoice") + panels.ShowPanel("dlChoice") + panels.SendToFront("dlChoice") App.SetFocus(dlChoiceModal) App.Draw() choice = <-dlChoiceCh } if choice == "Download" { - tabPages.HidePage("dlChoice") + panels.HidePanel("dlChoice") App.Draw() downloadURL(config.DownloadsDir, u, resp) resp.Body.Close() // Only close when the file is downloaded return } if choice == "Open" { - tabPages.HidePage("dlChoice") + panels.HidePanel("dlChoice") App.Draw() open(u, resp) return } - tabPages.SwitchToPage(strconv.Itoa(curTab)) + + // They chose the "Cancel" button + panels.HidePanel("dlChoice") App.SetFocus(tabs[curTab].view) App.Draw() } @@ -180,9 +199,11 @@ func open(u string, resp *gemini.Response) { if path == "" { return } - tabPages.SwitchToPage(strconv.Itoa(curTab)) + + panels.HidePanel("dl") App.SetFocus(tabs[curTab].view) App.Draw() + if mediaHandler.Cmd == nil { // Open with system default viewer _, err := sysopen.Open(path) @@ -246,15 +267,15 @@ func downloadURL(dir, u string, resp *gemini.Response) string { // Display dlModal.ClearButtons() dlModal.AddButtons([]string{"Downloading..."}) - tabPages.ShowPage("dl") - tabPages.SendToFront("dl") + panels.ShowPanel("dl") + panels.SendToFront("dl") App.SetFocus(dlModal) App.Draw() _, err = io.Copy(io.MultiWriter(f, bar), resp.Body) done = true if err != nil { - tabPages.HidePage("dl") + panels.HidePanel("dl") Error("Download Error", err.Error()) f.Close() os.Remove(savePath) // Remove partial file diff --git a/display/file.go b/display/file.go index 9349c86..a5ba3da 100644 --- a/display/file.go +++ b/display/file.go @@ -59,7 +59,7 @@ func handleFile(u string) (*structs.Page, bool) { } if mimetype == "text/gemini" { - rendered, links := renderer.RenderGemini(string(content), textWidth(), leftMargin(), false) + rendered, links := renderer.RenderGemini(string(content), textWidth(), false) page = &structs.Page{ Mediatype: structs.TextGemini, URL: u, @@ -73,7 +73,7 @@ func handleFile(u string) (*structs.Page, bool) { Mediatype: structs.TextPlain, URL: u, Raw: string(content), - Content: renderer.RenderPlainText(string(content), leftMargin()), + Content: renderer.RenderPlainText(string(content)), Links: []string{}, 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) } - rendered, links := renderer.RenderGemini(content, textWidth(), leftMargin(), false) + rendered, links := renderer.RenderGemini(content, textWidth(), false) page = &structs.Page{ Mediatype: structs.TextGemini, URL: u, diff --git a/display/handlers.go b/display/handlers.go index 9806102..9aed8ea 100644 --- a/display/handlers.go +++ b/display/handlers.go @@ -9,6 +9,7 @@ import ( "net/url" "os/exec" "path" + "strconv" "strings" "github.com/makeworld-the-better-one/amfora/cache" @@ -90,12 +91,12 @@ func handleOther(u string) { } // handleFavicon handles getting and displaying a favicon. -// `old` is the previous favicon for the tab. -func handleFavicon(t *tab, host, old string) { +func handleFavicon(t *tab, host string) { defer func() { // Update display if needed - if t.page.Favicon != old && isValidTab(t) { - rewriteTabRow() + if t.page.Favicon != "" && isValidTab(t) { + browser.SetTabLabel(strconv.Itoa(tabNumber(t)), makeTabLabel(t.page.Favicon)) + App.Draw() } }() @@ -117,7 +118,6 @@ func handleFavicon(t *tab, host, old string) { } if fav != "" { t.page.Favicon = fav - rewriteTabRow() return } @@ -389,7 +389,7 @@ func handleURL(t *tab, u string, numRedirects int) (string, bool) { res.Body = rr.NewRestartReader(res.Body) 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 if !isValidTab(t) { return ret("", false) diff --git a/display/help.go b/display/help.go index 467e2b7..444079f 100644 --- a/display/help.go +++ b/display/help.go @@ -2,72 +2,69 @@ package display import ( "fmt" - "strconv" "strings" + "text/tabwriter" - "github.com/gdamore/tcell" + "github.com/gdamore/tcell/v2" "github.com/makeworld-the-better-one/amfora/config" "gitlab.com/tslocum/cview" ) -var helpCells = strings.TrimSpace(` -?|Bring up this help. You can scroll! -Enter|Close this help page -Esc|Close this help page or any active modal popups -Arrow keys, h/j/k/l|Scroll and move a page. -%s|Go up a page in document -%s|Go down a page in document -g|Go to top of document -G|Go to bottom of document -Tab|Navigate to the next item in a popup. -Shift-Tab|Navigate to the previous item in a popup. -%s|Go back in the history -%s|Go forward in the history -%s|Open bar at the bottom - type a URL, link number, search term. -|You can also type two dots (..) to go up a directory in the URL. -|Typing new:N will open link number N in a new tab -|instead of the current one. -%s|Go to links 1-10 respectively. -%s|Edit current URL -Enter, Tab|On a page this will start link highlighting. -|Press Tab and Shift-Tab to pick different links. -|Press Enter again to go to one, or Esc to stop. -%s|Go to a specific tab. (Default: Shift-NUMBER) -%s|Go to the last tab. -%s|Previous tab -%s|Next tab -%s|Go home -%s|New tab, or if a link is selected, -|this will open the link in a new tab. -%s|Close tab. For now, only the right-most tab can be closed. -%s|Reload a page, discarding the cached version. -|This can also be used if you resize your terminal. -%s|View bookmarks -%s|Add, change, or remove a bookmark for the current page. -%s|Save the current page to your downloads. -%s|View subscriptions -%s|Add or update a subscription -%s|Quit -`) +var helpCells = strings.TrimSpace( + "?\tBring up this help. You can scroll!\n" + + "Esc\tLeave the help\n" + + "Arrow keys, h/j/k/l\tScroll and move a page.\n" + + "%s\tGo up a page in document\n" + + "%s\tGo down a page in document\n" + + "g\tGo to top of document\n" + + "G\tGo to bottom of document\n" + + "Tab\tNavigate to the next item in a popup.\n" + + "Shift-Tab\tNavigate to the previous item in a popup.\n" + + "%s\tGo back in the history\n" + + "%s\tGo forward in the history\n" + + "%s\tOpen bar at the bottom - type a URL, link number, search term.\n" + + "\tYou can also type two dots (..) to go up a directory in the URL.\n" + + "\tTyping new:N will open link number N in a new tab\n" + + "\tinstead of the current one.\n" + + "%s\tGo to links 1-10 respectively.\n" + + "%s\tEdit current URL\n" + + "Enter, Tab\tOn a page this will start link highlighting.\n" + + "\tPress Tab and Shift-Tab to pick different links.\n" + + "\tPress Enter again to go to one, or Esc to stop.\n" + + "%s\tGo to a specific tab. (Default: Shift-NUMBER)\n" + + "%s\tGo to the last tab.\n" + + "%s\tPrevious tab\n" + + "%s\tNext tab\n" + + "%s\tGo home\n" + + "%s\tNew tab, or if a link is selected,\n" + + "\tthis will open the link in a new tab.\n" + + "%s\tClose tab. For now, only the right-most tab can be closed.\n" + + "%s\tReload a page, discarding the cached version.\n" + + "\tThis can also be used if you resize your terminal.\n" + + "%s\tView bookmarks\n" + + "%s\tAdd, change, or remove a bookmark for the current page.\n" + + "%s\tSave the current page to your downloads.\n" + + "%s\tView subscriptions\n" + + "%s\tAdd or update a subscription\n" + + "%s\tQuit\n") -var helpTable = cview.NewTable(). - SetSelectable(false, false). - SetBorders(false). - SetScrollBarVisibility(cview.ScrollBarNever) +var helpTable = cview.NewTextView() // Help displays the help and keybindings. func Help() { helpTable.ScrollToBeginning() - tabPages.SwitchToPage("help") + panels.ShowPanel("help") + panels.SendToFront("help") App.SetFocus(helpTable) - App.Draw() } func helpInit() { // Populate help table + helpTable.SetBackgroundColor(config.GetColor("bg")) + helpTable.SetPadding(0, 0, 1, 1) helpTable.SetDoneFunc(func(key tcell.Key) { if key == tcell.KeyEsc || key == tcell.KeyEnter { - tabPages.SwitchToPage(strconv.Itoa(curTab)) + panels.HidePanel("help") App.SetFocus(tabs[curTab].view) App.Draw() } @@ -102,34 +99,16 @@ func helpInit() { config.GetKeyBinding(config.CmdQuit), ) - rows := strings.Count(helpCells, "\n") + 1 - cells := strings.Split( - strings.ReplaceAll(helpCells, "\n", "|"), - "|") - cell := 0 - extraRows := 0 // Rows continued from the previous, without spacing - for r := 0; r < rows; r++ { - for c := 0; c < 2; c++ { - var tableCell *cview.TableCell - if c == 0 { - // First column, the keybinding - tableCell = cview.NewTableCell(" " + cells[cell]). - SetAttributes(tcell.AttrBold). - SetAlign(cview.AlignLeft) - } else { - tableCell = cview.NewTableCell(" " + cells[cell]) - } - if c == 0 && cells[cell] == "" || (cell > 0 && cells[cell-1] == "" && c == 1) { - // The keybinding column for this row was blank, meaning the explanation - // column is continued from the previous row. - // The row should be added without any spacing rows - helpTable.SetCell(((2*r)-extraRows/2)-1, c, tableCell) - extraRows++ - } else { - helpTable.SetCell((2*r)-extraRows/2, c, tableCell) // Every other row, for readability - } - cell++ + lines := strings.Split(helpCells, "\n") + w := tabwriter.NewWriter(helpTable, 0, 8, 2, ' ', 0) + for i, line := range lines { + if i > 0 && line[0] != '\t' { + fmt.Fprintln(w, "\t") } + fmt.Fprintln(w, line) } - tabPages.AddPage("help", helpTable, true, false) + + w.Flush() + + panels.AddPanel("help", helpTable, true, false) } diff --git a/display/modals.go b/display/modals.go index 5a472d9..84e6a99 100644 --- a/display/modals.go +++ b/display/modals.go @@ -2,12 +2,11 @@ package display import ( "fmt" - "strconv" "strings" "time" humanize "github.com/dustin/go-humanize" - "github.com/gdamore/tcell" + "github.com/gdamore/tcell/v2" "github.com/makeworld-the-better-one/amfora/config" "github.com/spf13/viper" "gitlab.com/tslocum/cview" @@ -16,103 +15,133 @@ import ( // This file contains code for the popups / modals used in the display. // The bookmark modal is in bookmarks.go -var infoModal = cview.NewModal(). - AddButtons([]string{"Ok"}) +var infoModal = cview.NewModal() -var errorModal = cview.NewModal(). - AddButtons([]string{"Ok"}) +var errorModal = cview.NewModal() var inputModal = cview.NewModal() var inputCh = make(chan string) var inputModalText string // The current text of the input field in the modal -var yesNoModal = cview.NewModal(). - AddButtons([]string{"Yes", "No"}) +var yesNoModal = cview.NewModal() // Channel to receive yesNo answer on var yesNoCh = make(chan bool) func modalInit() { - tabPages.AddPage("info", infoModal, false, false). - AddPage("error", errorModal, false, false). - AddPage("input", inputModal, false, false). - AddPage("yesno", yesNoModal, false, false). - AddPage("bkmk", bkmkModal, false, false). - AddPage("dlChoice", dlChoiceModal, false, false). - AddPage("dl", dlModal, false, false) + infoModal.AddButtons([]string{"Ok"}) + + errorModal.AddButtons([]string{"Ok"}) + + yesNoModal.AddButtons([]string{"Yes", "No"}) + + panels.AddPanel("info", infoModal, false, false) + panels.AddPanel("error", errorModal, false, false) + panels.AddPanel("input", inputModal, false, false) + panels.AddPanel("yesno", yesNoModal, false, false) // Color setup if viper.GetBool("a-general.color") { - infoModal.SetBackgroundColor(config.GetColor("info_modal_bg")). - SetButtonBackgroundColor(config.GetColor("btn_bg")). - SetButtonTextColor(config.GetColor("btn_text")). - SetTextColor(config.GetColor("info_modal_text")) - infoModal.GetFrame(). - SetBorderColor(config.GetColor("info_modal_text")). - SetTitleColor(config.GetColor("info_modal_text")) + m := infoModal + m.SetBackgroundColor(config.GetColor("info_modal_bg")) + m.SetButtonBackgroundColor(config.GetColor("btn_bg")) + m.SetButtonTextColor(config.GetColor("btn_text")) + m.SetTextColor(config.GetColor("info_modal_text")) + form := m.GetForm() + form.SetButtonBackgroundColorFocused(config.GetColor("btn_text")) + form.SetButtonTextColorFocused(config.GetColor("btn_bg")) + frame := m.GetFrame() + frame.SetBorderColor(config.GetColor("info_modal_text")) + frame.SetTitleColor(config.GetColor("info_modal_text")) - errorModal.SetBackgroundColor(config.GetColor("error_modal_bg")). - SetButtonBackgroundColor(config.GetColor("btn_bg")). - SetButtonTextColor(config.GetColor("btn_text")). - SetTextColor(config.GetColor("error_modal_text")) - errorModal.GetFrame(). - SetBorderColor(config.GetColor("error_modal_text")). - SetTitleColor(config.GetColor("error_modal_text")) + m = errorModal + m.SetBackgroundColor(config.GetColor("error_modal_bg")) + m.SetButtonBackgroundColor(config.GetColor("btn_bg")) + m.SetButtonTextColor(config.GetColor("btn_text")) + m.SetTextColor(config.GetColor("error_modal_text")) + form = m.GetForm() + form.SetButtonBackgroundColorFocused(config.GetColor("btn_text")) + form.SetButtonTextColorFocused(config.GetColor("btn_bg")) + frame = errorModal.GetFrame() + frame.SetBorderColor(config.GetColor("error_modal_text")) + frame.SetTitleColor(config.GetColor("error_modal_text")) - inputModal.SetBackgroundColor(config.GetColor("input_modal_bg")). - SetButtonBackgroundColor(config.GetColor("btn_bg")). - SetButtonTextColor(config.GetColor("btn_text")). - SetTextColor(config.GetColor("input_modal_text")) - inputModal.GetFrame(). - SetBorderColor(config.GetColor("input_modal_text")). - SetTitleColor(config.GetColor("input_modal_text")) - inputModal.GetForm(). - SetFieldBackgroundColor(config.GetColor("input_modal_field_bg")). - SetFieldTextColor(config.GetColor("input_modal_field_text")) + m = inputModal + m.SetBackgroundColor(config.GetColor("input_modal_bg")) + m.SetButtonBackgroundColor(config.GetColor("btn_bg")) + m.SetButtonTextColor(config.GetColor("btn_text")) + m.SetTextColor(config.GetColor("input_modal_text")) + frame = inputModal.GetFrame() + frame.SetBorderColor(config.GetColor("input_modal_text")) + frame.SetTitleColor(config.GetColor("input_modal_text")) + form = inputModal.GetForm() + form.SetFieldBackgroundColor(config.GetColor("input_modal_field_bg")) + form.SetFieldTextColor(config.GetColor("input_modal_field_text")) + form.SetButtonBackgroundColorFocused(config.GetColor("btn_text")) + form.SetButtonTextColorFocused(config.GetColor("btn_bg")) - yesNoModal.SetButtonBackgroundColor(config.GetColor("btn_bg")). - SetButtonTextColor(config.GetColor("btn_text")) + m = yesNoModal + m.SetButtonBackgroundColor(config.GetColor("btn_bg")) + m.SetButtonTextColor(config.GetColor("btn_text")) + form = m.GetForm() + form.SetButtonBackgroundColorFocused(config.GetColor("btn_text")) + form.SetButtonTextColorFocused(config.GetColor("btn_bg")) } else { - infoModal.SetBackgroundColor(tcell.ColorBlack). - SetButtonBackgroundColor(tcell.ColorWhite). - SetButtonTextColor(tcell.ColorBlack). - SetTextColor(tcell.ColorWhite) - infoModal.GetFrame(). - SetBorderColor(tcell.ColorWhite). - SetTitleColor(tcell.ColorWhite) + m := infoModal + m.SetBackgroundColor(tcell.ColorBlack) + m.SetButtonBackgroundColor(tcell.ColorWhite) + m.SetButtonTextColor(tcell.ColorBlack) + m.SetTextColor(tcell.ColorWhite) + form := m.GetForm() + form.SetButtonBackgroundColorFocused(tcell.ColorBlack) + form.SetButtonTextColorFocused(tcell.ColorWhite) + frame := infoModal.GetFrame() + frame.SetBorderColor(tcell.ColorWhite) + frame.SetTitleColor(tcell.ColorWhite) - errorModal.SetBackgroundColor(tcell.ColorBlack). - SetButtonBackgroundColor(tcell.ColorWhite). - SetButtonTextColor(tcell.ColorBlack). - SetTextColor(tcell.ColorWhite) - errorModal.GetFrame(). - SetBorderColor(tcell.ColorWhite). - SetTitleColor(tcell.ColorWhite) + m = errorModal + m.SetBackgroundColor(tcell.ColorBlack) + m.SetButtonBackgroundColor(tcell.ColorWhite) + m.SetButtonTextColor(tcell.ColorBlack) + m.SetTextColor(tcell.ColorWhite) + form = m.GetForm() + form.SetButtonBackgroundColorFocused(tcell.ColorBlack) + form.SetButtonTextColorFocused(tcell.ColorWhite) + frame = errorModal.GetFrame() + frame.SetBorderColor(tcell.ColorWhite) + frame.SetTitleColor(tcell.ColorWhite) - inputModal.SetBackgroundColor(tcell.ColorBlack). - SetButtonBackgroundColor(tcell.ColorWhite). - SetButtonTextColor(tcell.ColorBlack). - SetTextColor(tcell.ColorWhite) - inputModal.GetFrame(). - SetBorderColor(tcell.ColorWhite). - SetTitleColor(tcell.ColorWhite) - inputModal.GetForm(). - SetFieldBackgroundColor(tcell.ColorWhite). - SetFieldTextColor(tcell.ColorBlack) + m = inputModal + m.SetBackgroundColor(tcell.ColorBlack) + m.SetButtonBackgroundColor(tcell.ColorWhite) + m.SetButtonTextColor(tcell.ColorBlack) + m.SetTextColor(tcell.ColorWhite) + frame = inputModal.GetFrame() + frame.SetBorderColor(tcell.ColorWhite) + frame.SetTitleColor(tcell.ColorWhite) + form = inputModal.GetForm() + form.SetFieldBackgroundColor(tcell.ColorWhite) + form.SetFieldTextColor(tcell.ColorBlack) + form.SetButtonBackgroundColorFocused(tcell.ColorBlack) + form.SetButtonTextColorFocused(tcell.ColorWhite) // YesNo background color is changed in funcs - yesNoModal.SetButtonBackgroundColor(tcell.ColorWhite). - SetButtonTextColor(tcell.ColorBlack) + m = yesNoModal + m.SetButtonBackgroundColor(tcell.ColorWhite) + m.SetButtonTextColor(tcell.ColorBlack) + form = m.GetForm() + form.SetButtonBackgroundColorFocused(tcell.ColorBlack) + form.SetButtonTextColorFocused(tcell.ColorWhite) } // Modal functions that can't be added up above, because they return the wrong type infoModal.SetBorder(true) - infoModal.GetFrame(). - SetTitleAlign(cview.AlignCenter). - SetTitle(" Info ") + frame := infoModal.GetFrame() + frame.SetTitleAlign(cview.AlignCenter) + frame.SetTitle(" Info ") infoModal.SetDoneFunc(func(buttonIndex int, buttonLabel string) { - tabPages.SwitchToPage(strconv.Itoa(curTab)) + panels.HidePanel("info") App.SetFocus(tabs[curTab].view) App.Draw() }) @@ -120,15 +149,15 @@ func modalInit() { errorModal.SetBorder(true) errorModal.GetFrame().SetTitleAlign(cview.AlignCenter) errorModal.SetDoneFunc(func(buttonIndex int, buttonLabel string) { - tabPages.SwitchToPage(strconv.Itoa(curTab)) + panels.HidePanel("error") App.SetFocus(tabs[curTab].view) App.Draw() }) inputModal.SetBorder(true) - inputModal.GetFrame(). - SetTitleAlign(cview.AlignCenter). - SetTitle(" Input ") + frame = inputModal.GetFrame() + frame.SetTitleAlign(cview.AlignCenter) + frame.SetTitle(" Input ") inputModal.SetDoneFunc(func(buttonIndex int, buttonLabel string) { if buttonLabel == "Send" { inputCh <- inputModalText @@ -167,8 +196,8 @@ func Error(title, text string) { errorModal.GetFrame().SetTitle(title) errorModal.SetText(text) - tabPages.ShowPage("error") - tabPages.SendToFront("error") + panels.ShowPanel("error") + panels.SendToFront("error") App.SetFocus(errorModal) App.Draw() } @@ -176,8 +205,8 @@ func Error(title, text string) { // Info displays some info on the screen in a modal. func Info(s string) { infoModal.SetText(s) - tabPages.ShowPage("info") - tabPages.SendToFront("info") + panels.ShowPanel("info") + panels.SendToFront("info") App.SetFocus(infoModal) App.Draw() } @@ -198,14 +227,14 @@ func Input(prompt string) (string, bool) { }) inputModal.SetText(prompt + " ") - tabPages.ShowPage("input") - tabPages.SendToFront("input") + panels.ShowPanel("input") + panels.SendToFront("input") App.SetFocus(inputModal) App.Draw() resp := <-inputCh - tabPages.SwitchToPage(strconv.Itoa(curTab)) + panels.HidePanel("input") App.SetFocus(tabs[curTab].view) App.Draw() @@ -218,29 +247,29 @@ func Input(prompt string) (string, bool) { // YesNo displays a modal asking a yes-or-no question. func YesNo(prompt string) bool { if viper.GetBool("a-general.color") { - yesNoModal. - SetBackgroundColor(config.GetColor("yesno_modal_bg")). - SetTextColor(config.GetColor("yesno_modal_text")) - yesNoModal.GetFrame(). - SetBorderColor(config.GetColor("yesno_modal_text")). - SetTitleColor(config.GetColor("yesno_modal_text")) + m := yesNoModal + m.SetBackgroundColor(config.GetColor("yesno_modal_bg")) + m.SetTextColor(config.GetColor("yesno_modal_text")) + frame := yesNoModal.GetFrame() + frame.SetBorderColor(config.GetColor("yesno_modal_text")) + frame.SetTitleColor(config.GetColor("yesno_modal_text")) } else { - yesNoModal. - SetBackgroundColor(tcell.ColorBlack). - SetTextColor(tcell.ColorWhite) - yesNoModal.GetFrame(). - SetBorderColor(tcell.ColorWhite). - SetTitleColor(tcell.ColorWhite) + m := yesNoModal + m.SetBackgroundColor(tcell.ColorBlack) + m.SetTextColor(tcell.ColorWhite) + frame := yesNoModal.GetFrame() + frame.SetBorderColor(tcell.ColorWhite) + frame.SetTitleColor(tcell.ColorWhite) } yesNoModal.GetFrame().SetTitle("") yesNoModal.SetText(prompt) - tabPages.ShowPage("yesno") - tabPages.SendToFront("yesno") + panels.ShowPanel("yesno") + panels.SendToFront("yesno") App.SetFocus(yesNoModal) App.Draw() resp := <-yesNoCh - tabPages.SwitchToPage(strconv.Itoa(curTab)) + panels.HidePanel("yesno") App.SetFocus(tabs[curTab].view) App.Draw() return resp @@ -251,36 +280,34 @@ func YesNo(prompt string) bool { func Tofu(host string, expiry time.Time) bool { // Reuses yesNoModal, with error color + m := yesNoModal + frame := yesNoModal.GetFrame() if viper.GetBool("a-general.color") { - yesNoModal. - SetBackgroundColor(config.GetColor("tofu_modal_bg")). - SetTextColor(config.GetColor("tofu_modal_text")) - yesNoModal.GetFrame(). - SetBorderColor(config.GetColor("tofu_modal_text")). - SetTitleColor(config.GetColor("tofu_modal_text")) + m.SetBackgroundColor(config.GetColor("tofu_modal_bg")) + m.SetTextColor(config.GetColor("tofu_modal_text")) + frame.SetBorderColor(config.GetColor("tofu_modal_text")) + frame.SetTitleColor(config.GetColor("tofu_modal_text")) } else { - yesNoModal. - SetBackgroundColor(tcell.ColorBlack). - SetTextColor(tcell.ColorWhite) - yesNoModal. - SetBorderColor(tcell.ColorWhite). - SetTitleColor(tcell.ColorWhite) + m.SetBackgroundColor(tcell.ColorBlack) + m.SetTextColor(tcell.ColorWhite) + m.SetBorderColor(tcell.ColorWhite) + m.SetTitleColor(tcell.ColorWhite) } - yesNoModal.GetFrame().SetTitle(" TOFU ") - yesNoModal.SetText( + frame.SetTitle(" TOFU ") + m.SetText( //nolint:lll fmt.Sprintf("%s's certificate has changed, possibly indicating an security issue. The certificate would have expired %s. Are you sure you want to continue? ", host, humanize.Time(expiry), ), ) - tabPages.ShowPage("yesno") - tabPages.SendToFront("yesno") + panels.ShowPanel("yesno") + panels.SendToFront("yesno") App.SetFocus(yesNoModal) App.Draw() resp := <-yesNoCh - tabPages.SwitchToPage(strconv.Itoa(curTab)) + panels.HidePanel("yesno") App.SetFocus(tabs[curTab].view) App.Draw() return resp diff --git a/display/private.go b/display/private.go index 1ead234..0bd7825 100644 --- a/display/private.go +++ b/display/private.go @@ -1,15 +1,12 @@ package display import ( - "fmt" "net/url" "strconv" "strings" - "github.com/makeworld-the-better-one/amfora/config" "github.com/makeworld-the-better-one/amfora/renderer" "github.com/makeworld-the-better-one/amfora/structs" - "github.com/spf13/viper" ) // This file contains the functions that aren't part of the public API. @@ -68,11 +65,11 @@ func reformatPage(p *structs.Page) { strings.HasPrefix(p.URL, "file") { proxied = false } - rendered, _ = renderer.RenderGemini(p.Raw, textWidth(), leftMargin(), proxied) + rendered, _ = renderer.RenderGemini(p.Raw, textWidth(), proxied) case structs.TextPlain: - rendered = renderer.RenderPlainText(p.Raw, leftMargin()) + rendered = renderer.RenderPlainText(p.Raw) case structs.TextAnsi: - rendered = renderer.RenderANSI(p.Raw, leftMargin()) + rendered = renderer.RenderANSI(p.Raw) default: // Rendering this type is not implemented return @@ -92,6 +89,8 @@ func reformatPageAndSetView(t *tab, p *structs.Page) { reformatPage(p) t.view.SetText(p.Content) t.applyScroll() // Go back to where you were, roughly + + App.Draw() } // setPage displays a Page on the passed tab number. @@ -107,20 +106,23 @@ func setPage(t *tab, p *structs.Page) { // Make sure the page content is fitted to the terminal every time it's displayed reformatPage(p) - oldFav := t.page.Favicon - t.page = p - go func() { - parsed, _ := url.Parse(p.URL) - handleFavicon(t, parsed.Host, oldFav) - }() - // Change page on screen t.view.SetText(p.Content) t.view.Highlight("") // Turn off highlights, other funcs may restore if necessary t.view.ScrollToBeginning() + // Set tab number in case a favicon from before overwrote it + tabNum := tabNumber(t) + browser.SetTabLabel(strconv.Itoa(tabNum), makeTabLabel(strconv.Itoa(tabNum+1))) + App.Draw() + + go func() { + parsed, _ := url.Parse(p.URL) + handleFavicon(t, parsed.Host) + }() + // Setup display App.SetFocus(t.view) @@ -144,32 +146,3 @@ func goURL(t *tab, u string) { t.applyBottomBar() } } - -// rewriteTabRow clears the tabRow and writes all the tabs number/favicons into it. -func rewriteTabRow() { - tabRow.Clear() - if viper.GetBool("a-general.color") { - for i := 0; i < NumTabs(); i++ { - char := strconv.Itoa(i + 1) - if tabs[i].page.Favicon != "" { - char = tabs[i].page.Favicon - } - fmt.Fprintf(tabRow, `["%d"][%s] %s [%s][""]|`, - i, - config.GetColorString("tab_num"), - char, - config.GetColorString("tab_divider"), - ) - } - } else { - for i := 0; i < NumTabs(); i++ { - char := strconv.Itoa(i + 1) - if tabs[i].page.Favicon != "" { - char = tabs[i].page.Favicon - } - fmt.Fprintf(tabRow, `["%d"] %s [""]|`, i, char) - } - } - tabRow.Highlight(strconv.Itoa(curTab)).ScrollToHighlight() - App.Draw() -} diff --git a/display/subscriptions.go b/display/subscriptions.go index 7c5ffbe..d65c19e 100644 --- a/display/subscriptions.go +++ b/display/subscriptions.go @@ -9,7 +9,7 @@ import ( "strings" "time" - "github.com/gdamore/tcell" + "github.com/gdamore/tcell/v2" "github.com/makeworld-the-better-one/amfora/cache" "github.com/makeworld-the-better-one/amfora/config" "github.com/makeworld-the-better-one/amfora/renderer" @@ -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{ Raw: rawPage, 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{ Raw: rawPage, Content: content, @@ -230,19 +230,19 @@ func openSubscriptionModal(validFeed, subscribed bool) bool { // Reuses yesNoModal if viper.GetBool("a-general.color") { - yesNoModal. - SetBackgroundColor(config.GetColor("subscription_modal_bg")). - SetTextColor(config.GetColor("subscription_modal_text")) - yesNoModal.GetFrame(). - SetBorderColor(config.GetColor("subscription_modal_text")). - SetTitleColor(config.GetColor("subscription_modal_text")) + m := yesNoModal + m.SetBackgroundColor(config.GetColor("subscription_modal_bg")) + m.SetTextColor(config.GetColor("subscription_modal_text")) + frame := yesNoModal.GetFrame() + frame.SetBorderColor(config.GetColor("subscription_modal_text")) + frame.SetTitleColor(config.GetColor("subscription_modal_text")) } else { - yesNoModal. - SetBackgroundColor(tcell.ColorBlack). - SetTextColor(tcell.ColorWhite) - yesNoModal.GetFrame(). - SetBorderColor(tcell.ColorWhite). - SetTitleColor(tcell.ColorWhite) + m := yesNoModal + m.SetBackgroundColor(tcell.ColorBlack) + m.SetTextColor(tcell.ColorWhite) + frame := yesNoModal.GetFrame() + frame.SetBorderColor(tcell.ColorWhite) + frame.SetTitleColor(tcell.ColorWhite) } if validFeed { yesNoModal.GetFrame().SetTitle("Feed Subscription") @@ -260,13 +260,13 @@ func openSubscriptionModal(validFeed, subscribed bool) bool { } } - tabPages.ShowPage("yesno") - tabPages.SendToFront("yesno") + panels.ShowPanel("yesno") + panels.SendToFront("yesno") App.SetFocus(yesNoModal) App.Draw() resp := <-yesNoCh - tabPages.SwitchToPage(strconv.Itoa(curTab)) + panels.HidePanel("yesno") App.SetFocus(tabs[curTab].view) App.Draw() return resp diff --git a/display/tab.go b/display/tab.go index 8c3f805..9052bd9 100644 --- a/display/tab.go +++ b/display/tab.go @@ -3,9 +3,9 @@ package display import ( "strconv" "strings" - "sync" - "github.com/gdamore/tcell" + "github.com/gdamore/tcell/v2" + "github.com/makeworld-the-better-one/amfora/config" "github.com/makeworld-the-better-one/amfora/structs" "gitlab.com/tslocum/cview" ) @@ -24,33 +24,34 @@ type tabHistory struct { // tab hold the information needed for each browser tab. type tab struct { - page *structs.Page - view *cview.TextView - history *tabHistory - mode tabMode - reformatMu *sync.Mutex // Mutex for reformatting, so there's only one reformat job at once - barLabel string // The bottomBar label for the tab - barText string // The bottomBar text for the tab + page *structs.Page + view *cview.TextView + history *tabHistory + mode tabMode + barLabel string // The bottomBar label for the tab + barText string // The bottomBar text for the tab } // makeNewTab initializes an tab struct with no content. func makeNewTab() *tab { t := tab{ - page: &structs.Page{Mode: structs.ModeOff}, - view: cview.NewTextView(). - SetDynamicColors(true). - SetRegions(true). - SetScrollable(true). - SetWrap(false). - SetChangedFunc(func() { - App.Draw() - }), - history: &tabHistory{}, - reformatMu: &sync.Mutex{}, - mode: tabModeDone, + page: &structs.Page{Mode: structs.ModeOff}, + view: cview.NewTextView(), + history: &tabHistory{}, + mode: tabModeDone, } + t.view.SetDynamicColors(true) + t.view.SetRegions(true) + t.view.SetScrollable(true) + t.view.SetWrap(false) + t.view.SetScrollBarVisibility(config.ScrollBar) + t.view.SetScrollBarColor(config.GetColor("scrollbar")) + t.view.SetChangedFunc(func() { + App.Draw() + }) t.view.SetDoneFunc(func(key tcell.Key) { - // Altered from: https://gitlab.com/tslocum/cview/-/blob/master/demos/textview/main.go + // Altered from: + // https://gitlab.com/tslocum/cview/-/blob/1f765c8695c3f4b35dae57f469d3aee0b1adbde7/demos/textview/main.go // Handles being able to select and "click" links with the enter and tab keys tab := curTab // Don't let it change in the middle of the code @@ -89,7 +90,8 @@ func makeNewTab() *tab { // They've started link highlighting tabs[tab].page.Mode = structs.ModeLinkSelect - tabs[tab].view.Highlight("0").ScrollToHighlight() + tabs[tab].view.Highlight("0") + tabs[tab].view.ScrollToHighlight() // Display link URL in bottomBar bottomBar.SetLabel("[::b]Link: [::-]") bottomBar.SetText(tabs[tab].page.Links[0]) @@ -109,7 +111,8 @@ func makeNewTab() *tab { } else { return } - tabs[tab].view.Highlight(strconv.Itoa(index)).ScrollToHighlight() + tabs[tab].view.Highlight(strconv.Itoa(index)) + tabs[tab].view.ScrollToHighlight() // Display link URL in bottomBar bottomBar.SetLabel("[::b]Link: [::-]") bottomBar.SetText(tabs[tab].page.Links[index]) diff --git a/display/util.go b/display/util.go index a74e09c..2594ff7 100644 --- a/display/util.go +++ b/display/util.go @@ -13,6 +13,43 @@ import ( // 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, as well as a single empty +// line at the top, for a top margin. +func makeContentLayout(tv *cview.TextView) *cview.Flex { + // Create horizontal flex with the left margin as an empty space + horiz := cview.NewFlex() + horiz.SetDirection(cview.FlexColumn) + horiz.AddItem(nil, leftMargin(), 0, false) + horiz.AddItem(tv, 0, 1, true) + + // Create a vertical flex with the other one and a top margin + vert := cview.NewFlex() + vert.SetDirection(cview.FlexRow) + vert.AddItem(nil, 1, 0, false) + vert.AddItem(horiz, 0, 1, true) + + return vert +} + +// makeTabLabel takes a string and adds spacing to it, making it +// suitable for display as a tab label. +func makeTabLabel(s string) string { + return " " + s + " " +} + +// tabNumber gets the index of the tab in the tabs slice. It returns -1 +// if the tab is not in that slice. +func tabNumber(t *tab) int { + tempTabs := tabs + for i := range tempTabs { + if tempTabs[i] == t { + return i + } + } + return -1 +} + // escapeMeta santizes a META string for use within a cview modal. func escapeMeta(meta string) string { return cview.Escape(strings.ReplaceAll(meta, "\n", "")) @@ -20,13 +57,7 @@ func escapeMeta(meta string) string { // isValidTab indicates whether the passed tab is still being used, even if it's not currently displayed. func isValidTab(t *tab) bool { - tempTabs := tabs - for i := range tempTabs { - if tempTabs[i] == t { - return true - } - } - return false + return tabNumber(t) != -1 } func leftMargin() int { diff --git a/go.mod b/go.mod index 349d222..2137519 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.14 require ( github.com/dustin/go-humanize v1.0.0 github.com/fsnotify/fsnotify v1.4.9 // indirect - github.com/gdamore/tcell v1.3.1-0.20200608133353-cb1e5d6fa606 + 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/makeworld-the-better-one/go-isemoji v1.1.0 @@ -19,12 +19,12 @@ require ( github.com/spf13/cast v1.3.1 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect - github.com/spf13/viper v1.7.0 + github.com/spf13/viper v1.7.1 github.com/stretchr/testify v1.6.1 - gitlab.com/tslocum/cview v1.4.8-0.20200713214710-cc7796c4ca44 - golang.org/x/text v0.3.5-0.20201208001344-75a595aef632 + gitlab.com/tslocum/cview v1.5.4-0.20210207045010-d776e728ef6d + golang.org/x/text v0.3.5 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect - gopkg.in/ini.v1 v1.57.0 // indirect + gopkg.in/ini.v1 v1.62.0 // indirect ) replace github.com/mmcdole/gofeed => github.com/makeworld-the-better-one/gofeed v1.1.1-0.20201123002655-c0c6354134fe diff --git a/go.sum b/go.sum index ae1e114..e8234ce 100644 --- a/go.sum +++ b/go.sum @@ -14,7 +14,7 @@ dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7 github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= +github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/PuerkitoBio/goquery v1.5.1 h1:PSPBGne8NIUWw+/7vFBV+kG2J/5MOjbzc7154OaKCSE= github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc= @@ -50,9 +50,9 @@ github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWo github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko= github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg= -github.com/gdamore/tcell v1.3.0/go.mod h1:Hjvr+Ofd+gLglo7RYKxxnzCBmev3BzsS67MebKS4zMM= -github.com/gdamore/tcell v1.3.1-0.20200608133353-cb1e5d6fa606 h1:Y00kKKKYVyn7InlCMRcnZbwcjHFIsgkjU0Bn1F5re4o= -github.com/gdamore/tcell v1.3.1-0.20200608133353-cb1e5d6fa606/go.mod h1:vxEiSDZdW3L+Uhjii9c3375IlDmR05bzxY404ZVSMo0= +github.com/gdamore/tcell/v2 v2.0.0-dev/go.mod h1:vSVL/GV5mCSlPC6thFP5kfOFdM9MGZcalipmpTxTgQA= +github.com/gdamore/tcell/v2 v2.1.1-0.20210125004847-19e17097d8fe h1:D4zQq/0ep0XCtgkmA+dUvQNYMoiW2+2336rdlAucr10= +github.com/gdamore/tcell/v2 v2.1.1-0.20210125004847-19e17097d8fe/go.mod h1:cTTuF84Dlj/RqmaCIV5p4w8uG1zWdk0SF6oBpwHp4fU= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= @@ -128,9 +128,10 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/lucasb-eyer/go-colorful v1.0.2/go.mod h1:0MS4r+7BZKSJ5mw4/S5MPN+qHFF1fYclkSPilDOKW0s= github.com/lucasb-eyer/go-colorful v1.0.3 h1:QIbQXiugsb+q10B+MI+7DI1oQLdmnep86tWFlaaUAac= github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= +github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= +github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/makeworld-the-better-one/go-gemini v0.11.0 h1:MNGiULJFvcqls9oCy40tE897hDeKvNmEK9i5kRucgQk= @@ -144,11 +145,11 @@ github.com/makeworld-the-better-one/progressbar/v3 v3.3.5-0.20201220005701-b036c github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-runewidth v0.0.8/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-runewidth v0.0.10 h1:CoZ3S2P7pvtP45xOtBw+/mDL2z0RKI576gSkzRRpdGg= +github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= @@ -192,8 +193,9 @@ github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8 github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= -github.com/rivo/uniseg v0.1.0 h1:+2KBaVoUmb9XzDsrx/Ct0W/EYOSFf/nWTauy++DprtY= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rkoesters/xdg v0.0.0-20181125232953-edd15b846f9b h1:8NiY6v9/IlFU8osj1L7kqzRbrG6e3izRQQjGze1Q1R0= github.com/rkoesters/xdg v0.0.0-20181125232953-edd15b846f9b/go.mod h1:T1HolqzmdHnJIH6p7A9LDuvYGQgEHx9ijX3vKgDKU60= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= @@ -221,8 +223,8 @@ github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0 github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.7.0 h1:xVKxvI7ouOI5I+U9s2eeiUfMaWBVoXA3AWskkrqK0VM= -github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= +github.com/spf13/viper v1.7.1 h1:pM5oEahlgWv/WnHXpgbKz7iLIxRf65tye2Ci+XFK5sk= +github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -235,10 +237,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/tslocum/cbind v0.1.1 h1:JXXtxMWHgWLvoF+QkrvcNvOQ59juy7OE1RhT7hZfdt0= -gitlab.com/tslocum/cbind v0.1.1/go.mod h1:rX7vkl0pUSg/yy427MmD1FZAf99S7WwpUlxF/qTpPqk= -gitlab.com/tslocum/cview v1.4.8-0.20200713214710-cc7796c4ca44 h1:YddMqXJ6jI3SkP8Nfxc+S2pcvI5o8mmXmHL2D9hkwQI= -gitlab.com/tslocum/cview v1.4.8-0.20200713214710-cc7796c4ca44/go.mod h1:QctoEJaR2AqZTy0KKo12P1ZjHgQJyVkAXaeDanBBhlE= +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= @@ -315,19 +317,22 @@ golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200610111108-226ff32320da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201013132646-2da7054afaeb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201113135734-0a15ea8d9b02/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c h1:VwygUrnw9jn88c4u8GD3rZQbqrP/tgas88tPUbBxQrk= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf h1:MZ2shdL+ZM/XzY3ZGOnh4Nlpnxz5GSOhOmtHo3iPU6M= +golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5-0.20201208001344-75a595aef632 h1:clKlpQ6BheG1zIRhU2SPRAXpLgol/tqWVEeRkjpsaDI= -golang.org/x/text v0.3.5-0.20201208001344-75a595aef632/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -378,8 +383,8 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogR gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.57.0 h1:9unxIsFcTt4I55uWluz+UmL95q4kdJ0buvQ1ZIqVQww= -gopkg.in/ini.v1 v1.57.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU= +gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/renderer/page.go b/renderer/page.go index 8f321ed..0e373e4 100644 --- a/renderer/page.go +++ b/renderer/page.go @@ -69,7 +69,7 @@ func CanDisplay(res *gemini.Response) bool { // MakePage creates a formatted, rendered Page from the given network response and params. // 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) { return nil, ErrCantDisplay } @@ -112,7 +112,7 @@ func MakePage(url string, res *gemini.Response, width, leftMargin int, proxied b } if mediatype == "text/gemini" { - rendered, links := RenderGemini(utfText, width, leftMargin, proxied) + rendered, links := RenderGemini(utfText, width, proxied) return &structs.Page{ Mediatype: structs.TextGemini, RawMediatype: mediatype, @@ -130,7 +130,7 @@ func MakePage(url string, res *gemini.Response, width, leftMargin int, proxied b RawMediatype: mediatype, URL: url, Raw: utfText, - Content: RenderANSI(utfText, leftMargin), + Content: RenderANSI(utfText), Links: []string{}, MadeAt: time.Now(), }, nil @@ -142,7 +142,7 @@ func MakePage(url string, res *gemini.Response, width, leftMargin int, proxied b RawMediatype: mediatype, URL: url, Raw: utfText, - Content: RenderPlainText(utfText, leftMargin), + Content: RenderPlainText(utfText), Links: []string{}, MadeAt: time.Now(), }, nil diff --git a/renderer/renderer.go b/renderer/renderer.go index 8ebee07..9552835 100644 --- a/renderer/renderer.go +++ b/renderer/renderer.go @@ -21,31 +21,27 @@ var ansiRegex = regexp.MustCompile(`\x1b\[[0-9;]*m`) // RenderANSI renders plain text pages containing ANSI codes. // 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) if viper.GetBool("a-general.color") && viper.GetBool("a-general.ansi") { s = cview.TranslateANSI(s) + // The TranslateANSI function injects tags like [-:-:-] + // but this will reset the background to use the user's terminal color. + // These tags need to be replaced with resets that use the theme color. + s = strings.ReplaceAll(s, "[-:-:-]", + fmt.Sprintf("[-:%s:-]", config.GetColorString("bg"))) } else { s = ansiRegex.ReplaceAllString(s, "") } - var shifted string - lines := strings.Split(s, "\n") - for i := range lines { - shifted += strings.Repeat(" ", leftMargin) + lines[i] + "\n" - } - return shifted + return s } // RenderPlainText should be used to format plain text pages. -func RenderPlainText(s string, leftMargin int) string { - var shifted string - lines := strings.Split(cview.Escape(s), "\n") - for i := range lines { - shifted += strings.Repeat(" ", leftMargin) + - "[" + config.GetColorString("regular_text") + "]" + lines[i] + "[-]" + - "\n" - } - return shifted +func RenderPlainText(s string) string { + // It used to add a left margin, now this is done elsewhere. + // The function is kept for convenience and in case rendering + // is needed in the future. + return s } // wrapLine wraps a line to the provided width, and adds the provided prefix and suffix to each wrapped line. @@ -278,7 +274,7 @@ func convertRegularGemini(s string, numLinks, width int, proxied bool) (string, // // 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, leftMargin int, proxied bool) (string, []string) { +func RenderGemini(s string, width int, proxied bool) (string, []string) { s = cview.Escape(s) lines := strings.Split(s, "\n") @@ -289,30 +285,52 @@ func RenderGemini(s string, width, leftMargin int, proxied bool) (string, []stri rendered := "" // Final result pre := false buf := "" // Block of regular or preformatted lines + + // processPre is for rendering preformatted blocks + processPre := func() { + // Support ANSI color codes in preformatted blocks - see #59 + if viper.GetBool("a-general.color") && viper.GetBool("a-general.ansi") { + buf = cview.TranslateANSI(buf) + // The TranslateANSI function injects tags like [-:-:-] + // but this will reset the background to use the user's terminal color. + // These tags need to be replaced with resets that use the theme color. + buf = strings.ReplaceAll(buf, "[-:-:-]", + fmt.Sprintf("[%s:%s:-]", config.GetColorString("preformatted_text"), config.GetColorString("bg"))) + } else { + buf = ansiRegex.ReplaceAllString(buf, "") + } + + // The final newline is removed (and re-added) to prevent background glitches + // where the terminal background color slips through. This only happens on + // preformatted blocks with ANSI characters. + // + // Lines are modified below to always end with \r\n + buf = strings.TrimSuffix(buf, "\r\n") + + rendered += fmt.Sprintf("[%s]", config.GetColorString("preformatted_text")) + + buf + fmt.Sprintf("[%s:%s:-]\r\n", config.GetColorString("regular_text"), config.GetColorString("bg")) + } + + // processRegular processes non-preformatted sections + processRegular := func() { + // ANSI not allowed in regular text - see #59 + buf = ansiRegex.ReplaceAllString(buf, "") + + ren, lks := convertRegularGemini(buf, len(links), width, proxied) + links = append(links, lks...) + rendered += ren + } + for i := range lines { if strings.HasPrefix(lines[i], "```") { if pre { // In a preformatted block, so add the text as is // Don't add the current line with backticks + processPre() - // Support ANSI color codes in preformatted blocks - see #59 - if viper.GetBool("a-general.color") && viper.GetBool("a-general.ansi") { - buf = cview.TranslateANSI(buf) - } else { - buf = ansiRegex.ReplaceAllString(buf, "") - } - - rendered += fmt.Sprintf("[%s]", config.GetColorString("preformatted_text")) + - buf + "[-]" } else { // Not preformatted, regular text - - // ANSI not allowed in regular text - see #59 - buf = ansiRegex.ReplaceAllString(buf, "") - - ren, lks := convertRegularGemini(buf, len(links), width, proxied) - links = append(links, lks...) - rendered += ren + processRegular() } buf = "" // Clear buffer for next block pre = !pre @@ -324,32 +342,10 @@ func RenderGemini(s string, width, leftMargin int, proxied bool) (string, []stri // Gone through all the lines, but there still is likely a block in the buffer if pre { // File ended without closing the preformatted block - // Same code as in the loop above - - if viper.GetBool("a-general.color") && viper.GetBool("a-general.ansi") { - buf = cview.TranslateANSI(buf) - } else { - buf = ansiRegex.ReplaceAllString(buf, "") - } - rendered += fmt.Sprintf("[%s]", config.GetColorString("preformatted_text")) + - buf + "[-]" + processPre() } else { // Not preformatted, regular text - // Same code as in the loop above - - buf = ansiRegex.ReplaceAllString(buf, "") - - ren, lks := convertRegularGemini(buf, len(links), width, proxied) - links = append(links, lks...) - rendered += ren - } - - 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 + processRegular() } return rendered, links diff --git a/subscriptions/subscriptions.go b/subscriptions/subscriptions.go index 91535d3..ecee735 100644 --- a/subscriptions/subscriptions.go +++ b/subscriptions/subscriptions.go @@ -374,9 +374,9 @@ func updateAll() { defer wg.Done() for j := range jobs { if j[0] == "feed" { - updateFeed(j[1]) //nolint:errcheck + updateFeed(j[1]) } else if j[0] == "page" { - updatePage(j[1]) //nolint:errcheck + updatePage(j[1]) } } }