Skip to content
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

REPL/LineEdit to support "undo": part of #8447 #9596

Merged
merged 1 commit into from
Sep 5, 2017

Conversation

srp
Copy link
Contributor

@srp srp commented Jan 4, 2015

The REPL now supports undo via Ctrl-^ or Ctrl-_.

This should very closely minic the behavior of other readline/emacs
shells, except it doesn't let the user goto a historical entry (A),
edit it, goto a different historical entry (B), return to the first
(A) and undo the edits.


This change is Reviewable

@tkelman tkelman added the REPL Julia's REPL (Read Eval Print Loop) label Jan 4, 2015
@rfourquet
Copy link
Member

Thanks! I tested it and works fine. I don't know the LineEdit.jl code very well yet, but the patch looks good to me.
WRT the limitation you mention in the commit: would it be difficult to eventually attach an undo_buffers vector to each history entry? (but it's maybe not worth the effort, I don't think I use this functionality in readline).
There is also another difference from readline, which packs together individual characters inserts (i.e. typing "123" and then undo removes those 3 characters), but I don't see this as a problem.

base/LineEdit.jl Outdated
@@ -1512,7 +1538,7 @@ end

run_interface(::Prompt) = nothing

init_state(terminal, prompt::Prompt) = PromptState(terminal, prompt, IOBuffer(), InputAreaState(1, 1), length(prompt.prompt))
init_state(terminal, prompt::Prompt) = PromptState(terminal, prompt, IOBuffer(), (IOBuffer)[], InputAreaState(1, 1), length(prompt.prompt))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not IOBuffer[] (w/o parens) ?

@rfourquet
Copy link
Member

Oh, and special +1 for extensive tests.
If you didn't start yet implementing beginning/end-of-history from #8447, I have it here and will do a PR soon.

@srp srp force-pushed the repl-undo branch 2 times, most recently from 7e82a32 to da54cf6 Compare January 5, 2015 15:59
@srp
Copy link
Contributor Author

srp commented Jan 5, 2015

Looking forward to the beginning/end-of-history! My plan was to next tackle kill/yank which doesn't seem to work quite right: m-d and friends don't get added to yank ring, yank ring isn't yet a ring, etc.

@srp srp force-pushed the repl-undo branch 2 times, most recently from 307d525 to cf5dfc8 Compare January 15, 2015 03:58
@rfourquet
Copy link
Member

Bump. I'm really missing this functionality!

base/LineEdit.jl Outdated
@@ -146,16 +148,14 @@ function complete_line(s::PromptState, repeats)
elseif length(completions) == 1
# Replace word by completion
prev_pos = position(s.input_buffer)
seek(s.input_buffer, prev_pos-sizeof(partial))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why have the calls to seek been dropped here and elsewhere in this PR?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably because edit_replace already is "undo aware", whereas seek is not. When I compile without this change, after an undo the cursor is misplaced (e.g. at the beginning of a (prefix) word after undoing a completion).

@rfourquet
Copy link
Member

I rebased locally this branch on master, and for some reason the key-binding "^/" is not valid anymore (compilation fails), I don't know why yet.

@rfourquet
Copy link
Member

@srp would you consider rebasing this work to get it merged?

@StefanKarpinski
Copy link
Sponsor Member

Yes, please – this would be a lovely feature to have. Sorry the PR has languished!

@@ -1559,7 +1583,7 @@ end

run_interface(::Prompt) = nothing

