1use std::path::Path;
2
3pub use tree_sitter;
5pub use tree_sitter_highlight;
6pub use tree_sitter_highlight::HighlightConfiguration;
7
8#[cfg(feature = "tree-sitter-bash")]
10pub use tree_sitter_bash;
11#[cfg(feature = "tree-sitter-c")]
12pub use tree_sitter_c;
13#[cfg(feature = "tree-sitter-c-sharp")]
14pub use tree_sitter_c_sharp;
15#[cfg(feature = "tree-sitter-cpp")]
16pub use tree_sitter_cpp;
17#[cfg(feature = "tree-sitter-css")]
18pub use tree_sitter_css;
19#[cfg(feature = "tree-sitter-go")]
20pub use tree_sitter_go;
21#[cfg(feature = "tree-sitter-html")]
22pub use tree_sitter_html;
23#[cfg(feature = "tree-sitter-java")]
24pub use tree_sitter_java;
25#[cfg(feature = "tree-sitter-javascript")]
26pub use tree_sitter_javascript;
27#[cfg(feature = "tree-sitter-json")]
28pub use tree_sitter_json;
29#[cfg(feature = "tree-sitter-lua")]
30pub use tree_sitter_lua;
31#[cfg(feature = "tree-sitter-odin")]
32pub use tree_sitter_odin;
33#[cfg(feature = "tree-sitter-pascal")]
34pub use tree_sitter_pascal;
35#[cfg(feature = "tree-sitter-php")]
36pub use tree_sitter_php;
37#[cfg(feature = "tree-sitter-python")]
38pub use tree_sitter_python;
39#[cfg(feature = "tree-sitter-ruby")]
40pub use tree_sitter_ruby;
41#[cfg(feature = "tree-sitter-rust")]
42pub use tree_sitter_rust;
43#[cfg(feature = "tree-sitter-typescript")]
44pub use tree_sitter_typescript;
45
46#[derive(Debug, Clone, Copy, PartialEq, Eq)]
48pub enum HighlightCategory {
49 Attribute,
50 Comment,
51 Constant,
52 Function,
53 Keyword,
54 Number,
55 Operator,
56 PunctuationBracket,
57 PunctuationDelimiter,
58 Property,
59 String,
60 Type,
61 Variable,
62 Inserted,
67 Deleted,
70 Changed,
74}
75
76impl HighlightCategory {
77 pub fn bg_extends_to_line_end(&self) -> bool {
85 matches!(self, Self::Inserted | Self::Deleted | Self::Changed)
86 }
87
88 pub fn from_default_index(index: usize) -> Option<Self> {
90 match index {
91 0 => Some(Self::Attribute),
92 1 => Some(Self::Comment),
93 2 => Some(Self::Constant),
94 3 => Some(Self::Function),
95 4 => Some(Self::Keyword),
96 5 => Some(Self::Number),
97 6 => Some(Self::Operator),
98 7 => Some(Self::PunctuationBracket),
99 8 => Some(Self::PunctuationDelimiter),
100 9 => Some(Self::Property),
101 10 => Some(Self::String),
102 11 => Some(Self::Type),
103 12 => Some(Self::Variable),
104 _ => None,
105 }
106 }
107
108 pub fn from_typescript_index(index: usize) -> Option<Self> {
110 match index {
111 0 => Some(Self::Attribute), 1 => Some(Self::Comment), 2 => Some(Self::Constant), 3 => Some(Self::Constant), 4 => Some(Self::Type), 5 => Some(Self::String), 6 => Some(Self::Function), 7 => Some(Self::Function), 8 => Some(Self::Function), 9 => Some(Self::Keyword), 10 => Some(Self::Number), 11 => Some(Self::Operator), 12 => Some(Self::Property), 13 => Some(Self::PunctuationBracket), 14 => Some(Self::PunctuationDelimiter), 15 => Some(Self::Constant), 16 => Some(Self::String), 17 => Some(Self::String), 18 => Some(Self::Type), 19 => Some(Self::Type), 20 => Some(Self::Variable), 21 => Some(Self::Constant), 22 => Some(Self::Variable), _ => None,
135 }
136 }
137
138 pub fn theme_key(&self) -> &'static str {
140 match self {
141 Self::Keyword => "syntax.keyword",
142 Self::String => "syntax.string",
143 Self::Comment => "syntax.comment",
144 Self::Function => "syntax.function",
145 Self::Type => "syntax.type",
146 Self::Variable | Self::Property => "syntax.variable",
147 Self::Constant | Self::Number | Self::Attribute => "syntax.constant",
148 Self::Operator => "syntax.operator",
149 Self::PunctuationBracket => "syntax.punctuation_bracket",
150 Self::PunctuationDelimiter => "syntax.punctuation_delimiter",
151 Self::Inserted => "editor.diff_add_bg",
156 Self::Deleted => "editor.diff_remove_bg",
157 Self::Changed => "editor.diff_modify_bg",
158 }
159 }
160
161 pub fn display_name(&self) -> &'static str {
163 match self {
164 Self::Attribute => "Attribute",
165 Self::Comment => "Comment",
166 Self::Constant => "Constant",
167 Self::Function => "Function",
168 Self::Keyword => "Keyword",
169 Self::Number => "Number",
170 Self::Operator => "Operator",
171 Self::PunctuationBracket => "Punctuation Bracket",
172 Self::PunctuationDelimiter => "Punctuation Delimiter",
173 Self::Property => "Property",
174 Self::String => "String",
175 Self::Type => "Type",
176 Self::Variable => "Variable",
177 Self::Inserted => "Diff Inserted",
178 Self::Deleted => "Diff Deleted",
179 Self::Changed => "Diff Changed",
180 }
181 }
182}
183
184#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
186pub enum Language {
187 Rust,
188 Python,
189 JavaScript,
190 TypeScript,
191 HTML,
192 CSS,
193 C,
194 Cpp,
195 Go,
196 Json,
197 Jsonc,
198 Java,
199 CSharp,
200 Php,
201 Ruby,
202 Bash,
203 Lua,
204 Pascal,
205 Odin,
206}
207
208impl Language {
209 pub fn from_path(path: &Path) -> Option<Self> {
215 let ext = path.extension()?.to_str()?;
216 Self::all()
217 .iter()
218 .find(|lang| lang.extensions().contains(&ext))
219 .copied()
220 }
221
222 pub fn highlight_config(&self) -> Result<HighlightConfiguration, String> {
224 match self {
225 Self::Rust => {
226 #[cfg(feature = "tree-sitter-rust")]
227 {
228 let mut config = HighlightConfiguration::new(
229 tree_sitter_rust::LANGUAGE.into(),
230 "rust",
231 tree_sitter_rust::HIGHLIGHTS_QUERY,
232 "",
233 "",
234 )
235 .map_err(|e| format!("Failed to create Rust highlight config: {e}"))?;
236 config.configure(DEFAULT_HIGHLIGHT_CAPTURES);
237 Ok(config)
238 }
239 #[cfg(not(feature = "tree-sitter-rust"))]
240 Err("Rust language support not enabled".to_string())
241 }
242 Self::Python => {
243 #[cfg(feature = "tree-sitter-python")]
244 {
245 let mut config = HighlightConfiguration::new(
246 tree_sitter_python::LANGUAGE.into(),
247 "python",
248 tree_sitter_python::HIGHLIGHTS_QUERY,
249 "",
250 "",
251 )
252 .map_err(|e| format!("Failed to create Python highlight config: {e}"))?;
253 config.configure(DEFAULT_HIGHLIGHT_CAPTURES);
254 Ok(config)
255 }
256 #[cfg(not(feature = "tree-sitter-python"))]
257 Err("Python language support not enabled".to_string())
258 }
259 Self::JavaScript => {
260 #[cfg(feature = "tree-sitter-javascript")]
261 {
262 let mut config = HighlightConfiguration::new(
263 tree_sitter_javascript::LANGUAGE.into(),
264 "javascript",
265 tree_sitter_javascript::HIGHLIGHT_QUERY,
266 "",
267 "",
268 )
269 .map_err(|e| format!("Failed to create JavaScript highlight config: {e}"))?;
270 config.configure(DEFAULT_HIGHLIGHT_CAPTURES);
271 Ok(config)
272 }
273 #[cfg(not(feature = "tree-sitter-javascript"))]
274 Err("JavaScript language support not enabled".to_string())
275 }
276 Self::TypeScript => {
277 #[cfg(all(feature = "tree-sitter-typescript", feature = "tree-sitter-javascript"))]
278 {
279 let combined_highlights = format!(
280 "{}\n{}",
281 tree_sitter_typescript::HIGHLIGHTS_QUERY,
282 tree_sitter_javascript::HIGHLIGHT_QUERY
283 );
284 let mut config = HighlightConfiguration::new(
285 tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into(),
286 "typescript",
287 &combined_highlights,
288 "",
289 tree_sitter_typescript::LOCALS_QUERY,
290 )
291 .map_err(|e| format!("Failed to create TypeScript highlight config: {e}"))?;
292 config.configure(TYPESCRIPT_HIGHLIGHT_CAPTURES);
293 Ok(config)
294 }
295 #[cfg(not(all(
296 feature = "tree-sitter-typescript",
297 feature = "tree-sitter-javascript"
298 )))]
299 Err("TypeScript language support not enabled".to_string())
300 }
301 Self::HTML => {
302 #[cfg(feature = "tree-sitter-html")]
303 {
304 let mut config = HighlightConfiguration::new(
305 tree_sitter_html::LANGUAGE.into(),
306 "html",
307 tree_sitter_html::HIGHLIGHTS_QUERY,
308 "",
309 "",
310 )
311 .map_err(|e| format!("Failed to create HTML highlight config: {e}"))?;
312 config.configure(DEFAULT_HIGHLIGHT_CAPTURES);
313 Ok(config)
314 }
315 #[cfg(not(feature = "tree-sitter-html"))]
316 Err("HTML language support not enabled".to_string())
317 }
318 Self::CSS => {
319 #[cfg(feature = "tree-sitter-css")]
320 {
321 let mut config = HighlightConfiguration::new(
322 tree_sitter_css::LANGUAGE.into(),
323 "css",
324 tree_sitter_css::HIGHLIGHTS_QUERY,
325 "",
326 "",
327 )
328 .map_err(|e| format!("Failed to create CSS highlight config: {e}"))?;
329 config.configure(DEFAULT_HIGHLIGHT_CAPTURES);
330 Ok(config)
331 }
332 #[cfg(not(feature = "tree-sitter-css"))]
333 Err("CSS language support not enabled".to_string())
334 }
335 Self::C => {
336 #[cfg(feature = "tree-sitter-c")]
337 {
338 let mut config = HighlightConfiguration::new(
339 tree_sitter_c::LANGUAGE.into(),
340 "c",
341 tree_sitter_c::HIGHLIGHT_QUERY,
342 "",
343 "",
344 )
345 .map_err(|e| format!("Failed to create C highlight config: {e}"))?;
346 config.configure(DEFAULT_HIGHLIGHT_CAPTURES);
347 Ok(config)
348 }
349 #[cfg(not(feature = "tree-sitter-c"))]
350 Err("C language support not enabled".to_string())
351 }
352 Self::Cpp => {
353 #[cfg(feature = "tree-sitter-cpp")]
354 {
355 let mut config = HighlightConfiguration::new(
356 tree_sitter_cpp::LANGUAGE.into(),
357 "cpp",
358 tree_sitter_cpp::HIGHLIGHT_QUERY,
359 "",
360 "",
361 )
362 .map_err(|e| format!("Failed to create C++ highlight config: {e}"))?;
363 config.configure(DEFAULT_HIGHLIGHT_CAPTURES);
364 Ok(config)
365 }
366 #[cfg(not(feature = "tree-sitter-cpp"))]
367 Err("C++ language support not enabled".to_string())
368 }
369 Self::Go => {
370 #[cfg(feature = "tree-sitter-go")]
371 {
372 let mut config = HighlightConfiguration::new(
373 tree_sitter_go::LANGUAGE.into(),
374 "go",
375 tree_sitter_go::HIGHLIGHTS_QUERY,
376 "",
377 "",
378 )
379 .map_err(|e| format!("Failed to create Go highlight config: {e}"))?;
380 config.configure(DEFAULT_HIGHLIGHT_CAPTURES);
381 Ok(config)
382 }
383 #[cfg(not(feature = "tree-sitter-go"))]
384 Err("Go language support not enabled".to_string())
385 }
386 Self::Json => {
387 #[cfg(feature = "tree-sitter-json")]
388 {
389 let mut config = HighlightConfiguration::new(
390 tree_sitter_json::LANGUAGE.into(),
391 "json",
392 tree_sitter_json::HIGHLIGHTS_QUERY,
393 "",
394 "",
395 )
396 .map_err(|e| format!("Failed to create JSON highlight config: {e}"))?;
397 config.configure(DEFAULT_HIGHLIGHT_CAPTURES);
398 Ok(config)
399 }
400 #[cfg(not(feature = "tree-sitter-json"))]
401 Err("JSON language support not enabled".to_string())
402 }
403 Self::Jsonc => {
404 #[cfg(feature = "tree-sitter-json")]
409 {
410 let mut config = HighlightConfiguration::new(
411 tree_sitter_json::LANGUAGE.into(),
412 "jsonc",
413 tree_sitter_json::HIGHLIGHTS_QUERY,
414 "",
415 "",
416 )
417 .map_err(|e| format!("Failed to create JSONC highlight config: {e}"))?;
418 config.configure(DEFAULT_HIGHLIGHT_CAPTURES);
419 Ok(config)
420 }
421 #[cfg(not(feature = "tree-sitter-json"))]
422 Err("JSONC language support not enabled".to_string())
423 }
424 Self::Java => {
425 #[cfg(feature = "tree-sitter-java")]
426 {
427 let mut config = HighlightConfiguration::new(
428 tree_sitter_java::LANGUAGE.into(),
429 "java",
430 tree_sitter_java::HIGHLIGHTS_QUERY,
431 "",
432 "",
433 )
434 .map_err(|e| format!("Failed to create Java highlight config: {e}"))?;
435 config.configure(DEFAULT_HIGHLIGHT_CAPTURES);
436 Ok(config)
437 }
438 #[cfg(not(feature = "tree-sitter-java"))]
439 Err("Java language support not enabled".to_string())
440 }
441 Self::CSharp => {
442 #[cfg(feature = "tree-sitter-c-sharp")]
443 {
444 let mut config = HighlightConfiguration::new(
445 tree_sitter_c_sharp::LANGUAGE.into(),
446 "c_sharp",
447 "",
448 "",
449 "",
450 )
451 .map_err(|e| format!("Failed to create C# highlight config: {e}"))?;
452 config.configure(DEFAULT_HIGHLIGHT_CAPTURES);
453 Ok(config)
454 }
455 #[cfg(not(feature = "tree-sitter-c-sharp"))]
456 Err("C# language support not enabled".to_string())
457 }
458 Self::Php => {
459 #[cfg(feature = "tree-sitter-php")]
460 {
461 let mut config = HighlightConfiguration::new(
462 tree_sitter_php::LANGUAGE_PHP.into(),
463 "php",
464 tree_sitter_php::HIGHLIGHTS_QUERY,
465 "",
466 "",
467 )
468 .map_err(|e| format!("Failed to create PHP highlight config: {e}"))?;
469 config.configure(DEFAULT_HIGHLIGHT_CAPTURES);
470 Ok(config)
471 }
472 #[cfg(not(feature = "tree-sitter-php"))]
473 Err("PHP language support not enabled".to_string())
474 }
475 Self::Ruby => {
476 #[cfg(feature = "tree-sitter-ruby")]
477 {
478 let mut config = HighlightConfiguration::new(
479 tree_sitter_ruby::LANGUAGE.into(),
480 "ruby",
481 tree_sitter_ruby::HIGHLIGHTS_QUERY,
482 "",
483 "",
484 )
485 .map_err(|e| format!("Failed to create Ruby highlight config: {e}"))?;
486 config.configure(DEFAULT_HIGHLIGHT_CAPTURES);
487 Ok(config)
488 }
489 #[cfg(not(feature = "tree-sitter-ruby"))]
490 Err("Ruby language support not enabled".to_string())
491 }
492 Self::Bash => {
493 #[cfg(feature = "tree-sitter-bash")]
494 {
495 let mut config = HighlightConfiguration::new(
496 tree_sitter_bash::LANGUAGE.into(),
497 "bash",
498 tree_sitter_bash::HIGHLIGHT_QUERY,
499 "",
500 "",
501 )
502 .map_err(|e| format!("Failed to create Bash highlight config: {e}"))?;
503 config.configure(DEFAULT_HIGHLIGHT_CAPTURES);
504 Ok(config)
505 }
506 #[cfg(not(feature = "tree-sitter-bash"))]
507 Err("Bash language support not enabled".to_string())
508 }
509 Self::Lua => {
510 #[cfg(feature = "tree-sitter-lua")]
511 {
512 let mut config = HighlightConfiguration::new(
513 tree_sitter_lua::LANGUAGE.into(),
514 "lua",
515 tree_sitter_lua::HIGHLIGHTS_QUERY,
516 "",
517 "",
518 )
519 .map_err(|e| format!("Failed to create Lua highlight config: {e}"))?;
520 config.configure(DEFAULT_HIGHLIGHT_CAPTURES);
521 Ok(config)
522 }
523 #[cfg(not(feature = "tree-sitter-lua"))]
524 Err("Lua language support not enabled".to_string())
525 }
526 Self::Pascal => {
527 #[cfg(feature = "tree-sitter-pascal")]
528 {
529 let mut config = HighlightConfiguration::new(
530 tree_sitter_pascal::LANGUAGE.into(),
531 "pascal",
532 "",
533 "",
534 "",
535 )
536 .map_err(|e| format!("Failed to create Pascal highlight config: {e}"))?;
537 config.configure(DEFAULT_HIGHLIGHT_CAPTURES);
538 Ok(config)
539 }
540 #[cfg(not(feature = "tree-sitter-pascal"))]
541 Err("Pascal language support not enabled".to_string())
542 }
543 Self::Odin => {
544 #[cfg(feature = "tree-sitter-odin")]
545 {
546 let mut config = HighlightConfiguration::new(
547 tree_sitter_odin::LANGUAGE.into(),
548 "odin",
549 "",
550 "",
551 "",
552 )
553 .map_err(|e| format!("Failed to create Odin highlight config: {e}"))?;
554 config.configure(DEFAULT_HIGHLIGHT_CAPTURES);
555 Ok(config)
556 }
557 #[cfg(not(feature = "tree-sitter-odin"))]
558 Err("Odin language support not enabled".to_string())
559 }
560 }
561 }
562
563 pub fn highlight_category(&self, index: usize) -> Option<HighlightCategory> {
565 match self {
566 Self::TypeScript => HighlightCategory::from_typescript_index(index),
567 _ => HighlightCategory::from_default_index(index),
568 }
569 }
570}
571
572impl Language {
573 pub fn all() -> &'static [Language] {
575 &[
576 Language::Rust,
577 Language::Python,
578 Language::JavaScript,
579 Language::TypeScript,
580 Language::HTML,
581 Language::CSS,
582 Language::C,
583 Language::Cpp,
584 Language::Go,
585 Language::Json,
586 Language::Jsonc,
587 Language::Java,
588 Language::CSharp,
589 Language::Php,
590 Language::Ruby,
591 Language::Bash,
592 Language::Lua,
593 Language::Pascal,
594 Language::Odin,
595 ]
596 }
597
598 pub fn id(&self) -> &'static str {
600 match self {
601 Self::Rust => "rust",
602 Self::Python => "python",
603 Self::JavaScript => "javascript",
604 Self::TypeScript => "typescript",
605 Self::HTML => "html",
606 Self::CSS => "css",
607 Self::C => "c",
608 Self::Cpp => "cpp",
609 Self::Go => "go",
610 Self::Json => "json",
611 Self::Jsonc => "jsonc",
612 Self::Java => "java",
613 Self::CSharp => "csharp",
614 Self::Php => "php",
615 Self::Ruby => "ruby",
616 Self::Bash => "bash",
617 Self::Lua => "lua",
618 Self::Pascal => "pascal",
619 Self::Odin => "odin",
620 }
621 }
622
623 pub fn lsp_language_id(&self, path: &Path) -> &'static str {
631 let ext = path.extension().and_then(|e| e.to_str()).unwrap_or("");
632 match (self, ext) {
633 (Self::TypeScript, "tsx") => "typescriptreact",
634 (Self::JavaScript, "jsx") => "javascriptreact",
635 _ => self.id(),
636 }
637 }
638
639 pub fn extensions(&self) -> &'static [&'static str] {
645 match self {
646 Self::Rust => &["rs"],
647 Self::Python => &["py"],
648 Self::JavaScript => &["js", "jsx", "mjs", "cjs"],
649 Self::TypeScript => &["ts", "tsx", "mts", "cts"],
650 Self::HTML => &["html"],
651 Self::CSS => &["css"],
652 Self::C => &["c", "h"],
653 Self::Cpp => &["cpp", "hpp", "cc", "hh", "cxx", "hxx", "cppm", "ixx"],
654 Self::Go => &["go"],
655 Self::Json => &["json"],
656 Self::Jsonc => &["jsonc"],
657 Self::Java => &["java"],
658 Self::CSharp => &["cs"],
659 Self::Php => &["php"],
660 Self::Ruby => &["rb"],
661 Self::Bash => &["sh", "bash"],
662 Self::Lua => &["lua"],
663 Self::Pascal => &["pas", "p"],
664 Self::Odin => &["odin"],
665 }
666 }
667
668 pub fn display_name(&self) -> &'static str {
670 match self {
671 Self::Rust => "Rust",
672 Self::Python => "Python",
673 Self::JavaScript => "JavaScript",
674 Self::TypeScript => "TypeScript",
675 Self::HTML => "HTML",
676 Self::CSS => "CSS",
677 Self::C => "C",
678 Self::Cpp => "C++",
679 Self::Go => "Go",
680 Self::Json => "JSON",
681 Self::Jsonc => "JSON with Comments",
682 Self::Java => "Java",
683 Self::CSharp => "C#",
684 Self::Php => "PHP",
685 Self::Ruby => "Ruby",
686 Self::Bash => "Bash",
687 Self::Lua => "Lua",
688 Self::Pascal => "Pascal",
689 Self::Odin => "Odin",
690 }
691 }
692
693 pub fn from_id(id: &str) -> Option<Self> {
695 let id_lower = id.to_lowercase();
696 match id_lower.as_str() {
697 "rust" => Some(Self::Rust),
698 "python" => Some(Self::Python),
699 "javascript" => Some(Self::JavaScript),
700 "typescript" => Some(Self::TypeScript),
701 "html" => Some(Self::HTML),
702 "css" => Some(Self::CSS),
703 "c" => Some(Self::C),
704 "cpp" | "c++" => Some(Self::Cpp),
705 "go" => Some(Self::Go),
706 "json" => Some(Self::Json),
707 "jsonc" => Some(Self::Jsonc),
708 "java" => Some(Self::Java),
709 "c_sharp" | "c#" | "csharp" => Some(Self::CSharp),
710 "php" => Some(Self::Php),
711 "ruby" => Some(Self::Ruby),
712 "bash" => Some(Self::Bash),
713 "lua" => Some(Self::Lua),
714 "pascal" => Some(Self::Pascal),
715 "odin" => Some(Self::Odin),
716 _ => None,
717 }
718 }
719
720 pub fn from_name(name: &str) -> Option<Self> {
729 for lang in Self::all() {
731 if lang.display_name() == name {
732 return Some(*lang);
733 }
734 }
735
736 let name_lower = name.to_lowercase();
738 match name_lower.as_str() {
739 "rust" => Some(Self::Rust),
740 "python" => Some(Self::Python),
741 "javascript" | "javascript (babel)" => Some(Self::JavaScript),
742 "typescript" | "typescriptreact" => Some(Self::TypeScript),
743 "html" => Some(Self::HTML),
744 "css" => Some(Self::CSS),
745 "c" => Some(Self::C),
746 "c++" => Some(Self::Cpp),
747 "go" | "golang" => Some(Self::Go),
748 "json" => Some(Self::Json),
749 "jsonc" | "json with comments" => Some(Self::Jsonc),
750 "java" => Some(Self::Java),
751 "c#" => Some(Self::CSharp),
752 "php" => Some(Self::Php),
753 "ruby" => Some(Self::Ruby),
754 "lua" => Some(Self::Lua),
755 "pascal" => Some(Self::Pascal),
756 "odin" => Some(Self::Odin),
757 _ => {
758 if name_lower.contains("bash") || name_lower.contains("shell") {
760 return Some(Self::Bash);
761 }
762 None
763 }
764 }
765 }
766}
767
768impl std::fmt::Display for Language {
769 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
770 write!(f, "{}", self.id())
771 }
772}
773
774const DEFAULT_HIGHLIGHT_CAPTURES: &[&str] = &[
775 "attribute",
776 "comment",
777 "constant",
778 "function",
779 "keyword",
780 "number",
781 "operator",
782 "punctuation.bracket",
783 "punctuation.delimiter",
784 "property",
785 "string",
786 "type",
787 "variable",
788];
789
790const TYPESCRIPT_HIGHLIGHT_CAPTURES: &[&str] = &[
791 "attribute",
792 "comment",
793 "constant",
794 "constant.builtin",
795 "constructor",
796 "embedded",
797 "function",
798 "function.builtin",
799 "function.method",
800 "keyword",
801 "number",
802 "operator",
803 "property",
804 "punctuation.bracket",
805 "punctuation.delimiter",
806 "punctuation.special",
807 "string",
808 "string.special",
809 "type",
810 "type.builtin",
811 "variable",
812 "variable.builtin",
813 "variable.parameter",
814];
815
816#[cfg(test)]
817mod tests {
818 use super::*;
819 use std::path::Path;
820
821 #[test]
822 fn test_lsp_language_id_tsx() {
823 let lang = Language::TypeScript;
824 assert_eq!(
825 lang.lsp_language_id(Path::new("app.tsx")),
826 "typescriptreact"
827 );
828 }
829
830 #[test]
831 fn test_lsp_language_id_ts() {
832 let lang = Language::TypeScript;
833 assert_eq!(lang.lsp_language_id(Path::new("app.ts")), "typescript");
834 }
835
836 #[test]
837 fn test_lsp_language_id_jsx() {
838 let lang = Language::JavaScript;
839 assert_eq!(
840 lang.lsp_language_id(Path::new("component.jsx")),
841 "javascriptreact"
842 );
843 }
844
845 #[test]
846 fn test_lsp_language_id_js() {
847 let lang = Language::JavaScript;
848 assert_eq!(lang.lsp_language_id(Path::new("app.js")), "javascript");
849 }
850
851 #[test]
852 fn test_lsp_language_id_csharp() {
853 let lang = Language::CSharp;
854 assert_eq!(lang.lsp_language_id(Path::new("main.cs")), "csharp");
855 }
856
857 #[test]
858 fn test_lsp_language_id_other_languages() {
859 assert_eq!(Language::Rust.lsp_language_id(Path::new("main.rs")), "rust");
860 assert_eq!(
861 Language::Python.lsp_language_id(Path::new("script.py")),
862 "python"
863 );
864 assert_eq!(Language::Go.lsp_language_id(Path::new("main.go")), "go");
865 }
866
867 #[test]
868 fn test_csharp_id_matches_config_key() {
869 assert_eq!(Language::CSharp.id(), "csharp");
872 }
873
874 #[test]
878 fn test_from_path_matches_extensions() {
879 for lang in Language::all() {
880 for ext in lang.extensions() {
881 let path = std::path::PathBuf::from(format!("x.{}", ext));
882 let detected = Language::from_path(&path).unwrap_or_else(|| {
883 panic!(
884 "extension .{} listed by {:?} but from_path returned None",
885 ext, lang
886 )
887 });
888 assert_eq!(
889 detected, *lang,
890 "extension .{} listed by {:?} but from_path returned {:?}",
891 ext, lang, detected
892 );
893 }
894 }
895 }
896}