Skip to content

Commit

Permalink
Display exact matches separately
Browse files Browse the repository at this point in the history
  • Loading branch information
diamondburned committed Oct 31, 2024
1 parent 1920bf4 commit 868420c
Show file tree
Hide file tree
Showing 7 changed files with 98 additions and 48 deletions.
99 changes: 70 additions & 29 deletions cmd/nix-search/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"os/exec"
"os/signal"
"regexp"
"slices"
"strings"
"sync"

Expand Down Expand Up @@ -208,54 +209,94 @@ func mainAction(c *cli.Context) error {
searchOpts.Highlight = search.HighlightStyleANSI{}
}

pkgsCh, err := searcher.SearchPackages(ctx, query, searchOpts)
pkgsIter, err := searcher.SearchPackages(ctx, query, searchOpts)
if err != nil {
return errors.Wrap(err, "failed to search packages")
}

if c.Bool("json") {
var pkgs []search.SearchedPackage
for pkg := range pkgsCh {
pkgs = append(pkgs, pkg)
}
pkgs := slices.Collect(pkgsIter)

if c.Bool("json") {
enc := json.NewEncoder(out)
enc.SetIndent("", " ")
return enc.Encode(pkgs)
}

for pkg := range pkgsCh {
path := pkg.Path
// Fix red coloring when used with other attributes by replacing all
// resets with the default color.
path = strings.ReplaceAll(path, "\x1b[0m", "\x1b[39m")
if pkg.Broken || pkg.UnsupportedPlatform {
path = styler.strikethrough(path)
}
absoluteMatches := make([]search.SearchedPackage, 0, 1)
pkgs = slices.DeleteFunc(pkgs, func(p search.SearchedPackage) bool {
dotq := "." + query
if strings.HasSuffix(p.Path, dotq) {
// Rehighlight the package with the query in the path.
if p.Highlighted != nil {
dotqIx := strings.LastIndex(p.Path, dotq)

p.Highlighted.Path = "" +
p.Path[:dotqIx] +
"." + styler.style(query, search.DefaultANSIEscapeColor, "\x1b[0m") +
p.Path[dotqIx+len(dotq):]
}

fmt.Fprint(out, "- ", path)
fmt.Fprint(out, " ", styler.dim("("+pkg.Version+")"))
if pkg.Unfree {
fmt.Fprint(out, styler.dim(" (unfree)"))
}
if pkg.Broken {
fmt.Fprint(out, styler.dim(" (broken)"))
}
if pkg.UnsupportedPlatform {
fmt.Fprint(out, styler.dim(" (unsupported)"))
absoluteMatches = append(absoluteMatches, p)
return true
}
fmt.Fprint(out, "\n")

fmt.Fprint(out, wrap(pkg.Description, " "), "\n")
return false
})

if pkg.LongDescription != "" && pkg.Description != pkg.LongDescription {
fmt.Fprint(out, styleLongDescription(styler, pkg.LongDescription), "\n")
}
if len(absoluteMatches) > 0 {
fmt.Fprintln(out, styler.bold("* Exact matches:"))
fmt.Fprintln(out)
printPackages(out, styler, absoluteMatches)

fmt.Fprintln(out, styler.bold("* Other matches:"))
fmt.Fprintln(out)
}

printPackages(out, styler, pkgs)

return ctx.Err()
}

func printPackages(out io.Writer, styler textStyler, pkgs []search.SearchedPackage) {
for i := range pkgs {
printPackage(out, styler, &pkgs[i])
}
}

func printPackage(out io.Writer, styler textStyler, pkg *search.SearchedPackage) {
// Use the highlighted version of the package if available.
if pkg.Highlighted != nil {
pkg = pkg.Highlighted
}

path := pkg.Path
// Fix red coloring when used with other attributes by replacing all
// resets with the default color.
path = strings.ReplaceAll(path, "\x1b[0m", "\x1b[39m")
if pkg.Broken || pkg.UnsupportedPlatform {
path = styler.strikethrough(path)
}

fmt.Fprint(out, "- ", path)
fmt.Fprint(out, " ", styler.dim("("+pkg.Version+")"))
if pkg.Unfree {
fmt.Fprint(out, styler.dim(" (unfree)"))
}
if pkg.Broken {
fmt.Fprint(out, styler.dim(" (broken)"))
}
if pkg.UnsupportedPlatform {
fmt.Fprint(out, styler.dim(" (unsupported)"))
}
fmt.Fprint(out, "\n")

fmt.Fprint(out, wrap(pkg.Description, " "), "\n")

if pkg.LongDescription != "" && pkg.Description != pkg.LongDescription {
fmt.Fprint(out, styleLongDescription(styler, pkg.LongDescription), "\n")
}
}

var (
reFencedCodeBlock = regexp.MustCompile(`(?ms)\x60\x60\x60+\s*(.*?)\s*\x60\x60\x60+`)
reInlineHyperlink = regexp.MustCompile(`(?m)\[(.*?)\]\n*\((http.*?)\)`)
Expand Down
10 changes: 10 additions & 0 deletions cmd/nix-search/styler.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,16 @@ func (s textStyler) with(o textStyler) textStyler {
return s | o
}

func (s textStyler) style(text, prefix, suffix string) string {
if s&1 == 0 {
return text
}
if s&dontEndStyle != 0 {
suffix = ""
}
return prefix + text + suffix
}

func (s textStyler) styleTextBlock(text string, prefix, suffix string) string {
if s&1 == 0 {
return text
Expand Down
6 changes: 3 additions & 3 deletions flake.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
{
devShells.default = pkgs.mkShell {
packages = with pkgs; [
go_1_21
go_1_23
gopls
gotools
sqlc
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module libdb.so/nix-search

go 1.21
go 1.23

require (
github.com/alecthomas/assert/v2 v2.2.2
Expand Down
7 changes: 6 additions & 1 deletion search/search.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package search
import (
"context"
"html"
"iter"
"strings"
)

Expand All @@ -12,7 +13,7 @@ import (
type PackagesSearcher interface {
// SearchPackages returns a channel of packages that match the given query.
// The channel is closed when there are no more results or ctx is canceled.
SearchPackages(ctx context.Context, query string, opts Opts) (<-chan SearchedPackage, error)
SearchPackages(ctx context.Context, query string, opts Opts) (iter.Seq[SearchedPackage], error)
}

// Opts are options for searching.
Expand All @@ -35,6 +36,10 @@ type SearchedPackage struct {
// Path is the path to the derivation.
Path string `json:"path"`
Package

// Highlighted is the color-highlighted package, if any.
// This is only used if Highlight is set in Opts.
Highlighted *SearchedPackage `json:"unhighlighted"`
}

// HighlightStyle is a style of highlighting.
Expand Down
20 changes: 7 additions & 13 deletions search/searchers/blugesearcher/searcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"encoding/json"
"fmt"
"iter"
"os"
"path/filepath"
"strings"
Expand Down Expand Up @@ -76,7 +77,7 @@ func (s *PackagesSearcher) Close() error {

// SearchPackages implements search.PackagesSearcher. The searching is done by
// fuzzy matching the query.
func (s *PackagesSearcher) SearchPackages(ctx context.Context, query string, opts search.Opts) (<-chan search.SearchedPackage, error) {
func (s *PackagesSearcher) SearchPackages(ctx context.Context, query string, opts search.Opts) (iter.Seq[search.SearchedPackage], error) {
var highlighter blugehighlight.Highlighter
if opts.Highlight != nil {
switch highlight := opts.Highlight.(type) {
Expand Down Expand Up @@ -127,10 +128,7 @@ func (s *PackagesSearcher) SearchPackages(ctx context.Context, query string, opt
return nil, fmt.Errorf("cannot search: %w", err)
}

results := make(chan search.SearchedPackage)
go func() {
defer close(results)

return func(yield func(p search.SearchedPackage) bool) {
var locationBuf []blugesearch.Location

for {
Expand Down Expand Up @@ -218,19 +216,15 @@ func (s *PackagesSearcher) SearchPackages(ctx context.Context, query string, opt
}

if highlighter != nil {
result = highlightPackage(match, highlighter, result)
hresult := highlightPackage(match, highlighter, result)
result.Highlighted = &hresult
}

select {
case results <- result:
// ok
case <-ctx.Done():
if !yield(result) {
return
}
}
}()

return results, nil
}, nil
}

func highlightPackage(match *blugesearch.DocumentMatch, highlighter blugehighlight.Highlighter, pkg search.SearchedPackage) search.SearchedPackage {
Expand Down

0 comments on commit 868420c

Please sign in to comment.