From 482591947c9d0ba71c673b58a690ae3354cbd1c9 Mon Sep 17 00:00:00 2001 From: Pawel Bogut Date: Fri, 6 Oct 2023 10:17:42 +0200 Subject: [PATCH 01/16] feat: modify glob to search only through selected folders --- src/js.rs | 40 ++++++++++----- src/lsp/definition.rs | 2 + src/php.rs | 111 +++++++++++++++++++++++++----------------- 3 files changed, 98 insertions(+), 55 deletions(-) diff --git a/src/js.rs b/src/js.rs index 7571d96..90d95f8 100644 --- a/src/js.rs +++ b/src/js.rs @@ -32,19 +32,37 @@ pub struct JsCompletion { } pub fn update_index(index: &ArcIndexer, path: &PathBuf) { - let modules = glob(&path.append(&["**", "requirejs-config.js"]).to_path_string()) - .expect("Failed to read glob pattern"); + // if current workspace is magento module + process_glob(index, &path.append(&["view", "*", "requirejs-config.js"])); + // if current workspace is magento installation + process_glob( + index, + &path.append(&["vendor", "*", "*", "view", "*", "requirejs-config.js"]), + ); + process_glob( + index, + &path.append(&["vendor", "*", "*", "Magento_Theme", "requirejs-config.js"]), + ); + process_glob( + index, + &path.append(&["app", "code", "*", "*", "view", "*", "requirejs-config.js"]), + ); + process_glob( + index, + &path.append(&["app", "design", "**", "requirejs-config.js"]), + ); +} - for require_config in modules { - require_config.map_or_else( - |_e| panic!("buhu"), - |file_path| { - let content = std::fs::read_to_string(&file_path) - .expect("Should have been able to read the file"); +fn process_glob(index: &ArcIndexer, glob_path: &PathBuf) { + let modules = glob(&glob_path.to_path_string()) + .expect("Failed to read glob pattern") + .filter_map(Result::ok); - update_index_from_config(index, &content, &file_path.get_area()); - }, - ); + for file_path in modules { + let content = + std::fs::read_to_string(&file_path).expect("Should have been able to read the file"); + + update_index_from_config(index, &content, &file_path.get_area()); } } diff --git a/src/lsp/definition.rs b/src/lsp/definition.rs index 71bded0..041b6c1 100644 --- a/src/lsp/definition.rs +++ b/src/lsp/definition.rs @@ -42,10 +42,12 @@ pub fn get_location_from_params( path_to_location(&path).map(|location| vec![location]) } Some(M2Item::Component(comp)) => { + dbg!(&comp); let mut result = vec![]; let workspace_paths = index.lock().workspace_paths(); for path in workspace_paths { let path = path.append(&["lib", "web", &comp]).append_ext("js"); + dbg!(&path); if let Some(location) = path_to_location(&path) { result.push(location); } diff --git a/src/php.rs b/src/php.rs index 66cba98..4305ce1 100644 --- a/src/php.rs +++ b/src/php.rs @@ -84,54 +84,77 @@ fn register_param_to_module(param: &str) -> Option { } pub fn update_index(index: &ArcIndexer, path: &PathBuf) { - let modules = glob(&path.append(&["**", "registration.php"]).to_path_string()) - .expect("Failed to read glob pattern"); + // if current workspace is magento module + process_glob(index, &path.append(&["registration.php"])); + // if current workspace is magento installation + process_glob( + index, + &path.append(&["vendor", "*", "*", "registration.php"]), + ); + process_glob( + index, + &path.append(&["app", "code", "*", "*", "registration.php"]), + ); + process_glob( + index, + &path.append(&[ + "vendor", + "magento", + "magento2-base", + "setup", + "src", + "Magento", + "Setup", + "registration.php", + ]), + ); +} + +fn process_glob(index: &ArcIndexer, glob_path: &PathBuf) { + let modules = glob(&glob_path.to_path_string()) + .expect("Failed to read glob pattern") + .filter_map(Result::ok); let query = queries::php_registration(); - for moule_registration in modules { - moule_registration.map_or_else( - |_e| panic!("buhu"), - |file_path| { - if file_path.is_test() { - return; - } + for file_path in modules { + if file_path.is_test() { + return; + } + + let content = + std::fs::read_to_string(&file_path).expect("Should have been able to read the file"); + + let tree = tree_sitter_parsers::parse(&content, "php"); + let mut cursor = QueryCursor::new(); + let matches = cursor.matches(query, tree.root_node(), content.as_bytes()); + for m in matches { + let mod_name = get_node_text(m.captures[1].node, &content); + let mod_name = mod_name.trim_matches('"').trim_matches('\''); + + let mut parent = file_path.clone(); + parent.pop(); - let content = std::fs::read_to_string(&file_path) - .expect("Should have been able to read the file"); - - let tree = tree_sitter_parsers::parse(&content, "php"); - let mut cursor = QueryCursor::new(); - let matches = cursor.matches(query, tree.root_node(), content.as_bytes()); - for m in matches { - let mod_name = get_node_text(m.captures[1].node, &content); - let mod_name = mod_name.trim_matches('"').trim_matches('\''); - - let mut parent = file_path.clone(); - parent.pop(); - - index.lock().add_module_path(mod_name, parent.clone()); - - match register_param_to_module(mod_name) { - Some(M2Module::Module(m)) => { - index - .lock() - .add_module(mod_name) - .add_module_path(&m, parent); - } - Some(M2Module::Library(l)) => { - index.lock().add_module_path(&l, parent); - } - Some(M2Module::FrontTheme(t)) => { - index.lock().add_front_theme_path(&t, parent); - } - Some(M2Module::AdminTheme(t)) => { - index.lock().add_admin_theme_path(&t, parent); - } - _ => (), - } + index.lock().add_module_path(mod_name, parent.clone()); + + match register_param_to_module(mod_name) { + Some(M2Module::Module(m)) => { + index + .lock() + .add_module(mod_name) + .add_module_path(&m, parent); + } + Some(M2Module::Library(l)) => { + index.lock().add_module_path(&l, parent); + } + Some(M2Module::FrontTheme(t)) => { + index.lock().add_front_theme_path(&t, parent); } - }, - ); + Some(M2Module::AdminTheme(t)) => { + index.lock().add_admin_theme_path(&t, parent); + } + _ => (), + } + } } } From bb0cc15cb1409838d8f2bdbb314bf40c601bd8d8 Mon Sep 17 00:00:00 2001 From: Pawel Bogut Date: Sat, 7 Oct 2023 23:31:00 +0200 Subject: [PATCH 02/16] refactor: reduce string conversion where possible --- src/indexer.rs | 31 ++++++++----- src/js.rs | 50 ++++++++++---------- src/lsp/completion.rs | 27 +++++------ src/lsp/definition.rs | 4 +- src/m2.rs | 33 ++++++-------- src/main.rs | 12 ++--- src/php.rs | 40 +++++++--------- src/ts.rs | 8 ++-- src/xml.rs | 104 ++++++++++++++++++++---------------------- 9 files changed, 146 insertions(+), 163 deletions(-) diff --git a/src/indexer.rs b/src/indexer.rs index 7e9774b..4160ba3 100644 --- a/src/indexer.rs +++ b/src/indexer.rs @@ -92,21 +92,30 @@ impl Indexer { } pub fn add_module(&mut self, module: &str) -> &mut Self { - self.magento_modules.push(module.to_string()); + self.magento_modules.push(module.into()); self } - pub fn add_module_path(&mut self, module: &str, path: PathBuf) -> &mut Self { - self.magento_module_paths.insert(module.to_string(), path); + pub fn add_module_path(&mut self, module: S, path: PathBuf) -> &mut Self + where + S: Into, + { + self.magento_module_paths.insert(module.into(), path); self } - pub fn add_admin_theme_path(&mut self, name: &str, path: PathBuf) { - self.magento_admin_themes.insert(name.to_string(), path); + pub fn add_admin_theme_path(&mut self, name: S, path: PathBuf) + where + S: Into, + { + self.magento_admin_themes.insert(name.into(), path); } - pub fn add_front_theme_path(&mut self, name: &str, path: PathBuf) { - self.magento_front_themes.insert(name.to_string(), path); + pub fn add_front_theme_path(&mut self, name: S, path: PathBuf) + where + S: Into, + { + self.magento_front_themes.insert(name.into(), path); } pub fn get_component_map(&self, name: &str, area: &M2Area) -> Option<&String> { @@ -120,18 +129,18 @@ impl Indexer { .collect() } - pub fn add_component_map(&mut self, name: &str, val: S, area: &M2Area) -> Option + pub fn add_component_map(&mut self, name: S, val: S, area: &M2Area) -> Option where S: Into, { - self.js_maps[area.id()].insert(name.to_string(), val.into()) + self.js_maps[area.id()].insert(name.into(), val.into()) } - pub fn add_component_mixin(&mut self, name: &str, val: S) -> Option + pub fn add_component_mixin(&mut self, name: S, val: S) -> Option where S: Into, { - self.js_mixins.insert(name.to_string(), val.into()) + self.js_mixins.insert(name.into(), val.into()) } pub fn list_front_themes_paths(&self) -> Vec<&PathBuf> { diff --git a/src/js.rs b/src/js.rs index 90d95f8..17ce84c 100644 --- a/src/js.rs +++ b/src/js.rs @@ -54,7 +54,7 @@ pub fn update_index(index: &ArcIndexer, path: &PathBuf) { } fn process_glob(index: &ArcIndexer, glob_path: &PathBuf) { - let modules = glob(&glob_path.to_path_string()) + let modules = glob(glob_path.to_path_str()) .expect("Failed to read glob pattern") .filter_map(Result::ok); @@ -113,7 +113,7 @@ fn get_item_from_pos(index: &Indexer, content: &str, path: &Path, pos: Position) for m in matches { if node_at_position(m.captures[1].node, pos) { let text = get_node_text(m.captures[1].node, content); - let text = resolve_component_text(index, &text, &path.to_path_buf().get_area())?; + let text = resolve_component_text(index, text, &path.to_path_buf().get_area())?; return text_to_component(index, text, path); } } @@ -121,25 +121,27 @@ fn get_item_from_pos(index: &Indexer, content: &str, path: &Path, pos: Position) None } -pub fn resolve_component_text(index: &Indexer, text: &str, area: &M2Area) -> Option { +pub fn resolve_component_text<'a>( + index: &'a Indexer, + text: &'a str, + area: &M2Area, +) -> Option<&'a str> { index.get_component_map(text, area).map_or_else( || { - area.lower_area().map_or_else( - || Some(text.to_string()), - |a| resolve_component_text(index, text, &a), - ) + area.lower_area() + .map_or_else(|| Some(text), |a| resolve_component_text(index, text, &a)) }, |t| resolve_component_text(index, t, area), ) } -pub fn text_to_component(index: &Indexer, text: String, path: &Path) -> Option { +pub fn text_to_component(index: &Indexer, text: &str, path: &Path) -> Option { let begining = text.split('/').next().unwrap_or(""); if begining.chars().next().unwrap_or('a') == '.' { let mut path = path.to_path_buf(); path.pop(); - Some(M2Item::RelComponent(text, path)) + Some(M2Item::RelComponent(text.into(), path)) } else if text.split('/').count() > 1 && begining.matches('_').count() == 1 && begining.chars().next().unwrap_or('a').is_uppercase() @@ -149,11 +151,11 @@ pub fn text_to_component(index: &Indexer, text: String, path: &Path) -> Option index.add_component_map(&key, val, area), - Some(JSTypes::Mixins) => index.add_component_mixin(&key, val), + Some(JSTypes::Map | JSTypes::Paths) => index.add_component_map(key, val, area), + Some(JSTypes::Mixins) => index.add_component_mixin(key, val), None => continue, }; } @@ -179,7 +181,7 @@ fn update_index_from_config(index: &ArcIndexer, content: &str, area: &M2Area) { } fn get_kind(node: Node, content: &str) -> Option { - match get_node_text(node, content).as_str() { + match get_node_text(node, content) { "map" => Some(JSTypes::Map), "paths" => Some(JSTypes::Paths), "mixins" => Some(JSTypes::Mixins), @@ -187,21 +189,17 @@ fn get_kind(node: Node, content: &str) -> Option { } } -fn get_node_text(node: Node, content: &str) -> String { +fn get_node_text<'a>(node: Node, content: &'a str) -> &'a str { let result = node .utf8_text(content.as_bytes()) .unwrap_or("") - .trim_matches('\\') - .to_string(); + .trim_matches('\\'); if node.kind() == "string" { - match get_node_text(node.child(0).unwrap_or(node), content) + get_node_text(node.child(0).unwrap_or(node), content) .chars() .next() - { - Some(trim) => result.trim_matches(trim).to_string(), - None => result, - } + .map_or(result, |trim| result.trim_matches(trim)) } else { result } @@ -281,8 +279,8 @@ mod test { assert_eq!( item, Some(M2Item::ModComponent( - "Some_Module".to_string(), - "some/view".to_string(), + "Some_Module".into(), + "some/view".into(), PathBuf::from("/a/b/c/Some_Module") )) ); @@ -298,7 +296,7 @@ mod test { "#, "/a/b/c", ); - assert_eq!(item, Some(M2Item::Component("jquery".to_string()))); + assert_eq!(item, Some(M2Item::Component("jquery".into()))); } #[test] @@ -313,7 +311,7 @@ mod test { ); assert_eq!( item, - Some(M2Item::Component("jquery-ui-modules/widget".to_string())) + Some(M2Item::Component("jquery-ui-modules/widget".into())) ); } diff --git a/src/lsp/completion.rs b/src/lsp/completion.rs index 6f364d5..ef802f3 100644 --- a/src/lsp/completion.rs +++ b/src/lsp/completion.rs @@ -145,15 +145,12 @@ fn completion_for_classes_full( let module_class = module_name.replace('_', "\\"); let module_path = index.lock().get_module_path(&module_name)?; - let candidates = glob(&module_path.append(&["**", "*.php"]).to_path_string()) + let candidates = glob(module_path.append(&["**", "*.php"]).to_path_str()) .expect("Failed to read glob pattern"); let mut classes = vec![]; for p in candidates { let path = p.map_or_else(|_| std::path::PathBuf::new(), |p| p); - let rel_path = path - .relative_to(&module_path) - .string_components() - .join("\\"); + let rel_path = path.relative_to(&module_path).str_components().join("\\"); let class_suffix = rel_path.trim_end_matches(".php"); let class = format!("{}\\{}", &module_class, class_suffix); @@ -187,13 +184,13 @@ fn completion_for_template( Some(path) => { let mut files = vec![]; for area_string in area.path_candidates() { - let view_path = path.append(&["view", &area_string, "templates"]); + let view_path = path.append(&["view", area_string, "templates"]); let glob_path = view_path.append(&["**", "*.phtml"]); - files.extend(glob::glob(&glob_path.to_path_string()).ok()?.map(|file| { + files.extend(glob::glob(glob_path.to_path_str()).ok()?.map(|file| { let path = file .unwrap_or_default() .relative_to(&view_path) - .string_components() + .str_components() .join("/"); String::from(module_name) + "::" + &path })); @@ -217,13 +214,13 @@ fn completion_for_component( let mut files = vec![]; if let Some(path) = index.lock().get_module_path(module_name) { for area in area.path_candidates() { - let view_path = path.append(&["view", &area, "web"]); + let view_path = path.append(&["view", area, "web"]); let glob_path = view_path.append(&["**", "*.js"]); - files.extend(glob::glob(&glob_path.to_path_string()).ok()?.map(|file| { + files.extend(glob::glob(glob_path.to_path_str()).ok()?.map(|file| { let path = file .unwrap_or_default() .relative_to(&view_path) - .string_components() + .str_components() .join("/"); let path = path.trim_end_matches(".js"); String::from(module_name) + "/" + path @@ -234,11 +231,11 @@ fn completion_for_component( for path in workspaces { let view_path = path.append(&["lib", "web"]); let glob_path = view_path.append(&["**", "*.js"]); - files.extend(glob::glob(&glob_path.to_path_string()).ok()?.map(|file| { + files.extend(glob::glob(glob_path.to_path_str()).ok()?.map(|file| { let path = file .unwrap_or_default() .relative_to(&view_path) - .string_components() + .str_components() .join("/"); path.trim_end_matches(".js").to_string() })); @@ -260,11 +257,11 @@ fn completion_for_component( for path in workspaces { let view_path = path.append(&["lib", "web"]); let glob_path = view_path.append(&["**", "*.js"]); - modules.extend(glob::glob(&glob_path.to_path_string()).ok()?.map(|file| { + modules.extend(glob::glob(glob_path.to_path_str()).ok()?.map(|file| { let path = file .unwrap_or_default() .relative_to(&view_path) - .string_components() + .str_components() .join("/"); path.trim_end_matches(".js").to_string() })); diff --git a/src/lsp/definition.rs b/src/lsp/definition.rs index 041b6c1..f580d28 100644 --- a/src/lsp/definition.rs +++ b/src/lsp/definition.rs @@ -27,7 +27,7 @@ pub fn get_location_from_params( for area in [M2Area::Frontend, M2Area::Adminhtml, M2Area::Base] { let comp_path = mod_path - .append(&["view", &area.to_string(), "web", &file_path]) + .append(&["view", area.to_str(), "web", &file_path]) .append_ext("js"); if let Some(location) = path_to_location(&comp_path) { result.push(location); @@ -114,7 +114,7 @@ fn add_phtml_in_mod_location( let mod_path = index.lock().get_module_path(mod_name); if let Some(path) = mod_path { for area in area.path_candidates() { - let templ_path = path.append(&["view", &area, "templates", template]); + let templ_path = path.append(&["view", area, "templates", template]); if let Some(location) = path_to_location(&templ_path) { result.push(location); } diff --git a/src/m2.rs b/src/m2.rs index d88a1d7..563a3ae 100644 --- a/src/m2.rs +++ b/src/m2.rs @@ -25,15 +25,11 @@ pub enum M2Area { } impl M2Area { - pub fn path_candidates(&self) -> Vec { + pub fn path_candidates(&self) -> Vec<&str> { match self { - Self::Frontend => vec!["frontend".to_string(), "base".to_string()], - Self::Adminhtml => vec!["adminhtml".to_string(), "base".to_string()], - Self::Base => vec![ - "frontend".to_string(), - "adminhtml".to_string(), - "base".to_string(), - ], + Self::Frontend => vec!["frontend", "base"], + Self::Adminhtml => vec!["adminhtml", "base"], + Self::Base => vec!["frontend", "adminhtml", "base"], } } @@ -43,14 +39,12 @@ impl M2Area { Self::Base => None, } } -} -impl ToString for M2Area { - fn to_string(&self) -> String { + pub const fn to_str(&self) -> &str { match self { - Self::Frontend => "frontend".to_string(), - Self::Adminhtml => "adminhtml".to_string(), - Self::Base => "base".to_string(), + Self::Frontend => "frontend", + Self::Adminhtml => "adminhtml", + Self::Base => "base", } } } @@ -63,7 +57,7 @@ pub trait M2Uri { #[allow(clippy::module_name_repetitions)] pub trait M2Path { fn has_components(&self, parts: &[&str]) -> bool; - fn string_components(&self) -> Vec; + fn str_components(&self) -> Vec<&str>; fn relative_to>(&self, base: P) -> PathBuf; fn append(&self, parts: &[&str]) -> Self; fn append_ext(&self, ext: &str) -> Self; @@ -71,7 +65,7 @@ pub trait M2Path { fn is_frontend(&self) -> bool; fn is_test(&self) -> bool; fn get_area(&self) -> M2Area; - fn to_path_string(&self) -> String; + fn to_path_str(&self) -> &str; } impl M2Path for PathBuf { @@ -103,10 +97,9 @@ impl M2Path for PathBuf { self.strip_prefix(base).unwrap_or(self).to_path_buf() } - fn to_path_string(&self) -> String { + fn to_path_str(&self) -> &str { self.to_str() .expect("PathBuf should convert to path String") - .to_string() } fn get_area(&self) -> M2Area { @@ -125,9 +118,9 @@ impl M2Path for PathBuf { } } - fn string_components(&self) -> Vec { + fn str_components(&self) -> Vec<&str> { self.components() - .map(|c| c.as_os_str().to_str().unwrap_or_default().to_string()) + .map(|c| c.as_os_str().to_str().unwrap_or_default()) .collect() } diff --git a/src/main.rs b/src/main.rs index a18fdb3..b6171b3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -35,12 +35,12 @@ fn main() -> Result<(), Box> { completion_provider: Some(CompletionOptions { resolve_provider: Some(false), trigger_characters: Some(vec![ - ">".to_string(), - "\"".to_string(), - "'".to_string(), - ":".to_string(), - "\\".to_string(), - "/".to_string(), + String::from(">"), + String::from("\""), + String::from("'"), + String::from(":"), + String::from("\\"), + String::from("/"), ]), work_done_progress_options: WorkDoneProgressOptions { work_done_progress: None, diff --git a/src/php.rs b/src/php.rs index 4305ce1..cdb493c 100644 --- a/src/php.rs +++ b/src/php.rs @@ -9,15 +9,9 @@ use crate::{ indexer::ArcIndexer, m2::M2Path, queries, - ts::{get_node_text, get_range_from_node}, + ts::{self, get_range_from_node}, }; -#[derive(Debug, Clone)] -pub struct Callable { - pub class: String, - pub method: Option, -} - #[derive(Debug, Clone)] pub struct PHPClass { pub fqn: String, @@ -50,9 +44,9 @@ enum M2Module { fn register_param_to_module(param: &str) -> Option { if param.matches('/').count() == 2 { if param.starts_with("frontend") { - Some(M2Module::FrontTheme(param.to_string())) + Some(M2Module::FrontTheme(param.into())) } else { - Some(M2Module::AdminTheme(param.to_string())) + Some(M2Module::AdminTheme(param.into())) } } else if param.matches('/').count() == 1 { let mut parts = param.splitn(2, '/'); @@ -111,7 +105,7 @@ pub fn update_index(index: &ArcIndexer, path: &PathBuf) { } fn process_glob(index: &ArcIndexer, glob_path: &PathBuf) { - let modules = glob(&glob_path.to_path_string()) + let modules = glob(glob_path.to_path_str()) .expect("Failed to read glob pattern") .filter_map(Result::ok); @@ -128,8 +122,9 @@ fn process_glob(index: &ArcIndexer, glob_path: &PathBuf) { let mut cursor = QueryCursor::new(); let matches = cursor.matches(query, tree.root_node(), content.as_bytes()); for m in matches { - let mod_name = get_node_text(m.captures[1].node, &content); - let mod_name = mod_name.trim_matches('"').trim_matches('\''); + let mod_name = ts::get_node_str(m.captures[1].node, &content) + .trim_matches('"') + .trim_matches('\''); let mut parent = file_path.clone(); parent.pop(); @@ -138,19 +133,16 @@ fn process_glob(index: &ArcIndexer, glob_path: &PathBuf) { match register_param_to_module(mod_name) { Some(M2Module::Module(m)) => { - index - .lock() - .add_module(mod_name) - .add_module_path(&m, parent); + index.lock().add_module(mod_name).add_module_path(m, parent); } Some(M2Module::Library(l)) => { - index.lock().add_module_path(&l, parent); + index.lock().add_module_path(l, parent); } Some(M2Module::FrontTheme(t)) => { - index.lock().add_front_theme_path(&t, parent); + index.lock().add_front_theme_path(t, parent); } Some(M2Module::AdminTheme(t)) => { - index.lock().add_admin_theme_path(&t, parent); + index.lock().add_admin_theme_path(t, parent); } _ => (), } @@ -181,12 +173,12 @@ pub fn parse_php_file(file_path: &PathBuf) -> Option { } if m.pattern_index == 3 { let method_node = m.captures[1].node; - let method_name = method_node.utf8_text(content.as_bytes()).unwrap_or(""); + let method_name = ts::get_node_str(method_node, &content); if !method_name.is_empty() { methods.insert( - method_name.to_string(), + method_name.into(), PHPMethod { - name: method_name.to_string(), + name: method_name.into(), range: get_range_from_node(method_node), }, ); @@ -197,9 +189,9 @@ pub fn parse_php_file(file_path: &PathBuf) -> Option { let const_name = const_node.utf8_text(content.as_bytes()).unwrap_or(""); if !const_name.is_empty() { constants.insert( - const_name.to_string(), + const_name.into(), PHPConst { - name: const_name.to_string(), + name: const_name.into(), range: get_range_from_node(const_node), }, ); diff --git a/src/ts.rs b/src/ts.rs index 618dfa5..3657069 100644 --- a/src/ts.rs +++ b/src/ts.rs @@ -18,14 +18,13 @@ pub fn get_node_text_before_pos(node: Node, content: &str, pos: Position) -> Str let text = node .utf8_text(content.as_bytes()) .unwrap_or("") - .trim_matches('\\') - .to_string(); + .trim_matches('\\'); let node_start_pos = node.start_position(); let node_end_pos = node.end_position(); let text = if node_end_pos.row == node_start_pos.row { - text + text.to_string() } else { let take_lines = pos.line as usize - node_start_pos.row; text.split('\n') @@ -59,11 +58,10 @@ pub fn get_node_text_before_pos(node: Node, content: &str, pos: Position) -> Str } } -pub fn get_node_text(node: Node, content: &str) -> String { +pub fn get_node_str<'a>(node: Node, content: &'a str) -> &'a str { node.utf8_text(content.as_bytes()) .unwrap_or("") .trim_matches('\\') - .to_string() } pub fn node_at_position(node: Node, pos: Position) -> bool { diff --git a/src/xml.rs b/src/xml.rs index 5a1be6e..2ca4e81 100644 --- a/src/xml.rs +++ b/src/xml.rs @@ -10,7 +10,7 @@ use crate::{ js, m2::{M2Area, M2Item, M2Path}, queries, - ts::{get_node_text, get_node_text_before_pos, node_at_position, node_last_child}, + ts::{get_node_str, get_node_text_before_pos, node_at_position, node_last_child}, }; #[allow(clippy::module_name_repetitions)] @@ -143,12 +143,12 @@ fn node_to_tag(node: Node, content: &str) -> Option { while let Some(node) = node_walk_back(current_node) { current_node = node; if node.kind() == "self_closing_tag" || node.kind() == "start_tag" { - let text = get_node_text(node, content); + let text = get_node_str(node, content); if text.chars().last()? != '>' { return None; } return get_xml_tag_at_pos( - &text, + text, Position { line: 0, character: 0, @@ -166,14 +166,14 @@ fn node_to_path(node: Node, content: &str) -> Option { let mut node_ids = vec![]; let mut on_text_node = false; let mut pop_last = false; - let text = get_node_text(node, content); + let text = get_node_str(node, content); if node.kind() == ">" && text == ">" { on_text_node = true; } if node.kind() == "text" && node.prev_sibling().is_some() { if let Some(last) = node_last_child(node.prev_sibling()?) { - if last.kind() == ">" && get_node_text(last, content) == ">" { + if last.kind() == ">" && get_node_str(last, content) == ">" { on_text_node = true; } } @@ -186,7 +186,7 @@ fn node_to_path(node: Node, content: &str) -> Option { } node_ids.push(node.id()); if node.kind() == "attribute_name" && !has_attr { - let attr_name = get_node_text(node, content); + let attr_name = get_node_str(node, content); has_attr = true; path.push((node.kind(), attr_name)); } else if node.kind() == "self_closing_tag" || node.kind() == "start_tag" { @@ -194,10 +194,10 @@ fn node_to_path(node: Node, content: &str) -> Option { if node_ids.contains(&node.child(0)?.id()) { continue; } - path.push((node.kind(), get_node_text(node.child(1)?, content))); + path.push((node.kind(), get_node_str(node.child(1)?, content))); } } else if node.kind() == "tag_name" && node.parent()?.kind() != "end_tag" { - path.push((node.kind(), get_node_text(node, content))); + path.push((node.kind(), get_node_str(node, content))); } else if node.kind() == "tag_name" && node.parent()?.kind() == "end_tag" { pop_last = true; on_text_node = false; @@ -208,19 +208,19 @@ fn node_to_path(node: Node, content: &str) -> Option { path.pop(); } if on_text_node { - path.push(("text", "[$text]".into())); + path.push(("text", "[$text]")); } let mut result = String::new(); for (kind, name) in path { match kind { - "text" => result.push_str(&name), + "text" => result.push_str(name), "attribute_name" => { result.push_str("[@"); - result.push_str(&name); + result.push_str(name); result.push(']'); } "self_closing_tag" | "start_tag" | "tag_name" => { - result.push_str(&format!("/{}", &name)); + result.push_str(&format!("/{}", name)); } _ => (), @@ -259,7 +259,7 @@ fn get_item_from_pos( "object" => Some(get_class_item_from_str(text)), "init_parameter" => try_const_item_from_str(text), "string" => { - if tag.attributes.get("name") == Some(&"component".to_string()) { + if tag.attributes.get("name").is_some_and(|s| s == "component") { let text = js::resolve_component_text(index, text, &path.get_area())?; js::text_to_component(index, text, path) } else { @@ -280,7 +280,7 @@ fn get_xml_tag_at_pos(content: &str, pos: Position) -> Option { let mut cursor = QueryCursor::new(); let captures = cursor.captures(query, tree.root_node(), content.as_bytes()); - let mut last_attribute_name = String::new(); + let mut last_attribute_name = ""; let mut last_tag_id: Option = None; let mut tag = XmlTag::new(); @@ -299,22 +299,24 @@ fn get_xml_tag_at_pos(content: &str, pos: Position) -> Option { let hovered = node_at_position(node, pos); match node.kind() { "tag_name" => { - tag.name = get_node_text(node, content); + tag.name = get_node_str(node, content).into(); } "attribute_name" => { - last_attribute_name = get_node_text(node, content); + last_attribute_name = get_node_str(node, content); tag.attributes - .insert(last_attribute_name.clone(), String::new()); + .insert(last_attribute_name.into(), String::new()); } "attribute_value" => { - tag.attributes - .insert(last_attribute_name.clone(), get_node_text(node, content)); + tag.attributes.insert( + last_attribute_name.into(), + get_node_str(node, content).into(), + ); if hovered { - tag.hover_on = XmlPart::Attribute(last_attribute_name.clone()); + tag.hover_on = XmlPart::Attribute(last_attribute_name.into()); } } "text" => { - tag.text = get_node_text(node, content); + tag.text = get_node_str(node, content).into(); if hovered { tag.hover_on = XmlPart::Text; } @@ -345,17 +347,14 @@ fn try_any_item_from_str(text: &str, area: &M2Area) -> Option { fn try_const_item_from_str(text: &str) -> Option { if text.split("::").count() == 2 { let mut parts = text.split("::"); - Some(M2Item::Const( - parts.next()?.to_string(), - parts.next()?.to_string(), - )) + Some(M2Item::Const(parts.next()?.into(), parts.next()?.into())) } else { None } } fn get_class_item_from_str(text: &str) -> M2Item { - M2Item::Class(text.to_string()) + M2Item::Class(text.into()) } fn try_phtml_item_from_str(text: &str, area: &M2Area) -> Option { @@ -363,16 +362,16 @@ fn try_phtml_item_from_str(text: &str, area: &M2Area) -> Option { let mut parts = text.split("::"); match area { M2Area::Frontend => Some(M2Item::FrontPhtml( - parts.next()?.to_string(), - parts.next()?.to_string(), + parts.next()?.into(), + parts.next()?.into(), )), M2Area::Adminhtml => Some(M2Item::AdminPhtml( - parts.next()?.to_string(), - parts.next()?.to_string(), + parts.next()?.into(), + parts.next()?.into(), )), M2Area::Base => Some(M2Item::BasePhtml( - parts.next()?.to_string(), - parts.next()?.to_string(), + parts.next()?.into(), + parts.next()?.into(), )), } } else { @@ -383,13 +382,13 @@ fn try_phtml_item_from_str(text: &str, area: &M2Area) -> Option { fn try_method_item_from_tag(tag: &XmlTag) -> Option { if tag.attributes.get("instance").is_some() && tag.attributes.get("method").is_some() { Some(M2Item::Method( - tag.attributes.get("instance")?.to_string(), - tag.attributes.get("method")?.to_string(), + tag.attributes.get("instance")?.into(), + tag.attributes.get("method")?.into(), )) } else if tag.attributes.get("class").is_some() && tag.attributes.get("method").is_some() { Some(M2Item::Method( - tag.attributes.get("class")?.to_string(), - tag.attributes.get("method")?.to_string(), + tag.attributes.get("class")?.into(), + tag.attributes.get("method")?.into(), )) } else { None @@ -442,7 +441,7 @@ mod test { fn test_get_item_from_pos_class_in_tag_text() { let item = get_test_item_from_pos(r#"|A\B\C"#, "/a/b/c"); - assert_eq!(item, Some(M2Item::Class("A\\B\\C".to_string()))); + assert_eq!(item, Some(M2Item::Class("A\\B\\C".into()))); } #[test] @@ -454,8 +453,8 @@ mod test { assert_eq!( item, Some(M2Item::AdminPhtml( - "Some_Module".to_string(), - "path/to/file.phtml".to_string() + "Some_Module".into(), + "path/to/file.phtml".into() )) ); } @@ -469,8 +468,8 @@ mod test { assert_eq!( item, Some(M2Item::FrontPhtml( - "Some_Module".to_string(), - "path/to/file.phtml".to_string() + "Some_Module".into(), + "path/to/file.phtml".into() )) ); } @@ -483,7 +482,7 @@ mod test { ); assert_eq!( item, - Some(M2Item::Method("A\\B\\C".to_string(), "metHod".to_string())) + Some(M2Item::Method("A\\B\\C".into(), "metHod".into())) ); } @@ -495,7 +494,7 @@ mod test { ); assert_eq!( item, - Some(M2Item::Method("A\\B\\C".to_string(), "metHod".to_string())) + Some(M2Item::Method("A\\B\\C".into(), "metHod".into())) ); } @@ -507,7 +506,7 @@ mod test { ); assert_eq!( item, - Some(M2Item::Method("A\\B\\C".to_string(), "metHod".to_string())) + Some(M2Item::Method("A\\B\\C".into(), "metHod".into())) ); } @@ -517,13 +516,13 @@ mod test { r#"xx"#, "/a/a/c", ); - assert_eq!(item, Some(M2Item::Class("A\\B\\C".to_string()))); + assert_eq!(item, Some(M2Item::Class("A\\B\\C".into()))); } #[test] fn test_get_item_from_pos_class_in_text_in_tag() { let item = get_test_item_from_pos(r#"|A\B\C"#, "/a/a/c"); - assert_eq!(item, Some(M2Item::Class("A\\B\\C".to_string()))); + assert_eq!(item, Some(M2Item::Class("A\\B\\C".into()))); } #[test] @@ -534,10 +533,7 @@ mod test { ); assert_eq!( item, - Some(M2Item::Const( - "A\\B\\C".to_string(), - "CONST_ANT".to_string() - )) + Some(M2Item::Const("A\\B\\C".into(), "CONST_ANT".into())) ); } @@ -550,8 +546,8 @@ mod test { assert_eq!( item, Some(M2Item::AdminPhtml( - "Some_Module".to_string(), - "file.phtml".to_string() + "Some_Module".into(), + "file.phtml".into() )) ); } @@ -582,7 +578,7 @@ mod test { "#, "/a/a/c", ); - assert_eq!(item, Some(M2Item::Class("Some\\Class\\Name".to_string()))) + assert_eq!(item, Some(M2Item::Class("Some\\Class\\Name".into()))) } #[test] @@ -593,7 +589,7 @@ mod test { "#, "/a/a/c", ); - assert_eq!(item, Some(M2Item::Class("A\\B\\C".to_string()))) + assert_eq!(item, Some(M2Item::Class("A\\B\\C".into()))) } #[test] From d48ab741e90047467355e36b2769c1d5dde068c0 Mon Sep 17 00:00:00 2001 From: Pawel Bogut Date: Sat, 7 Oct 2023 23:53:59 +0200 Subject: [PATCH 03/16] refactor: rename indexer to state --- src/js.rs | 74 +++++++++++++++----------------- src/lsp.rs | 10 ++--- src/lsp/completion.rs | 82 ++++++++++++++++++------------------ src/lsp/definition.rs | 44 +++++++++---------- src/main.rs | 20 ++++----- src/php.rs | 24 +++++------ src/{indexer.rs => state.rs} | 28 ++++++------ src/xml.rs | 18 ++++---- 8 files changed, 148 insertions(+), 152 deletions(-) rename src/{indexer.rs => state.rs} (90%) diff --git a/src/js.rs b/src/js.rs index 17ce84c..74130b4 100644 --- a/src/js.rs +++ b/src/js.rs @@ -5,9 +5,9 @@ use lsp_types::{Position, Range}; use tree_sitter::{Node, QueryCursor}; use crate::{ - indexer::{ArcIndexer, Indexer}, m2::{M2Area, M2Item, M2Path}, queries, + state::{ArcState, State}, ts::{self, node_at_position}, }; @@ -31,29 +31,29 @@ pub struct JsCompletion { pub kind: JsCompletionType, } -pub fn update_index(index: &ArcIndexer, path: &PathBuf) { +pub fn update_index(state: &ArcState, path: &PathBuf) { // if current workspace is magento module - process_glob(index, &path.append(&["view", "*", "requirejs-config.js"])); + process_glob(state, &path.append(&["view", "*", "requirejs-config.js"])); // if current workspace is magento installation process_glob( - index, + state, &path.append(&["vendor", "*", "*", "view", "*", "requirejs-config.js"]), ); process_glob( - index, + state, &path.append(&["vendor", "*", "*", "Magento_Theme", "requirejs-config.js"]), ); process_glob( - index, + state, &path.append(&["app", "code", "*", "*", "view", "*", "requirejs-config.js"]), ); process_glob( - index, + state, &path.append(&["app", "design", "**", "requirejs-config.js"]), ); } -fn process_glob(index: &ArcIndexer, glob_path: &PathBuf) { +fn process_glob(state: &ArcState, glob_path: &PathBuf) { let modules = glob(glob_path.to_path_str()) .expect("Failed to read glob pattern") .filter_map(Result::ok); @@ -62,7 +62,7 @@ fn process_glob(index: &ArcIndexer, glob_path: &PathBuf) { let content = std::fs::read_to_string(&file_path).expect("Should have been able to read the file"); - update_index_from_config(index, &content, &file_path.get_area()); + update_index_from_config(state, &content, &file_path.get_area()); } } @@ -99,12 +99,12 @@ pub fn get_completion_item(content: &str, pos: Position) -> Option None } -pub fn get_item_from_position(index: &Indexer, path: &PathBuf, pos: Position) -> Option { - let content = index.get_file(path)?; - get_item_from_pos(index, content, path, pos) +pub fn get_item_from_position(state: &State, path: &PathBuf, pos: Position) -> Option { + let content = state.get_file(path)?; + get_item_from_pos(state, content, path, pos) } -fn get_item_from_pos(index: &Indexer, content: &str, path: &Path, pos: Position) -> Option { +fn get_item_from_pos(state: &State, content: &str, path: &Path, pos: Position) -> Option { let tree = tree_sitter_parsers::parse(content, "javascript"); let query = queries::js_item_from_pos(); let mut cursor = QueryCursor::new(); @@ -113,8 +113,8 @@ fn get_item_from_pos(index: &Indexer, content: &str, path: &Path, pos: Position) for m in matches { if node_at_position(m.captures[1].node, pos) { let text = get_node_text(m.captures[1].node, content); - let text = resolve_component_text(index, text, &path.to_path_buf().get_area())?; - return text_to_component(index, text, path); + let text = resolve_component_text(state, text, &path.to_path_buf().get_area())?; + return text_to_component(state, text, path); } } @@ -122,20 +122,20 @@ fn get_item_from_pos(index: &Indexer, content: &str, path: &Path, pos: Position) } pub fn resolve_component_text<'a>( - index: &'a Indexer, + state: &'a State, text: &'a str, area: &M2Area, ) -> Option<&'a str> { - index.get_component_map(text, area).map_or_else( + state.get_component_map(text, area).map_or_else( || { area.lower_area() - .map_or_else(|| Some(text), |a| resolve_component_text(index, text, &a)) + .map_or_else(|| Some(text), |a| resolve_component_text(state, text, &a)) }, - |t| resolve_component_text(index, t, area), + |t| resolve_component_text(state, t, area), ) } -pub fn text_to_component(index: &Indexer, text: &str, path: &Path) -> Option { +pub fn text_to_component(state: &State, text: &str, path: &Path) -> Option { let begining = text.split('/').next().unwrap_or(""); if begining.chars().next().unwrap_or('a') == '.' { @@ -148,7 +148,7 @@ pub fn text_to_component(index: &Indexer, text: &str, path: &Path) -> Option Option index.add_component_map(key, val, area), - Some(JSTypes::Mixins) => index.add_component_mixin(key, val), - None => continue, - }; - } + match get_kind(m.captures[1].node, content) { + Some(JSTypes::Map | JSTypes::Paths) => state.lock().add_component_map(key, val, area), + Some(JSTypes::Mixins) => state.lock().add_component_mixin(key, val), + None => continue, + }; } } @@ -213,7 +210,7 @@ mod test { #[test] fn test_update_index_from_config() { - let index = Indexer::new(); + let state = State::new(); let content = r#" var config = { map: { @@ -239,10 +236,10 @@ mod test { }; "#; - let arc_index = index.into_arc(); - update_index_from_config(&arc_index, content, &M2Area::Base); + let arc_state = state.into_arc(); + update_index_from_config(&arc_state, content, &M2Area::Base); - let mut result = Indexer::new(); + let mut result = State::new(); result.add_component_map( "other/core/extension", "Other_Module/js/core_ext", @@ -262,8 +259,7 @@ mod test { result.add_component_mixin("Mage_Module/js/smth", "My_Module/js/mixin/smth"); result.add_component_mixin("Adobe_Module", "My_Module/js/mixin/adobe"); - // FIX fix test without using to_owned - // assert_eq!(arc_index.lock().to_owned(), result); + assert_eq!(arc_state.lock().to_owned(), result); } #[test] @@ -328,8 +324,8 @@ mod test { } let pos = Position { line, character }; let uri = PathBuf::from(if cfg!(windows) { &win_path } else { path }); - let mut index = Indexer::new(); - index.add_module_path("Some_Module", PathBuf::from("/a/b/c/Some_Module")); - get_item_from_pos(&index, &xml.replace('|', ""), &uri, pos) + let mut state = State::new(); + state.add_module_path("Some_Module", PathBuf::from("/a/b/c/Some_Module")); + get_item_from_pos(&state, &xml.replace('|', ""), &uri, pos) } } diff --git a/src/lsp.rs b/src/lsp.rs index 9b63d34..90dcf9b 100644 --- a/src/lsp.rs +++ b/src/lsp.rs @@ -5,20 +5,20 @@ use lsp_types::{ CompletionParams, CompletionResponse, GotoDefinitionParams, GotoDefinitionResponse, }; -use crate::indexer::ArcIndexer; +use crate::state::ArcState; use self::{completion::get_completion_from_params, definition::get_location_from_params}; -pub fn completion_handler(indexer: &ArcIndexer, params: &CompletionParams) -> CompletionResponse { +pub fn completion_handler(state: &ArcState, params: &CompletionParams) -> CompletionResponse { CompletionResponse::Array( - get_completion_from_params(indexer, params).map_or(vec![], |loc_list| loc_list), + get_completion_from_params(state, params).map_or(vec![], |loc_list| loc_list), ) } pub fn definition_handler( - indexer: &ArcIndexer, + state: &ArcState, params: &GotoDefinitionParams, ) -> GotoDefinitionResponse { GotoDefinitionResponse::Array( - get_location_from_params(indexer, params).map_or(vec![], |loc_list| loc_list), + get_location_from_params(state, params).map_or(vec![], |loc_list| loc_list), ) } diff --git a/src/lsp/completion.rs b/src/lsp/completion.rs index ef802f3..e17be1e 100644 --- a/src/lsp/completion.rs +++ b/src/lsp/completion.rs @@ -9,14 +9,14 @@ use lsp_types::{ }; use crate::{ - indexer::ArcIndexer, js::{self, JsCompletionType}, m2::{self, M2Area, M2Path, M2Uri}, + state::ArcState, xml, }; pub fn get_completion_from_params( - index: &ArcIndexer, + state: &ArcState, params: &CompletionParams, ) -> Option> { let path = params @@ -25,17 +25,17 @@ pub fn get_completion_from_params( .uri .to_path_buf(); let pos = params.text_document_position.position; - let content = index.lock().get_file(&path)?.clone(); + let content = state.lock().get_file(&path)?.clone(); match path.get_ext().as_str() { - "xml" => xml_completion_handler(index, &content, &path, pos), - "js" => js_completion_handler(index, &content, &path, pos), + "xml" => xml_completion_handler(state, &content, &path, pos), + "js" => js_completion_handler(state, &content, &path, pos), _ => None, } } fn js_completion_handler( - index: &ArcIndexer, + state: &ArcState, content: &str, path: &PathBuf, pos: Position, @@ -44,7 +44,7 @@ fn js_completion_handler( match at_position.kind { JsCompletionType::Definition => completion_for_component( - index, + state, &at_position.text, at_position.range, &path.get_area(), @@ -53,7 +53,7 @@ fn js_completion_handler( } fn xml_completion_handler( - index: &ArcIndexer, + state: &ArcState, content: &str, path: &PathBuf, pos: Position, @@ -61,77 +61,77 @@ fn xml_completion_handler( let at_position = xml::get_current_position_path(content, pos)?; match at_position { x if x.match_path("[@template]") => { - completion_for_template(index, &x.text, x.range, &path.get_area()) + completion_for_template(state, &x.text, x.range, &path.get_area()) } x if x.attribute_eq("xsi:type", "string") && x.attribute_eq("name", "template") => { - completion_for_template(index, &x.text, x.range, &path.get_area()) + completion_for_template(state, &x.text, x.range, &path.get_area()) } x if x.attribute_eq("xsi:type", "string") && x.attribute_eq("name", "component") => { - completion_for_component(index, &x.text, x.range, &path.get_area()) + completion_for_component(state, &x.text, x.range, &path.get_area()) } x if x.match_path("/config/event[@name]") && path.ends_with("events.xml") => { Some(events::get_completion_items(x.range)) } x if x.match_path("/config/preference[@for]") && path.ends_with("di.xml") => { - completion_for_classes(index, &x.text, x.range) + completion_for_classes(state, &x.text, x.range) } x if x.match_path("/config/preference[@type]") && path.ends_with("di.xml") => { - completion_for_classes(index, &x.text, x.range) + completion_for_classes(state, &x.text, x.range) } x if x.match_path("/virtualType[@type]") && path.ends_with("di.xml") => { - completion_for_classes(index, &x.text, x.range) + completion_for_classes(state, &x.text, x.range) } x if x.match_path("[@class]") || x.match_path("[@instance]") => { - completion_for_classes(index, &x.text, x.range) + completion_for_classes(state, &x.text, x.range) } x if x.attribute_in("xsi:type", &["object", "const", "init_parameter"]) => { - completion_for_classes(index, &x.text, x.range) + completion_for_classes(state, &x.text, x.range) } - x if x.match_path("/type[@name]") => completion_for_classes(index, &x.text, x.range), + x if x.match_path("/type[@name]") => completion_for_classes(state, &x.text, x.range), // Should be /source_model[$text], but html parser dont like undersores x if x.match_path("/source[$text]") && x.attribute_eq("_model", "") => { - completion_for_classes(index, &x.text, x.range) + completion_for_classes(state, &x.text, x.range) } // Should be /backend_model[$text], but html parser dont like undersores x if x.match_path("/backend[$text]") && x.attribute_eq("_model", "") => { - completion_for_classes(index, &x.text, x.range) + completion_for_classes(state, &x.text, x.range) } // Should be /frontend_model[$text], but html parser dont like undersores x if x.match_path("/frontend[$text]") && x.attribute_eq("_model", "") => { - completion_for_classes(index, &x.text, x.range) + completion_for_classes(state, &x.text, x.range) } _ => None, } } fn completion_for_classes( - index: &ArcIndexer, + state: &ArcState, text: &str, range: Range, ) -> Option> { let text = text.trim_start_matches('\\'); if text.is_empty() || (m2::is_part_of_class_name(text) && text.matches('\\').count() == 0) { - Some(completion_for_classes_prefix(index, range)) + Some(completion_for_classes_prefix(state, range)) } else if text.matches('\\').count() == 1 { - let mut result = completion_for_classes_prefix(index, range); - if let Some(classes) = completion_for_classes_full(index, text, range) { + let mut result = completion_for_classes_prefix(state, range); + if let Some(classes) = completion_for_classes_full(state, text, range) { result.extend(classes); } Some(result) } else if text.matches('\\').count() >= 2 { - completion_for_classes_full(index, text, range) + completion_for_classes_full(state, text, range) } else { None } } -fn completion_for_classes_prefix(index: &ArcIndexer, range: Range) -> Vec { - let module_prefixes = index.lock().get_module_class_prefixes(); +fn completion_for_classes_prefix(state: &ArcState, range: Range) -> Vec { + let module_prefixes = state.lock().get_module_class_prefixes(); string_vec_and_range_to_completion_list(module_prefixes, range) } fn completion_for_classes_full( - index: &ArcIndexer, + state: &ArcState, text: &str, range: Range, ) -> Option> { @@ -144,7 +144,7 @@ fn completion_for_classes_full( let typed_class_prefix = parts.join("\\"); let module_class = module_name.replace('_', "\\"); - let module_path = index.lock().get_module_path(&module_name)?; + let module_path = state.lock().get_module_path(&module_name)?; let candidates = glob(module_path.append(&["**", "*.php"]).to_path_str()) .expect("Failed to read glob pattern"); let mut classes = vec![]; @@ -168,17 +168,17 @@ fn completion_for_classes_full( } fn completion_for_template( - index: &ArcIndexer, + state: &ArcState, text: &str, range: Range, area: &M2Area, ) -> Option> { if text.is_empty() || m2::is_part_of_module_name(text) { - let modules = index.lock().get_modules(); + let modules = state.lock().get_modules(); Some(string_vec_and_range_to_completion_list(modules, range)) } else if text.contains("::") { let module_name = text.split("::").next()?; - let path = index.lock().get_module_path(module_name); + let path = state.lock().get_module_path(module_name); match path { None => None, Some(path) => { @@ -204,7 +204,7 @@ fn completion_for_template( } fn completion_for_component( - index: &ArcIndexer, + state: &ArcState, text: &str, range: Range, area: &M2Area, @@ -212,7 +212,7 @@ fn completion_for_component( if text.contains('/') { let module_name = text.split('/').next()?; let mut files = vec![]; - if let Some(path) = index.lock().get_module_path(module_name) { + if let Some(path) = state.lock().get_module_path(module_name) { for area in area.path_candidates() { let view_path = path.append(&["view", area, "web"]); let glob_path = view_path.append(&["**", "*.js"]); @@ -227,7 +227,7 @@ fn completion_for_component( })); } } - let workspaces = index.lock().workspace_paths(); + let workspaces = state.lock().workspace_paths(); for path in workspaces { let view_path = path.append(&["lib", "web"]); let glob_path = view_path.append(&["**", "*.js"]); @@ -241,19 +241,19 @@ fn completion_for_component( })); } - files.extend(index.lock().get_component_maps_for_area(area)); + files.extend(state.lock().get_component_maps_for_area(area)); if let Some(lower_area) = area.lower_area() { - files.extend(index.lock().get_component_maps_for_area(&lower_area)); + files.extend(state.lock().get_component_maps_for_area(&lower_area)); } Some(string_vec_and_range_to_completion_list(files, range)) } else { let mut modules = vec![]; - modules.extend(index.lock().get_modules()); - modules.extend(index.lock().get_component_maps_for_area(area)); + modules.extend(state.lock().get_modules()); + modules.extend(state.lock().get_component_maps_for_area(area)); if let Some(lower_area) = area.lower_area() { - modules.extend(index.lock().get_component_maps_for_area(&lower_area)); + modules.extend(state.lock().get_component_maps_for_area(&lower_area)); } - let workspaces = index.lock().workspace_paths(); + let workspaces = state.lock().workspace_paths(); for path in workspaces { let view_path = path.append(&["lib", "web"]); let glob_path = view_path.append(&["**", "*.js"]); diff --git a/src/lsp/definition.rs b/src/lsp/definition.rs index f580d28..e603c66 100644 --- a/src/lsp/definition.rs +++ b/src/lsp/definition.rs @@ -4,13 +4,13 @@ use lsp_types::{GotoDefinitionParams, Location, Range, Url}; use parking_lot::MutexGuard; use crate::{ - indexer::{ArcIndexer, Indexer}, m2::{M2Area, M2Item, M2Path, M2Uri}, php::{self, PHPClass}, + state::{ArcState, State}, }; pub fn get_location_from_params( - index: &ArcIndexer, + state: &ArcState, params: &GotoDefinitionParams, ) -> Option> { let path = params @@ -20,7 +20,7 @@ pub fn get_location_from_params( .to_path_buf(); let pos = params.text_document_position_params.position; - let item = index.lock().get_item_from_position(&path, pos); + let item = state.lock().get_item_from_position(&path, pos); match item { Some(M2Item::ModComponent(_mod_name, file_path, mod_path)) => { let mut result = vec![]; @@ -44,7 +44,7 @@ pub fn get_location_from_params( Some(M2Item::Component(comp)) => { dbg!(&comp); let mut result = vec![]; - let workspace_paths = index.lock().workspace_paths(); + let workspace_paths = state.lock().workspace_paths(); for path in workspace_paths { let path = path.append(&["lib", "web", &comp]).append_ext("js"); dbg!(&path); @@ -56,32 +56,32 @@ pub fn get_location_from_params( } Some(M2Item::AdminPhtml(mod_name, template)) => { let mut result = vec![]; - add_phtml_in_mod_location(index, &mut result, &mod_name, &template, &M2Area::Adminhtml); - add_phtml_in_admin_theme_location(index, &mut result, &mod_name, &template); + add_phtml_in_mod_location(state, &mut result, &mod_name, &template, &M2Area::Adminhtml); + add_phtml_in_admin_theme_location(state, &mut result, &mod_name, &template); Some(result) } Some(M2Item::FrontPhtml(mod_name, template)) => { let mut result = vec![]; - add_phtml_in_mod_location(index, &mut result, &mod_name, &template, &M2Area::Frontend); - add_phtml_in_front_theme_location(index, &mut result, &mod_name, &template); + add_phtml_in_mod_location(state, &mut result, &mod_name, &template, &M2Area::Frontend); + add_phtml_in_front_theme_location(state, &mut result, &mod_name, &template); Some(result) } Some(M2Item::BasePhtml(mod_name, template)) => { let mut result = vec![]; - add_phtml_in_mod_location(index, &mut result, &mod_name, &template, &M2Area::Base); - add_phtml_in_front_theme_location(index, &mut result, &mod_name, &template); - add_phtml_in_admin_theme_location(index, &mut result, &mod_name, &template); + add_phtml_in_mod_location(state, &mut result, &mod_name, &template, &M2Area::Base); + add_phtml_in_front_theme_location(state, &mut result, &mod_name, &template); + add_phtml_in_admin_theme_location(state, &mut result, &mod_name, &template); Some(result) } Some(M2Item::Class(class)) => { - let phpclass = get_php_class_from_class_name(&index.lock(), &class)?; + let phpclass = get_php_class_from_class_name(&state.lock(), &class)?; Some(vec![Location { uri: phpclass.uri.clone(), range: phpclass.range, }]) } Some(M2Item::Method(class, method)) => { - let phpclass = get_php_class_from_class_name(&index.lock(), &class)?; + let phpclass = get_php_class_from_class_name(&state.lock(), &class)?; Some(vec![Location { uri: phpclass.uri.clone(), range: phpclass @@ -91,7 +91,7 @@ pub fn get_location_from_params( }]) } Some(M2Item::Const(class, constant)) => { - let phpclass = get_php_class_from_class_name(&index.lock(), &class)?; + let phpclass = get_php_class_from_class_name(&state.lock(), &class)?; Some(vec![Location { uri: phpclass.uri.clone(), range: phpclass @@ -105,13 +105,13 @@ pub fn get_location_from_params( } fn add_phtml_in_mod_location( - index: &ArcIndexer, + state: &ArcState, result: &mut Vec, mod_name: &str, template: &str, area: &M2Area, ) { - let mod_path = index.lock().get_module_path(mod_name); + let mod_path = state.lock().get_module_path(mod_name); if let Some(path) = mod_path { for area in area.path_candidates() { let templ_path = path.append(&["view", area, "templates", template]); @@ -123,13 +123,13 @@ fn add_phtml_in_mod_location( } fn add_phtml_in_admin_theme_location( - index: &ArcIndexer, + state: &ArcState, result: &mut Vec, mod_name: &str, template: &str, ) { #[allow(clippy::significant_drop_in_scrutinee)] - for theme_path in index.lock().list_admin_themes_paths() { + for theme_path in state.lock().list_admin_themes_paths() { let path = theme_path.append(&[mod_name, "templates", template]); if let Some(location) = path_to_location(&path) { result.push(location); @@ -138,13 +138,13 @@ fn add_phtml_in_admin_theme_location( } fn add_phtml_in_front_theme_location( - index: &ArcIndexer, + state: &ArcState, result: &mut Vec, mod_name: &str, template: &str, ) { #[allow(clippy::significant_drop_in_scrutinee)] - for theme_path in index.lock().list_front_themes_paths() { + for theme_path in state.lock().list_front_themes_paths() { let path = theme_path.append(&[mod_name, "templates", template]); if let Some(location) = path_to_location(&path) { result.push(location); @@ -152,8 +152,8 @@ fn add_phtml_in_front_theme_location( } } -fn get_php_class_from_class_name(index: &MutexGuard, class: &str) -> Option { - let module_path = index.split_class_to_path_and_suffix(class); +fn get_php_class_from_class_name(state: &MutexGuard, class: &str) -> Option { + let module_path = state.split_class_to_path_and_suffix(class); match module_path { None => None, Some((mut file_path, suffix)) => { diff --git a/src/main.rs b/src/main.rs index b6171b3..dbfa9cc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,9 @@ -mod indexer; mod js; mod lsp; mod m2; mod php; mod queries; +mod state; mod ts; mod xml; @@ -19,7 +19,7 @@ use lsp_types::{ WorkDoneProgressOptions, }; -use crate::{indexer::Indexer, m2::M2Uri}; +use crate::{m2::M2Uri, state::State}; fn main() -> Result<(), Box> { // Note that we must have our logging only write out to stderr. @@ -78,18 +78,18 @@ fn main_loop( let params: InitializeParams = serde_json::from_value(init_params).context("Deserializing initialize params")?; - let indexer = Indexer::new().into_arc(); + let state = State::new().into_arc(); let mut threads = vec![]; if let Some(uri) = params.root_uri { let path = uri.to_file_path().expect("Invalid root path"); - threads.extend(Indexer::update_index(&indexer, &path)); + threads.extend(State::update_index(&state, &path)); }; if let Some(folders) = params.workspace_folders { for folder in folders { let path = folder.uri.to_file_path().expect("Invalid workspace path"); - threads.extend(Indexer::update_index(&indexer, &path)); + threads.extend(State::update_index(&state, &path)); } } @@ -105,12 +105,12 @@ fn main_loop( match req.method.as_str() { "textDocument/completion" => { let (id, params) = cast::(req)?; - let result = lsp::completion_handler(&indexer, ¶ms); + let result = lsp::completion_handler(&state, ¶ms); connection.sender.send(get_response_message(id, result))?; } "textDocument/definition" => { let (id, params) = cast::(req)?; - let result = lsp::definition_handler(&indexer, ¶ms); + let result = lsp::definition_handler(&state, ¶ms); connection.sender.send(get_response_message(id, result))?; } _ => { @@ -127,7 +127,7 @@ fn main_loop( let params: DidOpenTextDocumentParams = serde_json::from_value(not.params) .context("Deserializing notification params")?; let path = params.text_document.uri.to_path_buf(); - indexer.lock().set_file(&path, params.text_document.text); + state.lock().set_file(&path, params.text_document.text); #[cfg(debug_assertions)] eprintln!("textDocument/didOpen: {path:?}"); } @@ -135,7 +135,7 @@ fn main_loop( let params: DidChangeTextDocumentParams = serde_json::from_value(not.params) .context("Deserializing notification params")?; let path = params.text_document.uri.to_path_buf(); - indexer + state .lock() .set_file(&path, ¶ms.content_changes[0].text); #[cfg(debug_assertions)] @@ -145,7 +145,7 @@ fn main_loop( let params: DidCloseTextDocumentParams = serde_json::from_value(not.params) .context("Deserializing notification params")?; let path = params.text_document.uri.to_path_buf(); - indexer.lock().del_file(&path); + state.lock().del_file(&path); #[cfg(debug_assertions)] eprintln!("textDocument/didClose: {path:?}"); } diff --git a/src/php.rs b/src/php.rs index cdb493c..8565c8a 100644 --- a/src/php.rs +++ b/src/php.rs @@ -6,9 +6,9 @@ use lsp_types::{Position, Range, Url}; use tree_sitter::{Node, QueryCursor}; use crate::{ - indexer::ArcIndexer, m2::M2Path, queries, + state::ArcState, ts::{self, get_range_from_node}, }; @@ -77,20 +77,20 @@ fn register_param_to_module(param: &str) -> Option { } } -pub fn update_index(index: &ArcIndexer, path: &PathBuf) { +pub fn update_index(state: &ArcState, path: &PathBuf) { // if current workspace is magento module - process_glob(index, &path.append(&["registration.php"])); + process_glob(state, &path.append(&["registration.php"])); // if current workspace is magento installation process_glob( - index, + state, &path.append(&["vendor", "*", "*", "registration.php"]), ); process_glob( - index, + state, &path.append(&["app", "code", "*", "*", "registration.php"]), ); process_glob( - index, + state, &path.append(&[ "vendor", "magento", @@ -104,7 +104,7 @@ pub fn update_index(index: &ArcIndexer, path: &PathBuf) { ); } -fn process_glob(index: &ArcIndexer, glob_path: &PathBuf) { +fn process_glob(state: &ArcState, glob_path: &PathBuf) { let modules = glob(glob_path.to_path_str()) .expect("Failed to read glob pattern") .filter_map(Result::ok); @@ -129,20 +129,20 @@ fn process_glob(index: &ArcIndexer, glob_path: &PathBuf) { let mut parent = file_path.clone(); parent.pop(); - index.lock().add_module_path(mod_name, parent.clone()); + state.lock().add_module_path(mod_name, parent.clone()); match register_param_to_module(mod_name) { Some(M2Module::Module(m)) => { - index.lock().add_module(mod_name).add_module_path(m, parent); + state.lock().add_module(mod_name).add_module_path(m, parent); } Some(M2Module::Library(l)) => { - index.lock().add_module_path(l, parent); + state.lock().add_module_path(l, parent); } Some(M2Module::FrontTheme(t)) => { - index.lock().add_front_theme_path(t, parent); + state.lock().add_front_theme_path(t, parent); } Some(M2Module::AdminTheme(t)) => { - index.lock().add_admin_theme_path(t, parent); + state.lock().add_admin_theme_path(t, parent); } _ => (), } diff --git a/src/indexer.rs b/src/state.rs similarity index 90% rename from src/indexer.rs rename to src/state.rs index 4160ba3..7055c7f 100644 --- a/src/indexer.rs +++ b/src/state.rs @@ -30,7 +30,7 @@ impl HashMapId for M2Area { } #[derive(Debug, Clone, PartialEq, Eq)] -pub struct Indexer { +pub struct State { buffers: HashMap, magento_modules: Vec, magento_module_paths: HashMap, @@ -42,9 +42,9 @@ pub struct Indexer { } #[allow(clippy::module_name_repetitions)] -pub type ArcIndexer = Arc>; +pub type ArcState = Arc>; -impl Indexer { +impl State { pub fn new() -> Self { Self { buffers: HashMap::new(), @@ -175,19 +175,19 @@ impl Indexer { } } - pub fn into_arc(self) -> ArcIndexer { + pub fn into_arc(self) -> ArcState { Arc::new(Mutex::new(self)) } - pub fn update_index(arc_index: &ArcIndexer, path: &Path) -> Vec> { - let mut index = arc_index.lock(); - if index.has_workspace_path(path) { + pub fn update_index(arc_state: &ArcState, path: &Path) -> Vec> { + let mut state = arc_state.lock(); + if state.has_workspace_path(path) { vec![] } else { - index.add_workspace_path(path); + state.add_workspace_path(path); vec![ - spawn_index(arc_index, path, php::update_index, "PHP Indexing"), - spawn_index(arc_index, path, js::update_index, "JS Indexing"), + spawn_index(arc_state, path, php::update_index, "PHP Indexing"), + spawn_index(arc_state, path, js::update_index, "JS Indexing"), ] } } @@ -213,19 +213,19 @@ impl Indexer { } fn spawn_index( - arc_indexer: &ArcIndexer, + state: &ArcState, path: &Path, - callback: fn(&ArcIndexer, &PathBuf), + callback: fn(&ArcState, &PathBuf), msg: &str, ) -> JoinHandle<()> { - let index = Arc::clone(arc_indexer); + let state = Arc::clone(state); let path = path.to_path_buf(); let msg = msg.to_owned(); spawn(move || { eprintln!("Start {}", msg); let index_start = SystemTime::now(); - callback(&index, &path); + callback(&state, &path); index_start.elapsed().map_or_else( |_| eprintln!("{} done", msg), |d| eprintln!("{} done in {:?}", msg, d), diff --git a/src/xml.rs b/src/xml.rs index 2ca4e81..03909f3 100644 --- a/src/xml.rs +++ b/src/xml.rs @@ -6,10 +6,10 @@ use std::{ use tree_sitter::{Node, QueryCursor}; use crate::{ - indexer::Indexer, js, m2::{M2Area, M2Item, M2Path}, queries, + state::State, ts::{get_node_str, get_node_text_before_pos, node_at_position, node_last_child}, }; @@ -229,13 +229,13 @@ fn node_to_path(node: Node, content: &str) -> Option { Some(result) } -pub fn get_item_from_position(index: &Indexer, path: &PathBuf, pos: Position) -> Option { - let content = index.get_file(path)?; - get_item_from_pos(index, content, path, pos) +pub fn get_item_from_position(state: &State, path: &PathBuf, pos: Position) -> Option { + let content = state.get_file(path)?; + get_item_from_pos(state, content, path, pos) } fn get_item_from_pos( - index: &Indexer, + state: &State, content: &str, path: &PathBuf, pos: Position, @@ -260,8 +260,8 @@ fn get_item_from_pos( "init_parameter" => try_const_item_from_str(text), "string" => { if tag.attributes.get("name").is_some_and(|s| s == "component") { - let text = js::resolve_component_text(index, text, &path.get_area())?; - js::text_to_component(index, text, path) + let text = js::resolve_component_text(state, text, &path.get_area())?; + js::text_to_component(state, text, path) } else { try_any_item_from_str(text, &path.get_area()) } @@ -428,8 +428,8 @@ mod test { let win_path = format!("c:{}", path.replace('/', "\\")); let pos = get_position_from_test_xml(xml); let uri = PathBuf::from(if cfg!(windows) { &win_path } else { path }); - let index = Indexer::new(); - get_item_from_pos(&index, &xml.replace('|', ""), &uri, pos) + let state = State::new(); + get_item_from_pos(&state, &xml.replace('|', ""), &uri, pos) } fn get_test_xml_tag_at_pos(xml: &str) -> Option { From 10baf269ab23d219530cbeb9bdeebfd4b84ddda5 Mon Sep 17 00:00:00 2001 From: Pawel Bogut Date: Sun, 8 Oct 2023 00:21:28 +0200 Subject: [PATCH 04/16] feat: remove unnecessary file content cloning --- src/lsp/completion.rs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/lsp/completion.rs b/src/lsp/completion.rs index e17be1e..c065ce1 100644 --- a/src/lsp/completion.rs +++ b/src/lsp/completion.rs @@ -25,22 +25,20 @@ pub fn get_completion_from_params( .uri .to_path_buf(); let pos = params.text_document_position.position; - let content = state.lock().get_file(&path)?.clone(); match path.get_ext().as_str() { - "xml" => xml_completion_handler(state, &content, &path, pos), - "js" => js_completion_handler(state, &content, &path, pos), + "xml" => xml_completion_handler(state, &path, pos), + "js" => js_completion_handler(state, &path, pos), _ => None, } } fn js_completion_handler( state: &ArcState, - content: &str, path: &PathBuf, pos: Position, ) -> Option> { - let at_position = js::get_completion_item(content, pos)?; + let at_position = js::get_completion_item(state.lock().get_file(path)?, pos)?; match at_position.kind { JsCompletionType::Definition => completion_for_component( @@ -54,11 +52,10 @@ fn js_completion_handler( fn xml_completion_handler( state: &ArcState, - content: &str, path: &PathBuf, pos: Position, ) -> Option> { - let at_position = xml::get_current_position_path(content, pos)?; + let at_position = xml::get_current_position_path(state.lock().get_file(path)?, pos)?; match at_position { x if x.match_path("[@template]") => { completion_for_template(state, &x.text, x.range, &path.get_area()) From 06ea4109f26c163c72bd8626a9060373fc712072 Mon Sep 17 00:00:00 2001 From: Pawel Bogut Date: Sun, 8 Oct 2023 00:36:38 +0200 Subject: [PATCH 05/16] feat: use M2Path trait to get extension --- src/state.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/state.rs b/src/state.rs index 7055c7f..7d0ef5d 100644 --- a/src/state.rs +++ b/src/state.rs @@ -11,7 +11,7 @@ use parking_lot::Mutex; use crate::{ js, - m2::{M2Area, M2Item}, + m2::{M2Area, M2Item, M2Path}, php, xml, }; @@ -168,7 +168,7 @@ impl State { } pub fn get_item_from_position(&self, path: &PathBuf, pos: Position) -> Option { - match path.extension()?.to_str()?.to_lowercase().as_str() { + match path.get_ext().as_str() { "js" => js::get_item_from_position(self, path, pos), "xml" => xml::get_item_from_position(self, path, pos), _ => None, From 8e73875dafb96279062831232a8004aed197414c Mon Sep 17 00:00:00 2001 From: Pawel Bogut Date: Sun, 8 Oct 2023 00:45:05 +0200 Subject: [PATCH 06/16] fix: index local themes --- src/php.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/php.rs b/src/php.rs index 8565c8a..4dc3e21 100644 --- a/src/php.rs +++ b/src/php.rs @@ -84,11 +84,15 @@ pub fn update_index(state: &ArcState, path: &PathBuf) { process_glob( state, &path.append(&["vendor", "*", "*", "registration.php"]), - ); + ); // vendor modules / themes process_glob( state, &path.append(&["app", "code", "*", "*", "registration.php"]), - ); + ); // local modules + process_glob( + state, + &path.append(&["app", "design", "*", "*", "*", "registration.php"]), + ); // local themes process_glob( state, &path.append(&[ @@ -101,7 +105,7 @@ pub fn update_index(state: &ArcState, path: &PathBuf) { "Setup", "registration.php", ]), - ); + ); // magento2-base setup module } fn process_glob(state: &ArcState, glob_path: &PathBuf) { From a4bb31540b3806e3cee5ada40e370c1e7cb1537d Mon Sep 17 00:00:00 2001 From: Pawel Bogut Date: Sun, 8 Oct 2023 14:47:07 +0200 Subject: [PATCH 07/16] refactor: split definition module --- src/lsp/definition.rs | 173 ++++---------------------------- src/lsp/definition/component.rs | 58 +++++++++++ src/lsp/definition/php.rs | 55 ++++++++++ src/lsp/definition/phtml.rs | 78 ++++++++++++++ 4 files changed, 211 insertions(+), 153 deletions(-) create mode 100644 src/lsp/definition/component.rs create mode 100644 src/lsp/definition/php.rs create mode 100644 src/lsp/definition/phtml.rs diff --git a/src/lsp/definition.rs b/src/lsp/definition.rs index e603c66..03c973b 100644 --- a/src/lsp/definition.rs +++ b/src/lsp/definition.rs @@ -1,12 +1,14 @@ +mod component; +mod php; +mod phtml; + use std::path::Path; use lsp_types::{GotoDefinitionParams, Location, Range, Url}; -use parking_lot::MutexGuard; use crate::{ - m2::{M2Area, M2Item, M2Path, M2Uri}, - php::{self, PHPClass}, - state::{ArcState, State}, + m2::{M2Item, M2Uri}, + state::ArcState, }; pub fn get_location_from_params( @@ -19,155 +21,20 @@ pub fn get_location_from_params( .uri .to_path_buf(); let pos = params.text_document_position_params.position; - - let item = state.lock().get_item_from_position(&path, pos); - match item { - Some(M2Item::ModComponent(_mod_name, file_path, mod_path)) => { - let mut result = vec![]; - - for area in [M2Area::Frontend, M2Area::Adminhtml, M2Area::Base] { - let comp_path = mod_path - .append(&["view", area.to_str(), "web", &file_path]) - .append_ext("js"); - if let Some(location) = path_to_location(&comp_path) { - result.push(location); - } - } - - Some(result) - } - Some(M2Item::RelComponent(comp, path)) => { - let mut path = path.join(comp); - path.set_extension("js"); - path_to_location(&path).map(|location| vec![location]) - } - Some(M2Item::Component(comp)) => { - dbg!(&comp); - let mut result = vec![]; - let workspace_paths = state.lock().workspace_paths(); - for path in workspace_paths { - let path = path.append(&["lib", "web", &comp]).append_ext("js"); - dbg!(&path); - if let Some(location) = path_to_location(&path) { - result.push(location); - } - } - Some(result) - } - Some(M2Item::AdminPhtml(mod_name, template)) => { - let mut result = vec![]; - add_phtml_in_mod_location(state, &mut result, &mod_name, &template, &M2Area::Adminhtml); - add_phtml_in_admin_theme_location(state, &mut result, &mod_name, &template); - Some(result) - } - Some(M2Item::FrontPhtml(mod_name, template)) => { - let mut result = vec![]; - add_phtml_in_mod_location(state, &mut result, &mod_name, &template, &M2Area::Frontend); - add_phtml_in_front_theme_location(state, &mut result, &mod_name, &template); - Some(result) - } - Some(M2Item::BasePhtml(mod_name, template)) => { - let mut result = vec![]; - add_phtml_in_mod_location(state, &mut result, &mod_name, &template, &M2Area::Base); - add_phtml_in_front_theme_location(state, &mut result, &mod_name, &template); - add_phtml_in_admin_theme_location(state, &mut result, &mod_name, &template); - Some(result) - } - Some(M2Item::Class(class)) => { - let phpclass = get_php_class_from_class_name(&state.lock(), &class)?; - Some(vec![Location { - uri: phpclass.uri.clone(), - range: phpclass.range, - }]) - } - Some(M2Item::Method(class, method)) => { - let phpclass = get_php_class_from_class_name(&state.lock(), &class)?; - Some(vec![Location { - uri: phpclass.uri.clone(), - range: phpclass - .methods - .get(&method) - .map_or(phpclass.range, |method| method.range), - }]) - } - Some(M2Item::Const(class, constant)) => { - let phpclass = get_php_class_from_class_name(&state.lock(), &class)?; - Some(vec![Location { - uri: phpclass.uri.clone(), - range: phpclass - .constants - .get(&constant) - .map_or(phpclass.range, |method| method.range), - }]) - } - None => None, - } -} - -fn add_phtml_in_mod_location( - state: &ArcState, - result: &mut Vec, - mod_name: &str, - template: &str, - area: &M2Area, -) { - let mod_path = state.lock().get_module_path(mod_name); - if let Some(path) = mod_path { - for area in area.path_candidates() { - let templ_path = path.append(&["view", area, "templates", template]); - if let Some(location) = path_to_location(&templ_path) { - result.push(location); - } - } - } -} - -fn add_phtml_in_admin_theme_location( - state: &ArcState, - result: &mut Vec, - mod_name: &str, - template: &str, -) { - #[allow(clippy::significant_drop_in_scrutinee)] - for theme_path in state.lock().list_admin_themes_paths() { - let path = theme_path.append(&[mod_name, "templates", template]); - if let Some(location) = path_to_location(&path) { - result.push(location); - } - } -} - -fn add_phtml_in_front_theme_location( - state: &ArcState, - result: &mut Vec, - mod_name: &str, - template: &str, -) { - #[allow(clippy::significant_drop_in_scrutinee)] - for theme_path in state.lock().list_front_themes_paths() { - let path = theme_path.append(&[mod_name, "templates", template]); - if let Some(location) = path_to_location(&path) { - result.push(location); - } - } -} - -fn get_php_class_from_class_name(state: &MutexGuard, class: &str) -> Option { - let module_path = state.split_class_to_path_and_suffix(class); - match module_path { - None => None, - Some((mut file_path, suffix)) => { - for part in suffix { - file_path.push(part); - } - file_path.set_extension("php"); - - match file_path.try_exists() { - Ok(true) => php::parse_php_file(&file_path), - _ => None, - } - } - } + let item = state.lock().get_item_from_position(&path, pos)?; + Some(match item { + M2Item::ModComponent(mod_name, file_path, mod_path) => { + component::mod_location(mod_name, &file_path, mod_path, &path) + } + M2Item::RelComponent(comp, path) => component::find_rel(comp, &path)?, + M2Item::Component(comp) => component::find_plain(state, &comp), + M2Item::AdminPhtml(mod_name, template) => phtml::find_admin(state, &mod_name, &template), + M2Item::FrontPhtml(mod_name, template) => phtml::find_front(state, &mod_name, &template), + M2Item::BasePhtml(mod_name, template) => phtml::find_base(state, &mod_name, &template), + M2Item::Class(class) => vec![php::find_class(state, &class)?], + M2Item::Method(class, method) => vec![php::find_method(state, &class, &method)?], + M2Item::Const(class, constant) => vec![php::find_const(state, &class, &constant)?], + }) } fn path_to_location(path: &Path) -> Option { diff --git a/src/lsp/definition/component.rs b/src/lsp/definition/component.rs new file mode 100644 index 0000000..8b17b41 --- /dev/null +++ b/src/lsp/definition/component.rs @@ -0,0 +1,58 @@ +use std::path::{Path, PathBuf}; + +use lsp_types::Location; + +use crate::{ + m2::{M2Item, M2Path}, + state::ArcState, +}; + +use super::path_to_location; + +pub fn find_plain(state: &ArcState, comp: &str) -> Vec { + let mut result = vec![]; + let workspace_paths = state.lock().workspace_paths(); + for path in workspace_paths { + let path = path.append(&["lib", "web", comp]).append_ext("js"); + if let Some(location) = path_to_location(&path) { + result.push(location); + } + } + result +} +pub fn find_rel(comp: String, path: &Path) -> Option> { + let mut path = path.join(comp); + path.set_extension("js"); + path_to_location(&path).map(|location| vec![location]) +} + +pub fn mod_location( + mod_name: String, + file_path: &str, + mod_path: PathBuf, + path: &PathBuf, +) -> Vec { + let mut result = vec![]; + let components = vec![M2Item::ModComponent( + mod_name.clone(), + file_path.to_string(), + mod_path, + )]; + + let area = path.get_area(); + + for component in components { + if let M2Item::ModComponent(_, file_path, mod_path) = component { + for area_path in area.path_candidates() { + let comp_path = mod_path + .append(&["view", area_path, "web", &file_path]) + .append_ext("js"); + if let Some(location) = path_to_location(&comp_path) { + result.push(location); + } + } + } + } + + result +} diff --git a/src/lsp/definition/php.rs b/src/lsp/definition/php.rs new file mode 100644 index 0000000..7ee47c8 --- /dev/null +++ b/src/lsp/definition/php.rs @@ -0,0 +1,55 @@ +use lsp_types::Location; +use parking_lot::MutexGuard; + +use crate::{ + php::{parse_php_file, PHPClass}, + state::{ArcState, State}, +}; + +pub fn find_class(state: &ArcState, class: &str) -> Option { + let phpclass = get_php_class_from_class_name(&state.lock(), class)?; + Some(Location { + uri: phpclass.uri.clone(), + range: phpclass.range, + }) +} + +pub fn find_method(state: &ArcState, class: &str, method: &str) -> Option { + let phpclass = get_php_class_from_class_name(&state.lock(), class)?; + Some(Location { + uri: phpclass.uri.clone(), + range: phpclass + .methods + .get(method) + .map_or(phpclass.range, |method| method.range), + }) +} + +pub fn find_const(state: &ArcState, class: &str, constant: &str) -> Option { + let phpclass = get_php_class_from_class_name(&state.lock(), class)?; + Some(Location { + uri: phpclass.uri.clone(), + range: phpclass + .constants + .get(constant) + .map_or(phpclass.range, |method| method.range), + }) +} + +fn get_php_class_from_class_name(state: &MutexGuard, class: &str) -> Option { + let module_path = state.split_class_to_path_and_suffix(class); + match module_path { + None => None, + Some((mut file_path, suffix)) => { + for part in suffix { + file_path.push(part); + } + file_path.set_extension("php"); + + match file_path.try_exists() { + Ok(true) => parse_php_file(&file_path), + _ => None, + } + } + } +} diff --git a/src/lsp/definition/phtml.rs b/src/lsp/definition/phtml.rs new file mode 100644 index 0000000..9bf75d5 --- /dev/null +++ b/src/lsp/definition/phtml.rs @@ -0,0 +1,78 @@ +use lsp_types::Location; + +use crate::{ + m2::{M2Area, M2Path}, + state::ArcState, +}; + +use super::path_to_location; + +pub fn find_admin(state: &ArcState, mod_name: &str, template: &str) -> Vec { + let mut result = vec![]; + add_phtml_in_mod_location(state, &mut result, mod_name, template, &M2Area::Adminhtml); + add_phtml_in_admin_theme_location(state, &mut result, mod_name, template); + result +} + +pub fn find_front(state: &ArcState, mod_name: &str, template: &str) -> Vec { + let mut result = vec![]; + add_phtml_in_mod_location(state, &mut result, mod_name, template, &M2Area::Frontend); + add_phtml_in_front_theme_location(state, &mut result, mod_name, template); + result +} + +pub fn find_base(state: &ArcState, mod_name: &str, template: &str) -> Vec { + let mut result = vec![]; + add_phtml_in_mod_location(state, &mut result, mod_name, template, &M2Area::Base); + add_phtml_in_front_theme_location(state, &mut result, mod_name, template); + add_phtml_in_admin_theme_location(state, &mut result, mod_name, template); + result +} + +fn add_phtml_in_mod_location( + state: &ArcState, + result: &mut Vec, + mod_name: &str, + template: &str, + area: &M2Area, +) { + let mod_path = state.lock().get_module_path(mod_name); + if let Some(path) = mod_path { + for area in area.path_candidates() { + let templ_path = path.append(&["view", area, "templates", template]); + if let Some(location) = path_to_location(&templ_path) { + result.push(location); + } + } + } +} + +fn add_phtml_in_admin_theme_location( + state: &ArcState, + result: &mut Vec, + mod_name: &str, + template: &str, +) { + #[allow(clippy::significant_drop_in_scrutinee)] + for theme_path in state.lock().list_admin_themes_paths() { + let path = theme_path.append(&[mod_name, "templates", template]); + if let Some(location) = path_to_location(&path) { + result.push(location); + } + } +} + +fn add_phtml_in_front_theme_location( + state: &ArcState, + result: &mut Vec, + mod_name: &str, + template: &str, +) { + #[allow(clippy::significant_drop_in_scrutinee)] + for theme_path in state.lock().list_front_themes_paths() { + let path = theme_path.append(&[mod_name, "templates", template]); + if let Some(location) = path_to_location(&path) { + result.push(location); + } + } +} From a81ed35ff8f9b46cc4b109e7bc9d4c3ded3fd20d Mon Sep 17 00:00:00 2001 From: Pawel Bogut Date: Sun, 8 Oct 2023 14:49:18 +0200 Subject: [PATCH 08/16] feat: list mixins when going to component definition --- src/js.rs | 10 +++++++--- src/lsp/definition.rs | 2 +- src/lsp/definition/component.rs | 8 +++++++- src/m2.rs | 8 -------- src/state.rs | 33 +++++++++++++++++++++++++++------ 5 files changed, 42 insertions(+), 19 deletions(-) diff --git a/src/js.rs b/src/js.rs index 74130b4..dee2444 100644 --- a/src/js.rs +++ b/src/js.rs @@ -171,7 +171,7 @@ fn update_index_from_config(state: &ArcState, content: &str, area: &M2Area) { let val = get_node_text(m.captures[3].node, content); match get_kind(m.captures[1].node, content) { Some(JSTypes::Map | JSTypes::Paths) => state.lock().add_component_map(key, val, area), - Some(JSTypes::Mixins) => state.lock().add_component_mixin(key, val), + Some(JSTypes::Mixins) => state.lock().add_component_mixin(key, val, area), None => continue, }; } @@ -256,8 +256,12 @@ mod test { &M2Area::Base, ); result.add_component_map("otherComp", "Some_Other/js/comp", &M2Area::Base); - result.add_component_mixin("Mage_Module/js/smth", "My_Module/js/mixin/smth"); - result.add_component_mixin("Adobe_Module", "My_Module/js/mixin/adobe"); + result.add_component_mixin( + "Mage_Module/js/smth", + "My_Module/js/mixin/smth", + &M2Area::Base, + ); + result.add_component_mixin("Adobe_Module", "My_Module/js/mixin/adobe", &M2Area::Base); assert_eq!(arc_state.lock().to_owned(), result); } diff --git a/src/lsp/definition.rs b/src/lsp/definition.rs index 03c973b..826a675 100644 --- a/src/lsp/definition.rs +++ b/src/lsp/definition.rs @@ -24,7 +24,7 @@ pub fn get_location_from_params( let item = state.lock().get_item_from_position(&path, pos)?; Some(match item { M2Item::ModComponent(mod_name, file_path, mod_path) => { - component::mod_location(mod_name, &file_path, mod_path, &path) + component::mod_location(state, mod_name, &file_path, mod_path, &path) } M2Item::RelComponent(comp, path) => component::find_rel(comp, &path)?, M2Item::Component(comp) => component::find_plain(state, &comp), diff --git a/src/lsp/definition/component.rs b/src/lsp/definition/component.rs index 8b17b41..61c6048 100644 --- a/src/lsp/definition/component.rs +++ b/src/lsp/definition/component.rs @@ -27,19 +27,25 @@ pub fn find_rel(comp: String, path: &Path) -> Option> { } pub fn mod_location( + state: &ArcState, mod_name: String, file_path: &str, mod_path: PathBuf, path: &PathBuf, ) -> Vec { let mut result = vec![]; - let components = vec![M2Item::ModComponent( + let mut components = vec![M2Item::ModComponent( mod_name.clone(), file_path.to_string(), mod_path, )]; let area = path.get_area(); + components.extend( + state + .lock() + .get_component_mixins_for_area(mod_name + "/" + file_path, &area), + ); for component in components { if let M2Item::ModComponent(_, file_path, mod_path) = component { diff --git a/src/m2.rs b/src/m2.rs index 563a3ae..8dce6e1 100644 --- a/src/m2.rs +++ b/src/m2.rs @@ -39,14 +39,6 @@ impl M2Area { Self::Base => None, } } - - pub const fn to_str(&self) -> &str { - match self { - Self::Frontend => "frontend", - Self::Adminhtml => "adminhtml", - Self::Base => "base", - } - } } #[allow(clippy::module_name_repetitions)] diff --git a/src/state.rs b/src/state.rs index 7d0ef5d..7b280e3 100644 --- a/src/state.rs +++ b/src/state.rs @@ -37,7 +37,7 @@ pub struct State { magento_front_themes: HashMap, magento_admin_themes: HashMap, js_maps: [HashMap; 3], - js_mixins: HashMap, + js_mixins: [HashMap>; 3], workspaces: Vec, } @@ -53,7 +53,7 @@ impl State { magento_front_themes: HashMap::new(), magento_admin_themes: HashMap::new(), js_maps: [HashMap::new(), HashMap::new(), HashMap::new()], - js_mixins: HashMap::new(), + js_mixins: [HashMap::new(), HashMap::new(), HashMap::new()], workspaces: vec![], } } @@ -129,18 +129,39 @@ impl State { .collect() } - pub fn add_component_map(&mut self, name: S, val: S, area: &M2Area) -> Option + pub fn add_component_map(&mut self, name: S, val: S, area: &M2Area) where S: Into, { - self.js_maps[area.id()].insert(name.into(), val.into()) + self.js_maps[area.id()].insert(name.into(), val.into()); } - pub fn add_component_mixin(&mut self, name: S, val: S) -> Option + pub fn add_component_mixin(&mut self, name: S, val: S, area: &M2Area) where S: Into, { - self.js_mixins.insert(name.into(), val.into()) + let name = name.into(); + let val = val.into(); + dbg!(&name); + dbg!(&val); + + if let Some(component) = js::text_to_component(self, &val, Path::new("")) { + if let Some(vec) = self.js_mixins[area.id()].get_mut(&name) { + vec.push(dbg!(component)); + } else { + self.js_mixins[area.id()].insert(name, vec![dbg!(component)]); + } + } + } + + pub fn get_component_mixins_for_area(&self, name: S, area: &M2Area) -> Vec + where + S: Into, + { + self.js_mixins[area.id()] + .get(&name.into()) + .cloned() + .unwrap_or_default() } pub fn list_front_themes_paths(&self) -> Vec<&PathBuf> { From 6e7d59444699ff46ecbdfeeecca734ff29d1bd66 Mon Sep 17 00:00:00 2001 From: Pawel Bogut Date: Mon, 9 Oct 2023 23:12:47 +0200 Subject: [PATCH 09/16] feat: track changes in indexed files --- src/js.rs | 49 ++++++++++++--- src/main.rs | 17 ++++-- src/php.rs | 78 ++++++++++++++---------- src/state.rs | 164 ++++++++++++++++++++++++++++++++++++++++++--------- 4 files changed, 234 insertions(+), 74 deletions(-) diff --git a/src/js.rs b/src/js.rs index dee2444..13a5a55 100644 --- a/src/js.rs +++ b/src/js.rs @@ -53,16 +53,26 @@ pub fn update_index(state: &ArcState, path: &PathBuf) { ); } +pub fn maybe_index_file(state: &mut State, content: &str, file_path: &PathBuf) { + if file_path.to_path_str().ends_with("requirejs-config.js") { + update_index_from_config(state, content, file_path); + } +} + +fn index_file(state: &ArcState, file_path: &PathBuf) { + let content = + std::fs::read_to_string(file_path).expect("Should have been able to read the file"); + + update_index_from_config(&mut state.lock(), &content, file_path); +} + fn process_glob(state: &ArcState, glob_path: &PathBuf) { let modules = glob(glob_path.to_path_str()) .expect("Failed to read glob pattern") .filter_map(Result::ok); for file_path in modules { - let content = - std::fs::read_to_string(&file_path).expect("Should have been able to read the file"); - - update_index_from_config(state, &content, &file_path.get_area()); + index_file(state, &file_path); } } @@ -159,7 +169,9 @@ pub fn text_to_component(state: &State, text: &str, path: &Path) -> Option state.lock().add_component_map(key, val, area), - Some(JSTypes::Mixins) => state.lock().add_component_mixin(key, val, area), + Some(JSTypes::Map | JSTypes::Paths) => state.add_component_map(key, val, area), + Some(JSTypes::Mixins) => state.add_component_mixin(key, val, area), None => continue, }; } @@ -237,7 +249,7 @@ mod test { "#; let arc_state = state.into_arc(); - update_index_from_config(&arc_state, content, &M2Area::Base); + update_index_from_config(&mut arc_state.lock(), content, &PathBuf::from("")); let mut result = State::new(); result.add_component_map( @@ -262,8 +274,27 @@ mod test { &M2Area::Base, ); result.add_component_mixin("Adobe_Module", "My_Module/js/mixin/adobe", &M2Area::Base); + result.set_source_file(&PathBuf::from("")); - assert_eq!(arc_state.lock().to_owned(), result); + let computed = arc_state.lock(); + assert_eq!(computed.get_modules(), result.get_modules()); + for module in [ + "prototype", + "otherComp", + "other/core/extension", + "some/js/component", + ] { + assert_eq!( + computed.get_component_map(module, &M2Area::Base), + result.get_component_map(module, &M2Area::Base) + ); + } + for mixin in ["Mage_Module/js/smth", "Adobe_Module"] { + assert_eq!( + computed.get_component_mixins_for_area(mixin, &M2Area::Base), + result.get_component_mixins_for_area(mixin, &M2Area::Base) + ); + } } #[test] diff --git a/src/main.rs b/src/main.rs index dbfa9cc..61c6346 100644 --- a/src/main.rs +++ b/src/main.rs @@ -19,7 +19,10 @@ use lsp_types::{ WorkDoneProgressOptions, }; -use crate::{m2::M2Uri, state::State}; +use crate::{ + m2::{M2Path, M2Uri}, + state::State, +}; fn main() -> Result<(), Box> { // Note that we must have our logging only write out to stderr. @@ -135,9 +138,15 @@ fn main_loop( let params: DidChangeTextDocumentParams = serde_json::from_value(not.params) .context("Deserializing notification params")?; let path = params.text_document.uri.to_path_buf(); - state - .lock() - .set_file(&path, ¶ms.content_changes[0].text); + match path.get_ext().as_str() { + "js" | "xml" => state + .lock() + .set_file(&path, ¶ms.content_changes[0].text), + "php" if path.ends_with("registration.php") => state + .lock() + .set_file(&path, ¶ms.content_changes[0].text), + _ => (), + } #[cfg(debug_assertions)] eprintln!("textDocument/didChange: {path:?}"); } diff --git a/src/php.rs b/src/php.rs index 4dc3e21..75b7120 100644 --- a/src/php.rs +++ b/src/php.rs @@ -1,4 +1,7 @@ -use std::{collections::HashMap, path::PathBuf}; +use std::{ + collections::HashMap, + path::{Path, PathBuf}, +}; use convert_case::{Case, Casing}; use glob::glob; @@ -8,7 +11,7 @@ use tree_sitter::{Node, QueryCursor}; use crate::{ m2::M2Path, queries, - state::ArcState, + state::{ArcState, State}, ts::{self, get_range_from_node}, }; @@ -108,12 +111,51 @@ pub fn update_index(state: &ArcState, path: &PathBuf) { ); // magento2-base setup module } +pub fn maybe_index_file(state: &mut State, content: &str, file_path: &PathBuf) { + if file_path.to_path_str().ends_with("registration.php") { + update_index_from_registration(state, content, file_path); + } +} + +fn update_index_from_registration(state: &mut State, content: &str, file_path: &Path) { + state.set_source_file(file_path); + let query = queries::php_registration(); + let tree = tree_sitter_parsers::parse(content, "php"); + let mut cursor = QueryCursor::new(); + let matches = cursor.matches(query, tree.root_node(), content.as_bytes()); + for m in matches { + let mod_name = ts::get_node_str(m.captures[1].node, content) + .trim_matches('"') + .trim_matches('\''); + + let mut parent = file_path.to_path_buf(); + parent.pop(); + + state.add_module_path(mod_name, parent.clone()); + + match register_param_to_module(mod_name) { + Some(M2Module::Module(m)) => { + state.add_module(mod_name).add_module_path(m, parent); + } + Some(M2Module::Library(l)) => { + state.add_module_path(l, parent); + } + Some(M2Module::FrontTheme(t)) => { + state.add_front_theme_path(t, parent); + } + Some(M2Module::AdminTheme(t)) => { + state.add_admin_theme_path(t, parent); + } + _ => (), + } + } +} + fn process_glob(state: &ArcState, glob_path: &PathBuf) { let modules = glob(glob_path.to_path_str()) .expect("Failed to read glob pattern") .filter_map(Result::ok); - let query = queries::php_registration(); for file_path in modules { if file_path.is_test() { return; @@ -122,35 +164,7 @@ fn process_glob(state: &ArcState, glob_path: &PathBuf) { let content = std::fs::read_to_string(&file_path).expect("Should have been able to read the file"); - let tree = tree_sitter_parsers::parse(&content, "php"); - let mut cursor = QueryCursor::new(); - let matches = cursor.matches(query, tree.root_node(), content.as_bytes()); - for m in matches { - let mod_name = ts::get_node_str(m.captures[1].node, &content) - .trim_matches('"') - .trim_matches('\''); - - let mut parent = file_path.clone(); - parent.pop(); - - state.lock().add_module_path(mod_name, parent.clone()); - - match register_param_to_module(mod_name) { - Some(M2Module::Module(m)) => { - state.lock().add_module(mod_name).add_module_path(m, parent); - } - Some(M2Module::Library(l)) => { - state.lock().add_module_path(l, parent); - } - Some(M2Module::FrontTheme(t)) => { - state.lock().add_front_theme_path(t, parent); - } - Some(M2Module::AdminTheme(t)) => { - state.lock().add_admin_theme_path(t, parent); - } - _ => (), - } - } + update_index_from_registration(&mut state.lock(), &content, &file_path); } } diff --git a/src/state.rs b/src/state.rs index 7b280e3..cb354a1 100644 --- a/src/state.rs +++ b/src/state.rs @@ -29,13 +29,50 @@ impl HashMapId for M2Area { } } +#[derive(Debug, Clone, PartialEq, Eq)] +enum Trackee { + Module(String), + ModulePath(String), + JsMap(M2Area, String), + JsMixin(M2Area, String), + Themes(M2Area, String), +} + +#[derive(Debug, Clone, PartialEq, Eq)] +struct TrackingList(HashMap>); + +impl TrackingList { + pub fn new() -> Self { + Self(HashMap::new()) + } + + pub fn track(&mut self, source_path: &Path, trackee: Trackee) { + self.0 + .entry(source_path.into()) + .or_insert_with(Vec::new) + .push(trackee); + } + + pub fn maybe_track(&mut self, source_path: Option<&PathBuf>, trackee: Trackee) { + if let Some(source_path) = source_path { + self.track(source_path, trackee); + } + } + + pub fn untrack(&mut self, source_path: &Path) -> Option> { + self.0.remove(source_path) + } +} + #[derive(Debug, Clone, PartialEq, Eq)] pub struct State { + source_file: Option, + track_entities: TrackingList, buffers: HashMap, - magento_modules: Vec, - magento_module_paths: HashMap, - magento_front_themes: HashMap, - magento_admin_themes: HashMap, + modules: Vec, + module_paths: HashMap, + front_themes: HashMap, + admin_themes: HashMap, js_maps: [HashMap; 3], js_mixins: [HashMap>; 3], workspaces: Vec, @@ -47,22 +84,66 @@ pub type ArcState = Arc>; impl State { pub fn new() -> Self { Self { + source_file: None, + track_entities: TrackingList::new(), buffers: HashMap::new(), - magento_modules: vec![], - magento_module_paths: HashMap::new(), - magento_front_themes: HashMap::new(), - magento_admin_themes: HashMap::new(), + modules: vec![], + module_paths: HashMap::new(), + front_themes: HashMap::new(), + admin_themes: HashMap::new(), js_maps: [HashMap::new(), HashMap::new(), HashMap::new()], js_mixins: [HashMap::new(), HashMap::new(), HashMap::new()], workspaces: vec![], } } + pub fn set_source_file(&mut self, path: &Path) { + self.source_file = Some(path.to_owned()); + } + + pub fn clear_from_source(&mut self, path: &Path) { + if let Some(list) = self.track_entities.untrack(path) { + for trackee in list { + match trackee { + Trackee::JsMap(area, name) => { + self.js_maps[area.id()].remove(&name); + } + Trackee::JsMixin(area, name) => { + self.js_mixins[area.id()].remove(&name); + } + Trackee::Module(module) => { + self.modules.retain(|m| m != &module); + } + Trackee::ModulePath(module) => { + self.module_paths.remove(&module); + } + Trackee::Themes(area, module) => match area { + M2Area::Frontend => { + self.front_themes.remove(&module); + } + M2Area::Adminhtml => { + self.admin_themes.remove(&module); + } + M2Area::Base => { + self.front_themes.remove(&module); + self.admin_themes.remove(&module); + } + }, + } + } + } + } + pub fn set_file(&mut self, path: &Path, content: S) where S: Into, { - self.buffers.insert(path.to_owned(), content.into()); + let content = content.into(); + self.clear_from_source(path); + js::maybe_index_file(self, &content, &path.to_owned()); + php::maybe_index_file(self, &content, &path.to_owned()); + + self.buffers.insert(path.to_owned(), content); } pub fn get_file(&self, path: &PathBuf) -> Option<&String> { @@ -74,7 +155,7 @@ impl State { } pub fn get_modules(&self) -> Vec { - let mut modules = self.magento_modules.clone(); + let mut modules = self.modules.clone(); modules.sort_unstable(); modules.dedup(); modules @@ -88,11 +169,14 @@ impl State { } pub fn get_module_path(&self, module: &str) -> Option { - self.magento_module_paths.get(module).cloned() + self.module_paths.get(module).cloned() } pub fn add_module(&mut self, module: &str) -> &mut Self { - self.magento_modules.push(module.into()); + self.track_entities + .maybe_track(self.source_file.as_ref(), Trackee::Module(module.into())); + + self.modules.push(module.into()); self } @@ -100,7 +184,13 @@ impl State { where S: Into, { - self.magento_module_paths.insert(module.into(), path); + let module = module.into(); + self.track_entities.maybe_track( + self.source_file.as_ref(), + Trackee::ModulePath(module.clone()), + ); + + self.module_paths.insert(module, path); self } @@ -108,14 +198,26 @@ impl State { where S: Into, { - self.magento_admin_themes.insert(name.into(), path); + let name = name.into(); + self.track_entities.maybe_track( + self.source_file.as_ref(), + Trackee::Themes(M2Area::Adminhtml, name.clone()), + ); + + self.admin_themes.insert(name, path); } pub fn add_front_theme_path(&mut self, name: S, path: PathBuf) where S: Into, { - self.magento_front_themes.insert(name.into(), path); + let name = name.into(); + self.track_entities.maybe_track( + self.source_file.as_ref(), + Trackee::Themes(M2Area::Frontend, name.clone()), + ); + + self.front_themes.insert(name, path); } pub fn get_component_map(&self, name: &str, area: &M2Area) -> Option<&String> { @@ -133,7 +235,13 @@ impl State { where S: Into, { - self.js_maps[area.id()].insert(name.into(), val.into()); + let name = name.into(); + self.track_entities.maybe_track( + self.source_file.as_ref(), + Trackee::JsMap(area.clone(), name.clone()), + ); + + self.js_maps[area.id()].insert(name, val.into()); } pub fn add_component_mixin(&mut self, name: S, val: S, area: &M2Area) @@ -142,15 +250,17 @@ impl State { { let name = name.into(); let val = val.into(); - dbg!(&name); - dbg!(&val); + + self.track_entities.maybe_track( + self.source_file.as_ref(), + Trackee::JsMixin(area.clone(), name.clone()), + ); if let Some(component) = js::text_to_component(self, &val, Path::new("")) { - if let Some(vec) = self.js_mixins[area.id()].get_mut(&name) { - vec.push(dbg!(component)); - } else { - self.js_mixins[area.id()].insert(name, vec![dbg!(component)]); - } + self.js_mixins[area.id()] + .entry(name) + .or_insert_with(Vec::new) + .push(component); } } @@ -165,15 +275,11 @@ impl State { } pub fn list_front_themes_paths(&self) -> Vec<&PathBuf> { - self.magento_front_themes - .values() - .collect::>() + self.front_themes.values().collect::>() } pub fn list_admin_themes_paths(&self) -> Vec<&PathBuf> { - self.magento_admin_themes - .values() - .collect::>() + self.admin_themes.values().collect::>() } pub fn workspace_paths(&self) -> Vec { From 1b4813422dac1c576f811425bc32d523049b9ec3 Mon Sep 17 00:00:00 2001 From: Pawel Bogut Date: Wed, 11 Oct 2023 21:41:13 +0200 Subject: [PATCH 10/16] fix: resolve mixin string to component when used (not when indexing) --- src/state.rs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/state.rs b/src/state.rs index cb354a1..cf9c92c 100644 --- a/src/state.rs +++ b/src/state.rs @@ -74,7 +74,7 @@ pub struct State { front_themes: HashMap, admin_themes: HashMap, js_maps: [HashMap; 3], - js_mixins: [HashMap>; 3], + js_mixins: [HashMap>; 3], workspaces: Vec, } @@ -256,22 +256,23 @@ impl State { Trackee::JsMixin(area.clone(), name.clone()), ); - if let Some(component) = js::text_to_component(self, &val, Path::new("")) { - self.js_mixins[area.id()] - .entry(name) - .or_insert_with(Vec::new) - .push(component); - } + self.js_mixins[area.id()] + .entry(name) + .or_insert_with(Vec::new) + .push(val); } pub fn get_component_mixins_for_area(&self, name: S, area: &M2Area) -> Vec where S: Into, { + let empty_path = Path::new(""); self.js_mixins[area.id()] .get(&name.into()) - .cloned() - .unwrap_or_default() + .unwrap_or(&vec![]) + .iter() + .filter_map(|mod_string| js::text_to_component(self, mod_string, empty_path)) + .collect() } pub fn list_front_themes_paths(&self) -> Vec<&PathBuf> { From 8999a0b5166fae8cbc01b2b47582ef18d7e0bafd Mon Sep 17 00:00:00 2001 From: Pawel Bogut Date: Thu, 12 Oct 2023 18:52:32 +0200 Subject: [PATCH 11/16] fix: suggestions for library modules --- src/lsp/completion.rs | 71 ++++++++++++++++++++++--------------------- src/php.rs | 4 ++- src/ts.rs | 5 +-- 3 files changed, 40 insertions(+), 40 deletions(-) diff --git a/src/lsp/completion.rs b/src/lsp/completion.rs index c065ce1..8f66b69 100644 --- a/src/lsp/completion.rs +++ b/src/lsp/completion.rs @@ -109,14 +109,10 @@ fn completion_for_classes( let text = text.trim_start_matches('\\'); if text.is_empty() || (m2::is_part_of_class_name(text) && text.matches('\\').count() == 0) { Some(completion_for_classes_prefix(state, range)) - } else if text.matches('\\').count() == 1 { + } else if text.matches('\\').count() >= 1 { let mut result = completion_for_classes_prefix(state, range); - if let Some(classes) = completion_for_classes_full(state, text, range) { - result.extend(classes); - } + result.extend(completion_for_classes_full(state, text, range)); Some(result) - } else if text.matches('\\').count() >= 2 { - completion_for_classes_full(state, text, range) } else { None } @@ -127,41 +123,46 @@ fn completion_for_classes_prefix(state: &ArcState, range: Range) -> Vec Option> { - let mut parts = text.split('\\'); - - let module_name = format!("{}_{}", parts.next()?, parts.next()?); +fn completion_for_classes_full(state: &ArcState, text: &str, range: Range) -> Vec { + let mut classes = vec![]; + let mut index = 0; + let splits: Vec = text + .chars() + .filter_map(|c| { + index += 1; + if c == '\\' { + Some(index) + } else { + None + } + }) + .collect(); - let mut parts = text.split('\\').collect::>(); - parts.pop(); - let typed_class_prefix = parts.join("\\"); + for spllit in splits { + let prefix = &text[..spllit - 1]; + if let Some(module_path) = state.lock().get_module_path(prefix) { + let candidates = glob(module_path.append(&["**", "*.php"]).to_path_str()) + .expect("Failed to read glob pattern"); + for p in candidates { + let path = p.map_or_else(|_| std::path::PathBuf::new(), |p| p); + let rel_path = path.relative_to(&module_path).str_components().join("\\"); + let class_suffix = rel_path.trim_end_matches(".php"); + let class = format!("{}\\{}", prefix, class_suffix); - let module_class = module_name.replace('_', "\\"); - let module_path = state.lock().get_module_path(&module_name)?; - let candidates = glob(module_path.append(&["**", "*.php"]).to_path_str()) - .expect("Failed to read glob pattern"); - let mut classes = vec![]; - for p in candidates { - let path = p.map_or_else(|_| std::path::PathBuf::new(), |p| p); - let rel_path = path.relative_to(&module_path).str_components().join("\\"); - let class_suffix = rel_path.trim_end_matches(".php"); - let class = format!("{}\\{}", &module_class, class_suffix); + if class.ends_with("\\registration") { + continue; + } - if class.ends_with("\\registration") { - continue; - } + if !class.starts_with(&text[..index - 1]) { + continue; + } - if !class.starts_with(&typed_class_prefix) { - continue; + classes.push(class); + } } - - classes.push(class); } - Some(string_vec_and_range_to_completion_list(classes, range)) + + string_vec_and_range_to_completion_list(classes, range) } fn completion_for_template( diff --git a/src/php.rs b/src/php.rs index 75b7120..4bbdb6e 100644 --- a/src/php.rs +++ b/src/php.rs @@ -138,7 +138,9 @@ fn update_index_from_registration(state: &mut State, content: &str, file_path: & state.add_module(mod_name).add_module_path(m, parent); } Some(M2Module::Library(l)) => { - state.add_module_path(l, parent); + state + .add_module(&l.replace('\\', "_")) + .add_module_path(l, parent); } Some(M2Module::FrontTheme(t)) => { state.add_front_theme_path(t, parent); diff --git a/src/ts.rs b/src/ts.rs index 3657069..0496914 100644 --- a/src/ts.rs +++ b/src/ts.rs @@ -15,10 +15,7 @@ pub fn get_range_from_node(node: Node) -> Range { } pub fn get_node_text_before_pos(node: Node, content: &str, pos: Position) -> String { - let text = node - .utf8_text(content.as_bytes()) - .unwrap_or("") - .trim_matches('\\'); + let text = node.utf8_text(content.as_bytes()).unwrap_or(""); let node_start_pos = node.start_position(); let node_end_pos = node.end_position(); From a7ba4be0026c1c8e7ff3f786c4c8f734680ddd63 Mon Sep 17 00:00:00 2001 From: Pawel Bogut Date: Thu, 12 Oct 2023 20:34:13 +0200 Subject: [PATCH 12/16] feat: publish extention on release --- .github/workflows/release.yml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4e991ef..84bfb16 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -162,3 +162,19 @@ jobs: prerelease: true env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Publish Extension (Code Marketplace, release) + if: env.SHOULD_RELEASE == 'yes' + shell: bash + run: | + cd "vscode" + npm install --include=dev + npx vsce publish --pat ${{ secrets.MARKETPLACE_TOKEN }} --packagePath ../dist/magento2-ls.*.vsix + + - name: Publish Extension (OpenVSX, release) + if: env.SHOULD_RELEASE == 'yes' + shell: bash + run: | + cd "vscode" + npm install --include=dev + npx ovsx publish --pat ${{ secrets.OPENVSX_TOKEN }} --packagePath ../dist/magento2-ls.*.vsix From 61fcf82bc1f374eb259461d97cf68dcb80e8ca4b Mon Sep 17 00:00:00 2001 From: Pawel Bogut Date: Thu, 12 Oct 2023 22:55:24 +0200 Subject: [PATCH 13/16] feat: reduce locking in completion --- src/lsp.rs | 5 +++-- src/lsp/completion.rs | 50 ++++++++++++++++++++----------------------- src/main.rs | 2 +- 3 files changed, 27 insertions(+), 30 deletions(-) diff --git a/src/lsp.rs b/src/lsp.rs index 90dcf9b..f1d8057 100644 --- a/src/lsp.rs +++ b/src/lsp.rs @@ -5,10 +5,11 @@ use lsp_types::{ CompletionParams, CompletionResponse, GotoDefinitionParams, GotoDefinitionResponse, }; -use crate::state::ArcState; +use crate::state::{ArcState, State}; use self::{completion::get_completion_from_params, definition::get_location_from_params}; -pub fn completion_handler(state: &ArcState, params: &CompletionParams) -> CompletionResponse { + +pub fn completion_handler(state: &State, params: &CompletionParams) -> CompletionResponse { CompletionResponse::Array( get_completion_from_params(state, params).map_or(vec![], |loc_list| loc_list), ) diff --git a/src/lsp/completion.rs b/src/lsp/completion.rs index 8f66b69..433905b 100644 --- a/src/lsp/completion.rs +++ b/src/lsp/completion.rs @@ -11,12 +11,12 @@ use lsp_types::{ use crate::{ js::{self, JsCompletionType}, m2::{self, M2Area, M2Path, M2Uri}, - state::ArcState, + state::State, xml, }; pub fn get_completion_from_params( - state: &ArcState, + state: &State, params: &CompletionParams, ) -> Option> { let path = params @@ -34,11 +34,11 @@ pub fn get_completion_from_params( } fn js_completion_handler( - state: &ArcState, + state: &State, path: &PathBuf, pos: Position, ) -> Option> { - let at_position = js::get_completion_item(state.lock().get_file(path)?, pos)?; + let at_position = js::get_completion_item(state.get_file(path)?, pos)?; match at_position.kind { JsCompletionType::Definition => completion_for_component( @@ -51,11 +51,11 @@ fn js_completion_handler( } fn xml_completion_handler( - state: &ArcState, + state: &State, path: &PathBuf, pos: Position, ) -> Option> { - let at_position = xml::get_current_position_path(state.lock().get_file(path)?, pos)?; + let at_position = xml::get_current_position_path(state.get_file(path)?, pos)?; match at_position { x if x.match_path("[@template]") => { completion_for_template(state, &x.text, x.range, &path.get_area()) @@ -101,11 +101,7 @@ fn xml_completion_handler( } } -fn completion_for_classes( - state: &ArcState, - text: &str, - range: Range, -) -> Option> { +fn completion_for_classes(state: &State, text: &str, range: Range) -> Option> { let text = text.trim_start_matches('\\'); if text.is_empty() || (m2::is_part_of_class_name(text) && text.matches('\\').count() == 0) { Some(completion_for_classes_prefix(state, range)) @@ -118,12 +114,12 @@ fn completion_for_classes( } } -fn completion_for_classes_prefix(state: &ArcState, range: Range) -> Vec { - let module_prefixes = state.lock().get_module_class_prefixes(); +fn completion_for_classes_prefix(state: &State, range: Range) -> Vec { + let module_prefixes = state.get_module_class_prefixes(); string_vec_and_range_to_completion_list(module_prefixes, range) } -fn completion_for_classes_full(state: &ArcState, text: &str, range: Range) -> Vec { +fn completion_for_classes_full(state: &State, text: &str, range: Range) -> Vec { let mut classes = vec![]; let mut index = 0; let splits: Vec = text @@ -140,7 +136,7 @@ fn completion_for_classes_full(state: &ArcState, text: &str, range: Range) -> Ve for spllit in splits { let prefix = &text[..spllit - 1]; - if let Some(module_path) = state.lock().get_module_path(prefix) { + if let Some(module_path) = state.get_module_path(prefix) { let candidates = glob(module_path.append(&["**", "*.php"]).to_path_str()) .expect("Failed to read glob pattern"); for p in candidates { @@ -166,17 +162,17 @@ fn completion_for_classes_full(state: &ArcState, text: &str, range: Range) -> Ve } fn completion_for_template( - state: &ArcState, + state: &State, text: &str, range: Range, area: &M2Area, ) -> Option> { if text.is_empty() || m2::is_part_of_module_name(text) { - let modules = state.lock().get_modules(); + let modules = state.get_modules(); Some(string_vec_and_range_to_completion_list(modules, range)) } else if text.contains("::") { let module_name = text.split("::").next()?; - let path = state.lock().get_module_path(module_name); + let path = state.get_module_path(module_name); match path { None => None, Some(path) => { @@ -202,7 +198,7 @@ fn completion_for_template( } fn completion_for_component( - state: &ArcState, + state: &State, text: &str, range: Range, area: &M2Area, @@ -210,7 +206,7 @@ fn completion_for_component( if text.contains('/') { let module_name = text.split('/').next()?; let mut files = vec![]; - if let Some(path) = state.lock().get_module_path(module_name) { + if let Some(path) = state.get_module_path(module_name) { for area in area.path_candidates() { let view_path = path.append(&["view", area, "web"]); let glob_path = view_path.append(&["**", "*.js"]); @@ -225,7 +221,7 @@ fn completion_for_component( })); } } - let workspaces = state.lock().workspace_paths(); + let workspaces = state.workspace_paths(); for path in workspaces { let view_path = path.append(&["lib", "web"]); let glob_path = view_path.append(&["**", "*.js"]); @@ -239,19 +235,19 @@ fn completion_for_component( })); } - files.extend(state.lock().get_component_maps_for_area(area)); + files.extend(state.get_component_maps_for_area(area)); if let Some(lower_area) = area.lower_area() { - files.extend(state.lock().get_component_maps_for_area(&lower_area)); + files.extend(state.get_component_maps_for_area(&lower_area)); } Some(string_vec_and_range_to_completion_list(files, range)) } else { let mut modules = vec![]; - modules.extend(state.lock().get_modules()); - modules.extend(state.lock().get_component_maps_for_area(area)); + modules.extend(state.get_modules()); + modules.extend(state.get_component_maps_for_area(area)); if let Some(lower_area) = area.lower_area() { - modules.extend(state.lock().get_component_maps_for_area(&lower_area)); + modules.extend(state.get_component_maps_for_area(&lower_area)); } - let workspaces = state.lock().workspace_paths(); + let workspaces = state.workspace_paths(); for path in workspaces { let view_path = path.append(&["lib", "web"]); let glob_path = view_path.append(&["**", "*.js"]); diff --git a/src/main.rs b/src/main.rs index 61c6346..0d2316a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -108,7 +108,7 @@ fn main_loop( match req.method.as_str() { "textDocument/completion" => { let (id, params) = cast::(req)?; - let result = lsp::completion_handler(&state, ¶ms); + let result = lsp::completion_handler(&state.lock(), ¶ms); connection.sender.send(get_response_message(id, result))?; } "textDocument/definition" => { From 95e1cfc18332fa584f867d86d64b67fae11cc5ec Mon Sep 17 00:00:00 2001 From: Pawel Bogut Date: Thu, 12 Oct 2023 23:00:24 +0200 Subject: [PATCH 14/16] feat: reduce locking in go-to-definition --- src/lsp.rs | 7 ++----- src/lsp/definition.rs | 6 +++--- src/lsp/definition/component.rs | 14 +++++--------- src/lsp/definition/php.rs | 17 ++++++++--------- src/lsp/definition/phtml.rs | 20 ++++++++++---------- src/main.rs | 2 +- 6 files changed, 29 insertions(+), 37 deletions(-) diff --git a/src/lsp.rs b/src/lsp.rs index f1d8057..8e7724e 100644 --- a/src/lsp.rs +++ b/src/lsp.rs @@ -5,7 +5,7 @@ use lsp_types::{ CompletionParams, CompletionResponse, GotoDefinitionParams, GotoDefinitionResponse, }; -use crate::state::{ArcState, State}; +use crate::state::State; use self::{completion::get_completion_from_params, definition::get_location_from_params}; @@ -15,10 +15,7 @@ pub fn completion_handler(state: &State, params: &CompletionParams) -> Completio ) } -pub fn definition_handler( - state: &ArcState, - params: &GotoDefinitionParams, -) -> GotoDefinitionResponse { +pub fn definition_handler(state: &State, params: &GotoDefinitionParams) -> GotoDefinitionResponse { GotoDefinitionResponse::Array( get_location_from_params(state, params).map_or(vec![], |loc_list| loc_list), ) diff --git a/src/lsp/definition.rs b/src/lsp/definition.rs index 826a675..6f0cf9d 100644 --- a/src/lsp/definition.rs +++ b/src/lsp/definition.rs @@ -8,11 +8,11 @@ use lsp_types::{GotoDefinitionParams, Location, Range, Url}; use crate::{ m2::{M2Item, M2Uri}, - state::ArcState, + state::State, }; pub fn get_location_from_params( - state: &ArcState, + state: &State, params: &GotoDefinitionParams, ) -> Option> { let path = params @@ -21,7 +21,7 @@ pub fn get_location_from_params( .uri .to_path_buf(); let pos = params.text_document_position_params.position; - let item = state.lock().get_item_from_position(&path, pos)?; + let item = state.get_item_from_position(&path, pos)?; Some(match item { M2Item::ModComponent(mod_name, file_path, mod_path) => { component::mod_location(state, mod_name, &file_path, mod_path, &path) diff --git a/src/lsp/definition/component.rs b/src/lsp/definition/component.rs index 61c6048..55aa3d8 100644 --- a/src/lsp/definition/component.rs +++ b/src/lsp/definition/component.rs @@ -4,14 +4,14 @@ use lsp_types::Location; use crate::{ m2::{M2Item, M2Path}, - state::ArcState, + state::State, }; use super::path_to_location; -pub fn find_plain(state: &ArcState, comp: &str) -> Vec { +pub fn find_plain(state: &State, comp: &str) -> Vec { let mut result = vec![]; - let workspace_paths = state.lock().workspace_paths(); + let workspace_paths = state.workspace_paths(); for path in workspace_paths { let path = path.append(&["lib", "web", comp]).append_ext("js"); if let Some(location) = path_to_location(&path) { @@ -27,7 +27,7 @@ pub fn find_rel(comp: String, path: &Path) -> Option> { } pub fn mod_location( - state: &ArcState, + state: &State, mod_name: String, file_path: &str, mod_path: PathBuf, @@ -41,11 +41,7 @@ pub fn mod_location( )]; let area = path.get_area(); - components.extend( - state - .lock() - .get_component_mixins_for_area(mod_name + "/" + file_path, &area), - ); + components.extend(state.get_component_mixins_for_area(mod_name + "/" + file_path, &area)); for component in components { if let M2Item::ModComponent(_, file_path, mod_path) = component { diff --git a/src/lsp/definition/php.rs b/src/lsp/definition/php.rs index 7ee47c8..ad074a4 100644 --- a/src/lsp/definition/php.rs +++ b/src/lsp/definition/php.rs @@ -1,21 +1,20 @@ use lsp_types::Location; -use parking_lot::MutexGuard; use crate::{ php::{parse_php_file, PHPClass}, - state::{ArcState, State}, + state::State, }; -pub fn find_class(state: &ArcState, class: &str) -> Option { - let phpclass = get_php_class_from_class_name(&state.lock(), class)?; +pub fn find_class(state: &State, class: &str) -> Option { + let phpclass = get_php_class_from_class_name(state, class)?; Some(Location { uri: phpclass.uri.clone(), range: phpclass.range, }) } -pub fn find_method(state: &ArcState, class: &str, method: &str) -> Option { - let phpclass = get_php_class_from_class_name(&state.lock(), class)?; +pub fn find_method(state: &State, class: &str, method: &str) -> Option { + let phpclass = get_php_class_from_class_name(state, class)?; Some(Location { uri: phpclass.uri.clone(), range: phpclass @@ -25,8 +24,8 @@ pub fn find_method(state: &ArcState, class: &str, method: &str) -> Option Option { - let phpclass = get_php_class_from_class_name(&state.lock(), class)?; +pub fn find_const(state: &State, class: &str, constant: &str) -> Option { + let phpclass = get_php_class_from_class_name(state, class)?; Some(Location { uri: phpclass.uri.clone(), range: phpclass @@ -36,7 +35,7 @@ pub fn find_const(state: &ArcState, class: &str, constant: &str) -> Option, class: &str) -> Option { +fn get_php_class_from_class_name(state: &State, class: &str) -> Option { let module_path = state.split_class_to_path_and_suffix(class); match module_path { None => None, diff --git a/src/lsp/definition/phtml.rs b/src/lsp/definition/phtml.rs index 9bf75d5..4758be6 100644 --- a/src/lsp/definition/phtml.rs +++ b/src/lsp/definition/phtml.rs @@ -2,26 +2,26 @@ use lsp_types::Location; use crate::{ m2::{M2Area, M2Path}, - state::ArcState, + state::State, }; use super::path_to_location; -pub fn find_admin(state: &ArcState, mod_name: &str, template: &str) -> Vec { +pub fn find_admin(state: &State, mod_name: &str, template: &str) -> Vec { let mut result = vec![]; add_phtml_in_mod_location(state, &mut result, mod_name, template, &M2Area::Adminhtml); add_phtml_in_admin_theme_location(state, &mut result, mod_name, template); result } -pub fn find_front(state: &ArcState, mod_name: &str, template: &str) -> Vec { +pub fn find_front(state: &State, mod_name: &str, template: &str) -> Vec { let mut result = vec![]; add_phtml_in_mod_location(state, &mut result, mod_name, template, &M2Area::Frontend); add_phtml_in_front_theme_location(state, &mut result, mod_name, template); result } -pub fn find_base(state: &ArcState, mod_name: &str, template: &str) -> Vec { +pub fn find_base(state: &State, mod_name: &str, template: &str) -> Vec { let mut result = vec![]; add_phtml_in_mod_location(state, &mut result, mod_name, template, &M2Area::Base); add_phtml_in_front_theme_location(state, &mut result, mod_name, template); @@ -30,13 +30,13 @@ pub fn find_base(state: &ArcState, mod_name: &str, template: &str) -> Vec, mod_name: &str, template: &str, area: &M2Area, ) { - let mod_path = state.lock().get_module_path(mod_name); + let mod_path = state.get_module_path(mod_name); if let Some(path) = mod_path { for area in area.path_candidates() { let templ_path = path.append(&["view", area, "templates", template]); @@ -48,13 +48,13 @@ fn add_phtml_in_mod_location( } fn add_phtml_in_admin_theme_location( - state: &ArcState, + state: &State, result: &mut Vec, mod_name: &str, template: &str, ) { #[allow(clippy::significant_drop_in_scrutinee)] - for theme_path in state.lock().list_admin_themes_paths() { + for theme_path in state.list_admin_themes_paths() { let path = theme_path.append(&[mod_name, "templates", template]); if let Some(location) = path_to_location(&path) { result.push(location); @@ -63,13 +63,13 @@ fn add_phtml_in_admin_theme_location( } fn add_phtml_in_front_theme_location( - state: &ArcState, + state: &State, result: &mut Vec, mod_name: &str, template: &str, ) { #[allow(clippy::significant_drop_in_scrutinee)] - for theme_path in state.lock().list_front_themes_paths() { + for theme_path in state.list_front_themes_paths() { let path = theme_path.append(&[mod_name, "templates", template]); if let Some(location) = path_to_location(&path) { result.push(location); diff --git a/src/main.rs b/src/main.rs index 0d2316a..a9122ed 100644 --- a/src/main.rs +++ b/src/main.rs @@ -113,7 +113,7 @@ fn main_loop( } "textDocument/definition" => { let (id, params) = cast::(req)?; - let result = lsp::definition_handler(&state, ¶ms); + let result = lsp::definition_handler(&state.lock(), ¶ms); connection.sender.send(get_response_message(id, result))?; } _ => { From ab29eaa9b727f7ad14753480c56774dc243f2b5f Mon Sep 17 00:00:00 2001 From: Pawel Bogut Date: Fri, 13 Oct 2023 20:45:58 +0200 Subject: [PATCH 15/16] docs: add requirements section --- CHANGELOG.md | 14 ++++++++++++++ README.md | 12 ++++++++++++ 2 files changed, 26 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7774704..0e16010 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ # Change Log +## [0.0.6 - Work In Progress] + +#### Features + +- [ ] No completion for "Magento\Framework" + +- Update state on `registration.php` and `requirejs-config.js` change. +- Added mixins to definition locations of JS components. +- Speed up indexing by searching only specific locations. + +#### Fixes + +- Please provide suggestions for library modules (such as `magento-framework`). + ## [0.0.5 - October 4, 2023] #### Features diff --git a/README.md b/README.md index 7d232b0..71d5c5a 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,18 @@ Be PHP Language Server (or XML LS) in any capacity. [Intelephense](https://intelephense.com/) works nice with Magento 2 if you need LS for your project. +## Requirements + +In order to complete Magento classes, the Magento root folder must be opened in the workspace. + +The language server detects Magento modules by searching for `registration.php` files in the following locations: + +- The root folder (for modules added to the workspace) +- `app/code/*/*/` - for local modules +- `vendor/*/*/` - for vendor modules +- `app/design/*/*/*/` - for themes. + + ## Contributing If you would like to contribute, please feel free to submit pull requests or open issues on the [GitHub repository](https://github.com/pbogut/magento2-ls). From 269ee8f9889ae007e2d3e576028b44dff658a6e4 Mon Sep 17 00:00:00 2001 From: Pawel Bogut Date: Fri, 13 Oct 2023 20:50:12 +0200 Subject: [PATCH 16/16] docs: update changelog --- CHANGELOG.md | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e16010..c041fd4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,15 +4,11 @@ #### Features -- [ ] No completion for "Magento\Framework" +- Added mixins to the definition locations of JS components. +- Added state updates when changes occur in `registration.php` and `requirejs-config.js`. +- Increased indexing speed by searching only specific locations. +- Added suggestions for library modules, such as `magento-framework`. -- Update state on `registration.php` and `requirejs-config.js` change. -- Added mixins to definition locations of JS components. -- Speed up indexing by searching only specific locations. - -#### Fixes - -- Please provide suggestions for library modules (such as `magento-framework`). ## [0.0.5 - October 4, 2023]