-
-
Notifications
You must be signed in to change notification settings - Fork 5.7k
The Fanciest REPL History in the Land #59819
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
This comment was marked as resolved.
This comment was marked as resolved.
|
If possible, it would be nice to cut this up into some orthogonal pieces. For example, the AnnotatedString perf improvements could be a separate PR that (with benchmarks) could be merged and would make the diff here smaller and easier to review. |
stdlib/REPL/src/History/search.jl
Outdated
| filename = try | ||
| readline(term.in_stream) | ||
| catch err | ||
| if err isa InterruptException | ||
| "" | ||
| else | ||
| rethrow() | ||
| end | ||
| end | ||
| isempty(filename) && (println(out, S"\e[F\e[2K{light,grey:{bold:history>} {red:×} History selection aborted}\n"); return) | ||
| write(filename, "# Julia REPL history excerpt\n\n", content) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe it should default write to a location? The prompt could default to ~/.julia/history.jl (or a more appropriate location).
Ideally, the prompt for the filename would also allow scrolling up and down with the up arrow and down arrow, typing some characters of the file and hitting up arrow to narrow the search, have a ghost text of the last file name or the default file name so right arrow completes it etc. But I can imagine this being awkward the way things are set up in the REPL?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ideally, the prompt for the filename would also allow scrolling up and down with the up arrow and down arrow, typing some characters of the file and hitting up arrow to narrow the search, have a ghost text of the last file name or the default file name so right arrow completes it etc. But I can imagine this being awkward the way things are set up in the REPL?
I've had exactly the same thought. I just can't be bothered to make it happen 😅
I'm happy to split this up, but there are really just three parts to it:
I could split 1-2 off into a separate PR to review, if that sounds like a nice idea?
There's only suspicious performance here, not perf improvements, unfortunately 😞. I say "suspicious" because if I remove the keyword argument from For now, I've left these julia/base/strings/annotated_io.jl Lines 202 to 226 in cc7ed22
julia/base/strings/annotated_io.jl Lines 236 to 240 in cc7ed22
|
cc7ed22 to
75fd56d
Compare
|
I've just added back ~400 loc of the original code for using up/down arrow keys to go through recent history without If you start a new REPL and immediately press up arrow, it does nothing the first time. Not quite sure why... (any ideas?) I would like to add session-awareness. Maybe I'll try to drop that in this PR too? |
75fd56d to
221b4e5
Compare
|
Rebased to the latest REPL Syntax Highlighting |
221b4e5 to
ee23322
Compare
|
Fixed some typos. |
|
The precompilation situation is looking pretty decent, this is all I'm seeing with |
ee23322 to
3a6d8d7
Compare
|
Rebased now that #59778 has been merged. |
3a6d8d7 to
4bac24f
Compare
Fixed. |
4bac24f to
c7a66cb
Compare
|
Thanks to Miguel, Camillo, and Sundar for taking this for a test-drive and providing feedback 🙂 Changes:
|
7126dd7 to
0da2a86
Compare
|
Rebase (get the REPL precompilation improvements) |
03ca9f0 to
4072ade
Compare
|
Extract the replacement loop body into `_replace_once` to ease future annotation tracking during string replacement operations. The new function returns match information (pattern index, match range, bytes written) that will be needed to properly adjust annotation positions when replacements occur.
Implement `replace` function for `AnnotatedString` that properly handles annotation regions during pattern replacement operations. The function tracks which bytes are replaced versus preserved, maintaining annotations only on original content and adding new annotations from replacement text. - Supports AnnotatedChar, AnnotatedString, and SubString replacements - Drops, shifts, and splits existing annotations appropriately - Refactored `_insert_annotations!` to work with annotation vectors directly - Adjacent replacements with identical annotations are merged into single regions - Lots of tests (thanks Claude!) Performance is strangely poor. For the test case mentioned in the REVIEW comment within `_insert_annotations!` we should be able to perform the replacement in ~200ns (compared to ~70ns for the equivalent unannotated case). However, for two reasons that are beyond me instead it takes ~4400ns. See the REVIEW comments for more details, help would be much appreciated.
Discarding the annotations can come as a bit of a surprise best avoided.
4072ade to
dd8bf6e
Compare
|
dd8bf6e to
cc2f31f
Compare
|
Since the dawn of the Julia REPL, history completion has been limited to a readline-style interface. OhMyREPL improved the experience with fzf, but those who yearned for a delightful history completion experience (me) were left underwhelmed. With this overhaul, I now find myself spending more time looking through my history because it's just *so nice* to do so. The new history system is organised as a standalone module in stdlib/REPL/src/History with a clear separation of concerns: 1. History file management 2. Event-driven prompt/UI updating 3. Incremental filtering 4. UI display 5. Search coordination (prompt + display + filter) I've attempted to pull out all the (reasonable) stops to make history searching as fluid and snappy as possible. By memory mapping the history file in the initial read, and optimising the parser, we can read ~2 million history items per second. Result filtering is incremental and resumable, performed in dynamically sized batches to ensure responsiveness. Rapid user inputs are debouced. We store a log-structured record of previous search result, and compare search strictness to resume from prior partial results instead of filtering the history from scratch every time. Syncronisation between the interface and filtering is enabled via a Channel-based event loop. Enjoy! (I know I am)
cc2f31f to
24a5ca2
Compare
|
I did? This is (happy) news to me. That said, I never quite got to the bottom of why it wasn't working. |
|
Let's merge this now so we get a "checkpoint" when CI is green and people can test it more, the remaining niggles shouldn't be a problem addressing post-merge. |
I think this is a good idea, it is often that I end up with the whole history being identical entries. |
I'm a fan of this idea, I'll put it in a follow-up PR.
I also wonder, however I'm wary the inconsistency from making a search mode that relies on a suffix.
I've noticed that the filtering isn't entirely working how I think it should. I think this is escaping working as you expect, but filtering not.
That you're currently doing a word-based search, and have result 12 out of 12 focused. |
FWIW, I don't think of this as a suffix really - I think of it as an exact match for "julia>", "pkg>", or "shell>" Custom REPL modes complicate that a bit. I don't think "suffix" is the right way to refer to it in the user help / documentation (even though it'd be accurate)
What are the other type of searches? Is there a discovery path for the user to learn what that means? I was really pleased that everything else (e.g. the colored dots for prompt mode) is pretty direct to learn through interactive usage - that seems like great design to me. The "words" was the one bit of TUI that I couldn't learn how to read on my own |
Done. You'll see this in #59953 soon
I've worked out something I'm happy with, making a
Did you look at the help? Based on the way you escaped |
I'd recommend a separate PR probably. Makes it easier to tweak / take in feedback for individual features, etc.
Nice, I like that! Feels very intuitive.
I did yeah! I think what might have tripped me up is that none of the help documentation mentions "words" In comparison, the "regexp" / "exact" / "fuzzy" / "initialism" statuses are quite clear and correspond directly to the help too. "words" is just a very generic term for the default search mode. I wonder if maybe "words" / "separator" are unnecessary statuses and we could just leave them blank and display only the "special" modes? |
I expect I'll end up doing that, but while I'm adding tweaks to the current behaviour it's good to see what accumulates before making a PR out of it: I don't want to do half a dozen individual PRs.
Yea, it "feels right" to me (which is what I was waiting for before going ahead with an option). I do wonder if we should pre-populate the query for a search made in mode XYZ with
This is good feedback. We could not mention words, but I'm more inclined to better mention words in the help. |




The Fanciest REPL History in the Land ✨
Do you dread typing out code for the second time? Are you a particular enjoyed of REPL history?
Well, I know I am, and for years I have yearned for something better than the current
readline-style completion, better than OhMyREPL.jl's fzf-driven completion, better than any REPL history I've seen before!It's not quite finished baking, but we're onto the final stretch 😀
repl_history_demo.webm
Thanks to @kdheepak, @jakobnissen, and @digital-carver for helping me design the UI and UX over on Zulip (#repl > Revamped REPL history).
Features
TODO
replacemethodreplacemethodreplacemethod (see: theREVIEW: ...code comments inannotated_io.jl)This PR is on top of #59778, because I think I can safely assume that will be merged first.