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

Added change_case command #441

Merged
merged 6 commits into from
Jul 16, 2021
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 23 additions & 20 deletions book/src/keymap.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,26 +41,29 @@

### Changes

| Key | Description |
| ----- | ----------- |
| `r` | Replace with a character |
| `R` | Replace with yanked text |
| `i` | Insert before selection |
| `a` | Insert after selection (append) |
| `I` | Insert at the start of the line |
| `A` | Insert at the end of the line |
| `o` | Open new line below selection |
| `o` | Open new line above selection |
| `u` | Undo change |
| `U` | Redo change |
| `y` | Yank selection |
| `p` | Paste after selection |
| `P` | Paste before selection |
| `>` | Indent selection |
| `<` | Unindent selection |
| `=` | Format selection |
| `d` | Delete selection |
| `c` | Change selection (delete and enter insert mode) |
| Key | Description |
| ----- | ----------- |
| `r` | Replace with a character |
| `R` | Replace with yanked text |
| `~` | Switch case of the selected text |
| `\`` | Set the selected text to upper case |
| `Alt-\`` | Set the selected text to lower case |
| `i` | Insert before selection |
| `a` | Insert after selection (append) |
| `I` | Insert at the start of the line |
| `A` | Insert at the end of the line |
| `o` | Open new line below selection |
| `o` | Open new line above selection |
| `u` | Undo change |
| `U` | Redo change |
| `y` | Yank selection |
| `p` | Paste after selection |
| `P` | Paste before selection |
| `>` | Indent selection |
| `<` | Unindent selection |
| `=` | Format selection |
| `d` | Delete selection |
| `c` | Change selection (delete and enter insert mode) |

### Selection manipulation

Expand Down
55 changes: 55 additions & 0 deletions helix-term/src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,9 @@ impl Command {
extend_till_prev_char,
extend_prev_char,
replace,
switch_case,
switch_to_uppercase,
switch_to_lowercase,
page_up,
page_down,
half_page_up,
Expand Down Expand Up @@ -780,6 +783,58 @@ fn replace(cx: &mut Context) {
})
}

fn switch_case(cx: &mut Context) {
let (view, doc) = current!(cx.editor);
let transaction =
Transaction::change_by_selection(doc.text(), doc.selection(view.id), |range| {
let text: Tendril = range
.fragment(doc.text().slice(..))
.chars()
.map(|ch| {
if ch.is_lowercase() {
ch.to_uppercase().collect()
Copy link
Member

Choose a reason for hiding this comment

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

Ah, these methods return an iterator, hmm. Maybe we can use flat_map and directly process these?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah yes, good point.

Copy link
Member

Choose a reason for hiding this comment

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

So with flat_map you should be able to return these iterators directly without .collect(). Might be a problem since all the branches return a different type though?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I must admit; this isn't my area of expertise, but I don't see much of an alternative. Or at-least no ones which also allocate.
With the exception of a custom enum combining the three branches, which also implements Iterator.

Any other suggestion?

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 the current implementation is fine for now :)

} else if ch.is_uppercase() {
ch.to_lowercase().collect()
} else {
vec![ch]
}
})
.flatten()
.collect();

(range.from(), range.to() + 1, Some(text))
});

doc.apply(&transaction, view.id);
doc.append_changes_to_history(view.id);
}

fn switch_to_uppercase(cx: &mut Context) {
let (view, doc) = current!(cx.editor);
let transaction =
Transaction::change_by_selection(doc.text(), doc.selection(view.id), |range| {
let text: Tendril = range.fragment(doc.text().slice(..)).to_lowercase().into();
Copy link
Member

Choose a reason for hiding this comment

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

You're using to_lowercase in the uppercase method.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Whoops, copy paste error; apologies.


(range.from(), range.to() + 1, Some(text))
});

doc.apply(&transaction, view.id);
doc.append_changes_to_history(view.id);
}

fn switch_to_lowercase(cx: &mut Context) {
let (view, doc) = current!(cx.editor);
let transaction =
Transaction::change_by_selection(doc.text(), doc.selection(view.id), |range| {
let text: Tendril = range.fragment(doc.text().slice(..)).to_lowercase().into();

(range.from(), range.to() + 1, Some(text))
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
(range.from(), range.to() + 1, Some(text))
(range.from(), range.to(), Some(text))

I think this should fix the bug.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

While this does fix it when having a selection of >1 character, it causes it not to work when having a selection of 1 character. While this is solvable; the replace command has the exact same issue.

If I understand correctly, this behaviour is currently due to ranges being inclusive. archseer above suggested that this behaviour should be fixed later on in #376.

If so, should I work around the bug, or accept it 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.

I think it's fine, you should accept it for now and we'll deal with it in #376

});

doc.apply(&transaction, view.id);
doc.append_changes_to_history(view.id);
}

fn scroll(cx: &mut Context, offset: usize, direction: Direction) {
use Direction::*;
let (view, doc) = current!(cx.editor);
Expand Down
4 changes: 4 additions & 0 deletions helix-term/src/keymap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,10 @@ impl Default for Keymaps {
key!('r') => Command::replace,
key!('R') => Command::replace_with_yanked,

key!('~') => Command::switch_case,
key!('`') => Command::switch_to_uppercase,
alt!('`') => Command::switch_to_lowercase,

key!(Home) => Command::goto_line_start,
key!(End) => Command::goto_line_end,

Expand Down