-
Notifications
You must be signed in to change notification settings - Fork 2.2k
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: Add Multi Replace mode in Vim #8469
Conversation
Heya. It almost works, but there are other hotkeys that take over and switch it back into other modes. For instance, if you go into replace mode and start typing "something", it will work for "som" and then the 'e' switches mode again. |
I was just about to leave a comment, but @schacon beat me to it. I can't get it to work locally: Another thought after looking at the implementation: should we model |
sorry i missed some charges, and the implementation was too simplistic, In order to be able to support more, I'll improve this part of the code, please wait for my latest good news XD |
sorry i missed some changes, I was a bit sloppy before, and I thought that the |
Thanks for this! I'd love to pair with you to get this over the line: https://calendly.com/conradirwin/pairing, otherwise some notes below to get this to a mergeable state.
I think we can get away without supporting |
Thank you for your help! I will start this journey |
@ConradIrwin Apologize, but I might need some of your help. I'm familiarizing myself with the vim mode code for this PR, and I've found the following method: pub fn vim_controlled(&self) -> bool {
!matches!(self.mode, Mode::Insert)
|| matches!(
self.operator_stack.last(),
Some(Operator::FindForward { .. }) | Some(Operator::FindBackward { .. })
)
} In this method, I feel that the fn replace_text_in_range(
&mut self,
range_utf16: Option<Range<usize>>,
text: &str,
cx: &mut ViewContext<Self>,
) {
if !self.input_enabled {
cx.emit(EditorEvent::InputIgnored { text: text.into() });
return;
} So, I'm not sure whether to rely on adding new properties or judgments to pass replace-related methods to vim when In addition, there are two small questions. The first one is about restoring the content of The second question is, when I was understanding the vim_controlled method mentioned above, I found that if if let Some(ime) = last_ime {
if let ImeInput::InsertText(text, _) = &ime {
if !is_composing {
window_state.lock().previous_keydown_inserted_text = Some(text.clone());
if let Some(callback) = callback.as_mut() {
event.keystroke.ime_key = Some(text.clone());
handled = callback(PlatformInput::KeyDown(event));
}
}
}
if !handled {
handled = true;
send_to_input_handler(this, ime);
}
} handled = callback(PlatformInput::KeyDown(event)); This line of code intercepts and handles the input event, and cannot be passed into the editor. If it's convenient, I'd like you to help me explain the logic here. How should I avoid input being intercepted here in some modes? |
@weartist I'm also happy to work on this together which may be more effective: https://calendly.com/conradirwin/pairing. Notes below:
I think we need to have Given that we'll need to implement the replacement ourselves in vim mode, again you can copy this from the existing replace action. (This will also be helpful when we add For backspace a Vec might work, but we'll want to maintain one per selection to work with multi-cursor. I'm not sure if there are edge-cases with An alternative idea would be to take a buffer snapshot when we enter replace mode, and then you can just read that to get the previous character at the current position. |
Okay, I'll submit a working version, hopefully smooth, |
@ConradIrwin Sorry im late, I've submitted an implementation based on snapshots, but during my recent testing, I discovered a drawback to this approach: e.g.:
typing
typing
At this point, if we type Because only when entering a My other proposal is, for each operation in replace mode (replace, insert |
crates/vim/src/replace.rs
Outdated
let mut range = selection.range(); | ||
// "\n" need to be handled separately, because when a "\n" is typing, | ||
// we don't do a replace, we need insert a "\n" | ||
if text.as_ref() != "\n" { |
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.
Does mapping "enter": "editor::Newline"
in the keymap do the right thing? If not quite we may still want to call editor.newline
as that also handles preserving leading indentation/comments etc.
@weartist don't fret – there's no late! I'm happy to go with the HashMap/Vec idea (most important is that it actually works). As always, I'd love to work with you on this, it's always easier to resolve this kind of question when we're both in the code. |
@ConradIrwin Hi, I submitted a version based on hashmap to record the original content in different locations in the replacement mode, and it supports There is also a situation that needs to be considered whether to support If the current solution is acceptable, I'll submit some unit tests of the boundary case later and further optimize the code :) |
…_R_support # Conflicts: # crates/vim/src/motion.rs
@weartist Makes sense. Looking at the current approach, I think it's not a good idea to store previous edits by offset (or even by point) as concurrent edits invalidate the map. We can instead store these by Anchor, which gives us a stable identifier for a position in the doc (at some loss of efficiency, but I suspect the typical number of replaces in replace mode is not more than a few tens and thousands or more will be very rare). I also think we should not be using an I'll push these changes to your branch, and then I think the last piece is a test or two. If you are still keen to do those, that'd be great; otherwise I'll pick this up again later in the week. |
* Not use motion for now (most motions like up/down/left/right) in replace mode should be handled as in normal mode. * Key replacements off anchors not offsets (this makes them robust to concurrent edits, at the cost of efficiency)
b6fd6fa
to
1978432
Compare
🎉 This works great! |
Been using this for an hour now. Game changer! |
Can we solve the problem of deleting replies if we configure automatic indentation when entering enter? I don't have a clue about this. I can provide test cases, and I've tested a lot of extreme cases when writing these codes. Other modifications may need to wait and so on, I'm writing the vim mode surround, hopefully it will be done today so I can continue over the weekend |
@ConradIrwin You're moving so quickly! and I'm just realizing that you've already submitted a change |
@weartist it seems to work for me and auto-indent correctly (the range in the replacements vector covers the new newline and indentation and marks the undo as ""). Anything else you think we need to fix? |
Looks cool, I'll test the issue in its entirety and remove some unnecessary comments |
@ConradIrwin I ran a full test and found a few minor issues:
I'm not sure if this situation needs to be fixed in this commit, or as a follow-up fix to a known issue, and the cursor movement is inconsistent with neovim behavior, I didn't add the corresponding test either |
* If the cursor is at the start more backspaces continually re-insert the first character * If you have two multi-cursors that overlap and then overlap on undo, this now works.
Thanks for this!
|
Learning the repair idea of the second problem is worth being happy XD, I didn't find any other issues, and I guess the third case has very few times to trigger when using it normally, and it's cool at the moment |
Thanks so much for your work on this! Quite the journey :D. This will likely ship to preview on Wednesday in 0.128.0-pre |
@weartist thanks so much for picking this up! Loving it. |
Thanks for your gift! Have a nice day! |
For #4440, I've only added support for normal, if it's visual mode, would we like this to delete the current selection row and enter insert mode?