-
-
Couldn't load subscription status.
- 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) |
73d36b0 to
9b7ceb7
Compare
|
9b7ceb7 to
29172d2
Compare
|
|
That's truly excellent!! Thank you! I just started using it, and noticed a couple of problems:
|
29172d2 to
03ca9f0
Compare
|
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. |


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.