Skip to content

Commit f9a25a7

Browse files
pascalkuthearchseer
authored andcommitted
detailed snippet tests
1 parent 85c9dbf commit f9a25a7

File tree

1 file changed

+368
-21
lines changed

1 file changed

+368
-21
lines changed

helix-lsp/src/snippet.rs

+368-21
Original file line numberDiff line numberDiff line change
@@ -633,31 +633,378 @@ mod parser {
633633
parse("macro_rules! $1 {\n ($2) => {\n $0\n };\n}")
634634
);
635635
}
636+
637+
fn assert_text(snippet: &str, parsed_text: &str) {
638+
let res = parse(snippet).unwrap();
639+
let text = crate::snippet::render(&res, "\n", true).0;
640+
assert_eq!(text, parsed_text)
641+
}
642+
636643
#[test]
637644
fn robust_parsing() {
638-
assert_eq!(
639-
Ok(Snippet {
640-
elements: vec![
641-
Text("$".into()),
642-
Text("{}".into()),
643-
Text("$".into()),
644-
Text("\\a$}\\".into()),
645-
]
646-
}),
647-
parse("${}$\\a\\$\\}\\\\")
645+
assert_text("$", "$");
646+
assert_text("\\\\$", "\\$");
647+
assert_text("{", "{");
648+
assert_text("\\}", "}");
649+
assert_text("\\abc", "\\abc");
650+
assert_text("foo${f:\\}}bar", "foo}bar");
651+
assert_text("\\{", "\\{");
652+
assert_text("I need \\\\\\$", "I need \\$");
653+
assert_text("\\", "\\");
654+
assert_text("\\{{", "\\{{");
655+
assert_text("{{", "{{");
656+
assert_text("{{dd", "{{dd");
657+
assert_text("}}", "}}");
658+
assert_text("ff}}", "ff}}");
659+
assert_text("farboo", "farboo");
660+
assert_text("far{{}}boo", "far{{}}boo");
661+
assert_text("far{{123}}boo", "far{{123}}boo");
662+
assert_text("far\\{{123}}boo", "far\\{{123}}boo");
663+
assert_text("far{{id:bern}}boo", "far{{id:bern}}boo");
664+
assert_text("far{{id:bern {{basel}}}}boo", "far{{id:bern {{basel}}}}boo");
665+
assert_text(
666+
"far{{id:bern {{id:basel}}}}boo",
667+
"far{{id:bern {{id:basel}}}}boo",
648668
);
649-
assert_eq!(
650-
Ok(Snippet {
651-
elements: vec![
652-
Placeholder {
653-
tabstop: 1,
654-
value: vec![Text("$".into()), Text("{".into())]
655-
},
656-
Text("}".into())
657-
]
658-
}),
659-
parse("${1:${}}")
669+
assert_text(
670+
"far{{id:bern {{id2:basel}}}}boo",
671+
"far{{id:bern {{id2:basel}}}}boo",
672+
);
673+
assert_text("${}$\\a\\$\\}\\\\", "${}$\\a$}\\");
674+
assert_text("farboo", "farboo");
675+
assert_text("far{{}}boo", "far{{}}boo");
676+
assert_text("far{{123}}boo", "far{{123}}boo");
677+
assert_text("far\\{{123}}boo", "far\\{{123}}boo");
678+
assert_text("far`123`boo", "far`123`boo");
679+
assert_text("far\\`123\\`boo", "far\\`123\\`boo");
680+
assert_text("\\$far-boo", "$far-boo");
681+
}
682+
683+
fn assert_snippet(snippet: &str, expect: &[SnippetElement]) {
684+
let parsed_snippet = parse(snippet).unwrap();
685+
assert_eq!(parsed_snippet.elements, expect.to_owned())
686+
}
687+
688+
#[test]
689+
fn parse_variable() {
690+
use SnippetElement::*;
691+
assert_snippet(
692+
"$far-boo",
693+
&[
694+
Variable {
695+
name: "far",
696+
default: None,
697+
regex: None,
698+
},
699+
Text("-boo".into()),
700+
],
701+
);
702+
assert_snippet(
703+
"far$farboo",
704+
&[
705+
Text("far".into()),
706+
Variable {
707+
name: "farboo",
708+
regex: None,
709+
default: None,
710+
},
711+
],
712+
);
713+
assert_snippet(
714+
"far${farboo}",
715+
&[
716+
Text("far".into()),
717+
Variable {
718+
name: "farboo",
719+
regex: None,
720+
default: None,
721+
},
722+
],
723+
);
724+
assert_snippet("$123", &[Tabstop { tabstop: 123 }]);
725+
assert_snippet(
726+
"$farboo",
727+
&[Variable {
728+
name: "farboo",
729+
regex: None,
730+
default: None,
731+
}],
732+
);
733+
assert_snippet(
734+
"$far12boo",
735+
&[Variable {
736+
name: "far12boo",
737+
regex: None,
738+
default: None,
739+
}],
740+
);
741+
assert_snippet(
742+
"000_${far}_000",
743+
&[
744+
Text("000_".into()),
745+
Variable {
746+
name: "far",
747+
regex: None,
748+
default: None,
749+
},
750+
Text("_000".into()),
751+
],
752+
);
753+
}
754+
755+
#[test]
756+
fn parse_variable_transform() {
757+
assert_snippet(
758+
"${foo///}",
759+
&[Variable {
760+
name: "foo",
761+
regex: Some(Regex {
762+
value: Tendril::new(),
763+
replacement: Vec::new(),
764+
options: Tendril::new(),
765+
}),
766+
default: None,
767+
}],
768+
);
769+
assert_snippet(
770+
"${foo/regex/format/gmi}",
771+
&[Variable {
772+
name: "foo",
773+
regex: Some(Regex {
774+
value: "regex".into(),
775+
replacement: vec![FormatItem::Text("format".into())],
776+
options: "gmi".into(),
777+
}),
778+
default: None,
779+
}],
780+
);
781+
assert_snippet(
782+
"${foo/([A-Z][a-z])/format/}",
783+
&[Variable {
784+
name: "foo",
785+
regex: Some(Regex {
786+
value: "([A-Z][a-z])".into(),
787+
replacement: vec![FormatItem::Text("format".into())],
788+
options: Tendril::new(),
789+
}),
790+
default: None,
791+
}],
792+
);
793+
794+
// invalid regex TODO: reneable tests once we actually parse this regex flavour
795+
// assert_text(
796+
// "${foo/([A-Z][a-z])/format/GMI}",
797+
// "${foo/([A-Z][a-z])/format/GMI}",
798+
// );
799+
// assert_text(
800+
// "${foo/([A-Z][a-z])/format/funky}",
801+
// "${foo/([A-Z][a-z])/format/funky}",
802+
// );
803+
// assert_text("${foo/([A-Z][a-z]/format/}", "${foo/([A-Z][a-z]/format/}");
804+
assert_text(
805+
"${foo/regex\\/format/options}",
806+
"${foo/regex\\/format/options}",
807+
);
808+
809+
// tricky regex
810+
assert_snippet(
811+
"${foo/m\\/atch/$1/i}",
812+
&[Variable {
813+
name: "foo",
814+
regex: Some(Regex {
815+
value: "m/atch".into(),
816+
replacement: vec![FormatItem::Capture(1)],
817+
options: "i".into(),
818+
}),
819+
default: None,
820+
}],
821+
);
822+
823+
// incomplete
824+
assert_text("${foo///", "${foo///");
825+
assert_text("${foo/regex/format/options", "${foo/regex/format/options");
826+
827+
// format string
828+
assert_snippet(
829+
"${foo/.*/${0:fooo}/i}",
830+
&[Variable {
831+
name: "foo",
832+
regex: Some(Regex {
833+
value: ".*".into(),
834+
replacement: vec![FormatItem::Conditional(0, None, Some("fooo".into()))],
835+
options: "i".into(),
836+
}),
837+
default: None,
838+
}],
839+
);
840+
assert_snippet(
841+
"${foo/.*/${1}/i}",
842+
&[Variable {
843+
name: "foo",
844+
regex: Some(Regex {
845+
value: ".*".into(),
846+
replacement: vec![FormatItem::Capture(1)],
847+
options: "i".into(),
848+
}),
849+
default: None,
850+
}],
851+
);
852+
assert_snippet(
853+
"${foo/.*/$1/i}",
854+
&[Variable {
855+
name: "foo",
856+
regex: Some(Regex {
857+
value: ".*".into(),
858+
replacement: vec![FormatItem::Capture(1)],
859+
options: "i".into(),
860+
}),
861+
default: None,
862+
}],
863+
);
864+
assert_snippet(
865+
"${foo/.*/This-$1-encloses/i}",
866+
&[Variable {
867+
name: "foo",
868+
regex: Some(Regex {
869+
value: ".*".into(),
870+
replacement: vec![
871+
FormatItem::Text("This-".into()),
872+
FormatItem::Capture(1),
873+
FormatItem::Text("-encloses".into()),
874+
],
875+
options: "i".into(),
876+
}),
877+
default: None,
878+
}],
879+
);
880+
assert_snippet(
881+
"${foo/.*/complex${1:else}/i}",
882+
&[Variable {
883+
name: "foo",
884+
regex: Some(Regex {
885+
value: ".*".into(),
886+
replacement: vec![
887+
FormatItem::Text("complex".into()),
888+
FormatItem::Conditional(1, None, Some("else".into())),
889+
],
890+
options: "i".into(),
891+
}),
892+
default: None,
893+
}],
894+
);
895+
assert_snippet(
896+
"${foo/.*/complex${1:-else}/i}",
897+
&[Variable {
898+
name: "foo",
899+
regex: Some(Regex {
900+
value: ".*".into(),
901+
replacement: vec![
902+
FormatItem::Text("complex".into()),
903+
FormatItem::Conditional(1, None, Some("else".into())),
904+
],
905+
options: "i".into(),
906+
}),
907+
default: None,
908+
}],
909+
);
910+
assert_snippet(
911+
"${foo/.*/complex${1:+if}/i}",
912+
&[Variable {
913+
name: "foo",
914+
regex: Some(Regex {
915+
value: ".*".into(),
916+
replacement: vec![
917+
FormatItem::Text("complex".into()),
918+
FormatItem::Conditional(1, Some("if".into()), None),
919+
],
920+
options: "i".into(),
921+
}),
922+
default: None,
923+
}],
924+
);
925+
assert_snippet(
926+
"${foo/.*/complex${1:?if:else}/i}",
927+
&[Variable {
928+
name: "foo",
929+
regex: Some(Regex {
930+
value: ".*".into(),
931+
replacement: vec![
932+
FormatItem::Text("complex".into()),
933+
FormatItem::Conditional(1, Some("if".into()), Some("else".into())),
934+
],
935+
options: "i".into(),
936+
}),
937+
default: None,
938+
}],
939+
);
940+
assert_snippet(
941+
"${foo/.*/complex${1:/upcase}/i}",
942+
&[Variable {
943+
name: "foo",
944+
regex: Some(Regex {
945+
value: ".*".into(),
946+
replacement: vec![
947+
FormatItem::Text("complex".into()),
948+
FormatItem::CaseChange(1, CaseChange::Upcase),
949+
],
950+
options: "i".into(),
951+
}),
952+
default: None,
953+
}],
954+
);
955+
assert_snippet(
956+
"${TM_DIRECTORY/src\\//$1/}",
957+
&[Variable {
958+
name: "TM_DIRECTORY",
959+
regex: Some(Regex {
960+
value: "src/".into(),
961+
replacement: vec![FormatItem::Capture(1)],
962+
options: Tendril::new(),
963+
}),
964+
default: None,
965+
}],
966+
);
967+
assert_snippet(
968+
"${TM_SELECTED_TEXT/a/\\/$1/g}",
969+
&[Variable {
970+
name: "TM_SELECTED_TEXT",
971+
regex: Some(Regex {
972+
value: "a".into(),
973+
replacement: vec![FormatItem::Text("/".into()), FormatItem::Capture(1)],
974+
options: "g".into(),
975+
}),
976+
default: None,
977+
}],
978+
);
979+
assert_snippet(
980+
"${TM_SELECTED_TEXT/a/in\\/$1ner/g}",
981+
&[Variable {
982+
name: "TM_SELECTED_TEXT",
983+
regex: Some(Regex {
984+
value: "a".into(),
985+
replacement: vec![
986+
FormatItem::Text("in/".into()),
987+
FormatItem::Capture(1),
988+
FormatItem::Text("ner".into()),
989+
],
990+
options: "g".into(),
991+
}),
992+
default: None,
993+
}],
994+
);
995+
assert_snippet(
996+
"${TM_SELECTED_TEXT/a/end\\//g}",
997+
&[Variable {
998+
name: "TM_SELECTED_TEXT",
999+
regex: Some(Regex {
1000+
value: "a".into(),
1001+
replacement: vec![FormatItem::Text("end/".into())],
1002+
options: "g".into(),
1003+
}),
1004+
default: None,
1005+
}],
6601006
);
6611007
}
1008+
// TODO port more tests from https://github.com/microsoft/vscode/blob/dce493cb6e36346ef2714e82c42ce14fc461b15c/src/vs/editor/contrib/snippet/test/browser/snippetParser.test.ts
6621009
}
6631010
}

0 commit comments

Comments
 (0)