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

unify keyboard shortcut and codemirror interaction #5296

Merged
merged 19 commits into from Mar 18, 2014

Conversation

ivanov
Copy link
Member

@ivanov ivanov commented Mar 8, 2014

Ok, this is in a state where it can use some review. I haven't yet verified that this provides everything one needs to integrate with CodeMirror's vim mode, but this makes the situation a lot better.

So to start with, the relevant code was repeated in both CodeCell and TextCell, both of which are extensions of Cell, so this just unifies the logic in Cell.

TextCell had logic here to check if the cell was rendered or not, but I don't believe it is possible to end up triggering such a code path. (Should that be required, I can always just add back these methods to TextCell, performing the .rendered==True check, and calling the Cell)

Prior to this PR, code mirror at_top would only return true on if the cursor was at the first character of the top line. Now, pressing up arrow on any character on the top line will take you to the cell above.

The same applies for the bottom line. Pressing down arrow would only go to the next cell if the cursor was at a location after the last character (something that is only possible to achieve in vim mode if the last line is empty, for example). Now, down arrow on any character of the last line will go to the next cell.

I've also added a use_shortcut method to ShortcutManager, which allows us to a remove a bunch of special-casing of key events like up and down arrow, escape, shift/ctrl/alt-enter.

For example, this PR makes it possible to remove our special-casing of up arrow at the top of the cell, preventing it from going to the cell above. This can be done via this javascript:

IPython.keyboard_manager.edit_shortcuts.remove_shortcut('up')

Previously, the up arrow functionality at the top of a cell was welded into the code base as an if statement.

This code was repeated in both CodeCell and TextCell, both of which are
extensions of Cell, so this just unifies the logic in Cell.

