~ knotless Search/
↑↓ move↵ open⇧↵ all results

chadtmiller.com/knotless

git that lives in your Atmosphere account

knotless.dev
1 collaborator

knotless 0.13.0 Latest 2 packages, 1 image

1
10

Code search ranks the languages it finds, each filled with its color

Merged
Merged chadtmiller.com asked to merge into main from chadtmiller.com/knotless:web/code-search-languages

What changes

Languages to pick from, ranked, each filled with its color. The side of a code search lists every language its files are in, most files first, under Code and Repositories. Each row is filled from the left with its language's color, mixed into the page's background, as far as its share of the most files: the top language fills its row, and every fill is at least a sliver wide. The fill grows stronger on hover and again on a press. Hovering a language says how many files; a screen reader hears it with the name. On a phone the languages are a row of chips under the tabs, scrolling sideways, filled the same way.

Picking one keeps the rest. A language picked has a stronger fill and its name in bold, the others stay listed, and picking another swaps it in for the first; picking it again takes lang: out. With nothing found in it, the page says "No TOML file matches." with a link to search every language.

searchCode answers languages. Each a name, color? and files, counted by a second Zoekt query run beside the first: the same query with its lang: and -lang: filters taken out, asking only for files (type:file), up to 1000. Only repositories the index lists count, each asked after once. An or or and joining a filter to the rest goes with it, and a ) it ends with stays, so foo or lang:Rust is counted as foo. The languages are secondary to the files: a languages query Zoekt still cannot read lists none, and the search answers as before. Colors are linguist's, from the table that counts a repository's languages, with Markdown, JSON, YAML and TOML added.

The Repositories count leaves out lang:. From the code tab, repositories are counted by the search's words alone.

Why

Picking a language used to hide all the others, as the list was made from the 20 files shown and dropped once lang: was in the query: there was no way to move from Rust to TOML but by editing the query. A fill rather than numbers: the shape of the results reads at a glance, in the languages' own colors, and the side already counts the kinds. The fill is mixed into the theme's background rather than laid over it, so it reads as a wash on light themes and a glow on dark ones, and the text keeps the theme's own color. Alternatives (a bar in a track, a bar under the name, one stacked bar, dots) were mocked and set aside.

How it was checked

  • searching_code asserts languages for a search, and that a search kept to Markdown finds no file yet still lists Rust with both files.
  • Unit tests: unlanguaged (quoted names, -lang:, a query of only lang:, an or before or after a filter, a group a filter closes), within, and color for a counted language, a prose one and an unknown one.
  • just look of a picked language, on desktop and as chips on a phone, in light and dark, and of a picked language with no file found (attached: the check does not draw a picked language).
  • The recorded searchCode?q=count answers have languages added, each counted from the answer's own files, which hold all it found.

Shots-moved: code, code-repo

Shortcomings

  • A chip's fill on a phone is a share of its own width, which follows its name, so it is rougher than the desktop's rows; the order still ranks them.
  • A dark language color on a dark theme (Markdown's navy) makes a faint fill; the name and the order still carry it.
  • Over 1000 files, the counts are of the first 1000.

🤖 Generated with Claude Code

lang-picked-desktop-light.png

lang-picked-phone-dark.png

lang-empty-desktop-light.png

Asked for changes

This pull makes searchCode also run a second Zoekt query, with the lang: and -lang: words taken out. It returns the ranked per-language file counts and colors, and the search page uses them to keep the language list when one is picked. I found one bug that can break a search that works today.

unlanguaged removes lang: words by splitting on whitespace and doesn't look at the operators around them. A query like foo or lang:Rust becomes type:file (foo or). lang:Rust or foo becomes type:file (or foo). Both are parse errors in Zoekt. search_code joins the two queries and applies ? to the second, so the whole request fails, and the user gets "code search: …" instead of results. These queries ran fine before the pull. The test covers only the plain cases.

Please make the languages query unable to break the search. Two ways to do that:

  • Drop a dangling or, and or not next to a removed filter.
  • Treat a failure of the second query as "no languages" (faceted.unwrap_or_default()), so the main results still show. I'd do this one in any case, since the language list is secondary to the results.

Please add a test for the or case.

The frontend change reads fine. The lang: strip for the Repositories count and the picked, current-row and "No X file matches" handling look right.

crates/knotless-serve/src/code.rsline 427Outdated
return q.to_string();
}
kept.join(" ")
}
review.knotless.dev

Removing a filter word next to or leaves foo or or or foo, which Zoekt rejects. See the review body for the failing queries and two fixes.

crates/knotless-serve/src/web/search.rsline 65Outdated
let limit = p.limit.unwrap_or(FILES).clamp(1, FILES_MAX);
let (found, faceted) =
tokio::join!(code.search(&p.q, within, limit), code.faceted(&p.q, within));
let (found, faceted) = (found?, faceted?);
review.knotless.dev

faceted? makes the secondary language count fail the whole search. Fall back to an empty list on error so the results still show.

Asked for changes

Round 2: the bug from my last review is still there, and I don't see a fix for it at this head. A failure of the second Zoekt query still fails the whole search. unlanguaged still leaves a dangling or, and or not where it removes a lang: word, and there is no test for the or case.

Neither of the two fixes I suggested was made. A query like foo or lang:Rust becomes type:file (foo or). Zoekt rejects that, faceted? returns the error, and the search that worked before this pull now fails with "code search: …". The language list is secondary to the results, so a failed languages query should not break the search.

Please do both:

  • Make a failed faceted call give no languages. For example, use faceted.unwrap_or_default() instead of faceted?, and log the error if you want it visible.
  • Drop an operator word (or, and, not) left dangling next to a removed filter, and add a test for foo or lang:Rust and lang:Rust or foo.

I read all the Rust changes; the frontend reads fine, as before.

crates/knotless-serve/src/web/search.rsline 65Outdated
let limit = p.limit.unwrap_or(FILES).clamp(1, FILES_MAX);
let (found, faceted) =
tokio::join!(code.search(&p.q, within, limit), code.faceted(&p.q, within));
let (found, faceted) = (found?, faceted?);
review.knotless.dev

faceted? still fails the whole request when the languages query errors. Use faceted.unwrap_or_default() so the main results always show.

crates/knotless-serve/src/code.rsline 426Outdated
if kept.is_empty() {
return q.to_string();
}
kept.join(" ")
review.knotless.dev

foo or lang:Rust still gives foo or, and lang:Rust or foo gives or foo. Both are parse errors in Zoekt. Drop the dangling operator, and add a test for it.

Approved

Both requests from earlier rounds are done at this head.

  • Failed languages query: search_code uses faceted.unwrap_or_default(), so a languages query Zoekt can't read lists no languages and the main results still show.
  • Dangling operators: unlanguaged takes out an or, and or not next to a removed filter, and keeps a ) the filter closed. foo or lang:Rust becomes foo, lang:Rust or foo becomes foo, and (foo or lang:Rust) bar becomes (foo ) bar. I traced these cases by hand and the logic holds. I did not run the tests.
  • Tests: there are new assertions for the or before and after a filter, and for the grouped cases.

I found nothing new that needs changing.

Approved

Nothing new needs changing at this head. Since my approval at 78933405 there are two changes. The first is a merge of main, which I did not review. The second is one commit to search_code in crates/knotless-serve/src/web/search.rs, which now ranks the languages by sorting (Reverse(files), name) tuples. That gives most files first and ties broken by name, and it drops the earlier sort and the comment that claimed the map's order. Ties now come out in a fixed order, so the language list stops shuffling. The logic is correct. I did not run the tests.

Merged as ae912f2