init_state(terminal, prompt::Prompt) = PromptState(terminal, prompt, IOBuffer(), InputAreaState(1, 1), #=indent(spaces)=#strwidth(prompt.prompt))
init_state(terminal, prompt::Prompt) = PromptState(terminal, prompt, IOBuffer(), IOBuffer[], InputAreaState(1, 1), #=indent(spaces)=#strwidth(prompt.prompt))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this line should probably be wrapped

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

The REPL now supports undo via Ctrl-^ or Ctrl-_.

This should very closely minic the behavior of other readline/emacs
shells, except it doesn't let the user goto a historical entry (A),
edit it, goto a different historical entry (B), return to the first
(A) and undo the edits.
@srp
Copy link
Contributor Author

srp commented Jul 22, 2017

@rfourquet,@StefanKarpinski: rebased, thanks!

Copy link
Member

@rfourquet rfourquet left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your very nice work! None of my comments are blocking, and can be addressed later if needed.
I will merge in a few days if no-one has objections.

@@ -572,15 +592,18 @@ end
edit_clear(buf::IOBuffer) = truncate(buf, 0)

function edit_clear(s::MIState)
push_undo(s)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe you could push_undo only if the buffer is not empty already? Another possibility could also be to not push a new state in push_undo when it's equal to the last pushed state?

end

function edit_yank(s::MIState)
push_undo(s)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe you could push_undo only if !isempty(s.kill_buffer)?

edit_insert(buffer(s), s.kill_buffer)
refresh_line(s)
end

function edit_kill_line(s::MIState)
push_undo(s)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

push_undo only if some characters are deleted?

push_undo(s) = nothing

function pop_undo(s::PromptState)
length(s.undo_buffers) > 0 || return false
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isempty(s.undo_buffers) && return false

@@ -401,3 +401,96 @@ let
Base.LineEdit.InputAreaState(0,0), "julia> ", indent = 7)
@test s == Base.LineEdit.InputAreaState(3,1)
end

# test Undo
let
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use @testset ?

@@ -172,6 +172,7 @@ to do so).
| `^Y` | "Yank" insert the text from the kill buffer |
| `^T` | Transpose the characters about the cursor |
| `^Q` | Write a number in REPL and press `^Q` to open editor at corresponding stackframe or method |
| `^/`, `^_` | Undo |
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't see you add a key for ^/, how does it work? (when I tried to rebase last year, I had compilation problems with this key...)
Also, your commit message refers to "Ctrl-^", you should change it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

^/ isn't a valid control character (eg after subtracting 64 you get a negative number), however it appears that vt102 terminal emulators map it to emit ^_ for convience, see: https://apple.stackexchange.com/a/227286

From the terminals I tested on this is accurate that pressing ctrl-/ causes "^_" to appear in the console.

end
empty_undo(s) = nothing

function push_undo(s::PromptState)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer the names of the new functions with an ending !, but it seems to be the style of the file, so better to keep as is for now.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, this one is a good candidate for short-form function syntax.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These should probably all be refactored to have !. It wasn't clear to me reading through this that it actually modified the PromptState until I saw the definition. The larger refactoring doesn't have to happen in this PR, but if you're adding a function now, might as well use the standard notation, IMO.

@KristofferC
Copy link
Sponsor Member

How do I type Ctrl+/ on a European (Swedish) keyboard? Would Ctrl + Shift + 7 work where Shift + 7 gives /?

@fredrikekre
Copy link
Member

Ctrl + Shift + 7 works for me @KristofferC 🇸🇪
I have to say that it is not a very convenient keyboard short cut for such keyboard layouts though.

@srp
Copy link
Contributor Author

srp commented Jul 23, 2017

Does Ctrl+_ work on a Swedish keyboard? If so, you might have to stick with that.

@fredrikekre
Copy link
Member

_ is Shift + -

@srp
Copy link
Contributor Author

srp commented Jul 23, 2017

Sounds the same as an english keyboard, i have to hit Ctrl + Shift + - to emit ^_

@srp
Copy link
Contributor Author

srp commented Jul 23, 2017

@rfourquet I plan on pushing an update addressing some of your comments, but in the process noticed that undo doesn't work right (any more?) with bracketed insert. I'm trying to fix that, but so far it's not entirely straight forward.

@rfourquet
Copy link
Member

rfourquet commented Aug 7, 2017

@srp, I rebased your branch and tried to fix undo with the bracketed paste mode, would you mind checking out this branch (just do git checkout srp/repl-undo && make after fetching from the julia repo) and verify if this fixes your concerns? You can use this to update your branch (which now has a small conflict). I wish to merge your work soon!
EDIT: I also updated your commit message with a corrected key-binding.

@rfourquet
Copy link
Member

Bump - I have few features in the pipeline which will increase the work to resolve conflicts here if we don't merge this first...

@rfourquet
Copy link
Member

I rebased again and updated the branch "srp/repl-undo" that I linked to above. I plan to merge by tomorrow. But I cannot update the PR directly, and I'm afraid that if I merge it on the command line, github won't recognize this PR has been merged (it happened to me already), someone would have a hint as to how to do it properly?

@rfourquet rfourquet merged commit fe77221 into JuliaLang:master Sep 5, 2017
rfourquet added a commit that referenced this pull request Sep 5, 2017
@rfourquet
Copy link
Member

Thanks again @srp !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
REPL Julia's REPL (Read Eval Print Loop)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants