1#[cfg(any(
10 feature = "syntax-rust",
11 feature = "syntax-python",
12 feature = "syntax-javascript",
13 feature = "syntax-typescript",
14 feature = "syntax-go",
15 feature = "syntax-bash",
16 feature = "syntax-json",
17 feature = "syntax-toml",
18 feature = "syntax-c",
19 feature = "syntax-cpp",
20 feature = "syntax-java",
21 feature = "syntax-ruby",
22 feature = "syntax-css",
23 feature = "syntax-html",
24 feature = "syntax-yaml",
25))]
26use crate::style::Color;
27use crate::style::{Style, Theme};
28
29#[cfg(any(
34 feature = "syntax-rust",
35 feature = "syntax-python",
36 feature = "syntax-javascript",
37 feature = "syntax-typescript",
38 feature = "syntax-go",
39 feature = "syntax-bash",
40 feature = "syntax-json",
41 feature = "syntax-toml",
42 feature = "syntax-c",
43 feature = "syntax-cpp",
44 feature = "syntax-java",
45 feature = "syntax-ruby",
46 feature = "syntax-css",
47 feature = "syntax-html",
48 feature = "syntax-yaml",
49))]
50const HIGHLIGHT_NAMES: &[&str] = &[
51 "attribute",
52 "comment",
53 "constant",
54 "constant.builtin",
55 "constructor",
56 "embedded",
57 "function",
58 "function.builtin",
59 "function.macro",
60 "keyword",
61 "module",
62 "number",
63 "operator",
64 "property",
65 "property.builtin",
66 "punctuation",
67 "punctuation.bracket",
68 "punctuation.delimiter",
69 "punctuation.special",
70 "string",
71 "string.special",
72 "tag",
73 "type",
74 "type.builtin",
75 "variable",
76 "variable.builtin",
77 "variable.parameter",
78];
79
80#[cfg(any(
81 feature = "syntax-rust",
82 feature = "syntax-python",
83 feature = "syntax-javascript",
84 feature = "syntax-typescript",
85 feature = "syntax-go",
86 feature = "syntax-bash",
87 feature = "syntax-json",
88 feature = "syntax-toml",
89 feature = "syntax-c",
90 feature = "syntax-cpp",
91 feature = "syntax-java",
92 feature = "syntax-ruby",
93 feature = "syntax-css",
94 feature = "syntax-html",
95 feature = "syntax-yaml",
96))]
97use std::sync::OnceLock;
98
99#[cfg(any(
100 feature = "syntax-rust",
101 feature = "syntax-python",
102 feature = "syntax-javascript",
103 feature = "syntax-typescript",
104 feature = "syntax-go",
105 feature = "syntax-bash",
106 feature = "syntax-json",
107 feature = "syntax-toml",
108 feature = "syntax-c",
109 feature = "syntax-cpp",
110 feature = "syntax-java",
111 feature = "syntax-ruby",
112 feature = "syntax-css",
113 feature = "syntax-html",
114 feature = "syntax-yaml",
115))]
116use tree_sitter_highlight::HighlightConfiguration;
117
118#[cfg(any(
121 feature = "syntax-rust",
122 feature = "syntax-python",
123 feature = "syntax-javascript",
124 feature = "syntax-typescript",
125 feature = "syntax-go",
126 feature = "syntax-bash",
127 feature = "syntax-json",
128 feature = "syntax-toml",
129 feature = "syntax-c",
130 feature = "syntax-cpp",
131 feature = "syntax-java",
132 feature = "syntax-ruby",
133 feature = "syntax-css",
134 feature = "syntax-html",
135 feature = "syntax-yaml",
136))]
137fn get_config(lang: &str) -> Option<&'static HighlightConfiguration> {
138 match lang {
139 #[cfg(feature = "syntax-rust")]
140 "rust" | "rs" => {
141 static CFG: OnceLock<Option<HighlightConfiguration>> = OnceLock::new();
142 CFG.get_or_init(|| {
143 HighlightConfiguration::new(
144 tree_sitter_rust::LANGUAGE.into(),
145 "rust",
146 tree_sitter_rust::HIGHLIGHTS_QUERY,
147 tree_sitter_rust::INJECTIONS_QUERY,
148 "",
149 )
150 .ok()
151 .map(|mut c| {
152 c.configure(HIGHLIGHT_NAMES);
153 c
154 })
155 })
156 .as_ref()
157 }
158
159 #[cfg(feature = "syntax-python")]
160 "python" | "py" => {
161 static CFG: OnceLock<Option<HighlightConfiguration>> = OnceLock::new();
162 CFG.get_or_init(|| {
163 HighlightConfiguration::new(
164 tree_sitter_python::LANGUAGE.into(),
165 "python",
166 tree_sitter_python::HIGHLIGHTS_QUERY,
167 "",
168 "",
169 )
170 .ok()
171 .map(|mut c| {
172 c.configure(HIGHLIGHT_NAMES);
173 c
174 })
175 })
176 .as_ref()
177 }
178
179 #[cfg(feature = "syntax-javascript")]
180 "javascript" | "js" | "jsx" => {
181 static CFG: OnceLock<Option<HighlightConfiguration>> = OnceLock::new();
182 CFG.get_or_init(|| {
183 HighlightConfiguration::new(
184 tree_sitter_javascript::LANGUAGE.into(),
185 "javascript",
186 tree_sitter_javascript::HIGHLIGHT_QUERY,
187 tree_sitter_javascript::INJECTIONS_QUERY,
188 tree_sitter_javascript::LOCALS_QUERY,
189 )
190 .ok()
191 .map(|mut c| {
192 c.configure(HIGHLIGHT_NAMES);
193 c
194 })
195 })
196 .as_ref()
197 }
198
199 #[cfg(feature = "syntax-go")]
200 "go" | "golang" => {
201 static CFG: OnceLock<Option<HighlightConfiguration>> = OnceLock::new();
202 CFG.get_or_init(|| {
203 HighlightConfiguration::new(
204 tree_sitter_go::LANGUAGE.into(),
205 "go",
206 tree_sitter_go::HIGHLIGHTS_QUERY,
207 "",
208 "",
209 )
210 .ok()
211 .map(|mut c| {
212 c.configure(HIGHLIGHT_NAMES);
213 c
214 })
215 })
216 .as_ref()
217 }
218
219 #[cfg(feature = "syntax-bash")]
220 "bash" | "sh" | "shell" | "zsh" => {
221 static CFG: OnceLock<Option<HighlightConfiguration>> = OnceLock::new();
222 CFG.get_or_init(|| {
223 HighlightConfiguration::new(
224 tree_sitter_bash::LANGUAGE.into(),
225 "bash",
226 tree_sitter_bash::HIGHLIGHT_QUERY,
227 "",
228 "",
229 )
230 .ok()
231 .map(|mut c| {
232 c.configure(HIGHLIGHT_NAMES);
233 c
234 })
235 })
236 .as_ref()
237 }
238
239 #[cfg(feature = "syntax-json")]
240 "json" | "jsonc" => {
241 static CFG: OnceLock<Option<HighlightConfiguration>> = OnceLock::new();
242 CFG.get_or_init(|| {
243 HighlightConfiguration::new(
244 tree_sitter_json::LANGUAGE.into(),
245 "json",
246 tree_sitter_json::HIGHLIGHTS_QUERY,
247 "",
248 "",
249 )
250 .ok()
251 .map(|mut c| {
252 c.configure(HIGHLIGHT_NAMES);
253 c
254 })
255 })
256 .as_ref()
257 }
258
259 #[cfg(feature = "syntax-toml")]
260 "toml" => {
261 static CFG: OnceLock<Option<HighlightConfiguration>> = OnceLock::new();
262 CFG.get_or_init(|| {
263 HighlightConfiguration::new(
264 tree_sitter_toml_ng::LANGUAGE.into(),
265 "toml",
266 tree_sitter_toml_ng::HIGHLIGHTS_QUERY,
267 "",
268 "",
269 )
270 .ok()
271 .map(|mut c| {
272 c.configure(HIGHLIGHT_NAMES);
273 c
274 })
275 })
276 .as_ref()
277 }
278
279 #[cfg(feature = "syntax-c")]
280 "c" | "h" => {
281 static CFG: OnceLock<Option<HighlightConfiguration>> = OnceLock::new();
282 CFG.get_or_init(|| {
283 HighlightConfiguration::new(
284 tree_sitter_c::LANGUAGE.into(),
285 "c",
286 tree_sitter_c::HIGHLIGHT_QUERY,
287 "",
288 "",
289 )
290 .ok()
291 .map(|mut c| {
292 c.configure(HIGHLIGHT_NAMES);
293 c
294 })
295 })
296 .as_ref()
297 }
298
299 #[cfg(feature = "syntax-cpp")]
300 "cpp" | "c++" | "cxx" | "cc" | "hpp" => {
301 static CFG: OnceLock<Option<HighlightConfiguration>> = OnceLock::new();
302 CFG.get_or_init(|| {
303 #[cfg(feature = "syntax-c")]
304 let highlights = {
305 let mut combined = String::with_capacity(
306 tree_sitter_c::HIGHLIGHT_QUERY.len()
307 + tree_sitter_cpp::HIGHLIGHT_QUERY.len()
308 + 1,
309 );
310 combined.push_str(tree_sitter_c::HIGHLIGHT_QUERY);
311 combined.push('\n');
312 combined.push_str(tree_sitter_cpp::HIGHLIGHT_QUERY);
313 combined
314 };
315 #[cfg(not(feature = "syntax-c"))]
316 let highlights = tree_sitter_cpp::HIGHLIGHT_QUERY.to_string();
317
318 HighlightConfiguration::new(
319 tree_sitter_cpp::LANGUAGE.into(),
320 "cpp",
321 &highlights,
322 "",
323 "",
324 )
325 .ok()
326 .map(|mut c| {
327 c.configure(HIGHLIGHT_NAMES);
328 c
329 })
330 })
331 .as_ref()
332 }
333
334 #[cfg(feature = "syntax-typescript")]
335 "typescript" | "ts" => {
336 static CFG: OnceLock<Option<HighlightConfiguration>> = OnceLock::new();
337 CFG.get_or_init(|| {
338 HighlightConfiguration::new(
339 tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into(),
340 "typescript",
341 tree_sitter_typescript::HIGHLIGHTS_QUERY,
342 tree_sitter_typescript::LOCALS_QUERY,
343 "",
344 )
345 .ok()
346 .map(|mut c| {
347 c.configure(HIGHLIGHT_NAMES);
348 c
349 })
350 })
351 .as_ref()
352 }
353
354 #[cfg(feature = "syntax-typescript")]
355 "tsx" => {
356 static CFG: OnceLock<Option<HighlightConfiguration>> = OnceLock::new();
357 CFG.get_or_init(|| {
358 HighlightConfiguration::new(
359 tree_sitter_typescript::LANGUAGE_TSX.into(),
360 "tsx",
361 tree_sitter_typescript::HIGHLIGHTS_QUERY,
362 tree_sitter_typescript::LOCALS_QUERY,
363 "",
364 )
365 .ok()
366 .map(|mut c| {
367 c.configure(HIGHLIGHT_NAMES);
368 c
369 })
370 })
371 .as_ref()
372 }
373
374 #[cfg(feature = "syntax-java")]
375 "java" => {
376 static CFG: OnceLock<Option<HighlightConfiguration>> = OnceLock::new();
377 CFG.get_or_init(|| {
378 HighlightConfiguration::new(
379 tree_sitter_java::LANGUAGE.into(),
380 "java",
381 tree_sitter_java::HIGHLIGHTS_QUERY,
382 "",
383 "",
384 )
385 .ok()
386 .map(|mut c| {
387 c.configure(HIGHLIGHT_NAMES);
388 c
389 })
390 })
391 .as_ref()
392 }
393
394 #[cfg(feature = "syntax-ruby")]
395 "ruby" | "rb" => {
396 static CFG: OnceLock<Option<HighlightConfiguration>> = OnceLock::new();
397 CFG.get_or_init(|| {
398 HighlightConfiguration::new(
399 tree_sitter_ruby::LANGUAGE.into(),
400 "ruby",
401 tree_sitter_ruby::HIGHLIGHTS_QUERY,
402 tree_sitter_ruby::LOCALS_QUERY,
403 "",
404 )
405 .ok()
406 .map(|mut c| {
407 c.configure(HIGHLIGHT_NAMES);
408 c
409 })
410 })
411 .as_ref()
412 }
413
414 #[cfg(feature = "syntax-css")]
415 "css" => {
416 static CFG: OnceLock<Option<HighlightConfiguration>> = OnceLock::new();
417 CFG.get_or_init(|| {
418 HighlightConfiguration::new(
419 tree_sitter_css::LANGUAGE.into(),
420 "css",
421 tree_sitter_css::HIGHLIGHTS_QUERY,
422 "",
423 "",
424 )
425 .ok()
426 .map(|mut c| {
427 c.configure(HIGHLIGHT_NAMES);
428 c
429 })
430 })
431 .as_ref()
432 }
433
434 #[cfg(feature = "syntax-html")]
435 "html" | "htm" => {
436 static CFG: OnceLock<Option<HighlightConfiguration>> = OnceLock::new();
437 CFG.get_or_init(|| {
438 HighlightConfiguration::new(
439 tree_sitter_html::LANGUAGE.into(),
440 "html",
441 tree_sitter_html::HIGHLIGHTS_QUERY,
442 tree_sitter_html::INJECTIONS_QUERY,
443 "",
444 )
445 .ok()
446 .map(|mut c| {
447 c.configure(HIGHLIGHT_NAMES);
448 c
449 })
450 })
451 .as_ref()
452 }
453
454 #[cfg(feature = "syntax-yaml")]
455 "yaml" | "yml" => {
456 static CFG: OnceLock<Option<HighlightConfiguration>> = OnceLock::new();
457 CFG.get_or_init(|| {
458 HighlightConfiguration::new(
459 tree_sitter_yaml::LANGUAGE.into(),
460 "yaml",
461 tree_sitter_yaml::HIGHLIGHTS_QUERY,
462 "",
463 "",
464 )
465 .ok()
466 .map(|mut c| {
467 c.configure(HIGHLIGHT_NAMES);
468 c
469 })
470 })
471 .as_ref()
472 }
473
474 _ => None,
475 }
476}
477
478#[cfg(any(
483 feature = "syntax-rust",
484 feature = "syntax-python",
485 feature = "syntax-javascript",
486 feature = "syntax-typescript",
487 feature = "syntax-go",
488 feature = "syntax-bash",
489 feature = "syntax-json",
490 feature = "syntax-toml",
491 feature = "syntax-c",
492 feature = "syntax-cpp",
493 feature = "syntax-java",
494 feature = "syntax-ruby",
495 feature = "syntax-css",
496 feature = "syntax-html",
497 feature = "syntax-yaml",
498))]
499fn highlight_name_to_style(name: &str, theme: &Theme) -> Style {
500 let dark = theme.is_dark;
501 match name {
502 "keyword" => Style::new().fg(if dark {
503 Color::Rgb(198, 120, 221)
504 } else {
505 Color::Rgb(166, 38, 164)
506 }),
507 "string" | "string.special" => Style::new().fg(if dark {
508 Color::Rgb(152, 195, 121)
509 } else {
510 Color::Rgb(80, 161, 79)
511 }),
512 "comment" => Style::new().fg(theme.text_dim).italic(),
513 "number" | "constant" | "constant.builtin" => Style::new().fg(if dark {
514 Color::Rgb(209, 154, 102)
515 } else {
516 Color::Rgb(152, 104, 1)
517 }),
518 "function" | "function.builtin" => Style::new().fg(if dark {
519 Color::Rgb(97, 175, 239)
520 } else {
521 Color::Rgb(64, 120, 242)
522 }),
523 "function.macro" => Style::new().fg(if dark {
524 Color::Rgb(86, 182, 194)
525 } else {
526 Color::Rgb(1, 132, 188)
527 }),
528 "type" | "type.builtin" | "constructor" => Style::new().fg(if dark {
529 Color::Rgb(229, 192, 123)
530 } else {
531 Color::Rgb(152, 104, 1)
532 }),
533 "variable.builtin" => Style::new().fg(if dark {
534 Color::Rgb(224, 108, 117)
535 } else {
536 Color::Rgb(166, 38, 164)
537 }),
538 "property" | "property.builtin" => Style::new().fg(if dark {
539 Color::Rgb(97, 175, 239)
540 } else {
541 Color::Rgb(64, 120, 242)
542 }),
543 "tag" => Style::new().fg(if dark {
544 Color::Rgb(224, 108, 117)
545 } else {
546 Color::Rgb(166, 38, 164)
547 }),
548 "attribute" => Style::new().fg(if dark {
549 Color::Rgb(209, 154, 102)
550 } else {
551 Color::Rgb(152, 104, 1)
552 }),
553 "module" | "embedded" | "operator" | "variable" | "variable.parameter" => {
554 Style::new().fg(theme.text)
555 }
556 "punctuation" | "punctuation.bracket" | "punctuation.delimiter" | "punctuation.special" => {
557 Style::new().fg(theme.text_dim)
558 }
559 _ => Style::new().fg(theme.text),
560 }
561}
562
563#[cfg(any(
564 feature = "syntax-rust",
565 feature = "syntax-python",
566 feature = "syntax-javascript",
567 feature = "syntax-typescript",
568 feature = "syntax-go",
569 feature = "syntax-bash",
570 feature = "syntax-json",
571 feature = "syntax-toml",
572 feature = "syntax-c",
573 feature = "syntax-cpp",
574 feature = "syntax-java",
575 feature = "syntax-ruby",
576 feature = "syntax-css",
577 feature = "syntax-html",
578 feature = "syntax-yaml",
579))]
580thread_local! {
581 static HIGHLIGHTER: std::cell::RefCell<tree_sitter_highlight::Highlighter> =
585 std::cell::RefCell::new(tree_sitter_highlight::Highlighter::new());
586}
587
588#[allow(unused_variables)]
605pub fn highlight_code(code: &str, lang: &str, theme: &Theme) -> Option<Vec<Vec<(String, Style)>>> {
606 #[cfg(any(
607 feature = "syntax-rust",
608 feature = "syntax-python",
609 feature = "syntax-javascript",
610 feature = "syntax-typescript",
611 feature = "syntax-go",
612 feature = "syntax-bash",
613 feature = "syntax-json",
614 feature = "syntax-toml",
615 feature = "syntax-c",
616 feature = "syntax-cpp",
617 feature = "syntax-java",
618 feature = "syntax-ruby",
619 feature = "syntax-css",
620 feature = "syntax-html",
621 feature = "syntax-yaml",
622 ))]
623 {
624 use tree_sitter_highlight::HighlightEvent;
625
626 let config = get_config(lang)?;
627 let highlights = HIGHLIGHTER.with(|cell| {
628 let mut highlighter = cell.borrow_mut();
629 highlighter
630 .highlight(config, code.as_bytes(), None, |_| None)
631 .ok()
632 .map(|iter| iter.collect::<Vec<_>>())
633 })?;
634 let highlights = highlights.into_iter();
635
636 let default_style = Style::new().fg(theme.text);
637 let mut result: Vec<Vec<(String, Style)>> = Vec::new();
638 let mut current_line: Vec<(String, Style)> = Vec::new();
639 let mut style_stack: Vec<Style> = vec![default_style];
640
641 for event in highlights {
642 match event.ok()? {
643 HighlightEvent::Source { start, end } => {
644 let text = &code[start..end];
645 let style = *style_stack.last().unwrap_or(&default_style);
646 for (i, part) in text.split('\n').enumerate() {
648 if i > 0 {
649 result.push(std::mem::take(&mut current_line));
650 }
651 if !part.is_empty() {
652 current_line.push((part.to_string(), style));
653 }
654 }
655 }
656 HighlightEvent::HighlightStart(highlight) => {
657 let name = HIGHLIGHT_NAMES.get(highlight.0).copied().unwrap_or("");
658 let style = highlight_name_to_style(name, theme);
659 style_stack.push(style);
660 }
661 HighlightEvent::HighlightEnd => {
662 style_stack.pop();
663 }
664 }
665 }
666
667 if !current_line.is_empty() {
668 result.push(current_line);
669 }
670
671 Some(result)
672 }
673
674 #[cfg(not(any(
675 feature = "syntax-rust",
676 feature = "syntax-python",
677 feature = "syntax-javascript",
678 feature = "syntax-typescript",
679 feature = "syntax-go",
680 feature = "syntax-bash",
681 feature = "syntax-json",
682 feature = "syntax-toml",
683 feature = "syntax-c",
684 feature = "syntax-cpp",
685 feature = "syntax-java",
686 feature = "syntax-ruby",
687 feature = "syntax-css",
688 feature = "syntax-html",
689 feature = "syntax-yaml",
690 )))]
691 {
692 None
693 }
694}
695
696#[allow(unused_variables)]
701pub fn is_language_supported(lang: &str) -> bool {
702 #[cfg(any(
703 feature = "syntax-rust",
704 feature = "syntax-python",
705 feature = "syntax-javascript",
706 feature = "syntax-typescript",
707 feature = "syntax-go",
708 feature = "syntax-bash",
709 feature = "syntax-json",
710 feature = "syntax-toml",
711 feature = "syntax-c",
712 feature = "syntax-cpp",
713 feature = "syntax-java",
714 feature = "syntax-ruby",
715 feature = "syntax-css",
716 feature = "syntax-html",
717 feature = "syntax-yaml",
718 ))]
719 {
720 get_config(lang).is_some()
721 }
722 #[cfg(not(any(
723 feature = "syntax-rust",
724 feature = "syntax-python",
725 feature = "syntax-javascript",
726 feature = "syntax-typescript",
727 feature = "syntax-go",
728 feature = "syntax-bash",
729 feature = "syntax-json",
730 feature = "syntax-toml",
731 feature = "syntax-c",
732 feature = "syntax-cpp",
733 feature = "syntax-java",
734 feature = "syntax-ruby",
735 feature = "syntax-css",
736 feature = "syntax-html",
737 feature = "syntax-yaml",
738 )))]
739 {
740 false
741 }
742}
743
744#[cfg(test)]
745mod tests {
746 #![allow(clippy::unwrap_used)]
747 use super::*;
748 use crate::style::Theme;
749
750 #[test]
751 fn highlight_returns_none_for_unknown_lang() {
752 let theme = Theme::dark();
753 assert!(highlight_code("let x = 1;", "brainfuck", &theme).is_none());
754 }
755
756 #[test]
757 fn is_language_supported_unknown() {
758 assert!(!is_language_supported("haskell"));
759 }
760
761 #[cfg(feature = "syntax-rust")]
762 #[test]
763 fn highlight_rust_basic() {
764 let theme = Theme::dark();
765 let result = highlight_code("let x = 1;", "rust", &theme);
766 assert!(result.is_some());
767 let lines = result.unwrap();
768 assert_eq!(lines.len(), 1);
769 let flat: String = lines[0].iter().map(|(t, _)| t.as_str()).collect();
771 assert!(flat.contains("let"));
772 assert!(flat.contains("1"));
773 }
774
775 #[cfg(feature = "syntax-rust")]
776 #[test]
777 fn highlight_rust_multiline() {
778 let theme = Theme::dark();
779 let code = "fn main() {\n println!(\"hello\");\n}";
780 let result = highlight_code(code, "rust", &theme).unwrap();
781 assert_eq!(result.len(), 3);
782 }
783
784 #[cfg(feature = "syntax-rust")]
785 #[test]
786 fn highlight_rust_rs_alias() {
787 let theme = Theme::dark();
788 assert!(highlight_code("let x = 1;", "rs", &theme).is_some());
789 }
790
791 #[cfg(feature = "syntax-python")]
792 #[test]
793 fn highlight_python_basic() {
794 let theme = Theme::dark();
795 let result = highlight_code("def foo():\n return 42", "python", &theme);
796 assert!(result.is_some());
797 let lines = result.unwrap();
798 assert_eq!(lines.len(), 2);
799 }
800
801 #[cfg(feature = "syntax-javascript")]
802 #[test]
803 fn highlight_javascript_basic() {
804 let theme = Theme::dark();
805 let result = highlight_code("const x = () => 42;", "js", &theme);
806 assert!(result.is_some());
807 }
808
809 #[cfg(feature = "syntax-bash")]
810 #[test]
811 fn highlight_bash_basic() {
812 let theme = Theme::dark();
813 let result = highlight_code("echo \"hello\"", "sh", &theme);
814 assert!(result.is_some());
815 }
816
817 #[cfg(feature = "syntax-json")]
818 #[test]
819 fn highlight_json_basic() {
820 let theme = Theme::dark();
821 let result = highlight_code("{\"key\": 42}", "json", &theme);
822 assert!(result.is_some());
823 }
824
825 #[cfg(feature = "syntax-toml")]
826 #[test]
827 fn highlight_toml_basic() {
828 let theme = Theme::dark();
829 let result = highlight_code("[package]\nname = \"slt\"", "toml", &theme);
830 assert!(result.is_some());
831 }
832
833 #[cfg(feature = "syntax-go")]
834 #[test]
835 fn highlight_go_basic() {
836 let theme = Theme::dark();
837 let result = highlight_code("package main\nfunc main() {}", "go", &theme);
838 assert!(result.is_some());
839 }
840
841 #[cfg(feature = "syntax-rust")]
842 #[test]
843 fn highlight_light_theme_differs() {
844 let dark = Theme::dark();
845 let light = Theme::light();
846 let dark_result = highlight_code("let x = 1;", "rust", &dark).unwrap();
847 let light_result = highlight_code("let x = 1;", "rust", &light).unwrap();
848 let dark_styles: Vec<Style> = dark_result[0].iter().map(|(_, s)| *s).collect();
850 let light_styles: Vec<Style> = light_result[0].iter().map(|(_, s)| *s).collect();
851 assert_ne!(dark_styles, light_styles);
852 }
853
854 #[cfg(feature = "syntax-rust")]
855 #[test]
856 fn highlight_incomplete_code_does_not_panic() {
857 let theme = Theme::dark();
858 let result = highlight_code("fn main( {", "rust", &theme);
859 assert!(result.is_some());
860 }
861
862 #[cfg(feature = "syntax-c")]
863 #[test]
864 fn highlight_c_basic() {
865 let theme = Theme::dark();
866 assert!(
867 highlight_code("#include <stdio.h>\nint main() { return 0; }", "c", &theme).is_some()
868 );
869 }
870
871 #[cfg(feature = "syntax-cpp")]
872 #[test]
873 fn highlight_cpp_basic() {
874 let theme = Theme::dark();
875 assert!(highlight_code("class Foo { public: void bar(); };", "cpp", &theme).is_some());
876 }
877
878 #[cfg(feature = "syntax-typescript")]
879 #[test]
880 fn highlight_typescript_basic() {
881 let theme = Theme::dark();
882 assert!(highlight_code("const x: number = 42;", "ts", &theme).is_some());
883 }
884
885 #[cfg(feature = "syntax-typescript")]
886 #[test]
887 fn highlight_tsx_basic() {
888 let theme = Theme::dark();
889 assert!(highlight_code("const App = () => <div>hello</div>;", "tsx", &theme).is_some());
890 }
891
892 #[cfg(feature = "syntax-java")]
893 #[test]
894 fn highlight_java_basic() {
895 let theme = Theme::dark();
896 assert!(highlight_code(
897 "public class Main { public static void main(String[] args) {} }",
898 "java",
899 &theme
900 )
901 .is_some());
902 }
903
904 #[cfg(feature = "syntax-ruby")]
905 #[test]
906 fn highlight_ruby_basic() {
907 let theme = Theme::dark();
908 assert!(highlight_code("def hello\n puts 'world'\nend", "ruby", &theme).is_some());
909 }
910
911 #[cfg(feature = "syntax-css")]
912 #[test]
913 fn highlight_css_basic() {
914 let theme = Theme::dark();
915 assert!(highlight_code("body { color: red; }", "css", &theme).is_some());
916 }
917
918 #[cfg(feature = "syntax-html")]
919 #[test]
920 fn highlight_html_basic() {
921 let theme = Theme::dark();
922 assert!(highlight_code("<div class=\"test\">hello</div>", "html", &theme).is_some());
923 }
924
925 #[cfg(feature = "syntax-yaml")]
926 #[test]
927 fn highlight_yaml_basic() {
928 let theme = Theme::dark();
929 assert!(highlight_code("name: slt\nversion: 0.14", "yaml", &theme).is_some());
930 }
931
932 #[cfg(feature = "syntax-rust")]
935 #[test]
936 fn highlight_reuse_does_not_panic() {
937 let theme = Theme::dark();
938 let first = highlight_code("let x = 1;", "rust", &theme);
940 let second = highlight_code("fn foo() {}", "rust", &theme);
941 assert!(first.is_some(), "first call should succeed");
942 assert!(second.is_some(), "second call should succeed");
943 }
944
945 #[cfg(all(feature = "syntax-rust", feature = "syntax-python"))]
948 #[test]
949 fn highlight_reuse_across_languages() {
950 let theme = Theme::dark();
951 let r1 = highlight_code("let x = 1;", "rust", &theme);
952 let r2 = highlight_code("def foo(): pass", "python", &theme);
953 let r3 = highlight_code("fn bar() {}", "rust", &theme);
954 assert!(r1.is_some());
955 assert!(r2.is_some());
956 assert!(r3.is_some());
957 }
958}