@@ -146,13 +146,7 @@ fn prev_char(doc: &Rope, pos: usize) -> Option<char> {
148/// calculate what the resulting range should be for an auto pair insertion
148/// calculate what the resulting range should be for an auto pair insertion
149−fn get_next_range(
149+fn get_next_range(doc: &Rope, start_range: &Range, offset: usize, len_inserted: usize) -> Range {
151− start_range: &Range,
154− len_inserted: usize,
156 // When the character under the cursor changes due to complete pair
150 // When the character under the cursor changes due to complete pair
157 // insertion, we must look backward a grapheme and then add the length
151 // insertion, we must look backward a grapheme and then add the length
158 // of the insertion to put the resulting cursor in the right place, e.g.
152 // of the insertion to put the resulting cursor in the right place, e.g.
@@ -172,8 +166,8 @@ fn get_next_range(
172 // inserting at the very end of the document after the last newline
166 // inserting at the very end of the document after the last newline
173 if start_range.head == doc.len_chars() && start_range.anchor == doc.len_chars() {
167 if start_range.head == doc.len_chars() && start_range.anchor == doc.len_chars() {
174 return Range::new(
168 return Range::new(
175− start_range.anchor + offset + typed_char.len_utf8(),
169+ start_range.anchor + offset + 1,
176− start_range.head + offset + typed_char.len_utf8(),
170+ start_range.head + offset + 1,
@@ -203,21 +197,18 @@ fn get_next_range(
203 // trivial case: only inserted a single-char opener, just move the selection
197 // trivial case: only inserted a single-char opener, just move the selection
204 if len_inserted == 1 {
198 if len_inserted == 1 {
205 let end_anchor = if single_grapheme || start_range.direction() == Direction::Backward {
199 let end_anchor = if single_grapheme || start_range.direction() == Direction::Backward {
206− start_range.anchor + offset + typed_char.len_utf8()
200+ start_range.anchor + offset + 1
208 start_range.anchor + offset
202 start_range.anchor + offset
213− start_range.head + offset + typed_char.len_utf8(),
205+ return Range::new(end_anchor, start_range.head + offset + 1);
217 // If the head = 0, then we must be in insert mode with a backward
208 // If the head = 0, then we must be in insert mode with a backward
218 // cursor, which implies the head will just move
209 // cursor, which implies the head will just move
219 let end_head = if start_range.head == 0 || start_range.direction() == Direction::Backward {
210 let end_head = if start_range.head == 0 || start_range.direction() == Direction::Backward {
220− start_range.head + offset + typed_char.len_utf8()
211+ start_range.head + offset + 1
222 // We must have a forward cursor, which means we must move to the
213 // We must have a forward cursor, which means we must move to the
223 // other end of the grapheme to get to where the new characters
214 // other end of the grapheme to get to where the new characters
@@ -243,8 +234,7 @@ fn get_next_range(
244 (_, Direction::Forward) => {
235 (_, Direction::Forward) => {
245 if single_grapheme {
236 if single_grapheme {
246− graphemes::prev_grapheme_boundary(doc.slice(..), start_range.head)
237+ graphemes::prev_grapheme_boundary(doc.slice(..), start_range.head) + 1
247− + typed_char.len_utf8()
249 // if we are appending, the anchor stays where it is; only offset
239 // if we are appending, the anchor stays where it is; only offset
250 // for multiple range insertions
240 // for multiple range insertions
@@ -258,7 +248,9 @@ fn get_next_range(
258 // if we're backward, then the head is at the first char
248 // if we're backward, then the head is at the first char
259 // of the typed char, so we need to add the length of
249 // of the typed char, so we need to add the length of
260 // the closing char
250 // the closing char
261− graphemes::prev_grapheme_boundary(doc.slice(..), start_range.anchor) + len_inserted
251+ graphemes::prev_grapheme_boundary(doc.slice(..), start_range.anchor)
263 // when we are inserting in front of a selection, we need to move
255 // when we are inserting in front of a selection, we need to move
264 // the anchor over by however many characters were inserted overall
256 // the anchor over by however many characters were inserted overall
@@ -279,9 +271,12 @@ fn handle_open(doc: &Rope, selection: &Selection, pair: &Pair) -> Transaction {
279 let next_char = doc.get_char(cursor);
271 let next_char = doc.get_char(cursor);
280 let len_inserted;
272 let len_inserted;
274+ // Since auto pairs are currently limited to single chars, we're either
275+ // inserting exactly one or two chars. When arbitrary length pairs are
276+ // added, these will need to be changed.
282 let change = match next_char {
277 let change = match next_char {
283 Some(_) if !pair.should_close(doc, start_range) => {
278 Some(_) if !pair.should_close(doc, start_range) => {
284− len_inserted = pair.open.len_utf8();
279+ len_inserted = 1;
285 let mut tendril = Tendril::new();
280 let mut tendril = Tendril::new();
286 tendril.push(pair.open);
281 tendril.push(pair.open);
287 (cursor, cursor, Some(tendril))
282 (cursor, cursor, Some(tendril))
@@ -289,12 +284,12 @@ fn handle_open(doc: &Rope, selection: &Selection, pair: &Pair) -> Transaction {
290 // insert open & close
285 // insert open & close
291 let pair_str = Tendril::from_iter([pair.open, pair.close]);
286 let pair_str = Tendril::from_iter([pair.open, pair.close]);
292− len_inserted = pair.open.len_utf8() + pair.close.len_utf8();
287+ len_inserted = 2;
293 (cursor, cursor, Some(pair_str))
288 (cursor, cursor, Some(pair_str))
297− let next_range = get_next_range(doc, start_range, offs, pair.open, len_inserted);
292+ let next_range = get_next_range(doc, start_range, offs, len_inserted);
298 end_ranges.push(next_range);
293 end_ranges.push(next_range);
299 offs += len_inserted;
294 offs += len_inserted;
@@ -308,7 +303,6 @@ fn handle_open(doc: &Rope, selection: &Selection, pair: &Pair) -> Transaction {
309fn handle_close(doc: &Rope, selection: &Selection, pair: &Pair) -> Transaction {
304fn handle_close(doc: &Rope, selection: &Selection, pair: &Pair) -> Transaction {
310 let mut end_ranges = SmallVec::with_capacity(selection.len());
305 let mut end_ranges = SmallVec::with_capacity(selection.len());
312 let mut offs = 0;
306 let mut offs = 0;
314 let transaction = Transaction::change_by_selection(doc, selection, |start_range| {
308 let transaction = Transaction::change_by_selection(doc, selection, |start_range| {
@@ -320,13 +314,13 @@ fn handle_close(doc: &Rope, selection: &Selection, pair: &Pair) -> Transaction {
320 // return transaction that moves past close
314 // return transaction that moves past close
321 (cursor, cursor, None) // no-op
315 (cursor, cursor, None) // no-op
323− len_inserted += pair.close.len_utf8();
317+ len_inserted = 1;
324 let mut tendril = Tendril::new();
318 let mut tendril = Tendril::new();
325 tendril.push(pair.close);
319 tendril.push(pair.close);
326 (cursor, cursor, Some(tendril))
320 (cursor, cursor, Some(tendril))
329− let next_range = get_next_range(doc, start_range, offs, pair.close, len_inserted);
323+ let next_range = get_next_range(doc, start_range, offs, len_inserted);
330 end_ranges.push(next_range);
324 end_ranges.push(next_range);
331 offs += len_inserted;
325 offs += len_inserted;
@@ -362,11 +356,11 @@ fn handle_same(doc: &Rope, selection: &Selection, pair: &Pair) -> Transaction {
362 pair_str.push(pair.close);
356 pair_str.push(pair.close);
365− len_inserted += pair_str.len();
359+ len_inserted += pair_str.chars().count();
366 (cursor, cursor, Some(pair_str))
360 (cursor, cursor, Some(pair_str))
369− let next_range = get_next_range(doc, start_range, offs, pair.open, len_inserted);
363+ let next_range = get_next_range(doc, start_range, offs, len_inserted);
370 end_ranges.push(next_range);
364 end_ranges.push(next_range);
371 offs += len_inserted;
365 offs += len_inserted;
@@ -377,551 +371,3 @@ fn handle_same(doc: &Rope, selection: &Selection, pair: &Pair) -> Transaction {
377 log::debug!("auto pair transaction: {:#?}", t);
371 log::debug!("auto pair transaction: {:#?}", t);
384− use smallvec::smallvec;
386− const LINE_END: &str = crate::DEFAULT_LINE_ENDING.as_str();
388− fn differing_pairs() -> impl Iterator<Item = &'static (char, char)> {
389− DEFAULT_PAIRS.iter().filter(|(open, close)| open != close)
392− fn matching_pairs() -> impl Iterator<Item = &'static (char, char)> {
393− DEFAULT_PAIRS.iter().filter(|(open, close)| open == close)
400− pairs: &[(char, char)],
401− expected_doc: &Rope,
402− expected_sel: &Selection,
404− let pairs = AutoPairs::new(pairs.iter());
405− let trans = hook(in_doc, in_sel, ch, &pairs).unwrap();
406− let mut actual_doc = in_doc.clone();
407− assert!(trans.apply(&mut actual_doc));
408− assert_eq!(expected_doc, &actual_doc);
409− assert_eq!(expected_sel, trans.selection().unwrap());
412− fn test_hooks_with_pairs<I, F, R>(
416− pairs: &[(char, char)],
417− get_expected_doc: F,
418− actual_sel: &Selection,
420− I: IntoIterator<Item = &'static (char, char)>,
421− F: Fn(char, char) -> R,
425− test_pairs.into_iter().for_each(|(open, close)| {
431− &Rope::from(get_expected_doc(*open, *close)),
437− // [] indicates range
439− /// [] -> insert ( -> ([])
441− fn test_insert_blank() {
442− test_hooks_with_pairs(
443− &Rope::from(LINE_END),
444− &Selection::single(1, 0),
447− |open, close| format!("{}{}{}", open, close, LINE_END),
448− &Selection::single(2, 1),
451− let empty_doc = Rope::from(format!("{line_end}{line_end}", line_end = LINE_END));
453− test_hooks_with_pairs(
455− &Selection::single(empty_doc.len_chars(), LINE_END.len()),
460− "{line_end}{open}{close}{line_end}",
466− &Selection::single(LINE_END.len() + 2, LINE_END.len() + 1),
471− fn test_insert_before_multi_code_point_graphemes() {
472− for (_, close) in differing_pairs() {
474− &Rope::from(format!("hello 👨👩👧👦 goodbye{}", LINE_END)),
475− &Selection::single(13, 6),
478− &Rope::from(format!("hello {}👨👩👧👦 goodbye{}", close, LINE_END)),
479− &Selection::single(14, 7),
485− fn test_insert_at_end_of_document() {
486− test_hooks_with_pairs(
487− &Rope::from(LINE_END),
488− &Selection::single(LINE_END.len(), LINE_END.len()),
491− |open, close| format!("{}{}{}", LINE_END, open, close),
492− &Selection::single(LINE_END.len() + 1, LINE_END.len() + 1),
495− test_hooks_with_pairs(
496− &Rope::from(format!("foo{}", LINE_END)),
497− &Selection::single(3 + LINE_END.len(), 3 + LINE_END.len()),
500− |open, close| format!("foo{}{}{}", LINE_END, open, close),
501− &Selection::single(LINE_END.len() + 4, LINE_END.len() + 4),
505− /// [] -> append ( -> ([])
507− fn test_append_blank() {
508− test_hooks_with_pairs(
509− // this is what happens when you have a totally blank document and then append
510− &Rope::from(format!("{line_end}{line_end}", line_end = LINE_END)),
511− // before inserting the pair, the cursor covers all of both empty lines
512− &Selection::single(0, LINE_END.len() * 2),
517− "{line_end}{open}{close}{line_end}",
518− line_end = LINE_END,
523− // after inserting pair, the cursor covers the first new line and the open char
524− &Selection::single(0, LINE_END.len() + 2),
529− /// [] -> insert -> ([])
532− fn test_insert_blank_multi_cursor() {
533− test_hooks_with_pairs(
534− &Rope::from("\n\n\n"),
536− smallvec!(Range::new(1, 0), Range::new(2, 1), Range::new(3, 2),),
543− "{open}{close}\n{open}{close}\n{open}{close}\n",
549− smallvec!(Range::new(2, 1), Range::new(5, 4), Range::new(8, 7),),
555− /// fo[o] -> append ( -> fo[o(])
558− test_hooks_with_pairs(
559− &Rope::from("foo\n"),
560− &Selection::single(2, 4),
563− |open, close| format!("foo{}{}\n", open, close),
564− &Selection::single(2, 5),
568− /// foo[] -> append to end of line ( -> foo([])
570− fn test_append_single_cursor() {
571− test_hooks_with_pairs(
572− &Rope::from(format!("foo{}", LINE_END)),
573− &Selection::single(3, 3 + LINE_END.len()),
576− |open, close| format!("foo{}{}{}", open, close, LINE_END),
577− &Selection::single(4, 5),
582− /// fo[o] -> append ( -> fo[o(])
585− fn test_append_multi() {
586− test_hooks_with_pairs(
587− &Rope::from("foo\nfoo\nfoo\n"),
589− smallvec!(Range::new(2, 4), Range::new(6, 8), Range::new(10, 12)),
596− "foo{open}{close}\nfoo{open}{close}\nfoo{open}{close}\n",
602− smallvec!(Range::new(2, 5), Range::new(8, 11), Range::new(14, 17)),
608− /// ([)] -> insert ) -> ()[]
610− fn test_insert_close_inside_pair() {
611− for (open, close) in DEFAULT_PAIRS {
612− let doc = Rope::from(format!("{}{}{}", open, close, LINE_END));
616− &Selection::single(2, 1),
620− &Selection::single(2 + LINE_END.len(), 2),
625− /// [(]) -> append ) -> [()]
627− fn test_append_close_inside_pair() {
628− for (open, close) in DEFAULT_PAIRS {
629− let doc = Rope::from(format!("{}{}{}", open, close, LINE_END));
633− &Selection::single(0, 2),
637− &Selection::single(0, 2 + LINE_END.len()),
643− /// ([]) -> insert ) -> ()[]
646− fn test_insert_close_inside_pair_multi_cursor() {
647− let sel = Selection::new(
648− smallvec!(Range::new(2, 1), Range::new(5, 4), Range::new(8, 7),),
652− let expected_sel = Selection::new(
653− smallvec!(Range::new(3, 2), Range::new(6, 5), Range::new(9, 8),),
657− for (open, close) in DEFAULT_PAIRS {
658− let doc = Rope::from(format!(
659− "{open}{close}\n{open}{close}\n{open}{close}\n",
664− test_hooks(&doc, &sel, *close, DEFAULT_PAIRS, &doc, &expected_sel);
669− /// [(]) -> append ) -> [()]
672− fn test_append_close_inside_pair_multi_cursor() {
673− let sel = Selection::new(
674− smallvec!(Range::new(0, 2), Range::new(3, 5), Range::new(6, 8),),
678− let expected_sel = Selection::new(
679− smallvec!(Range::new(0, 3), Range::new(3, 6), Range::new(6, 9),),
683− for (open, close) in DEFAULT_PAIRS {
684− let doc = Rope::from(format!(
685− "{open}{close}\n{open}{close}\n{open}{close}\n",
690− test_hooks(&doc, &sel, *close, DEFAULT_PAIRS, &doc, &expected_sel);
694− /// ([]) -> insert ( -> (([]))
696− fn test_insert_open_inside_pair() {
697− let sel = Selection::single(2, 1);
698− let expected_sel = Selection::single(3, 2);
700− for (open, close) in differing_pairs() {
701− let doc = Rope::from(format!("{}{}", open, close));
702− let expected_doc = Rope::from(format!(
703− "{open}{open}{close}{close}",
719− /// [word(]) -> append ( -> [word((]))
721− fn test_append_open_inside_pair() {
722− let sel = Selection::single(0, 6);
723− let expected_sel = Selection::single(0, 7);
725− for (open, close) in differing_pairs() {
726− let doc = Rope::from(format!("word{}{}", open, close));
727− let expected_doc = Rope::from(format!(
728− "word{open}{open}{close}{close}",
744− /// ([]) -> insert " -> ("[]")
746− fn test_insert_nested_open_inside_pair() {
747− let sel = Selection::single(2, 1);
748− let expected_sel = Selection::single(3, 2);
750− for (outer_open, outer_close) in differing_pairs() {
751− let doc = Rope::from(format!("{}{}", outer_open, outer_close,));
753− for (inner_open, inner_close) in matching_pairs() {
754− let expected_doc = Rope::from(format!(
756− outer_open, inner_open, inner_close, outer_close
771− /// [(]) -> append " -> [("]")
773− fn test_append_nested_open_inside_pair() {
774− let sel = Selection::single(0, 2);
775− let expected_sel = Selection::single(0, 3);
777− for (outer_open, outer_close) in differing_pairs() {
778− let doc = Rope::from(format!("{}{}", outer_open, outer_close,));
780− for (inner_open, inner_close) in matching_pairs() {
781− let expected_doc = Rope::from(format!(
783− outer_open, inner_open, inner_close, outer_close
798− /// []word -> insert ( -> ([]word
800− fn test_insert_open_before_non_pair() {
801− test_hooks_with_pairs(
802− &Rope::from("word"),
803− &Selection::single(1, 0),
806− |open, _| format!("{}word", open),
807− &Selection::single(2, 1),
811− /// [wor]d -> insert ( -> ([wor]d
813− fn test_insert_open_with_selection() {
814− test_hooks_with_pairs(
815− &Rope::from("word"),
816− &Selection::single(3, 0),
819− |open, _| format!("{}word", open),
820− &Selection::single(4, 1),
824− /// [wor]d -> append ) -> [wor)]d
826− fn test_append_close_inside_non_pair_with_selection() {
827− let sel = Selection::single(0, 4);
828− let expected_sel = Selection::single(0, 5);
830− for (_, close) in DEFAULT_PAIRS {
831− let doc = Rope::from("word");
832− let expected_doc = Rope::from(format!("wor{}d", close));
844− /// foo[ wor]d -> insert ( -> foo([) wor]d
846− fn test_insert_open_trailing_word_with_selection() {
847− test_hooks_with_pairs(
848− &Rope::from("foo word"),
849− &Selection::single(7, 3),
852− |open, close| format!("foo{}{} word", open, close),
853− &Selection::single(9, 4),
857− /// foo([) wor]d -> insert ) -> foo()[ wor]d
859− fn test_insert_close_inside_pair_trailing_word_with_selection() {
860− for (open, close) in differing_pairs() {
862− &Rope::from(format!("foo{}{} word{}", open, close, LINE_END)),
863− &Selection::single(9, 4),
866− &Rope::from(format!("foo{}{} word{}", open, close, LINE_END)),
867− &Selection::single(9, 5),
872− /// we want pairs that are *not* the same char to be inserted after
873− /// a non-pair char, for cases like functions, but for pairs that are
874− /// the same char, we want to *not* insert a pair to handle cases like "I'm"
876− /// word[] -> insert ( -> word([])
877− /// word[] -> insert ' -> word'[]
879− fn test_insert_open_after_non_pair() {
880− let doc = Rope::from(format!("word{}", LINE_END));
881− let sel = Selection::single(5, 4);
882− let expected_sel = Selection::single(6, 5);
884− test_hooks_with_pairs(
889− |open, close| format!("word{}{}{}", open, close, LINE_END),
893− test_hooks_with_pairs(
898− |open, _| format!("word{}{}", open, LINE_END),
904− fn test_configured_pairs() {
905− let test_pairs = &[('`', ':'), ('+', '-')];
907− test_hooks_with_pairs(
908− &Rope::from(LINE_END),
909− &Selection::single(1, 0),
912− |open, close| format!("{}{}{}", open, close, LINE_END),
913− &Selection::single(2, 1),
916− let doc = Rope::from(format!("foo`: word{}", LINE_END));
920− &Selection::single(9, 4),
924− &Selection::single(9, 5),