aboutsummaryrefslogtreecommitdiff
path: root/helix-view
diff options
context:
space:
mode:
Diffstat (limited to 'helix-view')
-rw-r--r--helix-view/src/commands.rs88
-rw-r--r--helix-view/src/keymap.rs13
2 files changed, 93 insertions, 8 deletions
diff --git a/helix-view/src/commands.rs b/helix-view/src/commands.rs
index e5e3244d..56e1c5db 100644
--- a/helix-view/src/commands.rs
+++ b/helix-view/src/commands.rs
@@ -241,7 +241,7 @@ pub fn select_line(view: &mut View, _count: usize) {
let text = view.state.doc();
let line = text.char_to_line(pos.head);
let start = text.line_to_char(line);
- let end = text.line_to_char(line + 1);
+ let end = text.line_to_char(line + 1).saturating_sub(1);
// TODO: use a transaction
view.state.selection = Selection::single(start, end);
@@ -249,7 +249,7 @@ pub fn select_line(view: &mut View, _count: usize) {
pub fn delete_selection(view: &mut View, _count: usize) {
let transaction =
- Transaction::change_by_selection(&view.state, |range| (range.from(), range.to(), None));
+ Transaction::change_by_selection(&view.state, |range| (range.from(), range.to() + 1, None));
transaction.apply(&mut view.state);
append_changes_to_history(view);
@@ -267,6 +267,13 @@ pub fn collapse_selection(view: &mut View, _count: usize) {
.transform(|range| Range::new(range.head, range.head))
}
+pub fn flip_selections(view: &mut View, _count: usize) {
+ view.state.selection = view
+ .state
+ .selection
+ .transform(|range| Range::new(range.head, range.anchor))
+}
+
fn enter_insert_mode(view: &mut View) {
view.state.mode = Mode::Insert;
@@ -463,7 +470,7 @@ pub fn delete_char_forward(view: &mut View, count: usize) {
pub fn undo(view: &mut View, _count: usize) {
view.history.undo(&mut view.state);
- // TODO: each command should simply return a Option<transaction>, then the higher level handles storing it?
+ // TODO: each command could simply return a Option<transaction>, then the higher level handles storing it?
}
pub fn redo(view: &mut View, _count: usize) {
@@ -481,11 +488,15 @@ pub fn yank(view: &mut View, _count: usize) {
.map(|cow| cow.into_owned())
.collect();
- register::set('"', values);
+ // TODO: allow specifying reg
+ let reg = '"';
+ register::set(reg, values);
}
pub fn paste(view: &mut View, _count: usize) {
- if let Some(values) = register::get('"') {
+ // TODO: allow specifying reg
+ let reg = '"';
+ if let Some(values) = register::get(reg) {
let repeat = std::iter::repeat(
values
.last()
@@ -493,13 +504,74 @@ pub fn paste(view: &mut View, _count: usize) {
.unwrap(),
);
+ // TODO: if any of values ends \n it's linewise paste
+ //
+ // p => paste after
+ // P => paste before
+ // alt-p => paste every yanked selection after selected text
+ // alt-P => paste every yanked selection before selected text
+ // R => replace selected text with yanked text
+ // alt-R => replace selected text with every yanked text
+ //
+ // append => insert at next line
+ // insert => insert at start of line
+ // replace => replace
+ // default insert
+
+ let linewise = values.iter().any(|value| value.ends_with('\n'));
+
let mut values = values.into_iter().map(Tendril::from).chain(repeat);
- let transaction = Transaction::change_by_selection(&view.state, |range| {
- (range.head + 1, range.head + 1, Some(values.next().unwrap()))
- });
+ let transaction = if linewise {
+ // paste on the next line
+ // TODO: can simply take a range + modifier and compute the right pos without ifs
+ let text = view.state.doc();
+ Transaction::change_by_selection(&view.state, |range| {
+ let line_end = text.line_to_char(text.char_to_line(range.head) + 1);
+ (line_end, line_end, Some(values.next().unwrap()))
+ })
+ } else {
+ Transaction::change_by_selection(&view.state, |range| {
+ (range.head + 1, range.head + 1, Some(values.next().unwrap()))
+ })
+ };
transaction.apply(&mut view.state);
append_changes_to_history(view);
}
}
+
+const TAB_WIDTH: usize = 4;
+
+pub fn indent(view: &mut View, _count: usize) {
+ let mut lines = Vec::new();
+
+ // Get all line numbers
+ for range in view.state.selection.ranges() {
+ let start = view.state.doc.char_to_line(range.from());
+ let end = view.state.doc.char_to_line(range.to());
+
+ for line in start..=end {
+ lines.push(line)
+ }
+ }
+ lines.sort_unstable(); // sorting by usize so _unstable is preferred
+ lines.dedup();
+
+ // Indent by one level
+ let indent = Tendril::from(" ".repeat(TAB_WIDTH));
+
+ let transaction = Transaction::change(
+ &view.state,
+ lines.into_iter().map(|line| {
+ let pos = view.state.doc.line_to_char(line);
+ (pos, pos, Some(indent.clone()))
+ }),
+ );
+ transaction.apply(&mut view.state);
+ append_changes_to_history(view);
+}
+
+pub fn unindent(view: &mut View, _count: usize) {
+ unimplemented!()
+}
diff --git a/helix-view/src/keymap.rs b/helix-view/src/keymap.rs
index 8c53b403..1d7505d2 100644
--- a/helix-view/src/keymap.rs
+++ b/helix-view/src/keymap.rs
@@ -117,6 +117,15 @@ macro_rules! ctrl {
};
}
+macro_rules! alt {
+ ($ch:expr) => {
+ Key {
+ code: KeyCode::Char($ch),
+ modifiers: Modifiers::ALT,
+ }
+ };
+}
+
pub fn default() -> Keymaps {
hashmap!(
state::Mode::Normal =>
@@ -145,11 +154,15 @@ pub fn default() -> Keymaps {
vec![key!('c')] => commands::change_selection,
vec![key!('s')] => commands::split_selection_on_newline,
vec![key!(';')] => commands::collapse_selection,
+ // TODO should be alt(;)
+ vec![key!('%')] => commands::flip_selections,
vec![key!('x')] => commands::select_line,
vec![key!('u')] => commands::undo,
vec![shift!('U')] => commands::redo,
vec![key!('y')] => commands::yank,
vec![key!('p')] => commands::paste,
+ vec![key!('>')] => commands::indent,
+ vec![key!('<')] => commands::unindent,
vec![Key {
code: KeyCode::Esc,
modifiers: Modifiers::NONE