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