TextCell had logic here to check if the cell was rendered or not, but I
don't believe it is possible to end up triggering such a code path.
(Should that be required, I can always just add back these methods to
TextCell, performing the .rendered==True check, and calling the Cell

prior to this, code mirror at_top would only return true on if the
cursor was at the first character of the top line. Now, pressing up
arrow on any character on the top line will take you to the cell above.

The same applies for the bottom line. Pressing down arrow would only go
to the next cell if the cursor was at a location *after* the last
character (something that is only possible to achieve in vim mode if the
last line is empty, for example). Now, down arrow on any character of
the last line will go to the next cell.
This method was identical in both CodeCell and TextCell
Our edit mode keyboard shortcuts don't distinguish between being in a
code cell or in a text cell, so it makes sense to handle both in one
place. This is a first step in that direction.
this way, you can ask if a particular event will be handled by the
shortcuts system. This takes away the need to special-case many
different possible keys which should be ignored by codemirror by
ignoring them en masse.
@@ -237,6 +237,12 @@ IPython.keyboard = (function (IPython) {
return true;
}

ShortcutManager.prototype.use_shortcut = function (event) {
Copy link
Member Author

Choose a reason for hiding this comment

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

Not sure I like this name, just that inline on our shortcut managers it would look like edit_shortcuts.use_shortcut(event)

Copy link
Member

Choose a reason for hiding this comment

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

What about replacing it with .use(event)?

Copy link
Member

Choose a reason for hiding this comment

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

use sounds imperative, but this is just a query, right? What about .uses(event)?

Copy link
Member

Choose a reason for hiding this comment

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

since it's events, let's use handles.

@ivanov
Copy link
Member Author

ivanov commented Mar 8, 2014

pinging fellow keyboard handling co-conspirators @ellisonbg and @jdfreder

@ellisonbg
Copy link
Member

First impressions:

  • I am strongly +1 on this cleanup. Fantastic!
  • I am hesitant to try to get this into 2.0. The changes are substantial, it doesn't fix any bugs, and would need a good amount of time to bake in master before release. We are having enough trouble as it is trying to kill all the focus bugs without another significant change like this. @minrk @takluyver @fperez thoughts?
  • I view the current behavior of at_top and at_bottom as desirable. This is how text areas usually behave and I think it is confusing to go to the next/prev cell as this PR implements unconditionally. Is this absolutely required to get the vim stuff working properly?

@takluyver
Copy link
Member

I'm inclined to agree - unless there's a bug that can't practically be fixed without it, it seems best to push refactoring to after the release.

@fperez
Copy link
Member

fperez commented Mar 10, 2014

+1 for treading lightly on this so close to release. We already have a candidate out, let's leave this for post-2.0, no matter how good. We always find surprises with big changes :)

It could go into 2.1, which I'm sure we'll need to put out before too long. @ivanov, thanks for the great work!

@ivanov
Copy link
Member Author

ivanov commented Mar 10, 2014

I understand the hesitation, so let me work on a summary of the things that would not be possible to do if this doesn't go in, just so we can make an informed decision about whether or not this would be worth it.

@ellisonbg
Copy link
Member

Thanks @ivanov that would help

On Mon, Mar 10, 2014 at 12:11 PM, Paul Ivanov notifications@github.comwrote:

I understand the hesitation, so let me work on a summary of the things
that would not be possible to do if this doesn't go in, just so we can make
an informed decision about whether or not this would be worth it.

Reply to this email directly or view it on GitHubhttps://github.com//pull/5296#issuecomment-37221897
.

Brian E. Granger
Cal Poly State University, San Luis Obispo
bgranger@calpoly.edu and ellisonbg@gmail.com

@ivanov
Copy link
Member Author

ivanov commented Mar 11, 2014

Ok, so here are the issues this PR fixes which are currently in master:

It is not possible to have Ctrl-Enter, Shift-Enter, Alt-Enter be handled by CodeMirror, even if user removes those shortcuts from our edit mode shortcuts. This means, for example, that one can't swap out using Enter for execution, and using Shift-Enter to insert a new line in CodeMirror.

In master, we have code that lets CodeMirror handle Esc when in vim-insert mode. That's great, but most vimmers use Ctrl-[, which acts the same as Esc in the terminal, since it's actually the traditional escape sequence.

This inconsistency leads to the following: you cannot get Ctrl-[ to act like Esc. Either Ctrl-[ is handled by CodeMirror vim mode only, or Ctrl-[ is specified as an Edit Mode shorcut, in which case it is still passed along to CodeMirror - thus pressing Ctrl-[ in vim-insert mode leaves both vim-insert mode and IPython Edit Mode. (Whereas Esc behavior is to leave vim-insert mode on first keypress, and require another Esc to leave IPython Edit Mode).

But the special casing for vim-insert mode in master is not enough. Esc should also be passed along to CodeMirror when CM is in vim visual mode, but our code doesn't pass it along. So a user who sets up vim-mode for CodeMirror will be in for a rude awakening when he presses escape to cancel a visual selection, only to find himself in IPython Command mode.

The check for that is

if ( IPython.notebook.get_selected_cell().code_mirror.vim.visualMode )

I don't expect such code to make it into IPython proper, which is why I created vimception.

More generally, any IPython Edit Mode keyboard shortcuts that a user wishes to have will still be passed along to code mirror.

Except for up and down arrow keys. Even though it is possible to remove them from IPython Edit Mode shortcuts, they cannot be replaced with anything else (for example, to go to the next cell), because an event involving either of those two in master is passed along to be handled by our shortcut system only if the cursor is before the first character at the top line for up arrow, and only if the cursor is after the last character at the bottom of a cell for down arrow.

So, overall, this PR minimizes the number of keyboard shortcuts which are hard-coded, allowing for their removal and re-assignment, and reduces the number of places customization of non-default behavior needs to happen (due to master code duplication between TextCell and CodeCell).

Pinging @minrk who wanted to weigh in on this as well.

@minrk
Copy link
Member

minrk commented Mar 11, 2014

I was waiting for Paul to enumerate the issues this fixes, but I would prefer this to get into 2.0. The changes here have been planned for quite a while, but Paul kept having to rebase as events and keyboard stuff kept being refactored in master. To me, this is a basic fix for shortcut customization.

If I had known people would be hesitant about this change, I would not have cut the beta without it.

@@ -230,6 +254,53 @@ var IPython = (function (IPython) {
};

/**
* Either delegates keyboard shortcut handling to either IPython keyboard
Copy link
Member

Choose a reason for hiding this comment

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

Don't need 'Either...either'

@fperez
Copy link
Member

fperez commented Mar 11, 2014

Fair enough, I didn't originally realize that they really were necessary for things we want now. At first, I thought it was mostly refactoring/cleanup to make future work easier.

Given it seems like it's really important work for 2.0, let's just be extra careful in the review process, so we don't miss anything surprising. We should also cut an RC with this included, just to be safe.

@ellisonbg
Copy link
Member

I am fine with this, but do feel that this stuff should be used in master for more than a few days. +1 for an RC with this stuff.

*
* @method handle_keyevent
* @param {CodeMirror} editor - The codemirror instance bound to the cell
* @param {event} event -
Copy link
Member

Choose a reason for hiding this comment

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

missing after -

Copy link
Member

Choose a reason for hiding this comment

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

still missing here

Copy link
Member Author

Choose a reason for hiding this comment

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

Min RK, on 2014-03-17 20:21, wrote:

still missing here

ok, I tried... but it's the event parameter of a function called
handle_keyevent, so I'm not sure how to be more descriptive
there.

@jdfreder
Copy link
Member

@pi I just had a chance to look through this code some more, looks good.

@ellisonbg
Copy link
Member

Let's move Cell.prototype.handle_keyevent up next to handle_codemirror_keyevent.

var that = this;
var shortcuts = IPython.keyboard_manager.edit_shortcuts;

// if this is an edit_shortcuts shortcut, we've already handled it.
Copy link
Member

Choose a reason for hiding this comment

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

I think technically the code mirror event fires before the document events that the global keyboard manager is listening to. Maybe change this from "we've already handle it" to "the global keyboard/shortcut manager will handle it"

@ellisonbg
Copy link
Member

Two problems in interactive testing:

  • The down arrow key doesn't work sometimes in a multiline code cell.
  • The up arrow key unconditionally leaves the code cell - it should behave like master: first go to position 0 on that line, then to previous cell.
  • If you have two multiline cells. 1) use the mouse to put the cursor at the end of the 2nd cell, 2) use the mouse to put the cursor at the end of the 1st cell, 3) press down. The result is that the cursor will be at the end of the 2nd cell, rather than at the beginning. The bug is that when we enter a cell using mouse navigation, we should not use the cells previous cursor state. We should always put it at the beginning (if entering from above) or end (if entering from below).

@takluyver
Copy link
Member

  • Down arrow: can't reproduce yet - any pattern as to when you see a problem
  • Up arrow leaving the code cell: I see that it works like that, though I can see a case to be made that this behaviour is more natural. I guess with long output between cells, though, you want more protection against jumping between cells.

@takluyver
Copy link
Member

I can also replicate the problem you describe with moving between multiline cells.

@minrk
Copy link
Member

minrk commented Mar 14, 2014

I think ↑ at the top of a cell moving to the previous cell is at least as reasonable as treating a cell boundary as a document boundary unless you are at cursor position zero. I think the two most logical choices are:

  1. the cursor treats all cells together as the document (↑ on the first line moves to the previous cell)
  2. the cursor treats the single cell as the document (↑ on the first line moves to the beginning, never to the previous cell without entering command mode first)

I think the current in-between behavior is the most confusing choice we could make. I would lean toward 1, personally.

@ellisonbg
Copy link
Member

I agree that 1 is better than 2, but still feel that our current behavior
is the best. It has a nice balance between acting just like all browser
text areas (up on the top line moves to position 0), but still allowing
intercell navigation without entering command mode.

On Fri, Mar 14, 2014 at 4:24 PM, Min RK notifications@github.com wrote:

I think ↑ at the top of a cell moving to the previous cell is at least as
reasonable as treating a cell boundary as a document boundary _unless_you are at cursor position zero. I think the two most logical choices are:

  1. the cursor treats the whole document together (↑ on the first line
    moves to the previous cell)
  2. the cursor treats the single text area as a document (↑ on the
    first line moves to the beginning, never to the previous cell without
    entering command mode first)

I think the current in-between behavior is the most confusing choice we
could make. I would lean toward 1, personally.


Reply to this email directly or view it on GitHubhttps://github.com//pull/5296#issuecomment-37707319
.

Brian E. Granger
Cal Poly State University, San Luis Obispo
bgranger@calpoly.edu and ellisonbg@gmail.com

Sets the cursor on the last line of the cell when moved up from the top
of the cell below, and sets the cursors to the first line when moving
down from the bottom of a last line.

Here, we retain the character that the cursor was on, so that users
wishing to have up-down functionality like one document can still use
this shortcut handler and simple adjust the at_top and at_bottom methods
@fperez
Copy link
Member

fperez commented Mar 17, 2014

@ivanov just came over to bring this to my attention; just by chatting about it, it appears I'm on the side of @ellisonbg on this one. Paul can provide further details if needed, and we also talked about the edge case for vim-mode extensively.

up arrow at the top line first goes to char 0, and only goes to the cell
above if already on char 0. Same with down arrow on the bottom line:
transition cursor to the end of the line, and only go down a cell if
already at the end of the last line.

this makes for an unhappy experience in code-mirror's vim mode  for j
and k keys, but we'll fix that in the next commit
@ivanov
Copy link
Member Author

ivanov commented Mar 18, 2014

I've fixed the multi-line cursor memory behavior. The up and down arrow keys didn't work properly in the top and bottom cells, that's also been fixed.

Also, upon further thought and experimentation with this, it doesn't really make sense to have any kind of logic for vim mode here, since up and down keys still work the right way even when vim mode is enabled, and getting j and k keys to behave like and keys involves too much trickery to put logic for it in ipython.

This is now ready for final review and merge.

@minrk
Copy link
Member

minrk commented Mar 18, 2014

Strictly an improvement over master, merging.

minrk added a commit that referenced this pull request Mar 18, 2014
unify keyboard shortcut and codemirror interaction
@minrk minrk merged commit 07be9db into ipython:master Mar 18, 2014
mattvonrocketstein pushed a commit to mattvonrocketstein/ipython that referenced this pull request Nov 3, 2014
unify keyboard shortcut and codemirror interaction
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants