gpui_component/input/
language_config.rs1pub use gpui_base::input::language_config::LanguageConfig;
4
5use gpui_base::input::{AutoClosingPair, BracketPair, IndentationRules, SyntaxContext};
6use regex::Regex;
7use std::sync::LazyLock;
8
9struct ComponentLanguages {
10 defaults: std::collections::HashMap<&'static str, std::rc::Rc<LanguageConfig>>,
11 fallback: std::rc::Rc<LanguageConfig>,
12}
13
14impl gpui_base::input::LanguageProvider for ComponentLanguages {
15 fn language_name(&self, name: &str) -> gpui::SharedString {
16 crate::highlighter::LanguageRegistry::singleton().editing_language_name(name)
17 }
18
19 fn config(&self, name: &str) -> std::rc::Rc<LanguageConfig> {
20 self.defaults.get(name).unwrap_or(&self.fallback).clone()
21 }
22
23 fn syntax_context_provider(
24 &self,
25 name: &str,
26 ) -> Option<std::rc::Rc<dyn gpui_base::input::SyntaxContextProvider>> {
27 super::syntax_context::syntax_context_provider(name)
28 }
29}
30
31pub(crate) fn init(cx: &mut gpui::App) {
32 gpui_base::input::set_language_provider(
33 std::rc::Rc::new(ComponentLanguages {
34 defaults: ["text", "json", "python"]
35 .into_iter()
36 .map(|name| (name, std::rc::Rc::new(default_language_config(name))))
37 .collect(),
38 fallback: std::rc::Rc::new(LanguageConfig::default()),
39 }),
40 cx,
41 );
42}
43
44fn default_language_config(language: &str) -> LanguageConfig {
48 static PYTHON_INDENT: LazyLock<IndentationRules> = LazyLock::new(|| {
49 IndentationRules::new(
50 Regex::new(r"[\{\(\[:]\s*$").unwrap(),
51 Regex::new(r"^\s*[\}\)\]]").unwrap(),
52 )
53 });
54 match language.to_lowercase().as_str() {
55 "text" => LanguageConfig::default()
56 .brackets([])
57 .auto_closing_pairs([]),
58 "json" => LanguageConfig::default()
59 .brackets([BracketPair::new("{", "}"), BracketPair::new("[", "]")])
60 .auto_closing_pairs(
61 [
62 AutoClosingPair::new("{", "}"),
63 AutoClosingPair::new("[", "]"),
64 AutoClosingPair::new("\"", "\""),
65 ]
66 .into_iter()
67 .map(|p| p.not_in([SyntaxContext::String, SyntaxContext::Comment])),
68 ),
69 "python" => LanguageConfig::default().indentation_rules(PYTHON_INDENT.clone()),
70 _ => LanguageConfig::default(),
71 }
72}
73
74#[cfg(test)]
75mod tests {
76 use super::*;
77
78 #[test]
79 fn plain_text_has_no_editing_rules() {
80 let rules = default_language_config("text");
81 assert!(rules.brackets.is_empty());
82 assert!(rules.auto_closing_pairs.unwrap().is_empty());
83 assert!(rules.indentation_rules.is_none());
84 }
85
86 #[test]
87 fn json_only_closes_json_delimiters() {
88 let rules = default_language_config("json");
89 let pairs = rules.auto_closing_pairs.unwrap();
90 assert_eq!(pairs.len(), 3);
91 assert!(
92 pairs
93 .iter()
94 .all(|p| p.open.as_ref() != "'" && p.open.as_ref() != "(")
95 );
96 }
97
98 #[test]
99 fn colon_indentation_is_language_specific() {
100 assert!(
101 default_language_config("python")
102 .indentation_rules
103 .unwrap()
104 .increase_indent_pattern
105 .unwrap()
106 .is_match("if enabled:")
107 );
108 for language in ["rust", "javascript", "json", "unknown"] {
109 assert!(
110 default_language_config(language)
111 .indentation_rules
112 .is_none()
113 );
114 }
115 }
116}