chadtmiller.com/knotless
git that lives in your Atmosphere account
Code search ranks the languages it finds, each filled with its color
MergedThis 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,andornotnext 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(" ") | |
} |
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?); |
faceted? makes the secondary language count fail the whole search. Fall back to an empty list on error so the results still show.
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
facetedcall give no languages. For example, usefaceted.unwrap_or_default()instead offaceted?, 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 forfoo or lang:Rustandlang: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?); |
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(" ") |
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.
Both requests from earlier rounds are done at this head.
- Failed languages query:
search_codeusesfaceted.unwrap_or_default(), so a languages query Zoekt can't read lists no languages and the main results still show. - Dangling operators:
unlanguagedtakes out anor,andornotnext to a removed filter, and keeps a)the filter closed.foo or lang:Rustbecomesfoo,lang:Rust or foobecomesfoo, and(foo or lang:Rust) barbecomes(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
orbefore and after a filter, and for the grouped cases.
I found nothing new that needs changing.
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.
9 passed, 1 skipped
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.searchCodeanswerslanguages. Each aname,color?andfiles, counted by a second Zoekt query run beside the first: the same query with itslang:and-lang:filters taken out, asking only for files (type:file), up to 1000. Only repositories the index lists count, each asked after once. Anororandjoining a filter to the rest goes with it, and a)it ends with stays, sofoo or lang:Rustis counted asfoo. 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_codeassertslanguagesfor a search, and that a search kept to Markdown finds no file yet still lists Rust with both files.unlanguaged(quoted names,-lang:, a query of onlylang:, anorbefore or after a filter, a group a filter closes),within, andcolorfor a counted language, a prose one and an unknown one.just lookof 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).searchCode?q=countanswers havelanguagesadded, each counted from the answer's own files, which hold all it found.Shots-moved: code, code-repo
Shortcomings
🤖 Generated with Claude Code