css_variable_lsp/
workspace.rs1use globset::{Glob, GlobSetBuilder};
2use ls_types::Uri;
3use tokio::fs;
4use walkdir::WalkDir;
5
6use crate::manager::CssVariableManager;
7use crate::parsers::{parse_css_document, parse_html_document};
8
9pub async fn scan_workspace(
16 folders: Vec<Uri>,
17 manager: &CssVariableManager,
18 mut on_progress: impl FnMut(usize, usize),
19) -> Result<(), String> {
20 let config = manager.get_config().await;
21
22 let mut lookup_builder = GlobSetBuilder::new();
24 for pattern in &config.lookup_files {
25 if let Ok(glob) = Glob::new(pattern) {
26 lookup_builder.add(glob);
27 }
28 }
29 let lookup_set = lookup_builder
30 .build()
31 .map_err(|e| format!("Failed to build lookup glob set: {}", e))?;
32
33 let mut ignore_builder = GlobSetBuilder::new();
35 for pattern in &config.ignore_globs {
36 if let Ok(glob) = Glob::new(pattern) {
37 ignore_builder.add(glob);
38 }
39 }
40 let ignore_set = ignore_builder
41 .build()
42 .map_err(|e| format!("Failed to build ignore glob set: {}", e))?;
43
44 let mut all_files = Vec::new();
46
47 for folder_uri in folders {
48 let folder_path = match crate::path_display::to_normalized_fs_path(&folder_uri) {
49 Some(path) => path,
50 None => continue,
51 };
52
53 for entry in WalkDir::new(&folder_path)
54 .follow_links(false)
55 .into_iter()
56 .filter_map(|e| e.ok())
57 {
58 let path = entry.path();
59
60 if !path.is_file() {
62 continue;
63 }
64
65 let relative = match path.strip_prefix(&folder_path) {
67 Ok(rel) => rel,
68 Err(_) => continue,
69 };
70
71 let path_str = relative.to_string_lossy();
73
74 if ignore_set.is_match(&*path_str) {
76 continue;
77 }
78
79 if lookup_set.is_match(&*path_str) {
81 all_files.push(path.to_path_buf());
82 }
83 }
84 }
85
86 let total = all_files.len();
87
88 let lookup_map = crate::document_kind::build_lookup_extension_map(&config.lookup_files);
91
92 for (i, file_path) in all_files.iter().enumerate() {
94 on_progress(i + 1, total);
96
97 let content = match fs::read_to_string(file_path).await {
99 Ok(c) => c,
100 Err(_) => continue,
101 };
102
103 let file_uri = match Uri::from_file_path(file_path) {
105 Some(u) => u,
106 None => continue,
107 };
108
109 let path_str = file_path.to_string_lossy();
111 let kind = match crate::document_kind::resolve_document_kind(&path_str, None, &lookup_map) {
112 Some(kind) => kind,
113 None => continue,
114 };
115 let result = match kind {
116 crate::document_kind::DocumentKind::Html => {
117 parse_html_document(&content, &file_uri, manager).await
118 }
119 crate::document_kind::DocumentKind::Css => {
120 parse_css_document(&content, &file_uri, manager).await
121 }
122 crate::document_kind::DocumentKind::Js => continue,
126 };
127
128 if let Err(e) = result {
132 tracing::debug!(file = %file_path.display(), error = %e, "workspace scan: parse error");
133 }
134 }
135
136 Ok(())
137}