From f68996decffa7b4cccaaea1f75ddc0c170bb8ec2 Mon Sep 17 00:00:00 2001 From: sloumdrone Date: Mon, 16 Dec 2019 22:18:28 -0800 Subject: [PATCH 1/6] 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 2/6] 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 10f28027dc08813936eb739aa5d8abf4b703915b Mon Sep 17 00:00:00 2001 From: sloumdrone Date: Wed, 18 Dec 2019 22:17:52 -0800 Subject: [PATCH 3/6] 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 4/6] 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 5/6] 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 6/6] 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