1use completion::{
2 get_all_root_component_member_completions, get_block_declaration_completions,
3 get_class_completions, get_common_properties_setting_completions,
4 get_common_property_type_completion, get_id_completions, get_root_component_methods,
5 get_struct_property_setting_completions, get_struct_property_type_completion,
6 get_struct_static_member_completions,
7};
8use completion::{get_event_completions, get_struct_completion};
9use core::panic;
10use dashmap::DashMap;
11use lsp_types::request::Request;
12use pax_lang::{parse_pax_err, Rule};
13use pest::error::LineColLocation;
14use positional::is_inside_handlers_block;
15use positional::is_inside_selector_block;
16use positional::is_inside_settings_block;
17use positional::{
18 extract_positional_nodes, find_nodes_at_position, find_priority_node, find_relevant_ident,
19 find_relevant_tag, has_attribute_error, NodeType, PositionalNode,
20};
21use regex::Captures;
22use regex::Regex;
23use serde::*;
24use std::collections::HashSet;
25use std::path::PathBuf;
26
27use tower_lsp::jsonrpc::Error;
28use tower_lsp::jsonrpc::Result;
29use tower_lsp::lsp_types::*;
30use tower_lsp::{Client, LanguageServer, LspService, Server};
31
32mod index;
33use index::{
34 extract_import_positions, find_rust_file_with_macro, index_rust_file, IdentifierInfo,
35 IdentifierType, Info, InfoRequest,
36};
37
38mod positional;
39
40mod completion;
41
42use std::sync::{Arc, Mutex};
43
44use tokio::time::Duration;
45
46use ropey::Rope;
47
48#[derive(Debug, Deserialize, Serialize, Clone)]
49pub struct SymbolLocationParams {
50 symbol: SymbolData,
51}
52
53#[derive(Debug, Deserialize, Serialize, Clone)]
54struct SymbolData {
55 uri: String,
56 position: Position,
57}
58
59pub enum GetDefinitionRequest {}
60
61#[derive(Debug, Deserialize, Serialize, Clone)]
62pub struct GetDefinitionResult {
63 locations: Vec<LocationLink>,
64}
65
66impl Request for GetDefinitionRequest {
67 type Params = SymbolLocationParams;
68 type Result = GetDefinitionResult;
69 const METHOD: &'static str = "pax/getDefinition";
70}
71
72pub enum GetHoverRequest {}
73pub type GetHoverResult = String;
74
75impl Request for GetHoverRequest {
76 type Params = SymbolLocationParams;
77 type Result = GetHoverResult;
78 const METHOD: &'static str = "pax/getHover";
79}
80
81pub enum EnrichRequest {}
82
83#[allow(non_snake_case)]
84#[derive(Debug, Deserialize, Serialize, Clone)]
85pub struct EnrichParams {
86 symbol: SymbolData,
87 originatingPaxFile: String,
88}
89
90#[allow(non_snake_case)]
91#[derive(Debug, Deserialize, Serialize, Clone)]
92pub struct EnrichResult {
93 getDefinition: usize,
94 getHover: usize,
95}
96
97impl Request for EnrichRequest {
98 type Params = EnrichParams;
99 type Result = EnrichResult;
100 const METHOD: &'static str = "pax/enrich";
101}
102
103#[derive(Debug)]
104pub struct PaxComponent {
105 component_name: String,
106 identifier_map: DashMap<String, IdentifierInfo>,
107}
108
109#[derive(Debug)]
110pub struct SelectorData {
111 ids: HashSet<String>,
112 classes: HashSet<String>,
113}
114
115#[derive(Debug, Clone)]
116struct Backend {
117 client: Arc<Client>,
118 pax_map: Arc<DashMap<String, PaxComponent>>,
119 rs_to_pax_map: Arc<DashMap<String, String>>,
120 workspace_root: Arc<Mutex<Option<Url>>>,
121 pax_ast_cache: Arc<DashMap<String, Vec<PositionalNode>>>,
122 pax_selector_map: Arc<DashMap<String, SelectorData>>,
123 pending_changes: Arc<DashMap<String, DidChangeTextDocumentParams>>,
124 debounce_last_save: Arc<Mutex<std::time::Instant>>,
125 document_content: Arc<DashMap<String, Rope>>,
126}
127
128impl Backend {
129 pub async fn set_root(&self, url: Option<Url>) {
130 let mut root_guard = self.workspace_root.lock().unwrap();
131 *root_guard = url;
132 }
133
134 pub async fn handle_file(&self, pax_file: String, file_to_index: String) {
135 if let Some(component) = self.pax_map.get(&pax_file) {
136 let requests = match index_rust_file(&file_to_index, &component.identifier_map) {
137 Ok(reqs) => reqs,
138 Err(err) => {
139 eprintln!("Error indexing file {}: {:?}", file_to_index, err);
140 return;
141 }
142 };
143
144 for request in requests {
145 let backend_clone = self.clone();
146 let pax_file_clone = pax_file.clone();
147
148 tokio::spawn(async move {
149 backend_clone
150 .handle_info_request(pax_file_clone, request)
151 .await;
152 });
153 }
154 }
155 }
156
157 pub async fn handle_info_request(&self, pax_file: String, info_request: InfoRequest) {
158 if let Some(component) = self.pax_map.get(&pax_file) {
159 let symbol_data = SymbolData {
160 uri: info_request.info.path.clone(),
161 position: info_request.info.position,
162 };
163
164 let params = EnrichParams {
165 symbol: symbol_data,
166 originatingPaxFile: pax_file.clone(),
167 };
168
169 let mut new_info = info_request.info.clone();
170
171 match self.client.send_request::<EnrichRequest>(params).await {
172 Ok(response) => {
173 new_info.definition_id = Some(response.getDefinition);
174 new_info.hover_id = Some(response.getHover);
175 }
176 Err(e) => {
177 eprintln!("Error sending EnrichRequest: {:?}", e);
178 }
179 }
180
181 match &info_request.identifier_type {
182 IdentifierType::Component | IdentifierType::PaxType | IdentifierType::Enum => {
183 if let Some(mut identifier_info) =
184 component.identifier_map.remove(&info_request.identifier)
185 {
186 identifier_info.1.info = new_info;
187 component
188 .identifier_map
189 .insert((&info_request).identifier.clone(), identifier_info.1);
190 }
191 }
192 IdentifierType::Property => {
193 if let Some(mut struct_info) = component
194 .identifier_map
195 .remove(&(&info_request).owner_identifier.clone().unwrap())
196 {
197 if let Some(property) = struct_info
198 .1
199 .properties
200 .iter_mut()
201 .find(|prop| prop.identifier == info_request.identifier)
202 {
203 property.info = new_info;
204 }
205 component.identifier_map.insert(
206 (&info_request).owner_identifier.clone().unwrap().clone(),
207 struct_info.1,
208 );
209 }
210 }
211 IdentifierType::Method => {
212 if let Some(mut struct_info) = component
213 .identifier_map
214 .remove(&(&info_request).owner_identifier.clone().unwrap())
215 {
216 if let Some(method) = struct_info
217 .1
218 .methods
219 .iter_mut()
220 .find(|m| m.identifier == info_request.identifier)
221 {
222 method.info = new_info;
223 }
224 component.identifier_map.insert(
225 (&info_request).owner_identifier.clone().unwrap().clone(),
226 struct_info.1,
227 );
228 }
229 }
230 IdentifierType::EnumVariant => {
231 if let Some(mut enum_info) = component
232 .identifier_map
233 .remove(&(&info_request).owner_identifier.clone().unwrap())
234 {
235 if let Some(variant) = enum_info
236 .1
237 .variants
238 .iter_mut()
239 .find(|prop| prop.identifier == info_request.identifier)
240 {
241 variant.info = new_info;
242 }
243 component.identifier_map.insert(
244 (&info_request).owner_identifier.clone().unwrap().clone(),
245 enum_info.1,
246 );
247 }
248 }
249 }
250 }
251 }
252
253 pub async fn index_file(
254 &self,
255 pax_file: &str,
256 rust_file_path: PathBuf,
257 component_name: String,
258 ) {
259 let identifier_map: DashMap<String, IdentifierInfo> = DashMap::new();
260
261 let rust_file_path_str = rust_file_path.to_string_lossy().to_string();
262 let info_requests = match index_rust_file(&rust_file_path_str, &identifier_map) {
263 Ok(reqs) => reqs,
264 Err(err) => {
265 eprintln!("Error indexing file {}: {:?}", rust_file_path_str, err);
266 return;
267 }
268 };
269
270 for info_request in info_requests.clone() {
271 let backend_clone = self.clone();
272 let pax_file_clone = pax_file.to_string();
273 tokio::spawn(async move {
274 backend_clone
275 .handle_info_request(pax_file_clone, info_request)
276 .await;
277 });
278 }
279
280 self.pax_map.insert(
281 pax_file.to_string(),
282 PaxComponent {
283 component_name,
284 identifier_map,
285 },
286 );
287
288 self.rs_to_pax_map
289 .insert(rust_file_path_str, pax_file.to_string());
290
291 let positions = extract_import_positions(&rust_file_path);
292 for position in positions {
293 let symbol_data = SymbolData {
294 uri: rust_file_path.to_string_lossy().to_string(),
295 position,
296 };
297
298 let params = SymbolLocationParams {
299 symbol: symbol_data,
300 };
301
302 match self
303 .client
304 .send_request::<GetDefinitionRequest>(params)
305 .await
306 {
307 Ok(response) => {
308 for location_link in response.locations {
309 let target_uri = location_link.target_uri;
310 let _path = target_uri.clone().path().to_string();
311 let target_file = target_uri
312 .to_file_path()
313 .expect("Failed to convert URI to path")
314 .to_string_lossy()
315 .to_string();
316
317 let path = pax_file.to_string();
318 let backend_clone = self.clone();
319 tokio::spawn(async move {
320 backend_clone.handle_file(path, target_file).await;
321 });
322 }
323 }
324 Err(e) => {
325 self.client
326 .log_message(MessageType::INFO, format!("Error couldnt look up imports"))
327 .await;
328 eprintln!("Error sending request: {:?}", e);
329 }
330 }
331 }
332 }
333
334 fn parse_and_cache_pax_file(&self, pax: &str, uri: Url) -> Vec<Diagnostic> {
335 let parse_result = parse_pax_err(Rule::pax_component_definition, pax);
336
337 let path_str = uri.path();
338
339 match parse_result {
340 Ok(pax_component_definition) => {
341 let mut nodes = Vec::new();
342 let mut ids = HashSet::new();
343 let mut classes = HashSet::new();
344
345 extract_positional_nodes(
346 pax_component_definition.clone(),
347 &mut nodes,
348 &mut ids,
349 &mut classes,
350 );
351 self.pax_ast_cache
352 .insert(path_str.to_string(), nodes.clone());
353
354 self.pax_selector_map
355 .insert(path_str.to_string(), SelectorData { ids, classes });
356 Vec::new()
357 }
358 Err(e) => {
359 let range = match e.line_col {
360 LineColLocation::Pos((l, c)) => Range {
361 start: Position {
362 line: (l - 1) as u32,
363 character: (c - 1) as u32,
364 },
365 end: Position {
366 line: (l - 1) as u32,
367 character: (c - 1) as u32,
368 },
369 },
370 LineColLocation::Span((l_s, c_s), (l_e, c_e)) => Range {
371 start: Position {
372 line: (l_s - 1) as u32,
373 character: (c_s - 1) as u32,
374 },
375 end: Position {
376 line: (l_e - 1) as u32,
377 character: (c_e - 1) as u32,
378 },
379 },
380 };
381 vec![Diagnostic {
382 range: range,
383 message: format!("{e}"),
384 severity: Some(DiagnosticSeverity::ERROR),
385 code: None,
386 source: None,
387 related_information: None,
388 code_description: None,
389 tags: None,
390 data: None,
391 }]
392 }
393 }
394 }
395 async fn hover_id(&self, params: HoverParams) -> Result<Option<u32>> {
396 let uri_obj = ¶ms.text_document_position_params.text_document.uri;
397 let uri_path = uri_obj.path();
398 let pos = ¶ms.text_document_position_params.position;
399
400 if uri_path.ends_with(".pax") && !self.pax_map.contains_key(uri_path) {
401 self.process_pax_file(uri_obj).await;
402
403 let file_path = uri_obj
404 .to_file_path()
405 .map_err(|_| Error::invalid_params(format!("Invalid URI: {}", uri_obj)))?;
406 let file_content = std::fs::read_to_string(&file_path).map_err(|err| {
407 Error::invalid_params(format!("Failed to read {}: {}", file_path.display(), err))
408 })?;
409
410 let _ = self.parse_and_cache_pax_file(&file_content, uri_obj.clone());
411 }
412
413 if let Some(info) = self.get_info(uri_path, pos) {
414 if let Some(id) = info.hover_id {
415 return Ok(Some(id as u32));
416 }
417 }
418
419 Ok(None)
420 }
421
422 fn get_info(&self, uri: &str, pos: &Position) -> Option<Info> {
423 if let Some(component) = self.pax_map.get(uri) {
424 if let Some(cached_nodes) = self.pax_ast_cache.get(uri) {
425 let relevant_nodes = find_nodes_at_position(pos.clone(), &cached_nodes);
426 let priority_node = find_priority_node(&relevant_nodes);
427 let tag_node = find_relevant_tag(&relevant_nodes);
428 let relevant_ident = find_relevant_ident(&relevant_nodes);
429 if let Some(node) = priority_node {
430 let struct_name = if let Some(tag) = tag_node {
431 if let NodeType::Tag(tag_data) = &tag.node_type {
432 Some(tag_data.pascal_identifier.clone())
433 } else {
434 panic!("Expected NodeType::Tag, found {:?}", tag.node_type);
435 }
436 } else {
437 None
438 };
439
440 if let Some(ident) = relevant_ident {
441 if let NodeType::Identifier(ident_data) = &ident.node_type {
442 let ident_name = ident_data.identifier.clone();
443 match &node.node_type {
444 NodeType::Identifier(data) => {
445 let ident = data.identifier.clone();
446 if let Some(ident_info) =
447 component.identifier_map.get(ident.as_str())
448 {
449 return Some(ident_info.info.clone());
450 }
451 }
452 NodeType::LiteralFunction(data) => {
453 if let Some(ident_info) = component
454 .identifier_map
455 .get(component.component_name.clone().as_str())
456 {
457 if let Some(method) = ident_info
458 .methods
459 .iter()
460 .find(|m| m.identifier == data.function_name)
461 {
462 return Some(method.info.clone());
463 }
464 }
465 }
466 NodeType::LiteralEnumValue(data) => {
467 let mut struct_id = data.enum_name.clone();
468 if &struct_id == "Self" {
469 struct_id = component.component_name.clone();
470 }
471 if let Some(ident_info) =
472 component.identifier_map.get(struct_id.as_str())
473 {
474 if ident_name == data.enum_name {
475 return Some(ident_info.info.clone());
476 }
477 if let Some(variant) = ident_info
478 .variants
479 .iter()
480 .find(|p| p.identifier == data.property_name)
481 {
482 return Some(variant.info.clone());
483 }
484 }
485 }
486 NodeType::XoFunctionCall(data) => {
487 let mut struct_id = data.struct_name.clone();
488 if struct_id == "Self" {
489 struct_id = component.component_name.clone();
490 }
491 if let Some(ident_info) =
492 component.identifier_map.get(struct_id.as_str())
493 {
494 if ident_name == data.struct_name {
495 return Some(ident_info.info.clone());
496 }
497 if let Some(method) = ident_info
498 .methods
499 .iter()
500 .find(|m| m.identifier == data.function_name)
501 {
502 return Some(method.info.clone());
503 }
504 }
505 }
506 NodeType::AttributeKeyValuePair(data) => {
507 let property_names = vec![
508 "x",
509 "y",
510 "scale_x",
511 "scale_y",
512 "skew_x",
513 "skew_y",
514 "rotate",
515 "anchor_x",
516 "anchor_y",
517 "transform",
518 "width",
519 "height",
520 ];
521
522 if let Some(struct_ident) = struct_name {
523 let mut struct_id = struct_ident.clone();
524 if property_names.contains(&data.identifier.as_str()) {
525 struct_id = "CommonProperties".to_string();
526 }
527 if let Some(ident_info) =
528 component.identifier_map.get(struct_id.as_str())
529 {
530 if let Some(property) = ident_info
531 .properties
532 .iter()
533 .find(|p| p.identifier == data.identifier)
534 {
535 return Some(property.info.clone());
536 }
537 }
538 }
539 }
540 _ => {}
541 };
542 }
543 }
544 }
545 }
546 }
547 return None;
548 }
549
550 async fn definition_id(&self, params: GotoDefinitionParams) -> Result<Option<u32>> {
551 let uri = params
552 .text_document_position_params
553 .text_document
554 .uri
555 .path();
556 let pos = ¶ms.text_document_position_params.position;
557
558 if let Some(info) = self.get_info(uri, pos) {
559 if let Some(id) = info.definition_id {
560 return Ok(Some(id as u32));
561 }
562 }
563
564 Ok(None)
565 }
566
567 async fn process_pax_file(&self, uri: &Url) {
568 let file_path = uri.path();
569
570 if self.pax_map.contains_key(file_path) {
571 return;
572 }
573
574 let path = uri.clone().to_file_path().expect("Failed to get file path");
575 let directory = path.parent().expect("Failed to get parent directory");
576
577 if let Some((rust_file_path, component_name)) =
578 find_rust_file_with_macro(directory, &file_path)
579 {
580 let backend_clone = self.clone();
581 let path_str = file_path.to_string();
582 tokio::spawn(async move {
583 backend_clone
584 .index_file(&path_str, rust_file_path, component_name)
585 .await;
586 });
587 } else {
588 eprintln!("No matching Rust file found for {}", file_path);
589 }
590 }
591
592 async fn process_changes(&self, text: &str, uri: Url) {
593 let diagnostics = self.parse_and_cache_pax_file(text, uri.clone());
594
595 self.client
596 .publish_diagnostics(uri, diagnostics, None)
597 .await;
598 }
599
600 fn get_valid_setter(&self, uri: &str, pos: &Position) -> Option<String> {
601 if let Some(rope) = self.document_content.get(uri) {
602 let char_pos = rope.line_to_char(pos.line as usize) + pos.character as usize;
603
604 let start = if char_pos >= 50 { char_pos - 50 } else { 0 };
605 let text_before_pos = rope.slice(start..char_pos).to_string();
606
607 let pattern = r"(@?[A-Za-z_\d]+(=|::|:|\.| )*)";
608
609 let re = Regex::new(pattern).unwrap();
610
611 let mut largest_match: Option<String> = None;
612
613 let captures: Vec<Captures> = re.captures_iter(&text_before_pos).collect();
614
615 for captures in captures.into_iter().rev() {
616 if let Some(matched) = captures.get(0) {
617 let matched_str = matched.as_str().to_string();
618 if matched.end() == text_before_pos.len()
619 && (largest_match.is_none()
620 || matched_str.len() > largest_match.as_ref().unwrap().len())
621 {
622 largest_match = Some(matched_str.trim_end().to_string());
623 }
624 }
625 }
626
627 return largest_match;
628 }
629
630 None
631 }
632}
633
634#[tower_lsp::async_trait]
635impl LanguageServer for Backend {
636 async fn initialize(&self, params: InitializeParams) -> Result<InitializeResult> {
637 self.set_root(params.root_uri).await;
638
639 let pending_changes_clone = self.pending_changes.clone();
640 let self_clone = self.clone();
641
642 tokio::spawn(async move {
643 loop {
644 tokio::time::sleep(Duration::from_millis(500)).await;
645
646 let mut processed_keys = Vec::new();
647
648 for entry in pending_changes_clone.iter() {
649 let uri_path = entry.key().clone();
650 let change_params = entry.value().clone();
651
652 let uri = change_params.text_document.uri.clone();
653
654 if uri_path.ends_with(".pax") {
655 self_clone.process_pax_file(&uri).await;
656 if !change_params.content_changes.is_empty() {
657 self_clone
658 .process_changes(
659 &change_params.content_changes[0].text,
660 change_params.text_document.uri.clone(),
661 )
662 .await;
663 }
664 }
665 processed_keys.push(uri_path);
666 }
667
668 for key in processed_keys {
670 pending_changes_clone.remove(&key);
671 }
672 }
673 });
674
675 Ok(InitializeResult {
676 server_info: None,
677 capabilities: ServerCapabilities::default(),
678 offset_encoding: None,
679 })
680 }
681
682 async fn initialized(&self, _: tower_lsp::lsp_types::InitializedParams) {
683 self.client
684 .log_message(MessageType::INFO, "initialized!")
685 .await;
686 }
687
688 async fn shutdown(&self) -> Result<()> {
689 Ok(())
690 }
691
692 async fn did_open(&self, did_open_params: DidOpenTextDocumentParams) {
693 let uri = did_open_params.text_document.uri.clone();
694 let language_id = &did_open_params.text_document.language_id;
695 if language_id == "pax" {
696 self.process_pax_file(&uri).await;
697 let diagnostics = self
698 .parse_and_cache_pax_file(did_open_params.text_document.text.as_str(), uri.clone());
699 self.client
700 .publish_diagnostics(uri, diagnostics, None)
701 .await;
702 }
703 }
704
705 async fn did_change(&self, did_change_params: DidChangeTextDocumentParams) {
706 let uri = did_change_params.text_document.uri.clone();
707 let uri_path = uri.path().to_string();
708 let new_content = did_change_params.content_changes[0].text.clone();
709 if uri_path.ends_with(".pax") {
710 self.process_pax_file(&uri).await;
711 self.document_content
712 .insert(uri_path.clone(), Rope::from_str(&new_content));
713 }
714 self.pending_changes.insert(uri_path, did_change_params);
715 }
716
717 async fn did_save(&self, did_save_params: DidSaveTextDocumentParams) {
718 let uri_path = did_save_params.text_document.uri.path();
719
720 if uri_path.ends_with(".rs") {
721 if self.debounce_last_save.lock().unwrap().elapsed() < Duration::from_secs(10) {
722 return;
723 }
724 *self.debounce_last_save.lock().unwrap() = std::time::Instant::now();
725
726 if let Some(pax_file_path) = self.rs_to_pax_map.get(uri_path) {
727 self.pax_map.remove(pax_file_path.value());
728
729 let pax_uri = Url::from_file_path(pax_file_path.value()).unwrap();
730 self.process_pax_file(&pax_uri).await;
731 }
732 } else if uri_path.ends_with(".pax") {
733 self.pax_map.remove(uri_path);
734 self.process_pax_file(&did_save_params.text_document.uri)
735 .await;
736 }
737 }
738
739 async fn completion(
740 &self,
741 completion_params: CompletionParams,
742 ) -> Result<Option<CompletionResponse>> {
743 let uri = &completion_params.text_document_position.text_document.uri;
744 let pos = &completion_params.text_document_position.position;
745 let prior_identifier = self.get_valid_setter(uri.clone().path(), pos);
746 let selector_info = self.pax_selector_map.get(&uri.path().to_string());
747
748 let mut completions = Vec::new();
749 if let Some(cached_nodes) = self.pax_ast_cache.get(&uri.path().to_string()) {
750 let relevant_nodes = find_nodes_at_position(pos.clone(), &cached_nodes);
751 let tag_node = find_relevant_tag(&relevant_nodes);
752 let has_attribute_error = has_attribute_error(&relevant_nodes);
753 let is_inside_settings_block = is_inside_settings_block(&relevant_nodes);
754 let is_inside_handlers_block = is_inside_handlers_block(&relevant_nodes);
755 let is_inside_selector_block = is_inside_selector_block(&relevant_nodes);
756 if let Some(component) = self.pax_map.get(&uri.path().to_string()) {
757 if let Some(trigger_char) = &completion_params
758 .context
759 .and_then(|ctx| ctx.trigger_character)
760 {
761 if trigger_char == "<" {
762 for entry in component.identifier_map.iter() {
763 if entry.ty == IdentifierType::Component
764 && entry.identifier != component.component_name
765 {
766 let mut completion = CompletionItem::new_simple(
767 entry.identifier.clone(),
768 String::from(""),
769 );
770 completion.kind = Some(CompletionItemKind::CLASS);
771 completion.insert_text =
772 Some(format!("{} $0 />", entry.identifier.clone()));
773 completion.insert_text_format =
774 Some(lsp_types::InsertTextFormat::SNIPPET);
775 if let Some(prepared_completion) =
776 get_struct_completion(&entry.identifier)
777 {
778 completion = prepared_completion;
779 }
780 completions.push(completion);
781 }
782 }
783 return Ok(Some(CompletionResponse::Array(completions)));
784 } else if trigger_char == "@" {
785 if let Some(_tag) = tag_node {
786 completions.extend(get_event_completions("="));
787 return Ok(Some(CompletionResponse::Array(completions)));
788 } else if !is_inside_settings_block && !is_inside_handlers_block {
789 completions.extend(get_block_declaration_completions());
790 return Ok(Some(CompletionResponse::Array(completions)));
791 }
792 } else if trigger_char == "=" {
793 if let Some(setter) = prior_identifier {
794 if setter.contains("@") {
795 completions.extend(get_root_component_methods(&component));
796 return Ok(Some(CompletionResponse::Array(completions)));
797 } else {
798 let requested_property = setter.clone().replace("=", "");
799 if let Some(tag) = tag_node {
800 if let NodeType::Tag(tag_data) = &tag.node_type {
801 if requested_property == "class" {
802 completions.extend(get_class_completions(
803 &selector_info,
804 false,
805 false,
806 ));
807 return Ok(Some(CompletionResponse::Array(
808 completions,
809 )));
810 } else if requested_property == "id" {
811 completions.extend(get_id_completions(
812 &selector_info,
813 false,
814 false,
815 ));
816 return Ok(Some(CompletionResponse::Array(
817 completions,
818 )));
819 } else {
820 completions.extend(
821 get_struct_property_type_completion(
822 &component,
823 tag_data.pascal_identifier.clone(),
824 requested_property.clone(),
825 ),
826 );
827 }
828 }
829 }
830 completions.extend(get_common_property_type_completion(
831 &component,
832 requested_property.clone(),
833 ));
834 return Ok(Some(CompletionResponse::Array(completions)));
835 }
836 }
837 } else if trigger_char == ":" {
838 if let Some(word) = prior_identifier {
839 if word.contains("::") {
840 let requested_struct = word.clone().replace("::", "");
841 completions.extend(get_struct_static_member_completions(
842 &component,
843 requested_struct,
844 ));
845 return Ok(Some(CompletionResponse::Array(completions)));
846 } else {
847 if is_inside_handlers_block {
848 completions.extend(get_root_component_methods(&component));
849 return Ok(Some(CompletionResponse::Array(completions)));
850 } else {
851 let requested_property = word.clone().replace(":", "");
852 completions.extend(get_common_property_type_completion(
853 &component,
854 requested_property.clone(),
855 ));
856 return Ok(Some(CompletionResponse::Array(completions)));
857 }
858 }
859 }
860 } else if trigger_char == "." {
861 if let Some(word) = prior_identifier {
862 if word.contains("self") {
863 completions
864 .extend(get_all_root_component_member_completions(&component));
865 return Ok(Some(CompletionResponse::Array(completions)));
866 }
867 }
868 if is_inside_settings_block {
869 completions.extend(get_class_completions(&selector_info, true, false));
870 return Ok(Some(CompletionResponse::Array(completions)));
871 }
872 } else if trigger_char == "#" {
873 if is_inside_settings_block {
874 completions.extend(get_id_completions(&selector_info, true, false));
875 return Ok(Some(CompletionResponse::Array(completions)));
876 }
877 }
878 } else {
879 if let Some(tag) = tag_node {
880 if let NodeType::Tag(tag_data) = &tag.node_type {
881 if let Some(word) = prior_identifier.clone() {
882 if word.contains("@") {
883 completions.extend(get_root_component_methods(&component));
884 return Ok(Some(CompletionResponse::Array(completions)));
885 } else if word.contains("=") {
886 let requested_property = word.clone().replace("=", "");
887 completions.extend(get_struct_property_type_completion(
888 &component,
889 tag_data.pascal_identifier.clone(),
890 requested_property.clone(),
891 ));
892 completions.extend(get_common_property_type_completion(
893 &component,
894 requested_property.clone(),
895 ));
896 return Ok(Some(CompletionResponse::Array(completions)));
897 }
898 }
899 if !has_attribute_error {
900 completions.extend(get_struct_property_setting_completions(
901 &component,
902 tag_data.clone().pascal_identifier,
903 ));
904 completions.extend(get_common_properties_setting_completions(
905 &component, "=",
906 ));
907 return Ok(Some(CompletionResponse::Array(completions)));
908 }
909 }
910 }
911 if is_inside_settings_block {
912 if is_inside_selector_block {
913 completions
914 .extend(get_common_properties_setting_completions(&component, ":"));
915 return Ok(Some(CompletionResponse::Array(completions)));
916 }
917 if let Some(word) = prior_identifier.clone() {
918 let requested_property = word.clone().replace(":", "").replace("=", "");
919 completions.extend(get_common_property_type_completion(
920 &component,
921 requested_property.clone(),
922 ));
923 return Ok(Some(CompletionResponse::Array(completions)));
924 } else {
925 completions.extend(get_class_completions(&selector_info, true, true));
926 completions.extend(get_id_completions(&selector_info, true, true));
927 return Ok(Some(CompletionResponse::Array(completions)));
928 }
929 }
930 if is_inside_handlers_block {
931 if let Some(_) = prior_identifier.clone() {
932 completions.extend(get_root_component_methods(&component));
933 return Ok(Some(CompletionResponse::Array(completions)));
934 } else {
935 completions.extend(get_event_completions(":"));
936 return Ok(Some(CompletionResponse::Array(completions)));
937 }
938 }
939 }
940 }
941 }
942 return Ok(Some(CompletionResponse::Array(completions)));
943 }
944}
945
946pub async fn start_server() {
947 let stdin = tokio::io::stdin();
948 let stdout = tokio::io::stdout();
949
950 let (service, socket) = LspService::build(|client| Backend {
951 client: Arc::new(client),
952 pax_map: Arc::new(DashMap::new()),
953 rs_to_pax_map: Arc::new(DashMap::new()),
954 workspace_root: Arc::new(Mutex::new(None)),
955 pax_ast_cache: Arc::new(DashMap::new()),
956 pax_selector_map: Arc::new(DashMap::new()),
957 pending_changes: Arc::new(DashMap::new()),
958 debounce_last_save: Arc::new(Mutex::new(std::time::Instant::now())),
959 document_content: Arc::new(DashMap::new()),
960 })
961 .custom_method("pax/getHoverId", Backend::hover_id)
962 .custom_method("pax/getDefinitionId", Backend::definition_id)
963 .finish();
964
965 Server::new(stdin, stdout, socket).serve(service).await;
966}