html_languageservice/language_facts/
data_manager.rs

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