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

feat: vi copy mode features for '*' / '#' / 'o' #8099

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

fmgornick
Copy link

@fmgornick fmgornick commented Jul 22, 2024

Hi! I saw this issue and thought it'd be an easy way to hopefully start contributing to one of my fav projects! I added the 3 new commands and updated the scdoc files accordingly. I briefly describe the behavior with these new changes below...

Upon pressing * in visual mode, search for the highlighted text in the region, and in normal mode, grab the first semantic word at/after the cursor point in the same line, searching for that. # similarly searches backwards.

This matches any word containing the search pattern, unlike vim which matches the exact pattern. I tried getting the search to behave exactly like vim, but wrapping the region in "\<\>" didn't work, nor did using the negative lookahead/lookbehind operators ((?!),(?<!)), but i figured this was prolly good enough.

Also added the visual o keybinding, which will move the cursor to the other side of a visual region. Works as expected for all visual modes. Contemplated adding the O key for switching the column of a visual block, but i doubt anyone even knows about this key, let alone has use for it.

@fmgornick fmgornick changed the title feat: vi copy mode features for '*' / '#' / 'o' (#7961) feat: vi copy mode features for '*' / '#' / 'o' Jul 22, 2024
Comment on lines 330 to 333
/// Search forward for highlighted region or word under cursor.
SearchRegionForward,
/// Search backward for highlighted region or word under cursor.
SearchRegionBackward,
Copy link
Member

Choose a reason for hiding this comment

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

I find the name "region" extremely confusing here, why did you name it that way? We already have semantic bindings, iirc they're just called "…SemanticWord…"

Copy link
Author

Choose a reason for hiding this comment

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

There's no SemanticWord binding, but there are SemanticDir motions. I agree region is a bit weird but the only time we search for the semantic word is if we're in normal mode. In the case of visual mode we want to search for the highlighted selection, so i chose "region" as a sort of catch-all

Copy link
Author

Choose a reason for hiding this comment

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

does SearchCursorForward and SearchCursorBackward make more sense?

Comment on lines 997 to 1001
Some(_) => {
let text = term.selection_to_string().filter(|s| !s.is_empty());
self.clear_selection();
text
},
Copy link
Member

Choose a reason for hiding this comment

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

Does this require vi mode? I feel like in theory it should work just fine outside of it. The selection itself can be used as origin in both modes.

Copy link
Author

Choose a reason for hiding this comment

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

could you elaborate?

Comment on lines 1003 to 1031
let escape_chars = term.semantic_escape_chars();
let mut iter = term.grid().iter_from(cursor_point);

// if inside word, move left to start
while !escape_chars.contains(iter.cell().c) {
iter.prev();
}
if iter.point() != cursor_point {
iter.next();
}

// if not in word, move right to start
while escape_chars.contains(iter.cell().c)
&& iter.point().column != term.grid().last_column()
{
iter.next();
}
if escape_chars.contains(iter.cell().c) {
return;
}

iter.prev();
Some(
iter.take_while(|cell| !escape_chars.contains(cell.c))
.map(|cell| cell.c)
.collect(),
)
},
};
Copy link
Member

Choose a reason for hiding this comment

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

Haven't looked at this too closely, but this looks very much like our existing semantic search implementation. We already have logic for semantic search, I don't think we need to implement it again.

Copy link
Author

Choose a reason for hiding this comment

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

I initially tried using the semantic_search functions, but there are the edge cases where the cursor is on the first letter of a word and we search backwards to the previous word and similarly with the right end, when i had a solution with that it just looked messy and was about the same length anyway. I can give it another shot tho!

Comment on lines 1034 to 1032
// escape regex search characters
let mut regex = "".to_string();
text.chars().for_each(|c| {
if ".*+^$?\\()[]{}|".contains(c) {
regex.push('\\');
}
regex.push(c);
});
Copy link
Member

Choose a reason for hiding this comment

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

Allocation can be trivially improved here.

Comment on lines 298 to 304
/// Swap ends of highlighted region, returning new end of region location.
pub fn swap_ends(&mut self) -> Point {
let (start, end) = (self.region.start, self.region.end);
self.region.start = Anchor::new(end.point, end.side);
self.region.end = Anchor::new(start.point, start.side);
start.point
}
Copy link
Member

Choose a reason for hiding this comment

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

What is the motivation of this function? Why are we randomly swapping things here? I don't think we want to actually mutate the selection?

Copy link
Author

Choose a reason for hiding this comment

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

when moving the cursor to the other side of a region, we also need to make the selection range go in the other direction, so we must swap our anchors. otherwise moving the cursor from the end of the selection to the beginning will empty the selection. Also just doing mem::swap(&mut start, &mut end) doesn't work.

@fmgornick
Copy link
Author

sorry for taking so long! i sat on your comments for a while and i think this addresses most of them...

  1. names are a bit more specific, but if they're still too confusing and you have another suggestion then we can 100% go w that
  2. i was able to find a more elegant solution using the semantic commands like you mentioned, so it looks a bit cleaner now
  3. vi-mode is no longer required to search for a selection. also, I don't know if this is desired, but i added to the start_search function, so it'll put the highlighted region into the search result if there is one. this is more similar to how ctrl-f works in a browser, but it can also be removed if you don't find it necessary
  4. the swap_ends command was left unaltered because i don't really know how else to implement the vim-like o key, but if you have a suggestion then i can do that!

Copy link
Member

@chrisduerr chrisduerr left a comment

Choose a reason for hiding this comment

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

I have no interest in investing further reviews into this for now since it clearly requires a lot of guidance. I don't want to outright close this since the search_cursor function is basically the only thing that needs significant improvement, but I don't see that getting better without doing it myself.

@fmgornick fmgornick force-pushed the vi-features branch 4 times, most recently from 69f1a69 to 864bcb6 Compare August 19, 2024 21:23
- '*': search forward for current visual selection if in visual
       mode, or search for word under cursor in normal mode.

- '#': search backward for current visual selection if in visual
       mode, or search for word under cursor in normal mode.

- 'o': toggle cursor position between beggining and end of text
       selection in visual mode.
@fmgornick
Copy link
Author

I have no interest in investing further reviews into this for now since it clearly requires a lot of guidance. I don't want to outright close this since the search_cursor function is basically the only thing that needs significant improvement, but I don't see that getting better without doing it myself.

i apologize, for i have been a silly goose 😔🤚, fixed search_cursor tho. hopefully now when you find time to tackle this issue you can just copy these changes and make the necessary tweaks without too much extra work!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

2 participants