emmylua_code_analysis/
lib.rs

1#![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    pub fn remove_file_by_uri(&mut self, uri: &Uri) -> Option<FileId> {
157        if let Some(file_id) = self.compilation.get_db_mut().get_vfs_mut().remove_file(uri) {
158            self.compilation.remove_index(vec![file_id]);
159            return Some(file_id);
160        }
161
162        None
163    }
164
165    pub fn update_files_by_path(&mut self, files: Vec<(PathBuf, Option<String>)>) -> Vec<FileId> {
166        let files = files
167            .into_iter()
168            .filter_map(|(path, text)| {
169                let uri = file_path_to_uri(&path)?;
170                Some((uri, text))
171            })
172            .collect();
173        self.update_files_by_uri(files)
174    }
175
176    pub fn update_config(&mut self, config: Arc<Emmyrc>) {
177        self.emmyrc = config.clone();
178        self.compilation.update_config(config.clone());
179        self.diagnostic.update_config(config);
180    }
181
182    pub fn get_emmyrc(&self) -> Arc<Emmyrc> {
183        self.emmyrc.clone()
184    }
185
186    pub fn diagnose_file(
187        &self,
188        file_id: FileId,
189        cancel_token: CancellationToken,
190    ) -> Option<Vec<lsp_types::Diagnostic>> {
191        self.diagnostic
192            .diagnose_file(&self.compilation, file_id, cancel_token)
193    }
194
195    pub fn reindex(&mut self) {
196        let module = self.compilation.get_db().get_module_index();
197        let std_file_ids = module.get_std_file_ids();
198        let main_file_ids = module.get_main_workspace_file_ids();
199        let lib_file_ids = module.get_lib_file_ids();
200        self.compilation.clear_index();
201
202        self.compilation.update_index(std_file_ids);
203        self.compilation.update_index(lib_file_ids);
204        self.compilation.update_index(main_file_ids);
205    }
206}
207
208unsafe impl Send for EmmyLuaAnalysis {}
209unsafe impl Sync for EmmyLuaAnalysis {}