html_languageservice/language_facts/
data_manager.rs

1use lazy_static::lazy_static;
2use serde_json::{json, Value};
3
4use super::{
5    data_provider::{HTMLDataProvider, IHTMLDataProvider},
6    web_custom_data::HTML_DATA,
7};
8
9/// Provides tags, attributes, and attribute value and so on,
10/// for completion proposals and hover information.
11/// It has standard data built-in and can be customized
12pub struct HTMLDataManager {
13    data_providers: Vec<Box<dyn IHTMLDataProvider>>,
14}
15
16impl HTMLDataManager {
17    pub fn new(
18        use_default_data_provider: bool,
19        custom_data_providers: Option<Vec<Box<dyn IHTMLDataProvider>>>,
20    ) -> HTMLDataManager {
21        let mut data_manager = HTMLDataManager {
22            data_providers: vec![],
23        };
24        data_manager.set_data_providers(
25            use_default_data_provider,
26            custom_data_providers.unwrap_or(vec![]),
27        );
28        data_manager
29    }
30
31    /// Set up a data provider, and the old data will be cleaned
32    pub fn set_data_providers(
33        &mut self,
34        built_in: bool,
35        mut providers: Vec<Box<dyn IHTMLDataProvider>>,
36    ) {
37        self.data_providers.clear();
38        if built_in {
39            let data = serde_json::from_str(HTML_DATA).unwrap();
40            self.data_providers
41                .push(Box::new(HTMLDataProvider::new("html5".to_string(), data)));
42        }
43        self.data_providers.append(&mut providers);
44    }
45
46    pub fn get_data_providers(&self) -> &Vec<Box<dyn IHTMLDataProvider>> {
47        &self.data_providers
48    }
49
50    /// Is the tag void element
51    ///
52    /// `void_elements` is from `get_void_elements`, and you should cache it to avoid duplicate void_elements generation
53    pub fn is_void_element(&self, tag: &str, void_elements: &Vec<String>) -> bool {
54        void_elements.contains(&tag.to_string())
55    }
56
57    /// Get `void_elements` from data_provider and you should cache it if you make sure it doesn't change
58    pub fn get_void_elements(&self, language_id: &str) -> Vec<String> {
59        let mut void_tags: Vec<String> = vec![];
60        for provider in &self.data_providers {
61            if provider.is_applicable(language_id) {
62                provider
63                    .provide_tags()
64                    .iter()
65                    .filter(|tag| tag.void.is_some_and(|v| v))
66                    .for_each(|tag| void_tags.push(tag.name.clone()))
67            }
68        }
69        void_tags.sort();
70        void_tags
71    }
72
73    /// Is the `attr` of `tag` a path attribute
74    pub fn is_path_attribute(&self, tag: &str, attr: &str) -> bool {
75        if ["src", "href"].contains(&attr) {
76            return true;
77        }
78        let value = PATH_TAG_AND_ATTR.as_object().unwrap().get(tag);
79        if let Some(value) = value {
80            if value.is_array() {
81                value
82                    .as_array()
83                    .unwrap()
84                    .contains(&Value::String(attr.to_string()))
85            } else {
86                value.as_str().unwrap() == attr
87            }
88        } else {
89            false
90        }
91    }
92}
93
94impl Default for HTMLDataManager {
95    fn default() -> Self {
96        HTMLDataManager::new(true, None)
97    }
98}
99
100lazy_static! {
101    static ref PATH_TAG_AND_ATTR: Value = json!({
102        // HTML 4
103        "a": "href",
104        "area": "href",
105        "body": "background",
106        "blockquote": "cite",
107        "del": "cite",
108        "form": "action",
109        "frame": ["src", "longdesc"],
110        "img": ["src", "longdesc"],
111        "ins": "cite",
112        "link": "href",
113        "object": "data",
114        "q": "cite",
115        "script": "src",
116        // HTML 5
117        "audio": "src",
118        "button": "formaction",
119        "command": "icon",
120        "embed": "src",
121        "html": "manifest",
122        "input": ["src", "formaction"],
123        "source": "src",
124        "track": "src",
125        "video": ["src", "poster"]
126    });
127}