diff --git a/VERSION b/VERSION index 46b105a..7ec1d6d 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -v2.0.0 +2.1.0 diff --git a/bombadillo.1 b/bombadillo.1 index e393ecc..f2da50e 100644 --- a/bombadillo.1 +++ b/bombadillo.1 @@ -4,8 +4,7 @@ .SH SYNOPSIS .nf .fam C -\fBbombadillo\fP [\fIurl\fP] -\fBbombadillo\fP [\fBOPTION\fP] +\fBbombadillo\fP [\fIoptions\fP] [\fIurl\fP] .fam T .fi .SH DESCRIPTION @@ -15,12 +14,16 @@ .SH OPTIONS .TP .B -\fB-v\fP -Display version information and exit. +\fB-h\fP +Display usage help and exit. Provides a list of all command line options with a short description and exits. .TP .B -\fB-h\fP -Usage help. Displays all command line options with a short description. +\fB-t\fP +Set the window title to 'Bombadillo'. Can be used in a GUI environment, however not all terminals support this feature. +.TP +.B +\fB-v\fP +Display version information and exit. .SH PROTOCOL SUPPORT All of the below protocols are supported. With the exception of gopher, the protocol name must be present as the scheme component of a url in the form of \fI[protocol]://[the rest of the url]\fP. .TP @@ -38,7 +41,7 @@ Basic support is provided for the finger protocol. The format is: \fIfinger://[[ .TP .B local -Local is similar to the \fIfile\fP protocol used in web browsers or the like, with a smaller set of features. Users can use the local scheme to view files on their local system. Directories are supported as viewable text object as well as any files. Wildcards and globbing are not supported. Using \fI~\fP to represent a user's home directory, as well as relative paths, are supported. +Local is similar to the \fIfile\fP protocol used in web browsers or the like, with a smaller set of features. Users can use the local scheme to view files on their local system. Directories are supported as viewable text object as well as any files. Wildcards and globbing are not supported. Using \fI~\fP to represent a user's home directory, as well as relative paths, are supported. The \fIcolor\fP theme has no effect on this protocol and all terminal escape sequences will be rendered to the screen literally. .TP .B telnet @@ -88,6 +91,13 @@ k Scroll up a single line. .TP .B +n +Jump to next found text item. +.TP +.B +Jump to previous found text item. +.TP +.B q Quit \fBbombadillo\fP. .TP @@ -100,6 +110,10 @@ u Scroll up an amount corresponding to 75% of your terminal window height in the current document. .TP .B +/ +Search for text within current document. / followed by a text query will highlight and allow navigation of found text. / with an empty query will clear the current query. +.TP +.B Toggle the scroll focus between the bookmarks panel and the document panel. Only has an effect if the bookmarks panel is open. .TP @@ -150,7 +164,7 @@ check [setting name] Displays the current value for a given configuration setting. \fIc\fP can be used instead of the full \fIcheck\fP. .TP .B -delete [bookmark id]] +delete [bookmark id] Deletes the bookmark matching the bookmark id. \fId\fP can be used instead of the full \fIdelete\fP. .TP .B @@ -186,7 +200,7 @@ search [keywords\.\.\.] Submits a search to the search engine set by the \fIsearchengine\fP setting, with the query being the provided keyword(s). .TP .B -set [setting name] +set [setting name] [value] Sets the value for a given configuration setting. \fIs\fP can be used instead of the full \fIset\fP. .TP .B @@ -210,6 +224,10 @@ configlocation The path to the directory that the \fI.bombadillo.ini\fP configuration file is stored in. This is a \fBread only\fP setting and cannot be changed with the \fIset\fP command, but it can be read with the \fIcheck\fP command. .TP .B +defaultscheme +The scheme that should be used when no scheme is present in a given URL. \fIgopher\fP, \fIgemini\fP, \fIhttp\fP, and \fIhttps\fP are valid values. +.TP +.B homeurl The url that \fBbombadillo\fP navigates to when the program loads or when the \fIhome\fP or \fIh\fP LINE COMMAND is issued. This should be a valid url. If a scheme/protocol is not included, gopher will be assumed. .TP @@ -227,7 +245,7 @@ Tells the browser what command to use to start a telnet session. Should be a val .TP .B theme -Can toggle between visual modes. Valid values are \fInormal\fP and \fIinverse\fP. When set to inverse, the terminal color mode is inverted. +Can toggle between visual modes. Valid values are \fInormal\fP, \fIcolor\fP, and \fIinverse\fP. When set to inverse, the normal mode colors are inverted. Both normal and inverse modes filter out terminal escape sequences. When set to color, Bombadillo will render terminal escape sequences representing colors when it finds them in documents. .TP .B tlscertificate diff --git a/bombadillo.desktop b/bombadillo.desktop index 3ca2531..4510dcd 100644 --- a/bombadillo.desktop +++ b/bombadillo.desktop @@ -5,6 +5,6 @@ GenericName=Non-Web Browser Comment=View gopher, gemini, finger, telnet, http(s) sites over the internet Terminal=true Categories=Network;WebBrowser;ConsoleOnly; -Exec=bombadillo %U +Exec=bombadillo -t %u Icon=bombadillo-icon MimeType=x-scheme-handler/gopher;x-scheme-handler/gemini;x-scheme-handler/finger; diff --git a/client.go b/client.go index 1704ad6..ed642a2 100644 --- a/client.go +++ b/client.go @@ -47,8 +47,7 @@ func (c *client) GetSizeOnce() { cmd.Stdin = os.Stdin out, err := cmd.Output() if err != nil { - fmt.Println("Fatal error: Unable to retrieve terminal size") - os.Exit(5) + cui.Exit(5, "Fatal error: Unable to retrieve terminal size") } var h, w int _, _ = fmt.Sscan(string(out), &h, &w) @@ -66,8 +65,7 @@ func (c *client) GetSize() { cmd.Stdin = os.Stdin out, err := cmd.Output() if err != nil { - fmt.Println("Fatal error: Unable to retrieve terminal size") - os.Exit(5) + cui.Exit(5, "Fatal error: Unable to retrieve terminal size") } var h, w int _, _ = fmt.Sscan(string(out), &h, &w) @@ -88,10 +86,12 @@ func (c *client) Draw() { screen.WriteString("\033[0m") screen.WriteString(c.TopBar.Render(c.Width, c.Options["theme"])) screen.WriteString("\n") - pageContent := c.PageState.Render(c.Height, c.Width-1) + pageContent := c.PageState.Render(c.Height, c.Width-1, (c.Options["theme"] == "color")) + var re *regexp.Regexp if c.Options["theme"] == "inverse" { screen.WriteString("\033[7m") } + re = regexp.MustCompile(`\033\[(?:\d*;?)+[A-Za-z]`) if c.BookMarks.IsOpen { bm := c.BookMarks.Render(c.Width, c.Height) bmWidth := len([]rune(bm[0])) @@ -99,7 +99,14 @@ func (c *client) Draw() { if c.Width > bmWidth { contentWidth := c.Width - bmWidth if i < len(pageContent) { - screen.WriteString(fmt.Sprintf("%-*.*s", contentWidth, contentWidth, pageContent[i])) + extra := 0 + if c.Options["theme"] == "color" { + escapes := re.FindAllString(pageContent[i], -1) + for _, esc := range escapes { + extra += len(esc) + } + } + screen.WriteString(fmt.Sprintf("%-*.*s", contentWidth+extra, contentWidth+extra, pageContent[i])) } else { screen.WriteString(fmt.Sprintf("%-*.*s", contentWidth, contentWidth, " ")) } @@ -112,6 +119,9 @@ func (c *client) Draw() { screen.WriteString("\033[2m") } + if c.Options["theme"] == "color" { + screen.WriteString("\033[0m") + } screen.WriteString(bm[i]) if c.Options["theme"] == "inverse" && !c.BookMarks.IsFocused { @@ -125,7 +135,12 @@ func (c *client) Draw() { } else { for i := 0; i < c.Height-3; i++ { if i < len(pageContent) { - screen.WriteString(fmt.Sprintf("%-*.*s", c.Width, c.Width, pageContent[i])) + extra := 0 + escapes := re.FindAllString(pageContent[i], -1) + for _, esc := range escapes { + extra += len(esc) + } + screen.WriteString(fmt.Sprintf("%-*.*s", c.Width+extra, c.Width+extra, pageContent[i])) screen.WriteString("\n") } else { screen.WriteString(fmt.Sprintf("%-*.*s", c.Width, c.Width, " ")) @@ -157,7 +172,7 @@ func (c *client) TakeControlInput() { c.Scroll(-1) case 'q', 'Q': // quit bombadillo - cui.Exit() + cui.Exit(0, "") case 'g': // scroll to top c.ClearMessage() @@ -217,14 +232,53 @@ func (c *client) TakeControlInput() { // Toggle bookmark browser focus on/off c.BookMarks.ToggleFocused() c.Draw() + case 'n': + // Next search item + c.ClearMessage() + err := c.NextSearchItem(1) + if err != nil { + c.SetMessage(err.Error(), false) + c.DrawMessage() + } + case 'N': + // Previous search item + c.ClearMessage() + err := c.NextSearchItem(-1) + if err != nil { + c.SetMessage(err.Error(), false) + c.DrawMessage() + } + case '/': + // Search for text + c.ClearMessage() + c.ClearMessageLine() + if c.Options["theme"] == "normal" || c.Options["theme"] == "color" { + fmt.Printf("\033[7m%*.*s\r", c.Width, c.Width, "") + } + entry, err := cui.GetLine("/") + c.ClearMessageLine() + if err != nil { + c.SetMessage(err.Error(), true) + c.DrawMessage() + break + } + err = c.find(entry) + if err != nil { + c.SetMessage(err.Error(), true) + c.DrawMessage() + } + err = c.NextSearchItem(0) + if err != nil { + c.Draw() + } case ':', ' ': // Process a command c.ClearMessage() c.ClearMessageLine() - if c.Options["theme"] == "normal" { + if c.Options["theme"] == "normal" || c.Options["theme"] == "color" { fmt.Printf("\033[7m%*.*s\r", c.Width, c.Width, "") } - entry, err := cui.GetLine() + entry, err := cui.GetLine(": ") c.ClearMessageLine() if err != nil { c.SetMessage(err.Error(), true) @@ -278,7 +332,7 @@ func (c *client) simpleCommand(action string) { action = strings.ToUpper(action) switch action { case "Q", "QUIT": - cui.Exit() + cui.Exit(0, "") case "H", "HOME": if c.Options["homeurl"] != "unset" { go c.Visit(c.Options["homeurl"]) @@ -622,11 +676,11 @@ func (c *client) search(query, url, question string) { if query == "" { c.ClearMessage() c.ClearMessageLine() - if c.Options["theme"] == "normal" { + if c.Options["theme"] == "normal" || c.Options["theme"] == "color" { fmt.Printf("\033[7m%*.*s\r", c.Width, c.Width, "") } fmt.Print(question) - entry, err = cui.GetLine() + entry, err = cui.GetLine("? ") c.ClearMessageLine() if err != nil { c.SetMessage(err.Error(), true) @@ -728,9 +782,13 @@ func (c *client) ReloadPage() error { return fmt.Errorf("There is no page to reload") } url := c.PageState.History[c.PageState.Position].Location.Full - err := c.PageState.NavigateHistory(-1) - if err != nil { - return err + if c.PageState.Position == 0 { + c.PageState.Position-- + } else { + err := c.PageState.NavigateHistory(-1) + if err != nil { + return err + } } length := c.PageState.Length c.Visit(url) @@ -771,7 +829,7 @@ func (c *client) DrawMessage() { func (c *client) RenderMessage() string { leadIn, leadOut := "", "" - if c.Options["theme"] == "normal" { + if c.Options["theme"] == "normal" || c.Options["theme"] == "color" { leadIn = "\033[7m" leadOut = "\033[0m" } @@ -780,7 +838,7 @@ func (c *client) RenderMessage() string { leadIn = "\033[31;1m" leadOut = "\033[0m" - if c.Options["theme"] == "normal" { + if c.Options["theme"] == "normal" || c.Options["theme"] == "color" { leadIn = "\033[41;1;7m" } } @@ -889,7 +947,7 @@ func (c *client) handleGopher(u Url) { return } pg := MakePage(u, content, links) - pg.WrapContent(c.Width - 1) + pg.WrapContent(c.Width-1, (c.Options["theme"] == "color")) c.PageState.Add(pg) c.SetPercentRead() c.ClearMessage() @@ -912,7 +970,7 @@ func (c *client) handleGemini(u Url) { case 2: if capsule.MimeMaj == "text" { pg := MakePage(u, capsule.Content, capsule.Links) - pg.WrapContent(c.Width - 1) + pg.WrapContent(c.Width-1, (c.Options["theme"] == "color")) c.PageState.Add(pg) c.SetPercentRead() c.ClearMessage() @@ -960,7 +1018,7 @@ func (c *client) handleLocal(u Url) { return } pg := MakePage(u, content, links) - pg.WrapContent(c.Width - 1) + pg.WrapContent(c.Width-1, (c.Options["theme"] == "color")) c.PageState.Add(pg) c.SetPercentRead() c.ClearMessage() @@ -976,7 +1034,7 @@ func (c *client) handleFinger(u Url) { return } pg := MakePage(u, content, []string{}) - pg.WrapContent(c.Width - 1) + pg.WrapContent(c.Width-1, (c.Options["theme"] == "color")) c.PageState.Add(pg) c.SetPercentRead() c.ClearMessage() @@ -996,7 +1054,7 @@ func (c *client) handleWeb(u Url) { return } pg := MakePage(u, page.Content, page.Links) - pg.WrapContent(c.Width - 1) + pg.WrapContent(c.Width-1, (c.Options["theme"] == "color")) c.PageState.Add(pg) c.SetPercentRead() c.ClearMessage() @@ -1029,6 +1087,67 @@ func (c *client) handleWeb(u Url) { } } +func (c *client) find(s string) error { + c.PageState.History[c.PageState.Position].SearchTerm = s + c.PageState.History[c.PageState.Position].FindText() + if s == "" { + return nil + } + if len(c.PageState.History[c.PageState.Position].FoundLinkLines) == 0 { + return fmt.Errorf("No text matching %q was found", s) + } + return nil +} + +func (c *client) NextSearchItem(dir int) error { + page := c.PageState.History[c.PageState.Position] + if len(page.FoundLinkLines) == 0 { + return fmt.Errorf("The search is over before it has begun") + } + c.PageState.History[c.PageState.Position].SearchIndex += dir + page.SearchIndex += dir + if page.SearchIndex < 0 { + c.PageState.History[c.PageState.Position].SearchIndex = 0 + page.SearchIndex = 0 + } + + if page.SearchIndex >= len(page.FoundLinkLines) { + c.PageState.History[c.PageState.Position].SearchIndex = len(page.FoundLinkLines) - 1 + return fmt.Errorf("The search path goes no further") + } else if page.SearchIndex < 0 { + c.PageState.History[c.PageState.Position].SearchIndex = 0 + return fmt.Errorf("You are at the beginning of the search path") + } + + diff := page.FoundLinkLines[page.SearchIndex] - page.ScrollPosition + c.ScrollForSearch(diff) + c.Draw() + return nil +} + +func (c *client) ScrollForSearch(amount int) { + var percentRead int + page := c.PageState.History[c.PageState.Position] + bottom := len(page.WrappedContent) - c.Height + 3 // 3 for the three bars: top, msg, bottom + + newScrollPosition := page.ScrollPosition + amount + if newScrollPosition < 0 { + newScrollPosition = 0 + } else if newScrollPosition > bottom { + newScrollPosition = bottom + } + + c.PageState.History[c.PageState.Position].ScrollPosition = newScrollPosition + + if len(page.WrappedContent) < c.Height-3 { + percentRead = 100 + } else { + percentRead = int(float32(newScrollPosition+c.Height-3) / float32(len(page.WrappedContent)) * 100.0) + } + c.FootBar.SetPercentRead(percentRead) + c.Draw() +} + //------------------------------------------------\\ // + + + F U N C T I O N S + + + \\ //--------------------------------------------------\\ diff --git a/cui/cui.go b/cui/cui.go index 85b14cc..7f2be9f 100644 --- a/cui/cui.go +++ b/cui/cui.go @@ -44,9 +44,13 @@ func moveCursorToward(dir string, amount int) { } // Exit performs cleanup operations before exiting the application -func Exit() { +func Exit(exitCode int, msg string) { CleanupTerm() - os.Exit(0) + if msg != "" { + fmt.Print(msg, "\n") + } + fmt.Print("\033[23;0t") // Restore window title from terminal stack + os.Exit(exitCode) } // InitTerm sets the terminal modes appropriate for Bombadillo @@ -93,11 +97,11 @@ func Getch() rune { return char } -func GetLine() (string, error) { +func GetLine(prefix string) (string, error) { SetLineMode() reader := bufio.NewReader(os.Stdin) - fmt.Print(": ") + fmt.Print(prefix) text, err := reader.ReadString('\n') if err != nil { return "", err diff --git a/defaults.go b/defaults.go index a89e7f5..1c0a230 100644 --- a/defaults.go +++ b/defaults.go @@ -50,7 +50,8 @@ var defaultOptions = map[string]string{ "searchengine": "gopher://gopher.floodgap.com:70/7/v2/vs", "telnetcommand": "telnet", "configlocation": xdgConfigPath(), - "theme": "normal", // "normal", "inverted" + "defaultscheme": "gopher", // "gopher", "gemini", "http", "https" + "theme": "normal", // "normal", "inverted", "color" "tlscertificate": "", "tlskey": "", "webmode": "none", // "none", "gui", "lynx", "w3m", "elinks" diff --git a/main.go b/main.go index ef01c34..1362e78 100644 --- a/main.go +++ b/main.go @@ -47,10 +47,6 @@ func saveConfig() error { opts.WriteString("\n[SETTINGS]\n") for k, v := range bombadillo.Options { - if k == "theme" && v != "normal" && v != "inverse" { - v = "normal" - bombadillo.Options["theme"] = "normal" - } opts.WriteString(k) opts.WriteRune('=') opts.WriteString(v) @@ -66,8 +62,9 @@ func saveConfig() error { func validateOpt(opt, val string) bool { var validOpts = map[string][]string{ - "webmode": []string{"none", "gui", "lynx", "w3m", "elinks"}, - "theme": []string{"normal", "inverse"}, + "webmode": []string{"none", "gui", "lynx", "w3m", "elinks"}, + "theme": []string{"normal", "inverse", "color"}, + "defaultscheme": []string{"gopher", "gemini", "http", "https"}, } opt = strings.ToLower(opt) @@ -86,19 +83,27 @@ func validateOpt(opt, val string) bool { func lowerCaseOpt(opt, val string) string { switch opt { - case "webmode", "theme": + case "webmode", "theme", "defaultscheme": return strings.ToLower(val) default: return val } } -func loadConfig() error { - file, err := os.Open(bombadillo.Options["configlocation"] + "/.bombadillo.ini") +func loadConfig() { + err := os.MkdirAll(bombadillo.Options["configlocation"], 0755) + if err != nil { + exitMsg := fmt.Sprintf("Error creating 'configlocation' directory: %s", err.Error()) + cui.Exit(3, exitMsg) + } + + fp := filepath.Join(bombadillo.Options["configlocation"], ".bombadillo.ini") + file, err := os.Open(fp) if err != nil { err = saveConfig() if err != nil { - return err + exitMsg := fmt.Sprintf("Error writing config file during bootup: %s", err.Error()) + cui.Exit(4, exitMsg) } } @@ -108,11 +113,7 @@ func loadConfig() error { for _, v := range settings.Settings { lowerkey := strings.ToLower(v.Key) if lowerkey == "configlocation" { - // The config defaults to the home folder. - // Users cannot really edit this value. But - // a compile time override is available. - // It is still stored in the ini and as a part - // of the options map. + // Read only continue } @@ -132,17 +133,14 @@ func loadConfig() error { for _, v := range settings.Certs { bombadillo.Certs.Add(v.Key, v.Value) } - - return nil } -func initClient() error { +func initClient() { bombadillo = MakeClient(" ((( Bombadillo ))) ") - err := loadConfig() + loadConfig() if bombadillo.Options["tlscertificate"] != "" && bombadillo.Options["tlskey"] != "" { bombadillo.Certs.LoadCertificate(bombadillo.Options["tlscertificate"], bombadillo.Options["tlskey"]) } - return err } // In the event of specific signals, ensure the display is shown correctly. @@ -159,7 +157,7 @@ func handleSignals(c <-chan os.Signal) { cui.InitTerm() bombadillo.Draw() case syscall.SIGINT: - cui.Exit() + cui.Exit(130, "") } } } @@ -168,10 +166,10 @@ func handleSignals(c <-chan os.Signal) { func printHelp() { art := `Bombadillo - a non-web browser -Syntax: bombadillo [url] - bombadillo [options...] +Syntax: bombadillo [options] [url] Examples: bombadillo gopher://bombadillo.colorfield.space + bombadillo -t bombadillo -v Options: @@ -182,6 +180,7 @@ Options: func main() { getVersion := flag.Bool("v", false, "Display version information and exit") + addTitleToXWindow := flag.Bool("t", false, "Set the window title to 'Bombadillo'. Can be used in a GUI environment, however not all terminals support this feature.") flag.Usage = printHelp flag.Parse() if *getVersion { @@ -191,13 +190,15 @@ func main() { args := flag.Args() cui.InitTerm() - defer cui.Exit() - err := initClient() - if err != nil { - // if we can't initialize we should bail out - panic(err) + + if *addTitleToXWindow { + fmt.Print("\033[22;0t") // Store window title on terminal stack + fmt.Print("\033]0;Bombadillo\007") // Update window title } + defer cui.Exit(0, "") + initClient() + // watch for signals, send them to be handled c := make(chan os.Signal, 1) signal.Notify(c, syscall.SIGTSTP, syscall.SIGCONT, syscall.SIGINT) diff --git a/page.go b/page.go index 4c8c0d8..46c95ba 100644 --- a/page.go +++ b/page.go @@ -1,6 +1,7 @@ package main import ( + "fmt" "strings" ) @@ -17,6 +18,9 @@ type Page struct { Links []string Location Url ScrollPosition int + FoundLinkLines []int + SearchTerm string + SearchIndex int } //------------------------------------------------\\ @@ -47,15 +51,23 @@ func (p *Page) ScrollPositionRange(termHeight int) (int, int) { // width and updates the WrappedContent // of the Page struct width a string slice // of the wrapped data -func (p *Page) WrapContent(width int) { +func (p *Page) WrapContent(width int, color bool) { counter := 0 var content strings.Builder + var esc strings.Builder escape := false content.Grow(len(p.RawContent)) for _, ch := range []rune(p.RawContent) { if escape { + if color { + esc.WriteRune(ch) + } if (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') { escape = false + if ch == 'm' { + content.WriteString(esc.String()) + esc.Reset() + } } continue } @@ -74,10 +86,20 @@ func (p *Page) WrapContent(width int) { // Get rid of control characters we dont want continue } else if ch == 27 { + if p.Location.Scheme == "local" { + if counter+4 >= width { + content.WriteRune('\n') + } + content.WriteString("\\033") + continue + } escape = true + if color { + esc.WriteRune(ch) + } continue } else { - if counter < width { + if counter <= width { content.WriteRune(ch) counter++ } else { @@ -94,6 +116,47 @@ func (p *Page) WrapContent(width int) { } p.WrappedContent = strings.Split(content.String(), "\n") + p.HighlightFoundText() +} + +func (p *Page) HighlightFoundText() { + if p.SearchTerm == "" { + return + } + for i, ln := range p.WrappedContent { + found := strings.Index(ln, p.SearchTerm) + if found < 0 { + continue + } + format := "\033[7m%s\033[27m" + if bombadillo.Options["theme"] == "inverse" { + format = "\033[27m%s\033[7m" + } + ln = strings.ReplaceAll(ln, p.SearchTerm, fmt.Sprintf(format, p.SearchTerm)) + p.WrappedContent[i] = ln + } +} + +func (p *Page) FindText() { + p.FoundLinkLines = make([]int, 0, 10) + s := p.SearchTerm + p.SearchIndex = 0 + if s == "" { + return + } + format := "\033[7m%s\033[27m" + if bombadillo.Options["theme"] == "inverse" { + format = "\033[27m%s\033[7m" + } + for i, ln := range p.WrappedContent { + found := strings.Index(ln, s) + if found < 0 { + continue + } + ln = strings.ReplaceAll(ln, s, fmt.Sprintf(format, s)) + p.WrappedContent[i] = ln + p.FoundLinkLines = append(p.FoundLinkLines, i) + } } //------------------------------------------------\\ @@ -102,6 +165,6 @@ func (p *Page) WrapContent(width int) { // MakePage returns a Page struct with default values func MakePage(url Url, content string, links []string) Page { - p := Page{make([]string, 0), content, links, url, 0} + p := Page{make([]string, 0), content, links, url, 0, make([]int, 0), "", 0} return p } diff --git a/pages.go b/pages.go index 3a1db87..46d962b 100644 --- a/pages.go +++ b/pages.go @@ -60,13 +60,13 @@ func (p *Pages) Add(pg Page) { // Render wraps the content for the current page and returns // the page content as a string slice -func (p *Pages) Render(termHeight, termWidth int) []string { +func (p *Pages) Render(termHeight, termWidth int, color bool) []string { if p.Length < 1 { return make([]string, 0) } pos := p.History[p.Position].ScrollPosition prev := len(p.History[p.Position].WrappedContent) - p.History[p.Position].WrapContent(termWidth) + p.History[p.Position].WrapContent(termWidth, color) now := len(p.History[p.Position].WrappedContent) if prev > now { diff := prev - now diff --git a/url.go b/url.go index bc1895a..a6823fa 100644 --- a/url.go +++ b/url.go @@ -44,6 +44,7 @@ func MakeUrl(u string) (Url, error) { if len(u) < 1 { return Url{}, fmt.Errorf("Invalid url, unable to parse") } + if strings.HasPrefix(u, "finger://") { return parseFinger(u) } @@ -103,7 +104,7 @@ func MakeUrl(u string) (Url, error) { out.Scheme = strings.ToLower(out.Scheme) if out.Scheme == "" { - out.Scheme = "gopher" + out.Scheme = bombadillo.Options["defaultscheme"] } if out.Scheme == "gopher" && out.Port == "" {