Repository navigation
Conversation
[Medium impact] The latest changes appear safe to merge; the previous selection-loss issue is addressed. SummaryThe PR adds ranked search, path matching, typo support, and answers from names held in memory.
Reviews (20) · Last reviewed commit: "Keep a selection made again after a sear..." · Reviewed by Greptile |
thisisgm
force-pushed
the
search
branch
2 times, most recently
from
November 11, 2026 23:33
ecce9ca to
b78cc11
Compare
thisisgm
force-pushed
the
search
branch
2 times, most recently
from
November 12, 2026 02:06
541e13a to
283ae11
Compare
thisisgm
force-pushed
the
search
branch
2 times, most recently
from
November 12, 2026 06:04
705ce17 to
ffec451
Compare
Building a listing asked the disk about every file a second time, because Qt stats a file again before it reads the name. A regular file is now typed from the name the listing already read; a link, a pipe or a device keeps the full lookup.
A selection, a folder count or a second view of the same folder each made the model copy its whole listing. They now read it in place, so a large folder is held once however many views show it.
The preview's folder summary sorted every name to count them and to pick two pictures. It now keeps the first two pictures in one pass, so summarizing a large folder no longer holds every name.
Every entry kept its own copy of its type, kind and extension, and the listing kept spare room for entries that never came. The entries of a listing now share those strings, and the spare room is given back.
Opening a folder typed every file before its first row showed. The listing now leaves a regular file's type, kind and extension unread until a role asks, which touches no disk; a sort by kind still types every entry in the worker.
Every refresh of Places, the 15 second poll included, told all four sidebar lists they had changed. Each list now has its own signal and is announced only when its contents differ.
While active, Folio ran hyprctl once a second to read the corner rounding. It now reads it at start, on a palette change, on return to the window and when Hyprland's event socket reports a reloaded config.
Sorting 100,000 names collated both strings on every compare. A listing that arrives in a few ordered runs is now merged, and a name or kind sort makes one sort key an entry first. The comparator is the same, so every order and tie stays; the C locale compares as before.
A name or kind listing read every entry's size and date before its first row showed. It now reads the first 320 rows, and a later row when a view asks, one batch a turn, off the UI thread. A row not read yet shows no size and no date, never 0 B.
A re-read that found the same names kept each folder's item count, even when the folder's own date had moved. Such a folder's count is now dropped and counted again, as it was before the listing learned to keep its rows through a re-read.
The event socket was opened once. Hyprland hangs up on a reader that fell behind, and after that a config reload no longer reached the corners. The watch is now taken up again when Folio is back in use.
A text tile past the rows read at first asked for its page with no date, then again when the date arrived, and a page has no cache on disk, so the file was read and drawn twice. Like a thumbnail, a page is now asked for only once its date is known.
A folder past the rows read at first is counted without its date being read, so a re-read had nothing to compare and kept its old count. Its date is now read just before it is counted and kept with the count, and the re-read sees the folder changed since.
A burst of events was read whole into memory before any of it was looked at, on the UI thread. One turn of the event loop now reads 64 KiB at most and looks at each piece as it comes, and a line too long to keep is dropped to its end, its last piece included.
Hung up on by Hyprland, the theme took its watch up again only when Folio was left and come back to, so a config reload in between left the corners as they were. While in use it now connects again after a pause, and reads the rounding for a reload that went by untold.
Hung up on while Folio stayed in use, the theme tried Hyprland's event socket once more after a pause. A try that found no socket was the last, and reloads went untold until Folio was left and come back to. Each failed try now starts the pause again.
A re-read drops the answers on their way and reads the rows asked for itself. A row it could not read either stayed marked as being read, so no view could ask for it again. Such a row is now owed: a view may ask again, the folder's next re-read tries it again, and a view is told when another view read it.
A view asks for a row's kind, MIME type and extension each time it makes the row, and each ask read them from the name again: 48 us a row in /usr/bin. The model now keeps up to 4,096 names' types, until a folder is found changed.
A search asked the disk about every file it passed. The walk now reads names and types from the directory alone and asks only about folders it enters and rows it shows. The matches and their order are as before.
Every key walked the whole tree again. The first key now walks it once and later keys are answered from the names in memory, until the search closes, the folder changes or it is read again. Escape frees them.
Results came by name and stopped at 1,000. They are now ranked: the exact name, then a name or a word that starts with the query, then its letters in order. Rows arrive 350 at a time as the view scrolls, and the field says how many matched.
A query such as editor/src/render found nothing, since only names were compared. The folders above a row now take as much of the query as they can and the name takes the rest; a name that holds the query still comes first.
A slip such as redner for render found nothing. When no name holds the query whole or in runs that each start a word, names one edit away are listed, two for long words, ahead of names that only hold its letters scattered in order. Digits are never edited, and a query some name spells out never pays.
On a very large tree the first key waited for the whole walk. After a short hold the best matches read so far are shown and refreshed until the walk lands, and the field says it is still reading.
With matches shown a window at a time, Select All took only the rows scrolled to, and F5 or a change in the folder went back to the first window, dropping a selection past it. Select All now takes the matches past the rows by path, at once, from the answer in memory; a search read again shows as many rows as it did and keeps what is selected while it still matches, and a partial answer drops nothing.
A change on disk or F5 walked the folders again from nothing, so a landed search fell back to partial answers and its count. It now answers on until the new walk has answered, one walk at a time. Select All takes every match while still reading and under any sort, and a list cut to the best matches says so.
Only the open folder is watched, and a later key was answered from the first read alone, so a file made below it since stayed missing until a refresh. Each later key is still answered at once from what was read, and the folders are read again behind that answer.
The status at the end of the search field says where it searches and how many match, at its full width, and the field's padding is that width. In a narrow pane it took the room the query is typed in. It is now cut short with an ellipsis before the query has less than 160 pixels.
A search along the path ruled a row out when the folded query was longer than the path as stored. Folding can lengthen a letter, so a path typed out exactly went missing. Only ASCII paths take that test.
The bits of digits and marks, the first size of the lookup table and the classes of a pending score were bare numbers: they have names now, and a static_assert ties the classes to the tiers. Each comment is one line. Nothing a search does changes.
A folder swapped for a link after its own folders were listed was followed when they were opened: O_NOFOLLOW guards the last name only. Every folder is opened from the root's descriptor now, with RESOLVE_NO_SYMLINKS, and the root is set under the walk's lock.
A name with one letter past ASCII was folded whole, so MeinBüro lost the capital that starts its second word and ranked with names that only hold the letters. Only what is past ASCII is folded now.
A walk kept every name below the folder, with no limit on the sum. It ends at 2,000,000 names now, the nearest first, and the search field says that only those were read.
A held walk keeps the pool at two threads, so the cases pass on one processor. The held-pool cases wait until the walk's end has asked for its answer, the walk counts rest on no watcher timer, and a new case lands an answer the walk's end overtook. Each comment is one line.
The field waited for 150 ms without a key before it asked, a wait kept from when every key walked the tree. The model answers a key from the names it holds, so the field asks as each is typed.
A key cleared the selection before its answer was drawn, each answer left the cursor on its old row, and the end of a search showed the folder a frame before its cursor. The selection now goes with its rows, the cursor stays on the best match, and the end restores it at once.
Rows that replace others came with no folder counted, so at every key of a search the counts on screen went out for a frame and came back. A folder the rows before had now shows the count it had until its new one is in, and is counted again whatever overtakes that counting.
What is selected after a key is kept by that key's answer, but asking for the selection already there returned early and left the let-go standing: Select All pressed again while an answer was on its way was cleared by it. The let-go now ends at any ask, changed or not.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #26: its eighteen commits come first here, the last twenty are this pull request's, and I will rebase it as soon as #26 lands.
Ten changes to search (
/), one commit each with its own tests: a search no longer asks the disk about every file, later keys are answered from memory and still find a file made since, the best matches come first, a query can follow the path, one typo is forgiven, a very large folder shows matches while it is still read, Select All and a refresh reach the matches past the first window, a search goes on answering while it is read again, and the status in the search field leaves room for the query in a narrow pane. An eleventh commit answers the review: a path typed out exactly is found when folding its letters makes it longer than it is stored. Four more answer a second one: a search reads at most 2,000,000 names, the nearest folders first, and the field then says how many it read; no link is followed on the way to a folder; a capital starts a word in a name that has letters past ASCII; and the index's numbers have names. The last one is tests only: the cases that hold a walk pass on a machine with one processor, none rests on the folder watcher's timer, and one more lands an answer that the end of a walk overtook. The newest commit takes out a pause: the field waited for 150 ms without a key before it asked, and it now asks at every key. The last two take out a flicker while typing: the selection and the cursor change in the frame an answer is drawn and not before, the cursor stays on the best match, the end of a search puts it back on the item it left, and a folder's item count stays on screen from one answer to the next. One more answers the review of those two: a selection made again after a key, with nothing changed (Select All pressed a second time), is kept when the answer lands. The suite goes from 236 to 276 tests. No library,dependsoroptdependsentry is added, and nothing runs when no search is open: no index is kept on disk and nothing is read ahead of the first key.What you'd notice
3 items) went out for a frame at every key and came back. Now the selection changes in the frame the new rows are drawn, the cursor is on the first row of each answer until it is moved to another item, Escape shows the folder with the cursor back on the item it left in the same frame, and a folder that was already listed keeps its count on screen while it is counted again. A folder that was not on screen before still gets its count after its row, as it does when any folder opens. Showing or hiding hidden files clears the counts at once, since a count made the other way is wrong, and the folders are counted again.editor/src/renderfound nothing, since only names were compared. The folders above a row now take as much of the query as they can and the name takes the rest. A name that holds the query still comes first.rednerforrenderfound nothing. When no name spells the query, names one edit away are listed (two for long words), ahead of names that only hold its letters scattered in order. Digits are never edited, and a query that some name spells out never pays for this.Numbers
Measured on a Linux server with a small driver that links the same sources and types into the same model the window uses, on a folder tree of 100,000 files, warm cache, two runs of ten timings a query after two warm-ups. Times are from the key to the rows being ready.
render_frame.rsredner_frame.rseditor/src/components/renderarchive/2024/client/reports/quarterly.svg, 6,458 matchesOn main every key pays for the walk again, so its times are a walk and a compare. On the branch the walk is paid once, and a key is a pass over the names in memory. The typo pass is the slow one, since it runs only after that pass found no name that spells the query. The tree takes 7.0 MB while the search is open.
Screenshots
Each picture is main above and this branch below, in the list view, on the same small source tree.
render: main lists by name, the branch ranks the folder and the exact names first and counts the matches.editor/src/render: a path query.redner: one typo.A window split in two panes,
rendertyped in the right one: this branch before its last commit above, with it below.Tests
Forty new cases, each failing without its commit: a search asks the disk only about the folders it enters and the rows it shows; a later key is answered before any folder is read again; a file made since the folders were read is found at the next key; the exact name, a name or a word that starts with the query, and scattered letters come in that order; rows arrive a window at a time while the count is whole; a query follows the folders above a name; a typo is forgiven only when no name spells the query, and ranks above scattered letters; a tree still being read shows the matches found so far; Select All selects all 500 of 500 matches at once when 350 are shown, and names them all; a search read again keeps the 500 rows it showed and the selection at row 420; it keeps what Select All took past the rows, less a file that is gone; and through the partial answers of a tree being read again all 500 selected files stay selected and named, and the 500 rows come back; Select All while a tree is still read takes every match found so far; a search that landed keeps its rows and its count while it is read again, also while a download keeps changing the folder; Select All under another sort takes every match; what Select All names is whole by the time the selection says it changed; a search that is cleared forgets how many matched; a match gone from the disk since the walk still has its row; a change during the first walk is read once that walk is whole; a walk made again takes over only when it has answered; Select All after a key taken back takes every match; a row selected while a search is read again stays selected when the new answer leaves it past the rows shown; hiding hidden files starts the search over; a search that ends stops its second walk; the path bar says how many matched and how many are shown, whatever the folder is called; and the status in a narrow field is cut short so the query keeps its room, mirrored too, changes the field's padding and never its width, and is not drawn in a field with no room for it; a path whose letter grows when it is folded is found typed out, as a folder's name and as a file's, while a path in ASCII too short for the query is still ruled out before a folder is read. A tree of more names than a search keeps is read up to that many, the nearest, and the field says how many were read, beside how many rows are shown when the list is cut too; a folder swapped for a link while its own folders wait is not read through; a folder searched through a link that is turned elsewhere mid-read stays the tree it was; and
bürofindsMeinBüro.txtahead ofxbüro.txt. A key typed in the search field is the query at once, with no pause waited out; and keys typed while the first walk still reads start no walk beside it, are read once more together when it ends, and what lands is the answer to the last of them, with no earlier key's answer shown on the way; a key leaves the selection alone until its answer lands, and what was selected since is kept, also when it is the same selection asked for again, and is still cut to what the answer matches; the end of a search shows the folder and says so in one turn; in the window, the cursor is on the first row of each answer, follows a better match found later, stays with an item it was moved to, and is back on the item it left when the search ends; and a folder two answers list shows its count in the turn the second lands, is counted again all the same, and never shows a count made with hidden files the other way. Every commit builds and passes the suite on its own, also withTMPDIRset to a folder whose name has a space, where onlyextractRefusesWhatWouldLandOutsidefails, as it does on main.Limits
N matches in X and below, only the nearest 2,000,000 items read.openat2, so the walk needs Linux 5.6 or later.