search + regexp search
This commit is contained in:
parent
9a2a3aa12c
commit
e117402a85
@ -1,11 +1,14 @@
|
|||||||
package archive
|
package archive
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
"fmt"
|
"fmt"
|
||||||
"html/template"
|
"html/template"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"math"
|
"math"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -40,11 +43,6 @@ func (this *ArchiveState) selectSource(log *LogSource, slug string) {
|
|||||||
// - Mandatory: log, ym
|
// - Mandatory: log, ym
|
||||||
// - Optional: page
|
// - Optional: page
|
||||||
func (this *ArchiveState) renderView(w http.ResponseWriter) {
|
func (this *ArchiveState) renderView(w http.ResponseWriter) {
|
||||||
if this.log == nil {
|
|
||||||
this.renderError(w, "Invalid log source selected")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
fname, err := this.svr.LogFile(this.log, this.ym)
|
fname, err := this.svr.LogFile(this.log, this.ym)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
this.renderError(w, err.Error())
|
this.renderError(w, err.Error())
|
||||||
@ -81,6 +79,76 @@ func (this *ArchiveState) renderView(w http.ResponseWriter) {
|
|||||||
this.renderTemplate(w, []byte(output))
|
this.renderTemplate(w, []byte(output))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// renderSearch renders the search results.
|
||||||
|
// - Mandatory: log, query, queryIsRegex
|
||||||
|
func (this *ArchiveState) renderSearch(w http.ResponseWriter) {
|
||||||
|
|
||||||
|
var matcher func(line string) bool
|
||||||
|
if this.queryIsRegex {
|
||||||
|
rx, err := regexp.Compile(`(?i)` + this.query)
|
||||||
|
if err != nil {
|
||||||
|
this.renderError(w, "Invalid regular expression "+this.query+" ("+err.Error()+")")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
matcher = rx.MatchString
|
||||||
|
} else {
|
||||||
|
queryLower := strings.ToLower(this.query)
|
||||||
|
matcher = func(line string) bool {
|
||||||
|
return strings.Contains(strings.ToLower(line), queryLower)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.renderTemplateHead(w)
|
||||||
|
totalResults := 0
|
||||||
|
w.Write([]byte(`<ul>`))
|
||||||
|
|
||||||
|
limit := this.log.LatestDate().Next() // one off the end
|
||||||
|
for ympair := this.log.EarliestDate(); !ympair.Equals(limit); ympair = ympair.Next() {
|
||||||
|
|
||||||
|
fname, err := this.svr.LogFile(this.log, ympair)
|
||||||
|
if err != nil {
|
||||||
|
continue // no log exists for this ym
|
||||||
|
}
|
||||||
|
|
||||||
|
fh, err := os.Open(fname)
|
||||||
|
if err != nil {
|
||||||
|
continue // can't open this log file
|
||||||
|
}
|
||||||
|
|
||||||
|
func() {
|
||||||
|
defer fh.Close()
|
||||||
|
|
||||||
|
scanner := bufio.NewScanner(fh)
|
||||||
|
|
||||||
|
for i := 0; scanner.Scan(); i += 1 {
|
||||||
|
if !matcher(scanner.Text()) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
totalResults += 1
|
||||||
|
|
||||||
|
page := i / this.svr.cfg.LinesPerPage
|
||||||
|
lineNo := i % this.svr.cfg.LinesPerPage
|
||||||
|
url := fmt.Sprintf(`/%s/%d/%d/page-%d#line-%d`, this.logBestSlug, ympair.Year, ympair.Month, page, lineNo)
|
||||||
|
|
||||||
|
w.Write([]byte(`<li><a href="` + template.HTMLEscapeString(url) + `">»</a> ` + template.HTMLEscapeString(scanner.Text()) + `</li>`))
|
||||||
|
}
|
||||||
|
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Write([]byte(`</ul>`))
|
||||||
|
|
||||||
|
if totalResults == 0 {
|
||||||
|
w.Write([]byte(`No search results for "<em>` + template.HTMLEscapeString(this.query) + `</em>"`))
|
||||||
|
} else {
|
||||||
|
w.Write([]byte(`<br><em>Found ` + fmt.Sprintf("%d", totalResults) + ` total result(s).</em><br><br>`))
|
||||||
|
}
|
||||||
|
|
||||||
|
this.renderTemplateFoot(w)
|
||||||
|
}
|
||||||
|
|
||||||
|
// renderError renders a plain text string, escaping it for HTML use.
|
||||||
func (this *ArchiveState) renderError(w http.ResponseWriter, msg string) {
|
func (this *ArchiveState) renderError(w http.ResponseWriter, msg string) {
|
||||||
this.renderTemplate(w, []byte(template.HTMLEscapeString(msg)))
|
this.renderTemplate(w, []byte(template.HTMLEscapeString(msg)))
|
||||||
}
|
}
|
||||||
|
23
Router.go
23
Router.go
@ -118,12 +118,27 @@ func (this *ArchiveServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
arc.renderError(w, fmt.Sprintf("Unknown source '%s'", matches[1]))
|
arc.renderError(w, fmt.Sprintf("Unknown source '%s'", matches[1]))
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if this.rxSearch.MatchString(r.URL.Path) {
|
} else if matches := this.rxSearch.FindStringSubmatch(r.URL.Path); len(matches) > 0 {
|
||||||
arc.renderError(w, "Not implemented.") // FIXME
|
if ls := this.lookupSource(matches[1]); ls != nil {
|
||||||
|
arc.selectSource(ls, matches[1])
|
||||||
|
arc.query, _ = url.QueryUnescape(matches[2])
|
||||||
|
arc.queryIsRegex = false
|
||||||
|
arc.renderSearch(w)
|
||||||
|
|
||||||
} else if this.rxSearchRx.MatchString(r.URL.Path) {
|
} else {
|
||||||
arc.renderError(w, "Not implemented.") // FIXME
|
arc.renderError(w, fmt.Sprintf("Unknown source '%s'", matches[1]))
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if matches := this.rxSearchRx.FindStringSubmatch(r.URL.Path); len(matches) > 0 {
|
||||||
|
if ls := this.lookupSource(matches[1]); ls != nil {
|
||||||
|
arc.selectSource(ls, matches[1])
|
||||||
|
arc.query, _ = url.QueryUnescape(matches[2])
|
||||||
|
arc.queryIsRegex = true
|
||||||
|
arc.renderSearch(w)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
arc.renderError(w, fmt.Sprintf("Unknown source '%s'", matches[1]))
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
arc.renderError(w, "Unknown route.")
|
arc.renderError(w, "Unknown route.")
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user