Skip to content

Commit 868420c

Browse files
committed
Display exact matches separately
1 parent 1920bf4 commit 868420c

File tree

7 files changed

+98
-48
lines changed

7 files changed

+98
-48
lines changed

cmd/nix-search/main.go

Lines changed: 70 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"os/exec"
1212
"os/signal"
1313
"regexp"
14+
"slices"
1415
"strings"
1516
"sync"
1617

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

211-
pkgsCh, err := searcher.SearchPackages(ctx, query, searchOpts)
212+
pkgsIter, err := searcher.SearchPackages(ctx, query, searchOpts)
212213
if err != nil {
213214
return errors.Wrap(err, "failed to search packages")
214215
}
215216

216-
if c.Bool("json") {
217-
var pkgs []search.SearchedPackage
218-
for pkg := range pkgsCh {
219-
pkgs = append(pkgs, pkg)
220-
}
217+
pkgs := slices.Collect(pkgsIter)
221218

219+
if c.Bool("json") {
222220
enc := json.NewEncoder(out)
223221
enc.SetIndent("", " ")
224222
return enc.Encode(pkgs)
225223
}
226224

227-
for pkg := range pkgsCh {
228-
path := pkg.Path
229-
// Fix red coloring when used with other attributes by replacing all
230-
// resets with the default color.
231-
path = strings.ReplaceAll(path, "\x1b[0m", "\x1b[39m")
232-
if pkg.Broken || pkg.UnsupportedPlatform {
233-
path = styler.strikethrough(path)
234-
}
225+
absoluteMatches := make([]search.SearchedPackage, 0, 1)
226+
pkgs = slices.DeleteFunc(pkgs, func(p search.SearchedPackage) bool {
227+
dotq := "." + query
228+
if strings.HasSuffix(p.Path, dotq) {
229+
// Rehighlight the package with the query in the path.
230+
if p.Highlighted != nil {
231+
dotqIx := strings.LastIndex(p.Path, dotq)
232+
233+
p.Highlighted.Path = "" +
234+
p.Path[:dotqIx] +
235+
"." + styler.style(query, search.DefaultANSIEscapeColor, "\x1b[0m") +
236+
p.Path[dotqIx+len(dotq):]
237+
}
235238

236-
fmt.Fprint(out, "- ", path)
237-
fmt.Fprint(out, " ", styler.dim("("+pkg.Version+")"))
238-
if pkg.Unfree {
239-
fmt.Fprint(out, styler.dim(" (unfree)"))
240-
}
241-
if pkg.Broken {
242-
fmt.Fprint(out, styler.dim(" (broken)"))
243-
}
244-
if pkg.UnsupportedPlatform {
245-
fmt.Fprint(out, styler.dim(" (unsupported)"))
239+
absoluteMatches = append(absoluteMatches, p)
240+
return true
246241
}
247-
fmt.Fprint(out, "\n")
248242

249-
fmt.Fprint(out, wrap(pkg.Description, " "), "\n")
243+
return false
244+
})
250245

251-
if pkg.LongDescription != "" && pkg.Description != pkg.LongDescription {
252-
fmt.Fprint(out, styleLongDescription(styler, pkg.LongDescription), "\n")
253-
}
246+
if len(absoluteMatches) > 0 {
247+
fmt.Fprintln(out, styler.bold("* Exact matches:"))
248+
fmt.Fprintln(out)
249+
printPackages(out, styler, absoluteMatches)
250+
251+
fmt.Fprintln(out, styler.bold("* Other matches:"))
252+
fmt.Fprintln(out)
254253
}
255254

255+
printPackages(out, styler, pkgs)
256+
256257
return ctx.Err()
257258
}
258259

260+
func printPackages(out io.Writer, styler textStyler, pkgs []search.SearchedPackage) {
261+
for i := range pkgs {
262+
printPackage(out, styler, &pkgs[i])
263+
}
264+
}
265+
266+
func printPackage(out io.Writer, styler textStyler, pkg *search.SearchedPackage) {
267+
// Use the highlighted version of the package if available.
268+
if pkg.Highlighted != nil {
269+
pkg = pkg.Highlighted
270+
}
271+
272+
path := pkg.Path
273+
// Fix red coloring when used with other attributes by replacing all
274+
// resets with the default color.
275+
path = strings.ReplaceAll(path, "\x1b[0m", "\x1b[39m")
276+
if pkg.Broken || pkg.UnsupportedPlatform {
277+
path = styler.strikethrough(path)
278+
}
279+
280+
fmt.Fprint(out, "- ", path)
281+
fmt.Fprint(out, " ", styler.dim("("+pkg.Version+")"))
282+
if pkg.Unfree {
283+
fmt.Fprint(out, styler.dim(" (unfree)"))
284+
}
285+
if pkg.Broken {
286+
fmt.Fprint(out, styler.dim(" (broken)"))
287+
}
288+
if pkg.UnsupportedPlatform {
289+
fmt.Fprint(out, styler.dim(" (unsupported)"))
290+
}
291+
fmt.Fprint(out, "\n")
292+
293+
fmt.Fprint(out, wrap(pkg.Description, " "), "\n")
294+
295+
if pkg.LongDescription != "" && pkg.Description != pkg.LongDescription {
296+
fmt.Fprint(out, styleLongDescription(styler, pkg.LongDescription), "\n")
297+
}
298+
}
299+
259300
var (
260301
reFencedCodeBlock = regexp.MustCompile(`(?ms)\x60\x60\x60+\s*(.*?)\s*\x60\x60\x60+`)
261302
reInlineHyperlink = regexp.MustCompile(`(?m)\[(.*?)\]\n*\((http.*?)\)`)

cmd/nix-search/styler.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,16 @@ func (s textStyler) with(o textStyler) textStyler {
2929
return s | o
3030
}
3131

32+
func (s textStyler) style(text, prefix, suffix string) string {
33+
if s&1 == 0 {
34+
return text
35+
}
36+
if s&dontEndStyle != 0 {
37+
suffix = ""
38+
}
39+
return prefix + text + suffix
40+
}
41+
3242
func (s textStyler) styleTextBlock(text string, prefix, suffix string) string {
3343
if s&1 == 0 {
3444
return text

flake.lock

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

flake.nix

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
{
1313
devShells.default = pkgs.mkShell {
1414
packages = with pkgs; [
15-
go_1_21
15+
go_1_23
1616
gopls
1717
gotools
1818
sqlc

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module libdb.so/nix-search
22

3-
go 1.21
3+
go 1.23
44

55
require (
66
github.com/alecthomas/assert/v2 v2.2.2

search/search.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package search
33
import (
44
"context"
55
"html"
6+
"iter"
67
"strings"
78
)
89

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

1819
// Opts are options for searching.
@@ -35,6 +36,10 @@ type SearchedPackage struct {
3536
// Path is the path to the derivation.
3637
Path string `json:"path"`
3738
Package
39+
40+
// Highlighted is the color-highlighted package, if any.
41+
// This is only used if Highlight is set in Opts.
42+
Highlighted *SearchedPackage `json:"unhighlighted"`
3843
}
3944

4045
// HighlightStyle is a style of highlighting.

search/searchers/blugesearcher/searcher.go

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"encoding/json"
66
"fmt"
7+
"iter"
78
"os"
89
"path/filepath"
910
"strings"
@@ -76,7 +77,7 @@ func (s *PackagesSearcher) Close() error {
7677

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

130-
results := make(chan search.SearchedPackage)
131-
go func() {
132-
defer close(results)
133-
131+
return func(yield func(p search.SearchedPackage) bool) {
134132
var locationBuf []blugesearch.Location
135133

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

220218
if highlighter != nil {
221-
result = highlightPackage(match, highlighter, result)
219+
hresult := highlightPackage(match, highlighter, result)
220+
result.Highlighted = &hresult
222221
}
223222

224-
select {
225-
case results <- result:
226-
// ok
227-
case <-ctx.Done():
223+
if !yield(result) {
228224
return
229225
}
230226
}
231-
}()
232-
233-
return results, nil
227+
}, nil
234228
}
235229

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

0 commit comments

Comments
 (0)