@@ -633,31 +633,378 @@ mod parser {
633
633
parse( "macro_rules! $1 {\n ($2) => {\n $0\n };\n }" )
634
634
) ;
635
635
}
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
+
636
643
#[ test]
637
644
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" ,
648
668
) ;
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
+ } ] ,
660
1006
) ;
661
1007
}
1008
+ // TODO port more tests from https://github.com/microsoft/vscode/blob/dce493cb6e36346ef2714e82c42ce14fc461b15c/src/vs/editor/contrib/snippet/test/browser/snippetParser.test.ts
662
1009
}
663
1010
}
0 commit comments