@@ -255,7 +255,8 @@ impl Command {
255255 space_mode,
256256 view_mode,
257257 left_bracket_mode,
258− right_bracket_mode
258+ right_bracket_mode,
259+ surround
259260 );
260261}
261262
@@ -1906,6 +1907,7 @@ fn goto_mode(cx: &mut Context) {
19061907 match (doc.mode, ch) {
19071908 (_, 'g') => move_file_start(cx),
19081909 (_, 'e') => move_file_end(cx),
1910+ (_, 'm') => match_brackets(cx),
19091911 (_, 'a') => switch_to_last_accessed_file(cx),
19101912 (Mode::Normal, 'h') => move_line_start(cx),
19111913 (Mode::Normal, 'l') => move_line_end(cx),
@@ -3311,6 +3313,122 @@ fn right_bracket_mode(cx: &mut Context) {
33113313 })
33123314}
33133315
3316+fn surround(cx: &mut Context) {
3317+ let count = cx.count;
3318+ cx.on_next_key(move |cx, event| {
3319+ if let KeyEvent {
3320+ code: KeyCode::Char(ch),
3321+ ..
3322+ } = event
3323+ {
3324+ // FIXME: count gets reset because of cx.on_next_key()
3325+ cx.count = count;
3326+ match ch {
3327+ 'a' => surround_add(cx),
3328+ 'r' => surround_replace(cx),
3329+ 'd' => {
3330+ surround_delete(cx);
3331+ let (view, doc) = current!(cx.editor);
3332+ }
3333+ _ => (),
3334+ }
3335+ }
3336+ })
3337+}
3338+
3339+use helix_core::surround;
3340+
3341+fn surround_add(cx: &mut Context) {
3342+ cx.on_next_key(move |cx, event| {
3343+ if let KeyEvent {
3344+ code: KeyCode::Char(ch),
3345+ ..
3346+ } = event
3347+ {
3348+ let (view, doc) = current!(cx.editor);
3349+ let text = doc.text().slice(..);
3350+ let selection = doc.selection(view.id);
3351+ let (open, close) = surround::get_pair(ch);
3352+
3353+ let mut changes = Vec::new();
3354+ for (i, range) in selection.iter().enumerate() {
3355+ let (from, to) = (range.from(), range.to() + 1);
3356+ changes.push((from, from, Some(Tendril::from_char(open))));
3357+ changes.push((to, to, Some(Tendril::from_char(close))));
3358+ }
3359+
3360+ let transaction = Transaction::change(doc.text(), changes.into_iter());
3361+ doc.apply(&transaction, view.id);
3362+ doc.append_changes_to_history(view.id);
3363+ }
3364+ })
3365+}
3366+
3367+fn surround_replace(cx: &mut Context) {
3368+ let count = cx.count();
3369+ cx.on_next_key(move |cx, event| {
3370+ if let KeyEvent {
3371+ code: KeyCode::Char(from),
3372+ ..
3373+ } = event
3374+ {
3375+ cx.on_next_key(move |cx, event| {
3376+ if let KeyEvent {
3377+ code: KeyCode::Char(to),
3378+ ..
3379+ } = event
3380+ {
3381+ let (view, doc) = current!(cx.editor);
3382+ let text = doc.text().slice(..);
3383+ let selection = doc.selection(view.id);
3384+
3385+ let change_pos = match surround::get_surround_pos(text, selection, from, count)
3386+ {
3387+ Some(c) => c,
3388+ None => return,
3389+ };
3390+
3391+ let (open, close) = surround::get_pair(to);
3392+ let transaction = Transaction::change(
3393+ doc.text(),
3394+ change_pos.iter().enumerate().map(|(i, &pos)| {
3395+ let ch = if i % 2 == 0 { open } else { close };
3396+ (pos, pos + 1, Some(Tendril::from_char(ch)))
3397+ }),
3398+ );
3399+ doc.apply(&transaction, view.id);
3400+ doc.append_changes_to_history(view.id);
3401+ }
3402+ });
3403+ }
3404+ })
3405+}
3406+
3407+fn surround_delete(cx: &mut Context) {
3408+ let count = cx.count();
3409+ cx.on_next_key(move |cx, event| {
3410+ if let KeyEvent {
3411+ code: KeyCode::Char(ch),
3412+ ..
3413+ } = event
3414+ {
3415+ let (view, doc) = current!(cx.editor);
3416+ let text = doc.text().slice(..);
3417+ let selection = doc.selection(view.id);
3418+
3419+ let change_pos = match surround::get_surround_pos(text, selection, ch, count) {
3420+ Some(c) => c,
3421+ None => return,
3422+ };
3423+
3424+ let transaction =
3425+ Transaction::change(doc.text(), change_pos.into_iter().map(|p| (p, p + 1, None)));
3426+ doc.apply(&transaction, view.id);
3427+ doc.append_changes_to_history(view.id);
3428+ }
3429+ })
3430+}
3431+
33143432impl fmt::Display for Command {
33153433 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
33163434 let Command(name, _) = self;