1mod common;
4
5use ink_analyzer_ir::syntax::{AstNode, SyntaxKind, SyntaxToken, TextRange, TextSize};
6use ink_analyzer_ir::{InkEntity, InkFile};
7
8use super::utils;
9
10pub use common::*;
11
12#[derive(Debug, Clone, PartialEq, Eq, Hash)]
14pub struct TextEdit {
15 pub text: String,
17 pub range: TextRange,
19 pub snippet: Option<String>,
21}
22
23impl TextEdit {
24 pub fn new(text: String, range: TextRange, snippet: Option<String>) -> Self {
26 Self {
27 text,
28 range,
29 snippet,
30 }
31 }
32
33 pub fn insert(text: String, offset: TextSize) -> Self {
35 Self::insert_with_snippet(text, offset, None)
36 }
37
38 pub fn insert_with_snippet(text: String, offset: TextSize, snippet: Option<String>) -> Self {
40 Self {
41 text,
42 range: TextRange::new(offset, offset),
43 snippet,
44 }
45 }
46
47 pub fn replace(text: String, range: TextRange) -> Self {
49 Self::replace_with_snippet(text, range, None)
50 }
51
52 pub fn replace_with_snippet(text: String, range: TextRange, snippet: Option<String>) -> Self {
54 Self::new(text, range, snippet)
55 }
56
57 pub fn delete(range: TextRange) -> Self {
59 Self {
60 text: String::new(),
61 range,
62 snippet: None,
63 }
64 }
65}
66
67pub fn format_edits<'a>(
69 edits: impl Iterator<Item = TextEdit> + 'a,
70 file: &'a InkFile,
71) -> impl Iterator<Item = TextEdit> + 'a {
72 edits.map(|item| format_edit(item, file))
73}
74
75pub fn format_edit(mut edit: TextEdit, file: &InkFile) -> TextEdit {
77 fn add_affixes(edit: &mut TextEdit, prefix: Option<String>, suffix: Option<String>) {
79 if prefix.is_some() || suffix.is_some() {
80 edit.text = format!(
81 "{}{}{}",
82 prefix.as_deref().unwrap_or_default(),
83 edit.text,
84 suffix.as_deref().unwrap_or_default(),
85 );
86 edit.snippet = edit.snippet.as_ref().map(|snippet| {
87 format!(
88 "{}{snippet}{}",
89 prefix.as_deref().unwrap_or_default(),
90 suffix.as_deref().unwrap_or_default()
91 )
92 });
93 }
94 }
95
96 let affix_edit_between_whitespace_or_file_boundaries =
98 |whitespace_before: Option<&str>, whitespace_after: Option<&str>| {
99 let build_affix = |ws_text: &str| {
100 (!utils::starts_with_two_or_more_newlines(ws_text)).then(|| "\n".to_owned())
101 };
102 match (
103 whitespace_before.map(|ws_before| (ws_before.contains('\n'), ws_before)),
104 whitespace_after.map(|ws_after| (ws_after.contains('\n'), ws_after)),
105 ) {
106 (Some((true, ws_text_before)), Some((true, ws_text_after))) => {
108 (build_affix(ws_text_before), build_affix(ws_text_after))
109 }
110 (Some((true, ws_text_before)), None) => (build_affix(ws_text_before), None),
111 (None, Some((true, ws_text_after))) => (None, build_affix(ws_text_after)),
112 _ => (None, None),
113 }
114 };
115
116 let covering_whitespace = file
118 .syntax()
119 .covering_element(edit.range)
120 .into_token()
121 .filter(|token| {
122 token.kind() == SyntaxKind::WHITESPACE
123 && token.text_range().start() < edit.range.start()
124 && edit.range.end() < token.text_range().end()
125 });
126 if let Some(whitespace_token) = covering_whitespace {
127 if edit.text.is_empty() {
128 return edit;
129 }
130
131 let whitespace_text = whitespace_token.text();
132 let start = edit.range.start() - whitespace_token.text_range().start();
133 let (whitespace_before, whitespace_after) = if edit.range.is_empty() {
134 whitespace_text.split_at(start.into())
135 } else {
136 let end = whitespace_token.text_range().end() - edit.range.end();
137 let (whitespace_before, _) = whitespace_text.split_at(start.into());
138 let (_, whitespace_after) = whitespace_text.split_at(end.into());
139 (whitespace_before, whitespace_after)
140 };
141 let (prefix, suffix) = affix_edit_between_whitespace_or_file_boundaries(
142 Some(whitespace_before),
143 Some(whitespace_after),
144 );
145 add_affixes(&mut edit, prefix, suffix);
146 return edit;
147 }
148
149 let token_before_option = file
151 .syntax()
152 .token_at_offset(edit.range.start())
153 .left_biased()
154 .and_then(|token| {
155 if token.text_range().end() <= edit.range.start() {
156 Some(token)
157 } else {
158 token.prev_token()
159 }
160 });
161 let token_after_option = file
163 .syntax()
164 .token_at_offset(edit.range.end())
165 .right_biased()
166 .and_then(|token| {
167 if token.text_range().start() >= edit.range.end() {
168 Some(token)
169 } else {
170 token.next_token()
171 }
172 });
173
174 if edit.text.is_empty() {
175 if let Some(token_after) = token_after_option {
180 let is_whitespace_or_bof_before = token_before_option
181 .as_ref()
182 .is_none_or(|token_before| token_before.kind() == SyntaxKind::WHITESPACE);
183 let is_at_the_end_block = token_after
184 .next_token()
185 .is_some_and(|it| it.kind() == SyntaxKind::R_CURLY);
186 if is_whitespace_or_bof_before
187 && token_after.kind() == SyntaxKind::WHITESPACE
188 && !is_at_the_end_block
189 {
190 edit.range = TextRange::new(edit.range.start(), token_after.text_range().end());
191 }
192 }
193 } else {
194 let affix_edit_after_whitespace_or_bof =
196 |token_before_option: Option<&SyntaxToken>,
197 token_after_option: Option<&SyntaxToken>| {
198 let is_whitespace_or_bof_before = token_before_option
199 .as_ref()
200 .is_none_or(|token_before| token_before.kind() == SyntaxKind::WHITESPACE);
201 let is_whitespace_or_eof_after = token_after_option
202 .as_ref()
203 .is_none_or(|token_after| token_after.kind() == SyntaxKind::WHITESPACE);
204 if is_whitespace_or_bof_before && is_whitespace_or_eof_after {
205 affix_edit_between_whitespace_or_file_boundaries(
207 token_before_option.map(SyntaxToken::text),
208 token_after_option.map(SyntaxToken::text),
209 )
210 } else if is_whitespace_or_bof_before && !is_whitespace_or_eof_after {
211 (
213 None,
215 match token_before_option {
219 None => Some("\n".to_owned()),
221 Some(token_before) => token_before.text().contains('\n').then(|| {
222 format!(
223 "\n{}{}",
224 if utils::starts_with_two_or_more_newlines(token_before.text())
225 {
226 "\n"
227 } else {
228 ""
229 },
230 utils::end_indenting(token_before.text())
231 )
232 }),
233 },
234 )
235 } else {
236 (None, None)
237 }
238 };
239 let (prefix, suffix) = if let Some(token_before) = token_before_option {
240 match token_before.kind() {
241 SyntaxKind::WHITESPACE => affix_edit_after_whitespace_or_bof(
243 Some(&token_before),
244 token_after_option.as_ref(),
245 ),
246 SyntaxKind::L_CURLY => {
248 (
249 (!edit.text.starts_with('\n')).then(|| {
252 format!(
253 "\n{}",
254 (!edit.text.starts_with(' ') && !edit.text.starts_with('\t'))
255 .then(|| {
256 ink_analyzer_ir::parent_ast_item(&token_before)
257 .map(|it| utils::item_children_indenting(it.syntax()))
258 })
259 .flatten()
260 .as_deref()
261 .unwrap_or_default()
262 )
263 }),
264 token_after_option.as_ref().and_then(|token_after| {
269 ((token_after.kind() != SyntaxKind::WHITESPACE
270 || !utils::starts_with_two_or_more_newlines(token_after.text()))
271 && !edit.text.ends_with("\n\n"))
272 .then(|| {
273 format!(
274 "\n{}",
275 if token_after.text().starts_with('\n') {
276 ""
277 } else {
278 "\n"
279 }
280 )
281 })
282 }),
283 )
284 }
285 SyntaxKind::SEMICOLON | SyntaxKind::R_CURLY | SyntaxKind::COMMENT => {
287 (
288 (!edit.text.starts_with('\n')).then(|| {
291 format!(
292 "\n{}{}",
293 if token_before.kind() == SyntaxKind::COMMENT {
295 ""
296 } else {
297 "\n"
298 },
299 if !edit.text.starts_with(' ') && !edit.text.starts_with('\t') {
300 ink_analyzer_ir::parent_ast_item(&token_before)
301 .and_then(|it| utils::item_indenting(it.syntax()))
302 } else {
303 None
304 }
305 .as_deref()
306 .unwrap_or_default(),
307 )
308 }),
309 None,
311 )
312 }
313 _ => (None, None),
315 }
316 } else {
317 affix_edit_after_whitespace_or_bof(None, token_after_option.as_ref())
318 };
319
320 add_affixes(&mut edit, prefix, suffix);
322 }
323 edit
324}
325
326#[cfg(test)]
327mod tests {
328 use super::*;
329 use test_utils::parse_offset_at;
330
331 #[test]
332 fn format_insert_and_replace_works() {
333 for (input, output, source, start_pat, end_pat) in [
334 (
336 "#[ink::contract]",
337 "\n#[ink::contract]",
338 r#"
339#![cfg_attr(not(feature = "std"), no_std, no_main)]
340"#,
341 Some("\n->"),
342 Some("\n->"),
343 ),
344 (
345 "mod contract {}",
346 "mod contract {}\n\n",
347 r#"
348#![cfg_attr(not(feature = "std"), no_std, no_main)]
349
350#[cfg(test)]
351mod tests {}"#,
352 Some("<-#[cfg(test)]"),
353 Some("<-#[cfg(test)]"),
354 ),
355 (
356 "mod contract {}",
357 "\nmod contract {}\n",
358 r#"
359#![cfg_attr(not(feature = "std"), no_std, no_main)]
360
361#[cfg(test)]
362mod tests {}"#,
363 Some("<-\n#[cfg(test)]"),
364 Some("<-\n#[cfg(test)]"),
365 ),
366 (
367 "mod contract {}",
368 "\nmod contract {}\n",
369 "#![cfg_attr(not(feature = \"std\"), no_std)]\n \n#[cfg(test)]\nmod tests {}",
370 Some("<- \n#[cfg(test)]"),
371 Some("<-\n#[cfg(test)]"),
372 ),
373 (
374 "mod contract {}",
375 "mod contract {}\n",
376 r#"
377#![cfg_attr(not(feature = "std"), no_std, no_main)]
378
379
380#[cfg(test)]
381mod tests {}"#,
382 Some("<-\n#[cfg(test)]"),
383 Some("<-\n#[cfg(test)]"),
384 ),
385 (
386 "#[ink::contract]",
387 "#[ink::contract]\n",
388 "mod contract {}",
389 Some("<-mod contract {"),
390 Some("<-mod contract {"),
391 ),
392 (
393 "#[ink::contract]",
394 "#[ink::contract]\n",
395 r"
396#[doc(hidden)]
397mod contract {}",
398 Some("<-mod contract {"),
399 Some("<-mod contract {"),
400 ),
401 (
402 "#[ink::contract]",
403 "#[ink::contract]\n",
404 "#[doc(hidden)]\nmod contract {}",
405 Some("<-#[doc(hidden)]"),
406 Some("<-#[doc(hidden)]"),
407 ),
408 (
409 "#[ink::contract]",
410 "#[ink::contract]\n",
411 r"
412#[doc(hidden)]
413mod contract {}",
414 Some("<-#[doc(hidden)]"),
415 Some("<-#[doc(hidden)]"),
416 ),
417 (
418 "#[ink::contract]",
419 "#[ink::contract]\n",
420 r"
421#[doc(hidden)]
422mod contract {}",
423 Some("<-mod contract {"),
424 Some("<-mod contract {"),
425 ),
426 (
427 "#[ink(storage)]",
428 "#[ink(storage)]\n ",
429 r"
430mod contract {
431 struct MyContract {}
432}",
433 Some("<-struct MyContract {}"),
434 Some("<-struct MyContract {}"),
435 ),
436 (
437 "#[ink(topic)]",
438 "#[ink(topic)]\n ",
439 r"
440mod contract {
441 struct MyEvent {
442 status: bool,
443 }
444}",
445 Some("<-status: bool,"),
446 Some("<-status: bool,"),
447 ),
448 (
449 "#[ink(impl)]",
450 "#[ink(impl)]\n ",
451 r"
452mod contract {
453 impl MyContract {}
454}",
455 Some("<-impl MyContract {}"),
456 Some("<-impl MyContract {}"),
457 ),
458 (
459 "#[ink(impl)]",
460 "#[ink(impl)]\n ",
461 r#"
462mod contract {
463 #[ink(namespace = "my_namespace")]
464 impl MyContract {}
465}
466 "#,
467 Some(r#"<-#[ink(namespace = "my_namespace")]"#),
468 Some(r#"<-#[ink(namespace = "my_namespace")]"#),
469 ),
470 (
471 "#[ink(message)]",
472 "#[ink(message)]\n ",
473 r"
474mod contract {
475 impl MyContract {
476 pub fn message(&self) {}
477 }
478}",
479 Some("<-pub fn message(&self) {}"),
480 Some("<-pub fn message(&self) {}"),
481 ),
482 (
484 "struct MyContract {}",
485 "\n struct MyContract {}\n",
486 r"
487mod contract {
488}",
489 Some("mod contract {"),
490 Some("mod contract {"),
491 ),
492 (
493 "status: bool,",
494 "\n status: bool,\n",
495 r"
496mod contract {
497 struct MyContract {
498 }
499}",
500 Some("struct MyContract {"),
501 Some("struct MyContract {"),
502 ),
503 (
504 "impl MyContract {}",
505 "\n impl MyContract {}\n",
506 r"
507mod contract {
508}",
509 Some("mod contract {"),
510 Some("mod contract {"),
511 ),
512 (
513 "pub fn message(&self) {}",
514 "\n pub fn message(&self) {}\n",
515 r"
516mod contract {
517 impl MyContract {
518 }
519}",
520 Some("impl MyContract {"),
521 Some("impl MyContract {"),
522 ),
523 (
525 "struct MyEvent {}",
526 "\n\n struct MyEvent {}",
527 r"
528mod contract {
529 struct MyContract {}
530}",
531 Some("struct MyContract {}"),
532 Some("struct MyContract {}"),
533 ),
534 (
535 "struct MyEvent {}",
536 "\n\n struct MyEvent {}",
537 r"
538mod contract {
539 struct MyContract;
540}",
541 Some("struct MyContract;"),
542 Some("struct MyContract;"),
543 ),
544 (
545 "struct MyEvent {}",
546 "\n\n struct MyEvent {}",
547 r"
548mod contract {
549 struct MyContract {}
550
551 struct MyOtherEvent {}
552}",
553 Some("struct MyContract {}"),
554 Some("struct MyContract {}"),
555 ),
556 (
557 "struct MyEvent {}",
558 "\n\n struct MyEvent {}",
559 r"
560mod contract {
561 struct MyContract {}
562 struct MyOtherEvent {}
563}",
564 Some("struct MyContract {}"),
565 Some("struct MyContract {}"),
566 ),
567 (
568 "pub fn message(&self) {}",
569 "\n\n pub fn message(&self) {}",
570 r"
571mod contract {
572 impl MyContract {
573 pub fn constructor() {}
574 }
575}",
576 Some("pub fn constructor() {}"),
577 Some("pub fn constructor() {}"),
578 ),
579 (
580 "pub fn message(&self) {}",
581 "\n\n pub fn message(&self) {}",
582 r"
583mod contract {
584 impl MyContract {
585 pub fn constructor() {}
586
587 pub fn message2(&self) {}
588 }
589}",
590 Some("pub fn constructor() {}"),
591 Some("pub fn constructor() {}"),
592 ),
593 (
594 "pub fn message(&self) {}",
595 "\n\n pub fn message(&self) {}",
596 r"
597mod contract {
598 impl MyContract {
599 pub fn constructor() {}
600 pub fn message2(&self) {}
601 }
602}",
603 Some("pub fn constructor() {}"),
604 Some("pub fn constructor() {}"),
605 ),
606 (
608 "(env = crate::MyEnvironment)",
609 "(env = crate::MyEnvironment)",
610 r"
611#[ink::contract]
612mod contract {}",
613 Some("#[ink::contract"),
614 Some("#[ink::contract"),
615 ),
616 (
617 "env = crate::MyEnvironment",
618 "env = crate::MyEnvironment",
619 r"
620#[ink::contract()]
621mod contract {}",
622 Some("#[ink::contract("),
623 Some("#[ink::contract("),
624 ),
625 (
626 r#", keep_attr = "foo,bar""#,
627 r#", keep_attr = "foo,bar""#,
628 r"
629#[ink::contract(env = crate::MyEnvironment)]
630mod contract {}",
631 Some("#[ink::contract(env = crate::MyEnvironment"),
632 Some("#[ink::contract(env = crate::MyEnvironment"),
633 ),
634 (
635 "crate::MyEnvironment",
636 "crate::MyEnvironment",
637 r"
638#[ink::contract(env = self::MyEnvironment)]
639mod contract {}",
640 Some("#[ink::contract(env = "),
641 Some("#[ink::contract(env = self::MyEnvironment"),
642 ),
643 (
644 " crate::MyEnvironment",
645 " crate::MyEnvironment",
646 r"
647#[ink::contract(env = self::MyEnvironment)]
648mod contract {}",
649 Some("#[ink::contract(env ="),
650 Some("#[ink::contract(env = self::MyEnvironment"),
651 ),
652 (
653 "&self",
654 "&self",
655 "pub fn message() {}",
656 Some("pub fn message("),
657 Some("pub fn message("),
658 ),
659 (
660 ", status: bool",
661 ", status: bool",
662 "pub fn message(&self) {}",
663 Some("pub fn message(&self"),
664 Some("pub fn message(&self"),
665 ),
666 (
667 " status: bool",
668 " status: bool",
669 "pub fn message(&self,) {}",
670 Some("pub fn message(&self,"),
671 Some("pub fn message(&self,"),
672 ),
673 (
674 "status: bool",
675 "status: bool",
676 "pub fn message(&self, ) {}",
677 Some("pub fn message(&self, "),
678 Some("pub fn message(&self, "),
679 ),
680 (
681 " -> u8",
682 " -> u8",
683 "pub fn message(&self) {}",
684 Some("pub fn message(&self)"),
685 Some("pub fn message(&self)"),
686 ),
687 (
688 "-> u8",
689 "-> u8",
690 "pub fn message(&self) {}",
691 Some("pub fn message(&self) "),
692 Some("pub fn message(&self) "),
693 ),
694 ] {
695 let file = InkFile::parse(source);
696 let range = TextRange::new(
697 TextSize::from(parse_offset_at(source, start_pat).unwrap() as u32),
698 TextSize::from(parse_offset_at(source, end_pat).unwrap() as u32),
699 );
700 let edit = TextEdit {
701 text: input.to_owned(),
702 range,
703 snippet: None,
704 };
705 let result = format_edit(edit, &file);
706 let expected = TextEdit {
707 text: output.to_owned(),
708 range,
709 snippet: None,
710 };
711 assert_eq!(result, expected);
712 }
713 }
714
715 #[test]
716 fn format_delete_works() {
717 for (start_pat_input, end_pat_input, pat_range_output, source) in [
718 (
721 Some("<-#[ink::contract]"),
722 Some("#[ink::contract]"),
723 Some((Some("<-#[ink::contract]"), Some("<-mod contract {}"))),
724 "#[ink::contract]\nmod contract {}",
725 ),
726 (
727 Some("<-#[ink::contract]"),
728 Some("#[ink::contract]"),
729 Some((Some("<-#[ink::contract]"), Some("<-mod contract {}"))),
730 r"
731#[ink::contract]
732mod contract {}
733",
734 ),
735 (
736 Some("<-#[ink::contract]"),
737 Some("#[ink::contract]"),
738 Some((Some("<-#[ink::contract]"), Some("<-mod contract {}"))),
739 r"
740#[doc(hidden)]
741#[ink::contract]
742mod contract {}",
743 ),
744 (
745 Some("<-#[ink::contract]"),
746 Some("#[ink::contract]"),
747 Some((Some("<-#[ink::contract]"), Some("<-#[doc(hidden)]"))),
748 r"
749#[ink::contract]
750#[doc(hidden)]
751mod contract {}",
752 ),
753 (
754 Some("<-#[ink(storage)]"),
755 Some("#[ink(storage)]"),
756 Some((Some("<-#[ink(storage)]"), Some("<-struct MyContract {}"))),
757 r"
758mod contract {
759 #[ink(storage)]
760 struct MyContract {}
761}",
762 ),
763 (
764 Some("<-#[ink(topic)]"),
765 Some("#[ink(topic)]"),
766 Some((Some("<-#[ink(topic)]"), Some("<-status: bool,"))),
767 r"
768mod contract {
769 struct MyEvent {
770 #[ink(topic)]
771 status: bool,
772 }
773}",
774 ),
775 (
776 Some("<-#[ink(impl)]"),
777 Some("#[ink(impl)]"),
778 Some((Some("<-#[ink(impl)]"), Some("<-impl MyContract {}"))),
779 r"
780mod contract {
781 #[ink(impl)]
782 impl MyContract {}
783}",
784 ),
785 (
786 Some("<-#[ink(impl)]"),
787 Some("#[ink(impl)]"),
788 Some((
789 Some("<-#[ink(impl)]"),
790 Some(r#"<-#[ink(namespace = "my_namespace")]"#),
791 )),
792 r#"
793mod contract {
794 #[ink(impl)]
795 #[ink(namespace = "my_namespace")]
796 impl MyContract {}
797}"#,
798 ),
799 (
800 Some("<-#[ink(message)]"),
801 Some("#[ink(message)]"),
802 Some((
803 Some("<-#[ink(message)]"),
804 Some("<-pub fn message(&self) {}"),
805 )),
806 r"
807mod contract {
808 impl MyContract {
809 #[ink(message)]
810 pub fn message(&self) {}
811 }
812}",
813 ),
814 (
815 Some("<--> u8"),
816 Some("-> u8"),
817 Some((Some("<--> u8"), Some("-> u8 "))),
818 "pub fn message(&self) -> u8 {}",
819 ),
820 (
821 Some("<-struct MyEvent {}"),
822 Some("struct MyEvent {}"),
823 Some((
824 Some("<-struct MyEvent {}"),
825 Some("<-struct MyOtherEvent {}"),
826 )),
827 r"
828mod contract {
829 struct MyContract {}
830
831 struct MyEvent {}
832
833 struct MyOtherEvent {}
834}",
835 ),
836 (
837 Some("<-struct MyEvent {}"),
838 Some("struct MyEvent {}"),
839 Some((
840 Some("<-struct MyEvent {}"),
841 Some("<-struct MyOtherEvent {}"),
842 )),
843 r"
844mod contract {
845 struct MyContract {}
846 struct MyEvent {}
847 struct MyOtherEvent {}
848}",
849 ),
850 (
851 Some("<-pub fn message(&self) {}"),
852 Some("pub fn message(&self) {}"),
853 Some((
854 Some("<-pub fn message(&self) {}"),
855 Some("<-pub fn message2(&self) {}"),
856 )),
857 r"
858mod contract {
859 impl MyContract {
860 pub fn constructor() {}
861
862 pub fn message(&self) {}
863
864 pub fn message2(&self) {}
865 }
866}",
867 ),
868 (
869 Some("<-pub fn message(&self) {}"),
870 Some("pub fn message(&self) {}"),
871 Some((
872 Some("<-pub fn message(&self) {}"),
873 Some("<-pub fn message2(&self) {}"),
874 )),
875 r"
876mod contract {
877 impl MyContract {
878 pub fn constructor() {}
879 pub fn message(&self) {}
880 pub fn message2(&self) {}
881 }
882}",
883 ),
884 (
886 Some("<-struct MyContract {}"),
887 Some("struct MyContract {}"),
888 None,
889 r"
890mod contract {
891 struct MyContract {}
892}",
893 ),
894 (
895 Some("<-status: bool,"),
896 Some("status: bool,"),
897 None,
898 r"
899mod contract {
900 struct MyContract {
901 status: bool,
902 }
903}",
904 ),
905 (
906 Some("<-impl MyContract {}"),
907 Some("impl MyContract {}"),
908 None,
909 r"
910mod contract {
911 impl MyContract {}
912}",
913 ),
914 (
915 Some("<-pub fn message(&self) {}"),
916 Some("pub fn message(&self) {}"),
917 None,
918 r"
919mod contract {
920 impl MyContract {
921 pub fn message(&self) {}
922 }
923}",
924 ),
925 (
926 Some("<-struct MyEvent {}"),
927 Some("struct MyEvent {}"),
928 None,
929 r"
930mod contract {
931 struct MyContract {}
932
933 struct MyEvent {}
934}",
935 ),
936 (
937 Some("<-struct MyEvent {}"),
938 Some("struct MyEvent {}"),
939 None,
940 r"
941mod contract {
942 struct MyContract;
943
944 struct MyEvent {}
945}",
946 ),
947 (
948 Some("<-pub fn message(&self) {}"),
949 Some("pub fn message(&self) {}"),
950 None,
951 r"
952mod contract {
953 impl MyContract {
954 pub fn constructor() {}
955
956 pub fn message(&self) {}
957 }
958}",
959 ),
960 (
961 Some("<-(env = crate::MyEnvironment)"),
962 Some("(env = crate::MyEnvironment)"),
963 None,
964 r"
965#[ink::contract(env = crate::MyEnvironment)]
966mod contract {}",
967 ),
968 (
969 Some("<-env = crate::MyEnvironment"),
970 Some("env = crate::MyEnvironment"),
971 None,
972 r"
973#[ink::contract(env = crate::MyEnvironment)]
974mod contract {}",
975 ),
976 (
977 Some(r#"<-, keep_attr = "foo,bar""#),
978 Some(r#", keep_attr = "foo,bar""#),
979 None,
980 r#"
981#[ink::contract(env = crate::MyEnvironment, keep_attr = "foo,bar")]
982mod contract {}"#,
983 ),
984 (
985 Some("<-crate::MyEnvironment"),
986 Some("crate::MyEnvironment"),
987 None,
988 r"
989#[ink::contract(env = crate::MyEnvironment)]
990mod contract {}",
991 ),
992 (
993 Some("<- crate::MyEnvironment"),
994 Some(" crate::MyEnvironment"),
995 None,
996 r"
997#[ink::contract(env = crate::MyEnvironment)]
998mod contract {}",
999 ),
1000 (
1001 Some("<-&self"),
1002 Some("&self"),
1003 None,
1004 "pub fn message(&self) {}",
1005 ),
1006 (
1007 Some("<-, status: bool"),
1008 Some(", status: bool"),
1009 None,
1010 "pub fn message(&self, status: bool) {}",
1011 ),
1012 (
1013 Some("<- status: bool"),
1014 Some(" status: bool"),
1015 None,
1016 "pub fn message(&self, status: bool) {}",
1017 ),
1018 (
1019 Some("<-status: bool"),
1020 Some("status: bool"),
1021 None,
1022 "pub fn message(&self, status: bool) {}",
1023 ),
1024 (
1025 Some("<- -> u8"),
1026 Some(" -> u8"),
1027 None,
1028 "pub fn message(&self) -> u8 {}",
1029 ),
1030 ] {
1031 let file = InkFile::parse(source);
1032 let range_input = TextRange::new(
1033 TextSize::from(parse_offset_at(source, start_pat_input).unwrap() as u32),
1034 TextSize::from(parse_offset_at(source, end_pat_input).unwrap() as u32),
1035 );
1036 let range_output =
1037 pat_range_output.map_or(range_input, |(start_pat_output, end_pat_output)| {
1038 TextRange::new(
1039 TextSize::from(parse_offset_at(source, start_pat_output).unwrap() as u32),
1040 TextSize::from(parse_offset_at(source, end_pat_output).unwrap() as u32),
1041 )
1042 });
1043
1044 let edit = TextEdit {
1045 text: String::new(),
1046 range: range_input,
1047 snippet: None,
1048 };
1049 let result = format_edit(edit, &file);
1050 let expected = TextEdit {
1051 text: String::new(),
1052 range: range_output,
1053 snippet: None,
1054 };
1055 assert_eq!(result, expected);
1056 }
1057 }
1058}