Merge branch 'text-find' of sloum/bombadillo into develop

This commit is contained in:
Sloom Sloum Sluom IV 2020-01-04 14:20:28 -05:00 committed by Gitea
commit b551138c75
4 changed files with 166 additions and 13 deletions

View File

@ -88,6 +88,13 @@ k
Scroll up a single line. Scroll up a single line.
.TP .TP
.B .B
n
Jump to next found text item.
.TP
.B
Jump to previous found text item.
.TP
.B
q q
Quit \fBbombadillo\fP. Quit \fBbombadillo\fP.
.TP .TP
@ -100,6 +107,10 @@ u
Scroll up an amount corresponding to 75% of your terminal window height in the current document. Scroll up an amount corresponding to 75% of your terminal window height in the current document.
.TP .TP
.B .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
<tab> <tab>
Toggle the scroll focus between the bookmarks panel and the document panel. Only has an effect if the bookmarks panel is open. Toggle the scroll focus between the bookmarks panel and the document panel. Only has an effect if the bookmarks panel is open.
.TP .TP

115
client.go
View File

@ -90,9 +90,8 @@ func (c *client) Draw() {
var re *regexp.Regexp var re *regexp.Regexp
if c.Options["theme"] == "inverse" { if c.Options["theme"] == "inverse" {
screen.WriteString("\033[7m") 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 { if c.BookMarks.IsOpen {
bm := c.BookMarks.Render(c.Width, c.Height) bm := c.BookMarks.Render(c.Width, c.Height)
bmWidth := len([]rune(bm[0])) bmWidth := len([]rune(bm[0]))
@ -137,11 +136,9 @@ func (c *client) Draw() {
for i := 0; i < c.Height-3; i++ { for i := 0; i < c.Height-3; i++ {
if i < len(pageContent) { if i < len(pageContent) {
extra := 0 extra := 0
if c.Options["theme"] == "color" { escapes := re.FindAllString(pageContent[i], -1)
escapes := re.FindAllString(pageContent[i], -1) for _, esc := range escapes {
for _, esc := range escapes { extra += len(esc)
extra += len(esc)
}
} }
screen.WriteString(fmt.Sprintf("%-*.*s", c.Width+extra, c.Width+extra, pageContent[i])) screen.WriteString(fmt.Sprintf("%-*.*s", c.Width+extra, c.Width+extra, pageContent[i]))
screen.WriteString("\n") screen.WriteString("\n")
@ -235,6 +232,45 @@ func (c *client) TakeControlInput() {
// Toggle bookmark browser focus on/off // Toggle bookmark browser focus on/off
c.BookMarks.ToggleFocused() c.BookMarks.ToggleFocused()
c.Draw() 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 ':', ' ': case ':', ' ':
// Process a command // Process a command
c.ClearMessage() c.ClearMessage()
@ -242,7 +278,7 @@ func (c *client) TakeControlInput() {
if c.Options["theme"] == "normal" || c.Options["theme"] == "color" { if c.Options["theme"] == "normal" || c.Options["theme"] == "color" {
fmt.Printf("\033[7m%*.*s\r", c.Width, c.Width, "") fmt.Printf("\033[7m%*.*s\r", c.Width, c.Width, "")
} }
entry, err := cui.GetLine() entry, err := cui.GetLine(": ")
c.ClearMessageLine() c.ClearMessageLine()
if err != nil { if err != nil {
c.SetMessage(err.Error(), true) c.SetMessage(err.Error(), true)
@ -644,7 +680,7 @@ func (c *client) search(query, url, question string) {
fmt.Printf("\033[7m%*.*s\r", c.Width, c.Width, "") fmt.Printf("\033[7m%*.*s\r", c.Width, c.Width, "")
} }
fmt.Print(question) fmt.Print(question)
entry, err = cui.GetLine() entry, err = cui.GetLine("? ")
c.ClearMessageLine() c.ClearMessageLine()
if err != nil { if err != nil {
c.SetMessage(err.Error(), true) c.SetMessage(err.Error(), true)
@ -1051,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 + + + \\ // + + + F U N C T I O N S + + + \\
//--------------------------------------------------\\ //--------------------------------------------------\\

View File

@ -96,11 +96,11 @@ func Getch() rune {
return char return char
} }
func GetLine() (string, error) { func GetLine(prefix string) (string, error) {
SetLineMode() SetLineMode()
reader := bufio.NewReader(os.Stdin) reader := bufio.NewReader(os.Stdin)
fmt.Print(": ") fmt.Print(prefix)
text, err := reader.ReadString('\n') text, err := reader.ReadString('\n')
if err != nil { if err != nil {
return "", err return "", err

49
page.go
View File

@ -1,6 +1,7 @@
package main package main
import ( import (
"fmt"
"strings" "strings"
) )
@ -17,6 +18,9 @@ type Page struct {
Links []string Links []string
Location Url Location Url
ScrollPosition int ScrollPosition int
FoundLinkLines []int
SearchTerm string
SearchIndex int
} }
//------------------------------------------------\\ //------------------------------------------------\\
@ -95,7 +99,7 @@ func (p *Page) WrapContent(width int, color bool) {
} }
continue continue
} else { } else {
if counter < width { if counter <= width {
content.WriteRune(ch) content.WriteRune(ch)
counter++ counter++
} else { } else {
@ -112,6 +116,47 @@ func (p *Page) WrapContent(width int, color bool) {
} }
p.WrappedContent = strings.Split(content.String(), "\n") 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)
}
} }
//------------------------------------------------\\ //------------------------------------------------\\
@ -120,6 +165,6 @@ func (p *Page) WrapContent(width int, color bool) {
// MakePage returns a Page struct with default values // MakePage returns a Page struct with default values
func MakePage(url Url, content string, links []string) Page { 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 return p
} }