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
[vim] Reimplementation of Vim key mappings #984
Conversation
I agree that the current vim mode is starting to look messy. I'd like contributors to the old code (@jankeromnes, @xLanny, @misfo, @mightyguava, @chris-morgan, @mattsacks) to take a look, verify that their favorite features still work, maybe help extend this to cover missing features. It's hard for me as a non-vim user to interpret this code, but at a glance it does look clean. If it's viable to switch to this as the one vim mode in the distribution, that'd be fine with me. |
The code does look cleaner, and I like the orthogonality between motions and operators (even if code duplication was kind of avoided in current implementation). However, it's 1016 lines versus 897 lines and it doesn't yet reach full feature parity. I gave it a quick try: Missing features (not exhaustive):
Still missing in both implementations:
What do you mean exactly by "support for marks"? The previous implementation has a map of marks. Also, no need for a I also don't like side-by-side implementations, and maybe a branch is a bit overkill since your repo already holds the concurrent implementation. Personally I don't mind the new implementation, since it has some obvious benefits, but I'd like to see it come closer to feature parity before merging it. Also, a visual mode would help get my vote. |
Thanks for the comments! Looks like priority-wise for me, key-to-key mapping first and then move on to visual mode. Will wait for more comments here before trying to decide on whether to merge. If anyone can help this reach feature parity or add other improvements please feel free to. I'd really appreciate it. Replies to @jankeromnes
|
Alright repetition on Y and D are done, arrow keys and more are mapped. Repeat on insert mode is hard because it would require Vim to hook into CodeMirror's keyhandling, or have Vim take over insert mode. Neither sounds like a good approach. I could probably do * and / if that'll get your vote to merge :) |
Couple of things with regards to this branch:
About the implementation:
Besides that, it's a lot of code to comb through - but it all looks great for the most part. There's just some key things you need to address before this needs to be considered as an actual replacement of the current implementation. Thanks for all your work on this. sidenote: @marijnh, is |
Re: @mattsacks
About the implementation:
Thank you very much for reading the code over. I realize it is very long, but hopefully it wasn't difficult reading (that was the whole point of reimplementing). I will work on the key points you brought up. I'm fine with getting rid of |
Sorry, halfway or half-implemented is oversimplifying the effort put into this branch. I apologize. But honestly, text-objects are one of the most novel features of Vim and give it a lot of power. I think you're missing out on making them lower priority than visual mode (which I think will have a lot more complexity to it than text-objects, but maybe not). 80 chars is more standard of a text-width than 100. Marks are currently implemented using the 'm' prefix, but I'm not sure if you can actually jump to them yet. It's just an object that stores the current line (and should store the current cursor) number. |
Keep the 'use strict'! There is no reason not to use that. Also, I personally think an 80 chars limit is archaic. It requires a lot of effort being put into code formatting, and the benefits are dubious. |
Oops was trying to post a comment. Chose 80 char line limit in the end because that way I can fit a 3 way merge on my screen. Also can fit 2 files side-by-side on my laptop, which is very convenient. 100 is just a bit too long. |
Text objects have been ported over. Found tons of bugs while testing, but the only new one is a cursor jumping bug when I try da{ on the entire function block in vim2.html. Everything in the trace looks right up to the operation() function in CodeMirror. It seems like CodeMirror is adding 2 extra spaces after deletion. Anyways don't think it's a huge issue and will probably investigate more after visual mode. @marijnh do you know why the Chrome debugger can't step out of the operation function? Also implemented Ctrl-f and Ctrl-b, though I would really prefer if there was a way to get a page down position without actually moving the cursor, so I don't have to do the ugly hack I have now. Feature parity has been reached! |
A few bugs I ran into in a few minutes of testing :
Most of those aren't major with the exception of the word/WORD text objects (matt is right, text objects are one of the first things vim users look for) and none of them should be hard to fix. I'll hop on them in a few days if no one beats me to it. |
Thanks for testing this @xLanny I added [cyd][ai]W, but the off-by-one error is still there for inner words (and also reproduces in the old implementation). The problem is probably that the "inclusive" option does not behave correctly. It should get fixed automatically if text objects triggers are rewritten to use the same keyMap system as the rest of file uses. I practically did a copy and paste from the old implementation on this and had the not had the time to rewrite. It wouldn't be a hard fix if you change inclusion in the text objects, but a better fix would be to move to the defaultKeyMap. I have comments in the file if you are interested and have time. If not then a quick fix is fine too and I will do the rewrite after figuring out visual mode. Thanks for volunteering! The remaining things you mentioned are preserved from the existing behavior, with the exception of ex mode. I left it out on purpose because I haven't thought of a good way to handle ex mode mappings yet and how to properly process ex mode commands. This should probably be a longer discussion, so maybe we can move that into a separate issue? Not sure what the standard way to separate out topics of discussion on GitHub is. Finally, here's a summary of text object bugs found so far. All but the cursor jumping are carried over from the old implementation:
|
A basic, mostly functional visual mode has been implemented! I have to say, it's pretty cool. |
Code structure makes much more sense now. Order is: 1. Default keymap 2. Variable declarations and short basic helpers 3. Instance (External API) implementation 4. Internal state tracking objects (input state, counter) implementation and instanstiation 5. Key handler (the main command dispatcher) implementation 6. Motion, operator, and action implementations 7. Helper functions for the key handler, motions, operators, and actions.
…separate Vim instance per CodeMirror instance.
Are there objections toward replacing the old Vim that I haven't covered? |
The new implementation is 54K (21K minified) versus 31K (14K minified), but I guess the additional minified 7K are a good tradeoff for the improvements and new features. Thanks a lot for the visual mode, it's still not perfect (there is an off-by-1 error when you try to I didn't find any other problems, so if @marijnh, @mattsacks, @xLanny, @misfo and @chris-morgan don't have further objections, I think we could go for the new implementation. Thanks for all the good work! EDIT: Does the new implementation respect the behavior described in #997 ? |
I agree visual mode isn't perfect. There are many many more features in Vim, and including quite a few things in visual mode, that I haven't gotten to yet. I will try to cover the more important ones over time. The off-by-1 error was actually because I forgot to turn on "showCursorWhenSelecting", so the fat cursor wasn't showing up. The fat-cursor in CodeMirror always shows on the character after the current cursor index. In Vim's visual mode, the fat-cursor shows on the character at the current cursor index. So what the Vim keymap does is always include the character after the cursor, even though it's not in the selection. It should feel natural now. D, X, and Y are fixed. The Ctrl-[ behavior change has been integrated. |
Hmm, is there a reason that visual mode is implemented in vim.js as opposed to vim2.js? |
I replaced vim.js with vim2.js. It seemed the consensus was that we didn't want 2 alternate implementations at the same time, which is why I tried so hard to reach feature parity. I moved it over now since it seemed ready for merging. |
Ahh, I see. Well it's got the green light to merge from me in that case. |
I'll merge this in a few days. If anyone wants to do a detailed test drive and/or fix minor bugs they find, now is the time. |
I'm going to be adding more unit tests over the next few days, but won't be doing a test drive due to the obvious conflict of interest... I also welcome anyone to test and find/fix bugs. |
Broken during explicit state refactoring.
@wesalvaro Haha sorry about that, I selfishly implemented the visual mode behavior before there was a visual mode, because it was the one I was using the most. I think this was fixed a while ago, and both behaviors should be correct in the new implementation anyway. Funny how small details sometimes get more unnerving than bigger problems. |
D in the new implementation should, in normal mode, delete to end of line, and is repeatable. In visual mode, it should delete the entire selection linewise, and is not repeatable. |
Merged (in squashed form) as 206afdf (and equivalent on the v3 branch). |
See discussion in codemirror#984
See discussion in codemirror#984
This is a reimplementation of Vim key mappings. The reasons why I felt a reimplementation was necessary are listed in a previous pull request by @jankeromnes at #973 (comment). I'd be happy to go into more detail if anyone is interested. Sorry about the long pull request but there are a lot of changes and a lot to discuss. Thank you in advance for taking the time to read this.
Summary
This reimplementation has almost reached feature parity with the original, with some divergence in features. Overall the code is cleaner and more maintainable, which should make future additions significantly less time consuming and less error prone.
I opened this as a pull request because I would like to ask for advice from the community on where to go from here, and see if this can be added to the main repository so that others can use it and contribute. While the reimplementation has not reached full feature parity, there are only a few missing features and I'm not sure when I'll get to implement them. There are also many behavioral fixes and a few new features that would be useful to existing users.
Discussion
I am usually against having 2 parallel implementations, and would greatly appreciate any opinions on how we can get the features merged. However, due to the reasons above I think in the short term the best solution would be to have both available, maybe with this as a separate branch?
While investigating how to implement visual mode, I saw that setCursor() clears the selection (let me know if I am mistaken). Would it be possible to add to the exported CodeMirror API a version of setCursor() that does not reset to selection, and/or methods to expand the existing selection? Or do these APIs already exist? If they don't I'd be happy to add them with some assistance.
To support additional key-to-key mappings, like mapping 'Left' to 'j' and 'Y' to 'y$', the Vim keymap would need to expose some API functions to let users define the mapping. This could also be a feature in codemirror.js but it seems to be more of a Vim specific feature. I currently am exporting a Vim instance object as a property of CodeMirror. Is that fine or should we work out a different way?
Functional differences from original
This is not a complete list, only major points are listed. Also the list of supported keys on top of vim.js is a bit out of date and I may have missed a few things while looking through the code.
Additional features not in original
Yet to be implemented
Next steps
The immediate next steps I see for the reimplementation, in any order, are: