emmylua_code_analysis/
lib.rs1#![deny(
2 clippy::unwrap_used,
3 clippy::unwrap_in_result,
4 clippy::panic,
5 clippy::panic_in_result_fn
6)]
7
8mod compilation;
9mod config;
10mod db_index;
11mod diagnostic;
12mod locale;
13mod profile;
14mod resources;
15mod semantic;
16mod test_lib;
17mod vfs;
18
19pub use compilation::*;
20pub use config::*;
21pub use db_index::*;
22pub use diagnostic::*;
23pub use emmylua_codestyle::*;
24pub use locale::get_locale_code;
25use lsp_types::Uri;
26pub use profile::Profile;
27use resources::load_resource_std;
28pub use semantic::*;
29use std::{collections::HashSet, path::PathBuf, sync::Arc};
30pub use test_lib::VirtualWorkspace;
31use tokio_util::sync::CancellationToken;
32pub use vfs::*;
33
34#[macro_use]
35extern crate rust_i18n;
36
37rust_i18n::i18n!("./locales", fallback = "en");
38
39pub fn set_locale(locale: &str) {
40 rust_i18n::set_locale(locale);
41}
42
43#[derive(Debug)]
44pub struct EmmyLuaAnalysis {
45 pub compilation: LuaCompilation,
46 pub diagnostic: LuaDiagnostic,
47 pub emmyrc: Arc<Emmyrc>,
48 lib_workspace_counter: u32,
49}
50
51impl EmmyLuaAnalysis {
52 pub fn new() -> Self {
53 let emmyrc = Arc::new(Emmyrc::default());
54 Self {
55 compilation: LuaCompilation::new(emmyrc.clone()),
56 diagnostic: LuaDiagnostic::new(),
57 emmyrc,
58 lib_workspace_counter: 2,
59 }
60 }
61
62 pub fn init_std_lib(&mut self, create_resources_dir: Option<String>) {
63 let is_jit = matches!(self.emmyrc.runtime.version, EmmyrcLuaVersion::LuaJIT);
64 let (std_root, files) = load_resource_std(create_resources_dir, is_jit);
65 self.compilation
66 .get_db_mut()
67 .get_module_index_mut()
68 .add_workspace_root(std_root, WorkspaceId::STD);
69
70 let files = files
71 .into_iter()
72 .filter_map(|file| {
73 if file.path.ends_with(".lua") {
74 Some((PathBuf::from(file.path), Some(file.content)))
75 } else {
76 None
77 }
78 })
79 .collect::<Vec<_>>();
80 self.update_files_by_path(files);
81 }
82
83 pub fn get_file_id(&self, uri: &Uri) -> Option<FileId> {
84 self.compilation.get_db().get_vfs().get_file_id(uri)
85 }
86
87 pub fn get_uri(&self, file_id: FileId) -> Option<Uri> {
88 self.compilation.get_db().get_vfs().get_uri(&file_id)
89 }
90
91 pub fn add_main_workspace(&mut self, root: PathBuf) {
92 self.compilation
93 .get_db_mut()
94 .get_module_index_mut()
95 .add_workspace_root(root, WorkspaceId::MAIN);
96 }
97
98 pub fn add_library_workspace(&mut self, root: PathBuf) {
99 let id = WorkspaceId {
100 id: self.lib_workspace_counter,
101 };
102 self.lib_workspace_counter += 1;
103
104 self.compilation
105 .get_db_mut()
106 .get_module_index_mut()
107 .add_workspace_root(root, id);
108 }
109
110 pub fn update_file_by_uri(&mut self, uri: &Uri, text: Option<String>) -> Option<FileId> {
111 let is_removed = text.is_none();
112 let file_id = self
113 .compilation
114 .get_db_mut()
115 .get_vfs_mut()
116 .set_file_content(uri, text);
117
118 self.compilation.remove_index(vec![file_id]);
119 if !is_removed {
120 self.compilation.update_index(vec![file_id]);
121 }
122
123 Some(file_id)
124 }
125
126 pub fn update_file_by_path(&mut self, path: &PathBuf, text: Option<String>) -> Option<FileId> {
127 let uri = file_path_to_uri(&path)?;
128 self.update_file_by_uri(&uri, text)
129 }
130
131 pub fn update_files_by_uri(&mut self, files: Vec<(Uri, Option<String>)>) -> Vec<FileId> {
132 let mut removed_files = HashSet::new();
133 let mut updated_files = HashSet::new();
134 {
135 let _p = Profile::new("update files");
136 for (uri, text) in files {
137 let is_new_text = text.is_some();
138 let file_id = self
139 .compilation
140 .get_db_mut()
141 .get_vfs_mut()
142 .set_file_content(&uri, text);
143 removed_files.insert(file_id);
144 if is_new_text {
145 updated_files.insert(file_id);
146 }
147 }
148 }
149 self.compilation
150 .remove_index(removed_files.into_iter().collect());
151 let updated_files: Vec<FileId> = updated_files.into_iter().collect();
152 self.compilation.update_index(updated_files.clone());
153 updated_files
154 }
155
156 #[allow(unused)]
157 pub(crate) fn update_files_by_uri_sorted(
158 &mut self,
159 files: Vec<(Uri, Option<String>)>,
160 ) -> Vec<FileId> {
161 let mut removed_files = HashSet::new();
162 let mut updated_files = HashSet::new();
163 {
164 let _p = Profile::new("update files");
165 for (uri, text) in files {
166 let is_new_text = text.is_some();
167 let file_id = self
168 .compilation
169 .get_db_mut()
170 .get_vfs_mut()
171 .set_file_content(&uri, text);
172 removed_files.insert(file_id);
173 if is_new_text {
174 updated_files.insert(file_id);
175 }
176 }
177 }
178 self.compilation
179 .remove_index(removed_files.into_iter().collect());
180 let mut updated_files: Vec<FileId> = updated_files.into_iter().collect();
181 updated_files.sort();
182 self.compilation.update_index(updated_files.clone());
183 updated_files
184 }
185
186 pub fn remove_file_by_uri(&mut self, uri: &Uri) -> Option<FileId> {
187 if let Some(file_id) = self.compilation.get_db_mut().get_vfs_mut().remove_file(uri) {
188 self.compilation.remove_index(vec![file_id]);
189 return Some(file_id);
190 }
191
192 None
193 }
194
195 pub fn update_files_by_path(&mut self, files: Vec<(PathBuf, Option<String>)>) -> Vec<FileId> {
196 let files = files
197 .into_iter()
198 .filter_map(|(path, text)| {
199 let uri = file_path_to_uri(&path)?;
200 Some((uri, text))
201 })
202 .collect();
203 self.update_files_by_uri(files)
204 }
205
206 pub fn update_config(&mut self, config: Arc<Emmyrc>) {
207 self.emmyrc = config.clone();
208 self.compilation.update_config(config.clone());
209 self.diagnostic.update_config(config);
210 }
211
212 pub fn get_emmyrc(&self) -> Arc<Emmyrc> {
213 self.emmyrc.clone()
214 }
215
216 pub fn diagnose_file(
217 &self,
218 file_id: FileId,
219 cancel_token: CancellationToken,
220 ) -> Option<Vec<lsp_types::Diagnostic>> {
221 self.diagnostic
222 .diagnose_file(&self.compilation, file_id, cancel_token)
223 }
224
225 pub fn reindex(&mut self) {
226 let module = self.compilation.get_db().get_module_index();
227 let std_file_ids = module.get_std_file_ids();
228 let main_file_ids = module.get_main_workspace_file_ids();
229 let lib_file_ids = module.get_lib_file_ids();
230 self.compilation.clear_index();
231
232 self.compilation.update_index(std_file_ids);
233 self.compilation.update_index(lib_file_ids);
234 self.compilation.update_index(main_file_ids);
235 }
236
237 pub fn cleanup_nonexistent_files(&mut self) {
239 let mut files_to_remove = Vec::new();
240
241 let vfs = self.compilation.get_db().get_vfs();
243 for file_id in vfs.get_all_file_ids() {
244 if let Some(path) = vfs.get_file_path(&file_id) {
245 if !path.exists() {
246 if let Some(uri) = file_path_to_uri(path) {
247 files_to_remove.push(uri);
248 }
249 }
250 }
251 }
252
253 for uri in files_to_remove {
255 self.remove_file_by_uri(&uri);
256 }
257 }
258}
259
260unsafe impl Send for EmmyLuaAnalysis {}
261unsafe impl Sync for EmmyLuaAnalysis {}