@particle-academy/fancy-code 0.3.1 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -201,10 +201,12 @@ function RunButton() {
201
201
  | TypeScript | `ts`, `typescript`, `tsx` |
202
202
  | HTML | `html`, `htm` |
203
203
  | PHP | `php` |
204
+ | Python | `py`, `python` |
205
+ | Go | `go`, `golang` |
204
206
 
205
207
  ## Custom Language Registration
206
208
 
207
- Add languages beyond the four built-ins using `registerLanguage`:
209
+ Add languages beyond the built-ins using `registerLanguage`:
208
210
 
209
211
  ```tsx
210
212
  import { registerLanguage } from "@particle-academy/fancy-code";
package/dist/index.cjs CHANGED
@@ -662,6 +662,377 @@ var tokenizePhp = (source) => {
662
662
  return tokens;
663
663
  };
664
664
 
665
+ // src/engine/tokenizers/python.ts
666
+ var KEYWORDS2 = /* @__PURE__ */ new Set([
667
+ "and",
668
+ "as",
669
+ "assert",
670
+ "async",
671
+ "await",
672
+ "break",
673
+ "class",
674
+ "continue",
675
+ "def",
676
+ "del",
677
+ "elif",
678
+ "else",
679
+ "except",
680
+ "finally",
681
+ "for",
682
+ "from",
683
+ "global",
684
+ "if",
685
+ "import",
686
+ "in",
687
+ "is",
688
+ "lambda",
689
+ "nonlocal",
690
+ "not",
691
+ "or",
692
+ "pass",
693
+ "raise",
694
+ "return",
695
+ "try",
696
+ "while",
697
+ "with",
698
+ "yield",
699
+ "True",
700
+ "False",
701
+ "None"
702
+ ]);
703
+ var TYPES = /* @__PURE__ */ new Set([
704
+ "int",
705
+ "str",
706
+ "float",
707
+ "bool",
708
+ "list",
709
+ "dict",
710
+ "tuple",
711
+ "set",
712
+ "bytes",
713
+ "bytearray",
714
+ "memoryview",
715
+ "range",
716
+ "frozenset",
717
+ "complex",
718
+ "type",
719
+ "object",
720
+ "property",
721
+ "classmethod",
722
+ "staticmethod",
723
+ "Any",
724
+ "Optional",
725
+ "Union",
726
+ "Callable",
727
+ "List",
728
+ "Dict",
729
+ "Tuple",
730
+ "Set",
731
+ "Sequence",
732
+ "Mapping",
733
+ "Iterator",
734
+ "Generator",
735
+ "Coroutine"
736
+ ]);
737
+ var tokenizePython = (source) => {
738
+ const tokens = [];
739
+ const len = source.length;
740
+ let i = 0;
741
+ while (i < len) {
742
+ const ch = source[i];
743
+ if (ch === " " || ch === " " || ch === "\n" || ch === "\r") {
744
+ i++;
745
+ continue;
746
+ }
747
+ if (ch === "#") {
748
+ const pos = i;
749
+ i++;
750
+ while (i < len && source[i] !== "\n") i++;
751
+ tokens.push({ type: "comment", start: pos, end: i });
752
+ continue;
753
+ }
754
+ if (ch === "@" && i + 1 < len && /[a-zA-Z_]/.test(source[i + 1])) {
755
+ const pos = i;
756
+ i++;
757
+ while (i < len && /[a-zA-Z0-9_.]/.test(source[i])) i++;
758
+ tokens.push({ type: "keyword", start: pos, end: i });
759
+ continue;
760
+ }
761
+ if (ch === '"' || ch === "'" || (ch === "f" || ch === "r" || ch === "b" || ch === "F" || ch === "R" || ch === "B") && (source[i + 1] === '"' || source[i + 1] === "'")) {
762
+ const pos = i;
763
+ if (ch !== '"' && ch !== "'") i++;
764
+ const quote = source[i];
765
+ if (source[i + 1] === quote && source[i + 2] === quote) {
766
+ const triple = quote + quote + quote;
767
+ i += 3;
768
+ while (i < len && source.slice(i, i + 3) !== triple) {
769
+ if (source[i] === "\\") i++;
770
+ i++;
771
+ }
772
+ i += 3;
773
+ tokens.push({ type: "string", start: pos, end: i });
774
+ continue;
775
+ }
776
+ i++;
777
+ while (i < len && source[i] !== quote && source[i] !== "\n") {
778
+ if (source[i] === "\\") i++;
779
+ i++;
780
+ }
781
+ if (i < len && source[i] === quote) i++;
782
+ tokens.push({ type: "string", start: pos, end: i });
783
+ continue;
784
+ }
785
+ if (ch >= "0" && ch <= "9" || ch === "." && i + 1 < len && source[i + 1] >= "0" && source[i + 1] <= "9") {
786
+ const pos = i;
787
+ if (ch === "0" && (source[i + 1] === "x" || source[i + 1] === "X")) {
788
+ i += 2;
789
+ while (i < len && /[0-9a-fA-F_]/.test(source[i])) i++;
790
+ } else if (ch === "0" && (source[i + 1] === "b" || source[i + 1] === "B")) {
791
+ i += 2;
792
+ while (i < len && /[01_]/.test(source[i])) i++;
793
+ } else if (ch === "0" && (source[i + 1] === "o" || source[i + 1] === "O")) {
794
+ i += 2;
795
+ while (i < len && /[0-7_]/.test(source[i])) i++;
796
+ } else {
797
+ while (i < len && /[0-9_.]/.test(source[i])) i++;
798
+ if (i < len && (source[i] === "e" || source[i] === "E")) {
799
+ i++;
800
+ if (i < len && (source[i] === "+" || source[i] === "-")) i++;
801
+ while (i < len && source[i] >= "0" && source[i] <= "9") i++;
802
+ }
803
+ }
804
+ if (i < len && source[i] === "j") i++;
805
+ tokens.push({ type: "number", start: pos, end: i });
806
+ continue;
807
+ }
808
+ if (/[a-zA-Z_]/.test(ch)) {
809
+ const pos = i;
810
+ i++;
811
+ while (i < len && /[a-zA-Z0-9_]/.test(source[i])) i++;
812
+ const word = source.slice(pos, i);
813
+ let j = i;
814
+ while (j < len && (source[j] === " " || source[j] === " ")) j++;
815
+ if (KEYWORDS2.has(word)) {
816
+ tokens.push({ type: "keyword", start: pos, end: i });
817
+ } else if (TYPES.has(word)) {
818
+ tokens.push({ type: "type", start: pos, end: i });
819
+ } else if (source[j] === "(") {
820
+ tokens.push({ type: "function", start: pos, end: i });
821
+ } else if (word[0] >= "A" && word[0] <= "Z") {
822
+ tokens.push({ type: "type", start: pos, end: i });
823
+ } else {
824
+ tokens.push({ type: "variable", start: pos, end: i });
825
+ }
826
+ continue;
827
+ }
828
+ if ("+-*/%=<>!&|^~:@".includes(ch)) {
829
+ const pos = i;
830
+ i++;
831
+ while (i < len && "+-*/%=<>!&|^~:".includes(source[i])) i++;
832
+ tokens.push({ type: "operator", start: pos, end: i });
833
+ continue;
834
+ }
835
+ if ("()[]{},.;\\".includes(ch)) {
836
+ tokens.push({ type: "punctuation", start: i, end: i + 1 });
837
+ i++;
838
+ continue;
839
+ }
840
+ tokens.push({ type: "plain", start: i, end: i + 1 });
841
+ i++;
842
+ }
843
+ return tokens;
844
+ };
845
+
846
+ // src/engine/tokenizers/go.ts
847
+ var KEYWORDS3 = /* @__PURE__ */ new Set([
848
+ "break",
849
+ "case",
850
+ "chan",
851
+ "const",
852
+ "continue",
853
+ "default",
854
+ "defer",
855
+ "else",
856
+ "fallthrough",
857
+ "for",
858
+ "func",
859
+ "go",
860
+ "goto",
861
+ "if",
862
+ "import",
863
+ "interface",
864
+ "map",
865
+ "package",
866
+ "range",
867
+ "return",
868
+ "select",
869
+ "struct",
870
+ "switch",
871
+ "type",
872
+ "var",
873
+ "nil",
874
+ "true",
875
+ "false",
876
+ "iota"
877
+ ]);
878
+ var TYPES2 = /* @__PURE__ */ new Set([
879
+ "bool",
880
+ "byte",
881
+ "complex64",
882
+ "complex128",
883
+ "error",
884
+ "float32",
885
+ "float64",
886
+ "int",
887
+ "int8",
888
+ "int16",
889
+ "int32",
890
+ "int64",
891
+ "rune",
892
+ "string",
893
+ "uint",
894
+ "uint8",
895
+ "uint16",
896
+ "uint32",
897
+ "uint64",
898
+ "uintptr",
899
+ "any"
900
+ ]);
901
+ var BUILTINS = /* @__PURE__ */ new Set([
902
+ "make",
903
+ "len",
904
+ "cap",
905
+ "new",
906
+ "append",
907
+ "copy",
908
+ "close",
909
+ "delete",
910
+ "complex",
911
+ "real",
912
+ "imag",
913
+ "panic",
914
+ "recover",
915
+ "print",
916
+ "println"
917
+ ]);
918
+ var tokenizeGo = (source) => {
919
+ const tokens = [];
920
+ const len = source.length;
921
+ let i = 0;
922
+ while (i < len) {
923
+ const ch = source[i];
924
+ if (ch === " " || ch === " " || ch === "\n" || ch === "\r") {
925
+ i++;
926
+ continue;
927
+ }
928
+ if (ch === "/" && source[i + 1] === "/") {
929
+ const pos = i;
930
+ i += 2;
931
+ while (i < len && source[i] !== "\n") i++;
932
+ tokens.push({ type: "comment", start: pos, end: i });
933
+ continue;
934
+ }
935
+ if (ch === "/" && source[i + 1] === "*") {
936
+ const pos = i;
937
+ i += 2;
938
+ while (i < len && !(source[i] === "*" && source[i + 1] === "/")) i++;
939
+ i += 2;
940
+ tokens.push({ type: "comment", start: pos, end: i });
941
+ continue;
942
+ }
943
+ if (ch === "`") {
944
+ const pos = i;
945
+ i++;
946
+ while (i < len && source[i] !== "`") i++;
947
+ i++;
948
+ tokens.push({ type: "string", start: pos, end: i });
949
+ continue;
950
+ }
951
+ if (ch === '"') {
952
+ const pos = i;
953
+ i++;
954
+ while (i < len && source[i] !== '"' && source[i] !== "\n") {
955
+ if (source[i] === "\\") i++;
956
+ i++;
957
+ }
958
+ if (i < len && source[i] === '"') i++;
959
+ tokens.push({ type: "string", start: pos, end: i });
960
+ continue;
961
+ }
962
+ if (ch === "'") {
963
+ const pos = i;
964
+ i++;
965
+ while (i < len && source[i] !== "'" && source[i] !== "\n") {
966
+ if (source[i] === "\\") i++;
967
+ i++;
968
+ }
969
+ if (i < len && source[i] === "'") i++;
970
+ tokens.push({ type: "string", start: pos, end: i });
971
+ continue;
972
+ }
973
+ if (ch >= "0" && ch <= "9" || ch === "." && i + 1 < len && source[i + 1] >= "0" && source[i + 1] <= "9") {
974
+ const pos = i;
975
+ if (ch === "0" && (source[i + 1] === "x" || source[i + 1] === "X")) {
976
+ i += 2;
977
+ while (i < len && /[0-9a-fA-F_]/.test(source[i])) i++;
978
+ } else if (ch === "0" && (source[i + 1] === "b" || source[i + 1] === "B")) {
979
+ i += 2;
980
+ while (i < len && /[01_]/.test(source[i])) i++;
981
+ } else if (ch === "0" && (source[i + 1] === "o" || source[i + 1] === "O")) {
982
+ i += 2;
983
+ while (i < len && /[0-7_]/.test(source[i])) i++;
984
+ } else {
985
+ while (i < len && /[0-9_.]/.test(source[i])) i++;
986
+ if (i < len && (source[i] === "e" || source[i] === "E")) {
987
+ i++;
988
+ if (i < len && (source[i] === "+" || source[i] === "-")) i++;
989
+ while (i < len && source[i] >= "0" && source[i] <= "9") i++;
990
+ }
991
+ }
992
+ if (i < len && source[i] === "i") i++;
993
+ tokens.push({ type: "number", start: pos, end: i });
994
+ continue;
995
+ }
996
+ if (/[a-zA-Z_]/.test(ch)) {
997
+ const pos = i;
998
+ i++;
999
+ while (i < len && /[a-zA-Z0-9_]/.test(source[i])) i++;
1000
+ const word = source.slice(pos, i);
1001
+ let j = i;
1002
+ while (j < len && (source[j] === " " || source[j] === " ")) j++;
1003
+ if (KEYWORDS3.has(word)) {
1004
+ tokens.push({ type: "keyword", start: pos, end: i });
1005
+ } else if (TYPES2.has(word)) {
1006
+ tokens.push({ type: "type", start: pos, end: i });
1007
+ } else if (BUILTINS.has(word) && source[j] === "(") {
1008
+ tokens.push({ type: "function", start: pos, end: i });
1009
+ } else if (source[j] === "(") {
1010
+ tokens.push({ type: "function", start: pos, end: i });
1011
+ } else if (word[0] >= "A" && word[0] <= "Z") {
1012
+ tokens.push({ type: "type", start: pos, end: i });
1013
+ } else {
1014
+ tokens.push({ type: "variable", start: pos, end: i });
1015
+ }
1016
+ continue;
1017
+ }
1018
+ if ("+-*/%=<>!&|^~:".includes(ch)) {
1019
+ const pos = i;
1020
+ i++;
1021
+ while (i < len && "+-*/%=<>!&|^~:".includes(source[i])) i++;
1022
+ tokens.push({ type: "operator", start: pos, end: i });
1023
+ continue;
1024
+ }
1025
+ if ("(){}[];,.".includes(ch)) {
1026
+ tokens.push({ type: "punctuation", start: i, end: i + 1 });
1027
+ i++;
1028
+ continue;
1029
+ }
1030
+ tokens.push({ type: "plain", start: i, end: i + 1 });
1031
+ i++;
1032
+ }
1033
+ return tokens;
1034
+ };
1035
+
665
1036
  // src/languages/builtin.ts
666
1037
  registerLanguage({
667
1038
  name: "JavaScript",
@@ -684,6 +1055,16 @@ registerLanguage({
684
1055
  aliases: ["php"],
685
1056
  tokenize: tokenizePhp
686
1057
  });
1058
+ registerLanguage({
1059
+ name: "Python",
1060
+ aliases: ["py", "python"],
1061
+ tokenize: tokenizePython
1062
+ });
1063
+ registerLanguage({
1064
+ name: "Go",
1065
+ aliases: ["go", "golang"],
1066
+ tokenize: tokenizeGo
1067
+ });
687
1068
  var iconBtnClass = "inline-flex items-center justify-center rounded-md p-1 text-zinc-500 transition-colors hover:bg-zinc-100 hover:text-zinc-700 dark:text-zinc-400 dark:hover:bg-zinc-800 dark:hover:text-zinc-200";
688
1069
  function LanguageSelector() {
689
1070
  const { language, setLanguage } = useCodeEditor();