@@ -146,13 +146,7 @@ fn prev_char(doc: &Rope, pos: usize) -> Option<char> {
146146}
147147
148148/// calculate what the resulting range should be for an auto pair insertion
149−fn get_next_range(
150− doc: &Rope,
151− start_range: &Range,
152− offset: usize,
153− typed_char: char,
154− len_inserted: usize,
155−) -> Range {
149+fn get_next_range(doc: &Rope, start_range: &Range, offset: usize, len_inserted: usize) -> Range {
156150 // When the character under the cursor changes due to complete pair
157151 // insertion, we must look backward a grapheme and then add the length
158152 // of the insertion to put the resulting cursor in the right place, e.g.
@@ -172,8 +166,8 @@ fn get_next_range(
172166 // inserting at the very end of the document after the last newline
173167 if start_range.head == doc.len_chars() && start_range.anchor == doc.len_chars() {
174168 return Range::new(
175− start_range.anchor + offset + typed_char.len_utf8(),
176− start_range.head + offset + typed_char.len_utf8(),
169+ start_range.anchor + offset + 1,
170+ start_range.head + offset + 1,
177171 );
178172 }
179173
@@ -203,21 +197,18 @@ fn get_next_range(
203197 // trivial case: only inserted a single-char opener, just move the selection
204198 if len_inserted == 1 {
205199 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
207201 } else {
208202 start_range.anchor + offset
209203 };
210204
211− return Range::new(
212− end_anchor,
213− start_range.head + offset + typed_char.len_utf8(),
214− );
205+ return Range::new(end_anchor, start_range.head + offset + 1);
215206 }
216207
217208 // If the head = 0, then we must be in insert mode with a backward
218209 // cursor, which implies the head will just move
219210 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
221212 } else {
222213 // We must have a forward cursor, which means we must move to the
223214 // other end of the grapheme to get to where the new characters
@@ -243,8 +234,7 @@ fn get_next_range(
243234
244235 (_, Direction::Forward) => {
245236 if single_grapheme {
246− graphemes::prev_grapheme_boundary(doc.slice(..), start_range.head)
247− + typed_char.len_utf8()
237+ graphemes::prev_grapheme_boundary(doc.slice(..), start_range.head) + 1
248238
249239 // if we are appending, the anchor stays where it is; only offset
250240 // for multiple range insertions
@@ -258,7 +248,9 @@ fn get_next_range(
258248 // if we're backward, then the head is at the first char
259249 // of the typed char, so we need to add the length of
260250 // 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)
252+ + len_inserted
253+ + offset
262254 } else {
263255 // when we are inserting in front of a selection, we need to move
264256 // the anchor over by however many characters were inserted overall
@@ -279,9 +271,12 @@ fn handle_open(doc: &Rope, selection: &Selection, pair: &Pair) -> Transaction {
279271 let next_char = doc.get_char(cursor);
280272 let len_inserted;
281273
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.
282277 let change = match next_char {
283278 Some(_) if !pair.should_close(doc, start_range) => {
284− len_inserted = pair.open.len_utf8();
279+ len_inserted = 1;
285280 let mut tendril = Tendril::new();
286281 tendril.push(pair.open);
287282 (cursor, cursor, Some(tendril))
@@ -289,12 +284,12 @@ fn handle_open(doc: &Rope, selection: &Selection, pair: &Pair) -> Transaction {
289284 _ => {
290285 // insert open & close
291286 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;
293288 (cursor, cursor, Some(pair_str))
294289 }
295290 };
296291
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);
298293 end_ranges.push(next_range);
299294 offs += len_inserted;
300295
@@ -308,7 +303,6 @@ fn handle_open(doc: &Rope, selection: &Selection, pair: &Pair) -> Transaction {
308303
309304fn handle_close(doc: &Rope, selection: &Selection, pair: &Pair) -> Transaction {
310305 let mut end_ranges = SmallVec::with_capacity(selection.len());
311−
312306 let mut offs = 0;
313307
314308 let transaction = Transaction::change_by_selection(doc, selection, |start_range| {
@@ -320,13 +314,13 @@ fn handle_close(doc: &Rope, selection: &Selection, pair: &Pair) -> Transaction {
320314 // return transaction that moves past close
321315 (cursor, cursor, None) // no-op
322316 } else {
323− len_inserted += pair.close.len_utf8();
317+ len_inserted = 1;
324318 let mut tendril = Tendril::new();
325319 tendril.push(pair.close);
326320 (cursor, cursor, Some(tendril))
327321 };
328322
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);
330324 end_ranges.push(next_range);
331325 offs += len_inserted;
332326
@@ -362,11 +356,11 @@ fn handle_same(doc: &Rope, selection: &Selection, pair: &Pair) -> Transaction {
362356 pair_str.push(pair.close);
363357 }
364358
365− len_inserted += pair_str.len();
359+ len_inserted += pair_str.chars().count();
366360 (cursor, cursor, Some(pair_str))
367361 };
368362
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);
370364 end_ranges.push(next_range);
371365 offs += len_inserted;
372366
@@ -377,551 +371,3 @@ fn handle_same(doc: &Rope, selection: &Selection, pair: &Pair) -> Transaction {
377371 log::debug!("auto pair transaction: {:#?}", t);
378372 t
379373}
380−
381−#[cfg(test)]
382−mod test {
383− use super::*;
384− use smallvec::smallvec;
385−
386− const LINE_END: &str = crate::DEFAULT_LINE_ENDING.as_str();
387−
388− fn differing_pairs() -> impl Iterator<Item = &'static (char, char)> {
389− DEFAULT_PAIRS.iter().filter(|(open, close)| open != close)
390− }
391−
392− fn matching_pairs() -> impl Iterator<Item = &'static (char, char)> {
393− DEFAULT_PAIRS.iter().filter(|(open, close)| open == close)
394− }
395−
396− fn test_hooks(
397− in_doc: &Rope,
398− in_sel: &Selection,
399− ch: char,
400− pairs: &[(char, char)],
401− expected_doc: &Rope,
402− expected_sel: &Selection,
403− ) {
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());
410− }
411−
412− fn test_hooks_with_pairs<I, F, R>(
413− in_doc: &Rope,
414− in_sel: &Selection,
415− test_pairs: I,
416− pairs: &[(char, char)],
417− get_expected_doc: F,
418− actual_sel: &Selection,
419− ) where
420− I: IntoIterator<Item = &'static (char, char)>,
421− F: Fn(char, char) -> R,
422− R: Into<Rope>,
423− Rope: From<R>,
424− {
425− test_pairs.into_iter().for_each(|(open, close)| {
426− test_hooks(
427− in_doc,
428− in_sel,
429− *open,
430− pairs,
431− &Rope::from(get_expected_doc(*open, *close)),
432− actual_sel,
433− )
434− });
435− }
436−
437− // [] indicates range
438−
439− /// [] -> insert ( -> ([])
440− #[test]
441− fn test_insert_blank() {
442− test_hooks_with_pairs(
443− &Rope::from(LINE_END),
444− &Selection::single(1, 0),
445− DEFAULT_PAIRS,
446− DEFAULT_PAIRS,
447− |open, close| format!("{}{}{}", open, close, LINE_END),
448− &Selection::single(2, 1),
449− );
450−
451− let empty_doc = Rope::from(format!("{line_end}{line_end}", line_end = LINE_END));
452−
453− test_hooks_with_pairs(
454− &empty_doc,
455− &Selection::single(empty_doc.len_chars(), LINE_END.len()),
456− DEFAULT_PAIRS,
457− DEFAULT_PAIRS,
458− |open, close| {
459− format!(
460− "{line_end}{open}{close}{line_end}",
461− open = open,
462− close = close,
463− line_end = LINE_END
464− )
465− },
466− &Selection::single(LINE_END.len() + 2, LINE_END.len() + 1),
467− );
468− }
469−
470− #[test]
471− fn test_insert_before_multi_code_point_graphemes() {
472− for (_, close) in differing_pairs() {
473− test_hooks(
474− &Rope::from(format!("hello 👨👩👧👦 goodbye{}", LINE_END)),
475− &Selection::single(13, 6),
476− *close,
477− DEFAULT_PAIRS,
478− &Rope::from(format!("hello {}👨👩👧👦 goodbye{}", close, LINE_END)),
479− &Selection::single(14, 7),
480− );
481− }
482− }
483−
484− #[test]
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()),
489− DEFAULT_PAIRS,
490− DEFAULT_PAIRS,
491− |open, close| format!("{}{}{}", LINE_END, open, close),
492− &Selection::single(LINE_END.len() + 1, LINE_END.len() + 1),
493− );
494−
495− test_hooks_with_pairs(
496− &Rope::from(format!("foo{}", LINE_END)),
497− &Selection::single(3 + LINE_END.len(), 3 + LINE_END.len()),
498− DEFAULT_PAIRS,
499− DEFAULT_PAIRS,
500− |open, close| format!("foo{}{}{}", LINE_END, open, close),
501− &Selection::single(LINE_END.len() + 4, LINE_END.len() + 4),
502− );
503− }
504−
505− /// [] -> append ( -> ([])
506− #[test]
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),
513− DEFAULT_PAIRS,
514− DEFAULT_PAIRS,
515− |open, close| {
516− format!(
517− "{line_end}{open}{close}{line_end}",
518− line_end = LINE_END,
519− open = open,
520− close = close
521− )
522− },
523− // after inserting pair, the cursor covers the first new line and the open char
524− &Selection::single(0, LINE_END.len() + 2),
525− );
526− }
527−
528− /// [] ([])
529− /// [] -> insert -> ([])
530− /// [] ([])
531− #[test]
532− fn test_insert_blank_multi_cursor() {
533− test_hooks_with_pairs(
534− &Rope::from("\n\n\n"),
535− &Selection::new(
536− smallvec!(Range::new(1, 0), Range::new(2, 1), Range::new(3, 2),),
537− 0,
538− ),
539− DEFAULT_PAIRS,
540− DEFAULT_PAIRS,
541− |open, close| {
542− format!(
543− "{open}{close}\n{open}{close}\n{open}{close}\n",
544− open = open,
545− close = close
546− )
547− },
548− &Selection::new(
549− smallvec!(Range::new(2, 1), Range::new(5, 4), Range::new(8, 7),),
550− 0,
551− ),
552− );
553− }
554−
555− /// fo[o] -> append ( -> fo[o(])
556− #[test]
557− fn test_append() {
558− test_hooks_with_pairs(
559− &Rope::from("foo\n"),
560− &Selection::single(2, 4),
561− differing_pairs(),
562− DEFAULT_PAIRS,
563− |open, close| format!("foo{}{}\n", open, close),
564− &Selection::single(2, 5),
565− );
566− }
567−
568− /// foo[] -> append to end of line ( -> foo([])
569− #[test]
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()),
574− differing_pairs(),
575− DEFAULT_PAIRS,
576− |open, close| format!("foo{}{}{}", open, close, LINE_END),
577− &Selection::single(4, 5),
578− );
579− }
580−
581− /// fo[o] fo[o(])
582− /// fo[o] -> append ( -> fo[o(])
583− /// fo[o] fo[o(])
584− #[test]
585− fn test_append_multi() {
586− test_hooks_with_pairs(
587− &Rope::from("foo\nfoo\nfoo\n"),
588− &Selection::new(
589− smallvec!(Range::new(2, 4), Range::new(6, 8), Range::new(10, 12)),
590− 0,
591− ),
592− differing_pairs(),
593− DEFAULT_PAIRS,
594− |open, close| {
595− format!(
596− "foo{open}{close}\nfoo{open}{close}\nfoo{open}{close}\n",
597− open = open,
598− close = close
599− )
600− },
601− &Selection::new(
602− smallvec!(Range::new(2, 5), Range::new(8, 11), Range::new(14, 17)),
603− 0,
604− ),
605− );
606− }
607−
608− /// ([)] -> insert ) -> ()[]
609− #[test]
610− fn test_insert_close_inside_pair() {
611− for (open, close) in DEFAULT_PAIRS {
612− let doc = Rope::from(format!("{}{}{}", open, close, LINE_END));
613−
614− test_hooks(
615− &doc,
616− &Selection::single(2, 1),
617− *close,
618− DEFAULT_PAIRS,
619− &doc,
620− &Selection::single(2 + LINE_END.len(), 2),
621− );
622− }
623− }
624−
625− /// [(]) -> append ) -> [()]
626− #[test]
627− fn test_append_close_inside_pair() {
628− for (open, close) in DEFAULT_PAIRS {
629− let doc = Rope::from(format!("{}{}{}", open, close, LINE_END));
630−
631− test_hooks(
632− &doc,
633− &Selection::single(0, 2),
634− *close,
635− DEFAULT_PAIRS,
636− &doc,
637− &Selection::single(0, 2 + LINE_END.len()),
638− );
639− }
640− }
641−
642− /// ([]) ()[]
643− /// ([]) -> insert ) -> ()[]
644− /// ([]) ()[]
645− #[test]
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),),
649− 0,
650− );
651−
652− let expected_sel = Selection::new(
653− smallvec!(Range::new(3, 2), Range::new(6, 5), Range::new(9, 8),),
654− 0,
655− );
656−
657− for (open, close) in DEFAULT_PAIRS {
658− let doc = Rope::from(format!(
659− "{open}{close}\n{open}{close}\n{open}{close}\n",
660− open = open,
661− close = close
662− ));
663−
664− test_hooks(&doc, &sel, *close, DEFAULT_PAIRS, &doc, &expected_sel);
665− }
666− }
667−
668− /// [(]) [()]
669− /// [(]) -> append ) -> [()]
670− /// [(]) [()]
671− #[test]
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),),
675− 0,
676− );
677−
678− let expected_sel = Selection::new(
679− smallvec!(Range::new(0, 3), Range::new(3, 6), Range::new(6, 9),),
680− 0,
681− );
682−
683− for (open, close) in DEFAULT_PAIRS {
684− let doc = Rope::from(format!(
685− "{open}{close}\n{open}{close}\n{open}{close}\n",
686− open = open,
687− close = close
688− ));
689−
690− test_hooks(&doc, &sel, *close, DEFAULT_PAIRS, &doc, &expected_sel);
691− }
692− }
693−
694− /// ([]) -> insert ( -> (([]))
695− #[test]
696− fn test_insert_open_inside_pair() {
697− let sel = Selection::single(2, 1);
698− let expected_sel = Selection::single(3, 2);
699−
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}",
704− open = open,
705− close = close
706− ));
707−
708− test_hooks(
709− &doc,
710− &sel,
711− *open,
712− DEFAULT_PAIRS,
713− &expected_doc,
714− &expected_sel,
715− );
716− }
717− }
718−
719− /// [word(]) -> append ( -> [word((]))
720− #[test]
721− fn test_append_open_inside_pair() {
722− let sel = Selection::single(0, 6);
723− let expected_sel = Selection::single(0, 7);
724−
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}",
729− open = open,
730− close = close
731− ));
732−
733− test_hooks(
734− &doc,
735− &sel,
736− *open,
737− DEFAULT_PAIRS,
738− &expected_doc,
739− &expected_sel,
740− );
741− }
742− }
743−
744− /// ([]) -> insert " -> ("[]")
745− #[test]
746− fn test_insert_nested_open_inside_pair() {
747− let sel = Selection::single(2, 1);
748− let expected_sel = Selection::single(3, 2);
749−
750− for (outer_open, outer_close) in differing_pairs() {
751− let doc = Rope::from(format!("{}{}", outer_open, outer_close,));
752−
753− for (inner_open, inner_close) in matching_pairs() {
754− let expected_doc = Rope::from(format!(
755− "{}{}{}{}",
756− outer_open, inner_open, inner_close, outer_close
757− ));
758−
759− test_hooks(
760− &doc,
761− &sel,
762− *inner_open,
763− DEFAULT_PAIRS,
764− &expected_doc,
765− &expected_sel,
766− );
767− }
768− }
769− }
770−
771− /// [(]) -> append " -> [("]")
772− #[test]
773− fn test_append_nested_open_inside_pair() {
774− let sel = Selection::single(0, 2);
775− let expected_sel = Selection::single(0, 3);
776−
777− for (outer_open, outer_close) in differing_pairs() {
778− let doc = Rope::from(format!("{}{}", outer_open, outer_close,));
779−
780− for (inner_open, inner_close) in matching_pairs() {
781− let expected_doc = Rope::from(format!(
782− "{}{}{}{}",
783− outer_open, inner_open, inner_close, outer_close
784− ));
785−
786− test_hooks(
787− &doc,
788− &sel,
789− *inner_open,
790− DEFAULT_PAIRS,
791− &expected_doc,
792− &expected_sel,
793− );
794− }
795− }
796− }
797−
798− /// []word -> insert ( -> ([]word
799− #[test]
800− fn test_insert_open_before_non_pair() {
801− test_hooks_with_pairs(
802− &Rope::from("word"),
803− &Selection::single(1, 0),
804− DEFAULT_PAIRS,
805− DEFAULT_PAIRS,
806− |open, _| format!("{}word", open),
807− &Selection::single(2, 1),
808− )
809− }
810−
811− /// [wor]d -> insert ( -> ([wor]d
812− #[test]
813− fn test_insert_open_with_selection() {
814− test_hooks_with_pairs(
815− &Rope::from("word"),
816− &Selection::single(3, 0),
817− DEFAULT_PAIRS,
818− DEFAULT_PAIRS,
819− |open, _| format!("{}word", open),
820− &Selection::single(4, 1),
821− )
822− }
823−
824− /// [wor]d -> append ) -> [wor)]d
825− #[test]
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);
829−
830− for (_, close) in DEFAULT_PAIRS {
831− let doc = Rope::from("word");
832− let expected_doc = Rope::from(format!("wor{}d", close));
833− test_hooks(
834− &doc,
835− &sel,
836− *close,
837− DEFAULT_PAIRS,
838− &expected_doc,
839− &expected_sel,
840− );
841− }
842− }
843−
844− /// foo[ wor]d -> insert ( -> foo([) wor]d
845− #[test]
846− fn test_insert_open_trailing_word_with_selection() {
847− test_hooks_with_pairs(
848− &Rope::from("foo word"),
849− &Selection::single(7, 3),
850− differing_pairs(),
851− DEFAULT_PAIRS,
852− |open, close| format!("foo{}{} word", open, close),
853− &Selection::single(9, 4),
854− )
855− }
856−
857− /// foo([) wor]d -> insert ) -> foo()[ wor]d
858− #[test]
859− fn test_insert_close_inside_pair_trailing_word_with_selection() {
860− for (open, close) in differing_pairs() {
861− test_hooks(
862− &Rope::from(format!("foo{}{} word{}", open, close, LINE_END)),
863− &Selection::single(9, 4),
864− *close,
865− DEFAULT_PAIRS,
866− &Rope::from(format!("foo{}{} word{}", open, close, LINE_END)),
867− &Selection::single(9, 5),
868− )
869− }
870− }
871−
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"
875− ///
876− /// word[] -> insert ( -> word([])
877− /// word[] -> insert ' -> word'[]
878− #[test]
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);
883−
884− test_hooks_with_pairs(
885− &doc,
886− &sel,
887− differing_pairs(),
888− DEFAULT_PAIRS,
889− |open, close| format!("word{}{}{}", open, close, LINE_END),
890− &expected_sel,
891− );
892−
893− test_hooks_with_pairs(
894− &doc,
895− &sel,
896− matching_pairs(),
897− DEFAULT_PAIRS,
898− |open, _| format!("word{}{}", open, LINE_END),
899− &expected_sel,
900− );
901− }
902−
903− #[test]
904− fn test_configured_pairs() {
905− let test_pairs = &[('`', ':'), ('+', '-')];
906−
907− test_hooks_with_pairs(
908− &Rope::from(LINE_END),
909− &Selection::single(1, 0),
910− test_pairs,
911− test_pairs,
912− |open, close| format!("{}{}{}", open, close, LINE_END),
913− &Selection::single(2, 1),
914− );
915−
916− let doc = Rope::from(format!("foo`: word{}", LINE_END));
917−
918− test_hooks(
919− &doc,
920− &Selection::single(9, 4),
921− ':',
922− test_pairs,
923− &doc,
924− &Selection::single(9, 5),
925− )
926− }
927−}