diff --git a/display/private.go b/display/private.go index af1245d..11c5138 100644 --- a/display/private.go +++ b/display/private.go @@ -1,7 +1,6 @@ package display import ( - "errors" "net/url" "os/exec" "strings" @@ -17,63 +16,7 @@ import ( ) // This file contains the functions that aren't part of the public API. - -// 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 -} - -func leftMargin() int { - return int(float64(termW) * viper.GetFloat64("a-general.left_margin")) -} - -func textWidth() int { - if termW <= 0 { - // This prevent a flash of 1-column text on startup, when the terminal - // width hasn't been initialized. - return viper.GetInt("a-general.max_width") - } - - rightMargin := leftMargin() - if leftMargin() > 10 { - // 10 is the max right margin - rightMargin = 10 - } - - max := termW - leftMargin() - rightMargin - if max < viper.GetInt("a-general.max_width") { - return max - } - return viper.GetInt("a-general.max_width") -} - -// queryEscape is the same as url.PathEscape, but it also replaces the +. -// This is because Gemini requires percent-escaping for queries. -func queryEscape(path string) string { - return strings.ReplaceAll(url.PathEscape(path), "+", "%2B") -} - -// resolveRelLink returns an absolute link for the given absolute link and relative one. -// It also returns an error if it could not resolve the links, which should be displayed -// to the user. -func resolveRelLink(t *tab, prev, next string) (string, error) { - if !t.hasContent() { - return next, nil - } - - prevParsed, _ := url.Parse(prev) - nextParsed, err := url.Parse(next) - if err != nil { - return "", errors.New("link URL could not be parsed") - } - return prevParsed.ResolveReference(nextParsed).String(), nil -} +// The funcs are for network and displaying. // followLink should be used when the user "clicks" a link on a page. // Not when a URL is opened on a new tab for the first time. @@ -380,50 +323,3 @@ func handleURL(t *tab, u string) (string, bool) { go dlChoice("That file could not be displayed. What would you like to do?", u, res) return ret("", false) } - -// normalizeURL attempts to make URLs that are different strings -// but point to the same place all look the same. -// -// Example: gemini://gus.guru:1965/ and //gus.guru/. -// This function will take both output the same URL each time. -// -// The string passed must already be confirmed to be a URL. -// Detection of a search string vs. a URL must happen elsewhere. -// -// It only works with absolute URLs. -func normalizeURL(u string) string { - parsed, err := url.Parse(u) - if err != nil { - return u - } - - if !strings.Contains(u, "://") && !strings.HasPrefix(u, "//") { - // No scheme at all in the URL - parsed, err = url.Parse("gemini://" + u) - if err != nil { - return u - } - } - if parsed.Scheme == "" { - // Always add scheme - parsed.Scheme = "gemini" - } else if parsed.Scheme != "gemini" { - // Not a gemini URL, nothing to do - return u - } - - parsed.User = nil // No passwords in Gemini - parsed.Fragment = "" // No fragments either - if parsed.Port() == "1965" { - // Always remove default port - parsed.Host = parsed.Hostname() - } - - // Add slash to the end of a URL with just a domain - // gemini://example.com -> gemini://example.com/ - if parsed.Path == "" { - parsed.Path = "/" - } - - return parsed.String() -} diff --git a/display/util.go b/display/util.go new file mode 100644 index 0000000..e3e7124 --- /dev/null +++ b/display/util.go @@ -0,0 +1,115 @@ +package display + +import ( + "errors" + "net/url" + "strings" + + "github.com/spf13/viper" +) + +// This file contains funcs that are small, self-contained utilities. + +// 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 +} + +func leftMargin() int { + return int(float64(termW) * viper.GetFloat64("a-general.left_margin")) +} + +func textWidth() int { + if termW <= 0 { + // This prevent a flash of 1-column text on startup, when the terminal + // width hasn't been initialized. + return viper.GetInt("a-general.max_width") + } + + rightMargin := leftMargin() + if leftMargin() > 10 { + // 10 is the max right margin + rightMargin = 10 + } + + max := termW - leftMargin() - rightMargin + if max < viper.GetInt("a-general.max_width") { + return max + } + return viper.GetInt("a-general.max_width") +} + +// queryEscape is the same as url.PathEscape, but it also replaces the +. +// This is because Gemini requires percent-escaping for queries. +func queryEscape(path string) string { + return strings.ReplaceAll(url.PathEscape(path), "+", "%2B") +} + +// resolveRelLink returns an absolute link for the given absolute link and relative one. +// It also returns an error if it could not resolve the links, which should be displayed +// to the user. +func resolveRelLink(t *tab, prev, next string) (string, error) { + if !t.hasContent() { + return next, nil + } + + prevParsed, _ := url.Parse(prev) + nextParsed, err := url.Parse(next) + if err != nil { + return "", errors.New("link URL could not be parsed") + } + return prevParsed.ResolveReference(nextParsed).String(), nil +} + +// normalizeURL attempts to make URLs that are different strings +// but point to the same place all look the same. +// +// Example: gemini://gus.guru:1965/ and //gus.guru/. +// This function will take both output the same URL each time. +// +// The string passed must already be confirmed to be a URL. +// Detection of a search string vs. a URL must happen elsewhere. +// +// It only works with absolute URLs. +func normalizeURL(u string) string { + parsed, err := url.Parse(u) + if err != nil { + return u + } + + if !strings.Contains(u, "://") && !strings.HasPrefix(u, "//") { + // No scheme at all in the URL + parsed, err = url.Parse("gemini://" + u) + if err != nil { + return u + } + } + if parsed.Scheme == "" { + // Always add scheme + parsed.Scheme = "gemini" + } else if parsed.Scheme != "gemini" { + // Not a gemini URL, nothing to do + return u + } + + parsed.User = nil // No passwords in Gemini + parsed.Fragment = "" // No fragments either + if parsed.Port() == "1965" { + // Always remove default port + parsed.Host = parsed.Hostname() + } + + // Add slash to the end of a URL with just a domain + // gemini://example.com -> gemini://example.com/ + if parsed.Path == "" { + parsed.Path = "/" + } + + return parsed.String() +}