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 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 {}