From c7569123885572568291cb47aff1c40259eeb61c Mon Sep 17 00:00:00 2001 From: asdf Date: Tue, 3 Dec 2019 19:21:05 +1100 Subject: [PATCH 01/25] Small man page corrections as per #110 --- bombadillo.1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bombadillo.1 b/bombadillo.1 index d716ee0..cc3b47c 100644 --- a/bombadillo.1 +++ b/bombadillo.1 @@ -150,7 +150,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 +186,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 From 2900702313374f6d40ab48a94a218aa037048fab Mon Sep 17 00:00:00 2001 From: sloumdrone Date: Tue, 3 Dec 2019 19:22:15 -0800 Subject: [PATCH 02/25] Updates the XDG path creation to create needed dirs and panic on failure --- defaults.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/defaults.go b/defaults.go index a89e7f5..52bb828 100644 --- a/defaults.go +++ b/defaults.go @@ -71,7 +71,12 @@ func homePath() string { func xdgConfigPath() string { configPath := os.Getenv("XDG_CONFIG_HOME") if configPath == "" { - return filepath.Join(homePath(), ".config") + fp := filepath.Join(homePath(), ".config") + err := os.MkdirAll(fp, 0755) + if err != nil { + panic("Unable to create configuration directory: ~/.config") + } + return fp } return configPath } From 217b069172687d4bf7feaa36b94918681ef3aa9b Mon Sep 17 00:00:00 2001 From: sloumdrone Date: Tue, 3 Dec 2019 19:58:18 -0800 Subject: [PATCH 03/25] Moves folder creation and error handling to loadConfig --- defaults.go | 7 +------ main.go | 8 +++++++- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/defaults.go b/defaults.go index 52bb828..a89e7f5 100644 --- a/defaults.go +++ b/defaults.go @@ -71,12 +71,7 @@ func homePath() string { func xdgConfigPath() string { configPath := os.Getenv("XDG_CONFIG_HOME") if configPath == "" { - fp := filepath.Join(homePath(), ".config") - err := os.MkdirAll(fp, 0755) - if err != nil { - panic("Unable to create configuration directory: ~/.config") - } - return fp + return filepath.Join(homePath(), ".config") } return configPath } diff --git a/main.go b/main.go index ef01c34..3404b38 100644 --- a/main.go +++ b/main.go @@ -94,7 +94,13 @@ func lowerCaseOpt(opt, val string) string { } func loadConfig() error { - file, err := os.Open(bombadillo.Options["configlocation"] + "/.bombadillo.ini") + fp := filepath.Join(bombadillo.Options["configlocation"], ".bombadillo.ini") + err := os.MkdirAll(fp, 0755) + if err != nil { + return fmt.Errorf("configlocation cannot be written to or is invalid") + } + + file, err := os.Open(fp) if err != nil { err = saveConfig() if err != nil { From 637c23d4ccc21f2cad6d0326deb920da83f3ddb7 Mon Sep 17 00:00:00 2001 From: asdf Date: Wed, 4 Dec 2019 19:18:01 +1100 Subject: [PATCH 04/25] os.MkallDir now just makes directories --- main.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/main.go b/main.go index 3404b38..bc986c0 100644 --- a/main.go +++ b/main.go @@ -94,12 +94,12 @@ func lowerCaseOpt(opt, val string) string { } func loadConfig() error { - fp := filepath.Join(bombadillo.Options["configlocation"], ".bombadillo.ini") - err := os.MkdirAll(fp, 0755) + err := os.MkdirAll(bombadillo.Options["configlocation"], 0755) if err != nil { - return fmt.Errorf("configlocation cannot be written to or is invalid") + return fmt.Errorf("Error creating configlocation: %s", err.Error()) } + fp := filepath.Join(bombadillo.Options["configlocation"], ".bombadillo.ini") file, err := os.Open(fp) if err != nil { err = saveConfig() From 6f0be3b4e48dc77823435e6fd45c1e66a7824899 Mon Sep 17 00:00:00 2001 From: sloumdrone Date: Thu, 5 Dec 2019 21:46:31 -0800 Subject: [PATCH 05/25] Adds visible error handling for bad exits and improves exit code use --- client.go | 12 ++++++------ cui/cui.go | 9 +++++++-- main.go | 7 ++++--- 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/client.go b/client.go index 1704ad6..6ae0f40 100644 --- a/client.go +++ b/client.go @@ -47,8 +47,8 @@ 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.ExitMessage = "Fatal error: Unable to retrieve terminal size" + cui.Exit(5) } var h, w int _, _ = fmt.Sscan(string(out), &h, &w) @@ -66,8 +66,8 @@ 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.ExitMessage = "Fatal error: Unable to retrieve terminal size" + cui.Exit(5) } var h, w int _, _ = fmt.Sscan(string(out), &h, &w) @@ -157,7 +157,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() @@ -278,7 +278,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"]) diff --git a/cui/cui.go b/cui/cui.go index 85b14cc..3565461 100644 --- a/cui/cui.go +++ b/cui/cui.go @@ -26,6 +26,8 @@ var Shapes = map[string]string{ "abr": "▟", } +var ExitMessage string + func MoveCursorTo(row, col int) { fmt.Printf("\033[%d;%dH", row, col) } @@ -44,9 +46,12 @@ func moveCursorToward(dir string, amount int) { } // Exit performs cleanup operations before exiting the application -func Exit() { +func Exit(exitCode int) { CleanupTerm() - os.Exit(0) + if ExitMessage != "" { + fmt.Print(ExitMessage, "\n") + } + os.Exit(exitCode) } // InitTerm sets the terminal modes appropriate for Bombadillo diff --git a/main.go b/main.go index bc986c0..205f772 100644 --- a/main.go +++ b/main.go @@ -165,7 +165,7 @@ func handleSignals(c <-chan os.Signal) { cui.InitTerm() bombadillo.Draw() case syscall.SIGINT: - cui.Exit() + cui.Exit(130) } } } @@ -197,11 +197,12 @@ func main() { args := flag.Args() cui.InitTerm() - defer cui.Exit() + defer cui.Exit(0) err := initClient() if err != nil { // if we can't initialize we should bail out - panic(err) + cui.ExitMessage = err.Error() + cui.Exit(1) } // watch for signals, send them to be handled From 8b004df1d5d6b1697fa244914fb2c117cfd437de Mon Sep 17 00:00:00 2001 From: sloumdrone Date: Thu, 5 Dec 2019 21:55:07 -0800 Subject: [PATCH 06/25] Better specific errors on bootup --- main.go | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/main.go b/main.go index 205f772..3a6908f 100644 --- a/main.go +++ b/main.go @@ -93,10 +93,11 @@ func lowerCaseOpt(opt, val string) string { } } -func loadConfig() error { +func loadConfig() { err := os.MkdirAll(bombadillo.Options["configlocation"], 0755) if err != nil { - return fmt.Errorf("Error creating configlocation: %s", err.Error()) + cui.ExitMessage = fmt.Sprintf("Error creating configlocation: %s", err.Error()) + cui.Exit(3) } fp := filepath.Join(bombadillo.Options["configlocation"], ".bombadillo.ini") @@ -104,7 +105,8 @@ func loadConfig() error { if err != nil { err = saveConfig() if err != nil { - return err + cui.ExitMessage = fmt.Sprintf("Error saving config during bootup: %s", err.Error()) + cui.Exit(4) } } @@ -138,17 +140,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. @@ -198,12 +197,7 @@ func main() { cui.InitTerm() defer cui.Exit(0) - err := initClient() - if err != nil { - // if we can't initialize we should bail out - cui.ExitMessage = err.Error() - cui.Exit(1) - } + initClient() // watch for signals, send them to be handled c := make(chan os.Signal, 1) From fde463be0ffe437853e5c9af38398682b025e140 Mon Sep 17 00:00:00 2001 From: sloumdrone Date: Thu, 5 Dec 2019 22:01:22 -0800 Subject: [PATCH 07/25] Removes unnecessary variable from exit routine --- client.go | 10 ++++------ cui/cui.go | 8 +++----- main.go | 12 ++++++------ 3 files changed, 13 insertions(+), 17 deletions(-) diff --git a/client.go b/client.go index 6ae0f40..7a24c94 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 { - cui.ExitMessage = "Fatal error: Unable to retrieve terminal size" - cui.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 { - cui.ExitMessage = "Fatal error: Unable to retrieve terminal size" - cui.Exit(5) + cui.Exit(5, "Fatal error: Unable to retrieve terminal size") } var h, w int _, _ = fmt.Sscan(string(out), &h, &w) @@ -157,7 +155,7 @@ func (c *client) TakeControlInput() { c.Scroll(-1) case 'q', 'Q': // quit bombadillo - cui.Exit(0) + cui.Exit(0, "") case 'g': // scroll to top c.ClearMessage() @@ -278,7 +276,7 @@ func (c *client) simpleCommand(action string) { action = strings.ToUpper(action) switch action { case "Q", "QUIT": - cui.Exit(0) + cui.Exit(0, "") case "H", "HOME": if c.Options["homeurl"] != "unset" { go c.Visit(c.Options["homeurl"]) diff --git a/cui/cui.go b/cui/cui.go index 3565461..b6ca90b 100644 --- a/cui/cui.go +++ b/cui/cui.go @@ -26,8 +26,6 @@ var Shapes = map[string]string{ "abr": "▟", } -var ExitMessage string - func MoveCursorTo(row, col int) { fmt.Printf("\033[%d;%dH", row, col) } @@ -46,10 +44,10 @@ func moveCursorToward(dir string, amount int) { } // Exit performs cleanup operations before exiting the application -func Exit(exitCode int) { +func Exit(exitCode int, msg string) { CleanupTerm() - if ExitMessage != "" { - fmt.Print(ExitMessage, "\n") + if msg != "" { + fmt.Print(msg, "\n") } os.Exit(exitCode) } diff --git a/main.go b/main.go index 3a6908f..abaf0eb 100644 --- a/main.go +++ b/main.go @@ -96,8 +96,8 @@ func lowerCaseOpt(opt, val string) string { func loadConfig() { err := os.MkdirAll(bombadillo.Options["configlocation"], 0755) if err != nil { - cui.ExitMessage = fmt.Sprintf("Error creating configlocation: %s", err.Error()) - cui.Exit(3) + exitMsg := fmt.Sprintf("Error creating configlocation: %s", err.Error()) + cui.Exit(3, exitMsg) } fp := filepath.Join(bombadillo.Options["configlocation"], ".bombadillo.ini") @@ -105,8 +105,8 @@ func loadConfig() { if err != nil { err = saveConfig() if err != nil { - cui.ExitMessage = fmt.Sprintf("Error saving config during bootup: %s", err.Error()) - cui.Exit(4) + exitMsg := fmt.Sprintf("Error saving config during bootup: %s", err.Error()) + cui.Exit(4, exitMsg) } } @@ -164,7 +164,7 @@ func handleSignals(c <-chan os.Signal) { cui.InitTerm() bombadillo.Draw() case syscall.SIGINT: - cui.Exit(130) + cui.Exit(130, "") } } } @@ -196,7 +196,7 @@ func main() { args := flag.Args() cui.InitTerm() - defer cui.Exit(0) + defer cui.Exit(0, "") initClient() // watch for signals, send them to be handled From 817affcf1497136172dec325a422032245054a85 Mon Sep 17 00:00:00 2001 From: asdf Date: Sat, 7 Dec 2019 11:14:16 +1100 Subject: [PATCH 08/25] Amended error message wording --- main.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/main.go b/main.go index abaf0eb..3b6e8ba 100644 --- a/main.go +++ b/main.go @@ -96,7 +96,7 @@ func lowerCaseOpt(opt, val string) string { func loadConfig() { err := os.MkdirAll(bombadillo.Options["configlocation"], 0755) if err != nil { - exitMsg := fmt.Sprintf("Error creating configlocation: %s", err.Error()) + exitMsg := fmt.Sprintf("Error creating 'configlocation' directory: %s", err.Error()) cui.Exit(3, exitMsg) } @@ -105,7 +105,7 @@ func loadConfig() { if err != nil { err = saveConfig() if err != nil { - exitMsg := fmt.Sprintf("Error saving config during bootup: %s", err.Error()) + exitMsg := fmt.Sprintf("Error writing config file during bootup: %s", err.Error()) cui.Exit(4, exitMsg) } } From 2c9c00ab8dfeb1d868f2fa5158948641fdd39852 Mon Sep 17 00:00:00 2001 From: sloumdrone Date: Fri, 6 Dec 2019 21:53:21 -0800 Subject: [PATCH 09/25] Makes the first history position reloadable --- client.go | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/client.go b/client.go index 7a24c94..9eb1962 100644 --- a/client.go +++ b/client.go @@ -726,9 +726,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.Length == 1 { + c.PageState.Position-- + } else { + err := c.PageState.NavigateHistory(-1) + if err != nil { + return err + } } length := c.PageState.Length c.Visit(url) From d5ff5694d473eb05a6fc37c74895556677f3266d Mon Sep 17 00:00:00 2001 From: sloumdrone Date: Fri, 6 Dec 2019 22:19:25 -0800 Subject: [PATCH 10/25] Adds an option to set a default scheme --- bombadillo.1 | 4 ++++ defaults.go | 1 + main.go | 7 ++++--- url.go | 3 ++- 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/bombadillo.1 b/bombadillo.1 index 83a27c7..aa0296a 100644 --- a/bombadillo.1 +++ b/bombadillo.1 @@ -210,6 +210,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 diff --git a/defaults.go b/defaults.go index a89e7f5..022435f 100644 --- a/defaults.go +++ b/defaults.go @@ -50,6 +50,7 @@ var defaultOptions = map[string]string{ "searchengine": "gopher://gopher.floodgap.com:70/7/v2/vs", "telnetcommand": "telnet", "configlocation": xdgConfigPath(), + "defaultscheme": "gopher", // "gopher", "gemini", "http", "https" "theme": "normal", // "normal", "inverted" "tlscertificate": "", "tlskey": "", diff --git a/main.go b/main.go index 3b6e8ba..5fb0596 100644 --- a/main.go +++ b/main.go @@ -66,8 +66,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"}, + "defaultscheme": []string{"gopher", "gemini", "http", "https"}, } opt = strings.ToLower(opt) @@ -86,7 +87,7 @@ 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 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 == "" { From 275f7928eafd4392b0d1fb04c662662ff01ace7e Mon Sep 17 00:00:00 2001 From: sloumdrone Date: Sat, 7 Dec 2019 22:20:33 -0800 Subject: [PATCH 11/25] Fixes logic to not be based on length, but on position --- client.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client.go b/client.go index 9eb1962..dcdd8bc 100644 --- a/client.go +++ b/client.go @@ -726,7 +726,7 @@ func (c *client) ReloadPage() error { return fmt.Errorf("There is no page to reload") } url := c.PageState.History[c.PageState.Position].Location.Full - if c.PageState.Length == 1 { + if c.PageState.Position == 0 { c.PageState.Position-- } else { err := c.PageState.NavigateHistory(-1) From 021678d9da836d6e83205e8afe2c72e5d0e97205 Mon Sep 17 00:00:00 2001 From: sloumdrone Date: Tue, 10 Dec 2019 21:56:41 -0800 Subject: [PATCH 12/25] Adds support for a new color theme mode --- bombadillo.1 | 2 +- client.go | 44 ++++++++++++++++++++++++++++++++------------ defaults.go | 2 +- main.go | 16 ++++++---------- page.go | 8 +++++++- pages.go | 4 ++-- 6 files changed, 49 insertions(+), 27 deletions(-) diff --git a/bombadillo.1 b/bombadillo.1 index aa0296a..f044def 100644 --- a/bombadillo.1 +++ b/bombadillo.1 @@ -231,7 +231,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 mode is inverted. When set to color, Bombadillo will render color codes in documents. .TP .B tlscertificate diff --git a/client.go b/client.go index dcdd8bc..b0270ac 100644 --- a/client.go +++ b/client.go @@ -86,9 +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") + } else if c.Options["theme"] == "color" { + re = regexp.MustCompile(`\033\[(?:\d*;?)+[A-Za-z]`) } if c.BookMarks.IsOpen { bm := c.BookMarks.Render(c.Width, c.Height) @@ -97,7 +100,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, " ")) } @@ -110,6 +120,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 { @@ -123,7 +136,14 @@ 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 + if c.Options["theme"] == "color" { + 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, " ")) @@ -219,7 +239,7 @@ func (c *client) TakeControlInput() { // 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() @@ -620,7 +640,7 @@ 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) @@ -773,7 +793,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" } @@ -782,7 +802,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" } } @@ -891,7 +911,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() @@ -914,7 +934,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() @@ -962,7 +982,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() @@ -978,7 +998,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() @@ -998,7 +1018,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() diff --git a/defaults.go b/defaults.go index 022435f..1c0a230 100644 --- a/defaults.go +++ b/defaults.go @@ -51,7 +51,7 @@ var defaultOptions = map[string]string{ "telnetcommand": "telnet", "configlocation": xdgConfigPath(), "defaultscheme": "gopher", // "gopher", "gemini", "http", "https" - "theme": "normal", // "normal", "inverted" + "theme": "normal", // "normal", "inverted", "color" "tlscertificate": "", "tlskey": "", "webmode": "none", // "none", "gui", "lynx", "w3m", "elinks" diff --git a/main.go b/main.go index 5fb0596..998624f 100644 --- a/main.go +++ b/main.go @@ -47,10 +47,10 @@ 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" - } + // if k == "theme" && v != "normal" && v != "inverse" { + // v = "normal" + // bombadillo.Options["theme"] = "normal" + // } opts.WriteString(k) opts.WriteRune('=') opts.WriteString(v) @@ -67,7 +67,7 @@ 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"}, + "theme": []string{"normal", "inverse", "color"}, "defaultscheme": []string{"gopher", "gemini", "http", "https"}, } @@ -117,11 +117,7 @@ func loadConfig() { 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 } diff --git a/page.go b/page.go index 4c8c0d8..fe65387 100644 --- a/page.go +++ b/page.go @@ -47,7 +47,7 @@ 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 escape := false @@ -57,6 +57,9 @@ func (p *Page) WrapContent(width int) { if (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') { escape = false } + if color { + content.WriteRune(ch) + } continue } if ch == '\n' { @@ -75,6 +78,9 @@ func (p *Page) WrapContent(width int) { continue } else if ch == 27 { escape = true + if color { + content.WriteRune(ch) + } continue } else { if counter < width { 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 From f3f406260ea4cc26c0bd5485c4021d02ad7a7636 Mon Sep 17 00:00:00 2001 From: sloumdrone Date: Fri, 13 Dec 2019 20:19:50 -0800 Subject: [PATCH 13/25] Updates escape code handling to be safer plus renders literally in local scheme --- page.go | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/page.go b/page.go index fe65387..6ef719e 100644 --- a/page.go +++ b/page.go @@ -50,15 +50,20 @@ func (p *Page) ScrollPositionRange(termHeight int) (int, 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 color { - content.WriteRune(ch) + if ch == 'm' { + content.WriteString(esc.String()) + esc.Reset() + } } continue } @@ -77,9 +82,16 @@ func (p *Page) WrapContent(width int, color bool) { // 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 { - content.WriteRune(ch) + esc.WriteRune(ch) } continue } else { From 9ae6c3770790f12d31056382bab86aadea7e2d2b Mon Sep 17 00:00:00 2001 From: sloumdrone Date: Sat, 14 Dec 2019 07:26:11 -0800 Subject: [PATCH 14/25] Removes dead code --- main.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/main.go b/main.go index 998624f..88a70b0 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) From 982fba30197dd4b17ce3c5d1b6e17ebe53cdb3b6 Mon Sep 17 00:00:00 2001 From: sloumdrone Date: Sat, 14 Dec 2019 07:37:56 -0800 Subject: [PATCH 15/25] Updates man page --- bombadillo.1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bombadillo.1 b/bombadillo.1 index f044def..04d186c 100644 --- a/bombadillo.1 +++ b/bombadillo.1 @@ -38,7 +38,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 @@ -231,7 +231,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, \fIcolor\fP, and \fIinverse\fP. When set to inverse, the normal mode colors mode is inverted. When set to color, Bombadillo will render color codes in documents. +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 From aeee577e24963f844e6382c2ab2460b81426d84e Mon Sep 17 00:00:00 2001 From: sloumdrone Date: Sun, 15 Dec 2019 11:00:39 -0800 Subject: [PATCH 16/25] Adds title change escapes to cui --- cui/cui.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cui/cui.go b/cui/cui.go index b6ca90b..666f76d 100644 --- a/cui/cui.go +++ b/cui/cui.go @@ -49,14 +49,17 @@ func Exit(exitCode int, msg string) { if msg != "" { fmt.Print(msg, "\n") } + fmt.Print("\033[23;0t") os.Exit(exitCode) } // InitTerm sets the terminal modes appropriate for Bombadillo func InitTerm() { SetCharMode() + fmt.Print("\033[22;0t") Tput("rmam") // turn off line wrapping Tput("smcup") // use alternate screen + fmt.Print("\033]0;Bombadillo\007") } // CleanupTerm reverts changs to terminal mode made by InitTerm From 4182619a7858e0778c259b487ab2ea78c6b4190a Mon Sep 17 00:00:00 2001 From: sloumdrone Date: Mon, 16 Dec 2019 19:40:52 -0800 Subject: [PATCH 17/25] Adds flag for displaying title and adds documentation --- bombadillo.1 | 13 +++++++++---- bombadillo.desktop | 2 +- cui/cui.go | 4 +--- main.go | 9 +++++++++ 4 files changed, 20 insertions(+), 8 deletions(-) diff --git a/bombadillo.1 b/bombadillo.1 index 04d186c..9576233 100644 --- a/bombadillo.1 +++ b/bombadillo.1 @@ -6,6 +6,7 @@ .fam C \fBbombadillo\fP [\fIurl\fP] \fBbombadillo\fP [\fBOPTION\fP] +\fBbombadillo\fP [\fBOPTION\fP] [\fIurl\fP] .fam T .fi .SH DESCRIPTION @@ -15,12 +16,16 @@ .SH OPTIONS .TP .B -\fB-v\fP -Display version information and exit. +\fB-h\fP +Usage help. Displays 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 Bomabdillo and run Bombadillo. +.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 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/cui/cui.go b/cui/cui.go index 666f76d..90a6001 100644 --- a/cui/cui.go +++ b/cui/cui.go @@ -49,17 +49,15 @@ func Exit(exitCode int, msg string) { if msg != "" { fmt.Print(msg, "\n") } - fmt.Print("\033[23;0t") + fmt.Print("\033[23;0t") // Restore window title from terminal stack os.Exit(exitCode) } // InitTerm sets the terminal modes appropriate for Bombadillo func InitTerm() { SetCharMode() - fmt.Print("\033[22;0t") Tput("rmam") // turn off line wrapping Tput("smcup") // use alternate screen - fmt.Print("\033]0;Bombadillo\007") } // CleanupTerm reverts changs to terminal mode made by InitTerm diff --git a/main.go b/main.go index 88a70b0..8ab5979 100644 --- a/main.go +++ b/main.go @@ -168,8 +168,10 @@ func printHelp() { Syntax: bombadillo [url] bombadillo [options...] + bombadillo -t [url] Examples: bombadillo gopher://bombadillo.colorfield.space + bombadillo -t bombadillo -v Options: @@ -180,6 +182,7 @@ Options: func main() { getVersion := flag.Bool("v", false, "Display version information and exit") + addTitleToXWindow := flag.Bool("t", false, "Change the window title to Bomabdillo while running") flag.Usage = printHelp flag.Parse() if *getVersion { @@ -189,6 +192,12 @@ func main() { args := flag.Args() cui.InitTerm() + + 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() From f68996decffa7b4cccaaea1f75ddc0c170bb8ec2 Mon Sep 17 00:00:00 2001 From: sloumdrone Date: Mon, 16 Dec 2019 22:18:28 -0800 Subject: [PATCH 18/25] Beginnings of text search... semi-working --- client.go | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- cui/cui.go | 4 +-- page.go | 27 +++++++++++++++++- 3 files changed, 110 insertions(+), 5 deletions(-) diff --git a/client.go b/client.go index b0270ac..d43ca28 100644 --- a/client.go +++ b/client.go @@ -235,6 +235,41 @@ 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() + if err != nil { + c.SetMessage(err.Error(), false) + c.DrawMessage() + } + case 'N': + // Previous search item + c.ClearMessage() + err := c.PreviousSearchItem() + 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() + } case ':', ' ': // Process a command c.ClearMessage() @@ -242,7 +277,7 @@ func (c *client) TakeControlInput() { 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) @@ -644,7 +679,7 @@ func (c *client) search(query, url, question string) { 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) @@ -1051,6 +1086,51 @@ 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 len(c.PageState.History[c.PageState.Position].FoundLinkLines) == 0 { + return fmt.Errorf("No text matching %q was found", s) + } + loc := c.PageState.History[c.PageState.Position].ScrollPosition + diff := c.PageState.History[c.PageState.Position].FoundLinkLines[0] - loc + c.Scroll(diff) + c.Draw() + return nil +} + +func (c *client) NextSearchItem() error { + if len(c.PageState.History[c.PageState.Position].FoundLinkLines) == 0 { + return fmt.Errorf("The search is over before it has begun") + } + c.PageState.History[c.PageState.Position].SearchIndex++ + if c.PageState.History[c.PageState.Position].SearchIndex >= len(c.PageState.History[c.PageState.Position].FoundLinkLines) { + c.PageState.History[c.PageState.Position].SearchIndex = 0 + } + + loc := c.PageState.History[c.PageState.Position].ScrollPosition + diff := c.PageState.History[c.PageState.Position].FoundLinkLines[c.PageState.History[c.PageState.Position].SearchIndex] - loc + c.Scroll(diff) + c.Draw() + return nil +} + +func (c *client) PreviousSearchItem() error { + if len(c.PageState.History[c.PageState.Position].FoundLinkLines) == 0 { + return fmt.Errorf("The search is over before it has begun") + } + c.PageState.History[c.PageState.Position].SearchIndex-- + if c.PageState.History[c.PageState.Position].SearchIndex < 0 { + c.PageState.History[c.PageState.Position].SearchIndex = len(c.PageState.History[c.PageState.Position].FoundLinkLines) - 1 + } + + loc := c.PageState.History[c.PageState.Position].ScrollPosition + diff := c.PageState.History[c.PageState.Position].FoundLinkLines[c.PageState.History[c.PageState.Position].SearchIndex] - loc + c.Scroll(diff) + c.Draw() + return nil +} + //------------------------------------------------\\ // + + + F U N C T I O N S + + + \\ //--------------------------------------------------\\ diff --git a/cui/cui.go b/cui/cui.go index b6ca90b..f294ea0 100644 --- a/cui/cui.go +++ b/cui/cui.go @@ -96,11 +96,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/page.go b/page.go index 6ef719e..e32bd58 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 } //------------------------------------------------\\ @@ -112,6 +116,27 @@ func (p *Page) WrapContent(width int, color bool) { } p.WrappedContent = strings.Split(content.String(), "\n") + p.FindText() +} + +func (p *Page) FindText() { + matchLines := make([]int, 0) + s := p.SearchTerm + p.SearchIndex = 0 + if s == "" { + p.FoundLinkLines = matchLines + return + } + for i, ln := range p.WrappedContent { + found := strings.Index(ln, s) + if found < 0 { + continue + } + ln = strings.ReplaceAll(ln, s, fmt.Sprintf("\033[7m%s\033[0m", s)) + p.WrappedContent[i] = ln + matchLines = append(matchLines, i) + } + p.FoundLinkLines = matchLines } //------------------------------------------------\\ @@ -120,6 +145,6 @@ func (p *Page) WrapContent(width int, color bool) { // 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 } From 9e7ac3c866fbc2c5c8f0736b29ec939c087782f4 Mon Sep 17 00:00:00 2001 From: sloumdrone Date: Tue, 17 Dec 2019 22:32:45 -0800 Subject: [PATCH 19/25] Finally solved infuriating rendering issue --- client.go | 64 ++++++++++++++++++++++++++++++++++--------------------- page.go | 22 ++++++++++++++----- 2 files changed, 57 insertions(+), 29 deletions(-) diff --git a/client.go b/client.go index d43ca28..c6a916b 100644 --- a/client.go +++ b/client.go @@ -238,7 +238,7 @@ func (c *client) TakeControlInput() { case 'n': // Next search item c.ClearMessage() - err := c.NextSearchItem() + err := c.NextSearchItem(1) if err != nil { c.SetMessage(err.Error(), false) c.DrawMessage() @@ -246,7 +246,7 @@ func (c *client) TakeControlInput() { case 'N': // Previous search item c.ClearMessage() - err := c.PreviousSearchItem() + err := c.NextSearchItem(-1) if err != nil { c.SetMessage(err.Error(), false) c.DrawMessage() @@ -270,6 +270,7 @@ func (c *client) TakeControlInput() { c.SetMessage(err.Error(), true) c.DrawMessage() } + c.NextSearchItem(0) case ':', ' ': // Process a command c.ClearMessage() @@ -1089,46 +1090,61 @@ 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) } - loc := c.PageState.History[c.PageState.Position].ScrollPosition - diff := c.PageState.History[c.PageState.Position].FoundLinkLines[0] - loc - c.Scroll(diff) - c.Draw() return nil } -func (c *client) NextSearchItem() error { - if len(c.PageState.History[c.PageState.Position].FoundLinkLines) == 0 { +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++ - if c.PageState.History[c.PageState.Position].SearchIndex >= len(c.PageState.History[c.PageState.Position].FoundLinkLines) { + c.PageState.History[c.PageState.Position].SearchIndex += dir + if c.PageState.History[c.PageState.Position].SearchIndex < 0 { c.PageState.History[c.PageState.Position].SearchIndex = 0 } - loc := c.PageState.History[c.PageState.Position].ScrollPosition - diff := c.PageState.History[c.PageState.Position].FoundLinkLines[c.PageState.History[c.PageState.Position].SearchIndex] - loc - c.Scroll(diff) + if page.SearchIndex+dir >= 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+dir < 0 { + c.PageState.History[c.PageState.Position].SearchIndex = 0 + return fmt.Errorf("You are at the beginning of the search path") + } + + index := c.PageState.History[c.PageState.Position].SearchIndex + diff := c.PageState.History[c.PageState.Position].FoundLinkLines[index] - page.ScrollPosition + c.ScrollForSearch(diff) c.Draw() return nil } -func (c *client) PreviousSearchItem() error { - if len(c.PageState.History[c.PageState.Position].FoundLinkLines) == 0 { - return fmt.Errorf("The search is over before it has begun") - } - c.PageState.History[c.PageState.Position].SearchIndex-- - if c.PageState.History[c.PageState.Position].SearchIndex < 0 { - c.PageState.History[c.PageState.Position].SearchIndex = len(c.PageState.History[c.PageState.Position].FoundLinkLines) - 1 +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 } - loc := c.PageState.History[c.PageState.Position].ScrollPosition - diff := c.PageState.History[c.PageState.Position].FoundLinkLines[c.PageState.History[c.PageState.Position].SearchIndex] - loc - c.Scroll(diff) + 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() - return nil } //------------------------------------------------\\ diff --git a/page.go b/page.go index e32bd58..f287607 100644 --- a/page.go +++ b/page.go @@ -116,15 +116,28 @@ func (p *Page) WrapContent(width int, color bool) { } p.WrappedContent = strings.Split(content.String(), "\n") - p.FindText() + 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 + } + ln = strings.ReplaceAll(ln, p.SearchTerm, fmt.Sprintf("\033[7m%s\033[0m", p.SearchTerm)) + p.WrappedContent[i] = ln + } } func (p *Page) FindText() { - matchLines := make([]int, 0) + p.FoundLinkLines = make([]int, 0, 10) s := p.SearchTerm p.SearchIndex = 0 if s == "" { - p.FoundLinkLines = matchLines return } for i, ln := range p.WrappedContent { @@ -134,9 +147,8 @@ func (p *Page) FindText() { } ln = strings.ReplaceAll(ln, s, fmt.Sprintf("\033[7m%s\033[0m", s)) p.WrappedContent[i] = ln - matchLines = append(matchLines, i) + p.FoundLinkLines = append(p.FoundLinkLines, i) } - p.FoundLinkLines = matchLines } //------------------------------------------------\\ From 4bb0c84c5644fe848047e8c2a2e7c1b3254477e2 Mon Sep 17 00:00:00 2001 From: asdf Date: Thu, 19 Dec 2019 14:57:40 +1100 Subject: [PATCH 20/25] Amended man page and usage details synopsis --- bombadillo.1 | 8 +++----- main.go | 8 +++----- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/bombadillo.1 b/bombadillo.1 index 9576233..a8b9e07 100644 --- a/bombadillo.1 +++ b/bombadillo.1 @@ -4,9 +4,7 @@ .SH SYNOPSIS .nf .fam C -\fBbombadillo\fP [\fIurl\fP] -\fBbombadillo\fP [\fBOPTION\fP] -\fBbombadillo\fP [\fBOPTION\fP] [\fIurl\fP] +\fBbombadillo\fP [\fIoptions\fP] [\fIurl\fP] .fam T .fi .SH DESCRIPTION @@ -17,11 +15,11 @@ .TP .B \fB-h\fP -Usage help. Displays all command line options with a short description and exits. +Display usage help and exit. Provides a list of all command line options with a short description and exits. .TP .B \fB-t\fP -Set the window title to Bomabdillo and run Bombadillo. +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 diff --git a/main.go b/main.go index 8ab5979..1362e78 100644 --- a/main.go +++ b/main.go @@ -166,12 +166,10 @@ func handleSignals(c <-chan os.Signal) { func printHelp() { art := `Bombadillo - a non-web browser -Syntax: bombadillo [url] - bombadillo [options...] - bombadillo -t [url] +Syntax: bombadillo [options] [url] Examples: bombadillo gopher://bombadillo.colorfield.space - bombadillo -t + bombadillo -t bombadillo -v Options: @@ -182,7 +180,7 @@ Options: func main() { getVersion := flag.Bool("v", false, "Display version information and exit") - addTitleToXWindow := flag.Bool("t", false, "Change the window title to Bomabdillo while running") + 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 { From 10f28027dc08813936eb739aa5d8abf4b703915b Mon Sep 17 00:00:00 2001 From: sloumdrone Date: Wed, 18 Dec 2019 22:17:52 -0800 Subject: [PATCH 21/25] Working search --- client.go | 29 ++++++++++++++++------------- page.go | 12 ++++++++++-- 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/client.go b/client.go index c6a916b..8be1973 100644 --- a/client.go +++ b/client.go @@ -90,9 +90,8 @@ func (c *client) Draw() { var re *regexp.Regexp if c.Options["theme"] == "inverse" { screen.WriteString("\033[7m") - } else if c.Options["theme"] == "color" { - re = regexp.MustCompile(`\033\[(?:\d*;?)+[A-Za-z]`) } + 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])) @@ -137,12 +136,12 @@ func (c *client) Draw() { for i := 0; i < c.Height-3; i++ { if i < len(pageContent) { extra := 0 - if c.Options["theme"] == "color" { - escapes := re.FindAllString(pageContent[i], -1) - for _, esc := range escapes { - extra += len(esc) - } + // if c.Options["theme"] == "color" { + 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 { @@ -270,7 +269,10 @@ func (c *client) TakeControlInput() { c.SetMessage(err.Error(), true) c.DrawMessage() } - c.NextSearchItem(0) + err = c.NextSearchItem(0) + if err != nil { + c.Draw() + } case ':', ' ': // Process a command c.ClearMessage() @@ -1105,20 +1107,21 @@ func (c *client) NextSearchItem(dir int) error { return fmt.Errorf("The search is over before it has begun") } c.PageState.History[c.PageState.Position].SearchIndex += dir - if c.PageState.History[c.PageState.Position].SearchIndex < 0 { + page.SearchIndex += dir + if page.SearchIndex < 0 { c.PageState.History[c.PageState.Position].SearchIndex = 0 + page.SearchIndex = 0 } - if page.SearchIndex+dir >= len(page.FoundLinkLines) { + 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+dir < 0 { + } 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") } - index := c.PageState.History[c.PageState.Position].SearchIndex - diff := c.PageState.History[c.PageState.Position].FoundLinkLines[index] - page.ScrollPosition + diff := page.FoundLinkLines[page.SearchIndex] - page.ScrollPosition c.ScrollForSearch(diff) c.Draw() return nil diff --git a/page.go b/page.go index f287607..10d23b0 100644 --- a/page.go +++ b/page.go @@ -128,7 +128,11 @@ func (p *Page) HighlightFoundText() { if found < 0 { continue } - ln = strings.ReplaceAll(ln, p.SearchTerm, fmt.Sprintf("\033[7m%s\033[0m", p.SearchTerm)) + 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 } } @@ -140,12 +144,16 @@ func (p *Page) FindText() { 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("\033[7m%s\033[0m", s)) + ln = strings.ReplaceAll(ln, s, fmt.Sprintf(format, s)) p.WrappedContent[i] = ln p.FoundLinkLines = append(p.FoundLinkLines, i) } From 71e21543f02902baea32a5057cf1ca6442a80d1c Mon Sep 17 00:00:00 2001 From: sloumdrone Date: Sat, 4 Jan 2020 10:53:24 -0800 Subject: [PATCH 22/25] Fixes line wrapping to 80 columns rather than 79 --- page.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/page.go b/page.go index 10d23b0..46c95ba 100644 --- a/page.go +++ b/page.go @@ -99,7 +99,7 @@ func (p *Page) WrapContent(width int, color bool) { } continue } else { - if counter < width { + if counter <= width { content.WriteRune(ch) counter++ } else { From ef36895c5d4f367c4e52f2c7a75d3a27910ee490 Mon Sep 17 00:00:00 2001 From: sloumdrone Date: Sat, 4 Jan 2020 10:58:56 -0800 Subject: [PATCH 23/25] Removes dead code --- client.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/client.go b/client.go index 8be1973..ed642a2 100644 --- a/client.go +++ b/client.go @@ -136,12 +136,10 @@ func (c *client) Draw() { for i := 0; i < c.Height-3; i++ { if i < len(pageContent) { 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", c.Width+extra, c.Width+extra, pageContent[i])) screen.WriteString("\n") } else { @@ -682,7 +680,7 @@ func (c *client) search(query, url, question string) { 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) From daacae276c11f850a931c1f5adda3d691f1001c7 Mon Sep 17 00:00:00 2001 From: sloumdrone Date: Sat, 4 Jan 2020 11:19:01 -0800 Subject: [PATCH 24/25] Adds man page documentation for search --- bombadillo.1 | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/bombadillo.1 b/bombadillo.1 index 04d186c..bd7f0c8 100644 --- a/bombadillo.1 +++ b/bombadillo.1 @@ -88,6 +88,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 +107,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 From 8d2ebd5c6debbfef8fabcf6d461990fd82608f39 Mon Sep 17 00:00:00 2001 From: sloumdrone Date: Sat, 4 Jan 2020 11:28:07 -0800 Subject: [PATCH 25/25] Updates version file --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 46b105a..7ec1d6d 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -v2.0.0 +2.1.0