html_languageservice/language_facts/
data_provider.rs1use std::collections::HashMap;
2
3use lsp_types::{MarkupContent, MarkupKind};
4
5use crate::{
6 html_data::{Description, HTMLDataV1, IAttributeData, IReference, ITagData, IValueData},
7 utils::markup,
8};
9
10pub struct HTMLDataProvider {
12 id: String,
13 tags: Vec<ITagData>,
14 tag_map: HashMap<String, usize>,
15 global_attributes: Vec<IAttributeData>,
16 value_set_map: HashMap<String, Vec<IValueData>>,
17}
18
19pub trait IHTMLDataProvider: Send + Sync {
21 fn get_id(&self) -> &str;
24 fn is_applicable(&self, language_id: &str) -> bool;
25 fn provide_tags(&self) -> &Vec<ITagData>;
26 fn provide_attributes(&self, tag: &str) -> Vec<&IAttributeData>;
27 fn provide_values(&self, tag: &str, attribute: &str) -> Vec<&IValueData>;
28}
29
30impl HTMLDataProvider {
31 pub fn new(id: String, custom_data: HTMLDataV1) -> HTMLDataProvider {
32 let mut tag_map = HashMap::new();
33 if let Some(tags) = &custom_data.tags {
34 for (i, tag) in tags.iter().enumerate() {
35 tag_map.insert(tag.name.clone(), i);
36 }
37 }
38
39 let mut value_set_map = HashMap::new();
40
41 if let Some(value_sets) = custom_data.value_sets {
42 for vs in value_sets {
43 value_set_map.insert(vs.name, vs.values);
44 }
45 }
46
47 HTMLDataProvider {
48 id,
49 tags: custom_data.tags.unwrap_or_default(),
50 tag_map,
51 global_attributes: custom_data.global_attributes.unwrap_or_default(),
52 value_set_map,
53 }
54 }
55}
56
57impl IHTMLDataProvider for HTMLDataProvider {
58 fn get_id(&self) -> &str {
59 &self.id
60 }
61
62 fn is_applicable(&self, _language_id: &str) -> bool {
63 true
64 }
65
66 fn provide_tags(&self) -> &Vec<ITagData> {
67 &self.tags
68 }
69
70 fn provide_attributes(&self, tag: &str) -> Vec<&IAttributeData> {
71 let mut attributes = vec![];
72
73 let tag_entry_index = self.tag_map.get(&tag.to_lowercase());
74 if let Some(tag_entry_index) = tag_entry_index {
75 let tag_entry = &self.tags[*tag_entry_index];
76 for attribute in &tag_entry.attributes {
77 attributes.push(attribute);
78 }
79 }
80 for attribute in &self.global_attributes {
81 attributes.push(&attribute);
82 }
83
84 attributes
85 }
86
87 fn provide_values(&self, tag: &str, attribute: &str) -> Vec<&IValueData> {
88 let mut values = vec![];
89
90 let attribute = attribute.to_lowercase();
91
92 let tag_entry = self.tag_map.get(&tag.to_lowercase());
93 if let Some(tag_entry_index) = tag_entry {
94 let tag_entry = &self.tags[*tag_entry_index];
95 for a in &tag_entry.attributes {
96 if a.name.to_lowercase() == attribute {
97 if let Some(a_values) = &a.values {
98 for value in a_values {
99 values.push(value);
100 }
101 }
102 if let Some(value_set) = &a.value_set {
103 if let Some(set) = &self.value_set_map.get(value_set) {
104 for v in *set {
105 values.push(v);
106 }
107 }
108 }
109 }
110 }
111 }
112 for a in &self.global_attributes {
113 if a.name.to_lowercase() == attribute {
114 if let Some(a_values) = &a.values {
115 for value in a_values {
116 values.push(value);
117 }
118 }
119 if let Some(value_set) = &a.value_set {
120 if let Some(set) = &self.value_set_map.get(value_set) {
121 for v in *set {
122 values.push(v);
123 }
124 }
125 }
126 }
127 }
128
129 values
130 }
131}
132
133pub fn generate_documentation(
135 item: GenerateDocumentationItem,
136 setting: GenerateDocumentationSetting,
137) -> Option<MarkupContent> {
138 let mut result = MarkupContent {
139 kind: if setting.does_support_markdown {
140 MarkupKind::Markdown
141 } else {
142 MarkupKind::PlainText
143 },
144 value: String::new(),
145 };
146
147 if item.description.is_some() && setting.documentation {
148 let normalized_description = markup::normalize_markup_content(item.description.unwrap());
149 result.value += &normalized_description.value;
150 }
151
152 if item.references.as_deref().is_some_and(|r| r.len() > 0) && setting.references {
153 if result.value.len() > 0 {
154 result.value += "\n\n";
155 }
156 let references = item.references.unwrap();
157 if setting.does_support_markdown {
158 result.value += &references
159 .iter()
160 .map(|r| format!("[{}]({})", r.name, r.url))
161 .collect::<Vec<String>>()
162 .join(" | ");
163 } else {
164 result.value += &references
165 .iter()
166 .map(|r| format!("{}: {}", r.name, r.url))
167 .collect::<Vec<String>>()
168 .join("\n");
169 }
170 }
171
172 if result.value.len() > 0 {
173 Some(result)
174 } else {
175 None
176 }
177}
178
179pub struct GenerateDocumentationItem {
180 pub description: Option<Description>,
181 pub references: Option<Vec<IReference>>,
182}
183
184pub struct GenerateDocumentationSetting {
185 pub documentation: bool,
186 pub references: bool,
187 pub does_support_markdown: bool,
188}