From d1ad8a2fe9606b4b0f01045f72386b08801832b1 Mon Sep 17 00:00:00 2001 From: aster <137767097+aster-void@users.noreply.github.com> Date: Fri, 6 Jun 2025 17:50:10 +0900 Subject: [PATCH 1/2] feat: handle triple quotes in auto-pairs correctly --- helix-core/src/auto_pairs.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/helix-core/src/auto_pairs.rs b/helix-core/src/auto_pairs.rs index 853290404..5797b623e 100644 --- a/helix-core/src/auto_pairs.rs +++ b/helix-core/src/auto_pairs.rs @@ -335,17 +335,22 @@ fn handle_close(doc: &Rope, selection: &Selection, pair: &Pair) -> Transaction { /// handle cases where open and close is the same, or in triples ("""docstring""") fn handle_same(doc: &Rope, selection: &Selection, pair: &Pair) -> Transaction { let mut end_ranges = SmallVec::with_capacity(selection.len()); - let mut offs = 0; let transaction = Transaction::change_by_selection(doc, selection, |start_range| { let cursor = start_range.cursor(doc.slice(..)); let mut len_inserted = 0; + let is_triple = cursor >= 2 + && doc.get_char(cursor - 1) == Some(pair.open) + && doc.get_char(cursor - 2) == Some(pair.open); let next_char = doc.get_char(cursor); let change = if next_char == Some(pair.open) { // return transaction that moves past close - (cursor, cursor, None) // no-op + (cursor, cursor, None) // no-op - don't insert char + } else if is_triple { + // ignore triple-quotes + (cursor, cursor, Some(Tendril::from_iter([pair.open]))) // only insert one char (normal operation) } else { let mut pair_str = Tendril::new(); pair_str.push(pair.open); From 52696de6def6ff5ed49da34b130ac59bf6aef574 Mon Sep 17 00:00:00 2001 From: aster <137767097+aster-void@users.noreply.github.com> Date: Mon, 16 Jun 2025 13:55:03 +0900 Subject: [PATCH 2/2] feat: auto-pair triple quotes --- helix-core/src/auto_pairs.rs | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/helix-core/src/auto_pairs.rs b/helix-core/src/auto_pairs.rs index 5797b623e..866fafaab 100644 --- a/helix-core/src/auto_pairs.rs +++ b/helix-core/src/auto_pairs.rs @@ -340,17 +340,32 @@ fn handle_same(doc: &Rope, selection: &Selection, pair: &Pair) -> Transaction { let transaction = Transaction::change_by_selection(doc, selection, |start_range| { let cursor = start_range.cursor(doc.slice(..)); let mut len_inserted = 0; - let is_triple = cursor >= 2 + let has_at_least_two_before = cursor >= 2 && doc.get_char(cursor - 1) == Some(pair.open) && doc.get_char(cursor - 2) == Some(pair.open); + let has_at_least_three_before = + has_at_least_two_before && cursor >= 3 && doc.get_char(cursor - 3) == Some(pair.open); + let next_char = doc.get_char(cursor); - let change = if next_char == Some(pair.open) { - // return transaction that moves past close + // (from, to, replacement) + let change = if next_char == Some(pair.close) { + // moves past close (cursor, cursor, None) // no-op - don't insert char - } else if is_triple { - // ignore triple-quotes - (cursor, cursor, Some(Tendril::from_iter([pair.open]))) // only insert one char (normal operation) + } else if has_at_least_three_before { + // don't auto pair more than triple quotes + (cursor, cursor, Some(Tendril::from_iter([pair.close]))) // only insert one char (normal operation) + } else if has_at_least_two_before { + // exactly two before: auto-pair 3 quotes + // `''|` -> `'''|'''` + ( + cursor, + cursor, + Some(Tendril::from_iter([ + // first is the default keystroke, the rest 3 are auto-pair + pair.close, pair.close, pair.close, pair.close, + ])), + ) } else { let mut pair_str = Tendril::new(); pair_str.push(pair.open);