diff --git a/crates/els/completion.rs b/crates/els/completion.rs index a9aadeaf2..21526aa99 100644 --- a/crates/els/completion.rs +++ b/crates/els/completion.rs @@ -419,6 +419,45 @@ impl CompletionCache { } impl Server { + /// Returns completion candidates from modules in the same directory + fn neighbor_completion( + &self, + uri: &NormalizedUrl, + arg_pt: Option, + ) -> Vec { + let mut comps = vec![]; + for mod_ctx in self.get_neighbor_ctxs(uri) { + for (name, vi) in mod_ctx.local_dir() { + if vi.vis.is_private() { + continue; + } + let path = vi.def_loc.module.as_ref().unwrap(); + let path = path.file_stem().unwrap().to_string_lossy(); + let mut item = CompletionItem::new_simple( + format!("{name} (import from {path})"), + vi.t.to_string(), + ); + CompletionOrderSetter::new(vi, arg_pt.as_ref(), mod_ctx, item.label.clone()) + .set(&mut item); + // item.sort_text = Some(format!("{}_{}", CompletionOrder::OtherNamespace, item.label)); + item.kind = Some(comp_item_kind(vi)); + let import = if PYTHON_MODE { + format!("from {path} import {name}\n") + } else { + format!("{{{name};}} = import \"{path}\"\n") + }; + item.additional_text_edits = Some(vec![TextEdit { + range: Range::new(Position::new(0, 0), Position::new(0, 0)), + new_text: import, + }]); + item.insert_text = Some(name.inspect().trim_end_matches('\0').to_string()); + item.filter_text = Some(name.inspect().to_string()); + comps.push(item); + } + } + comps + } + pub(crate) fn handle_completion( &mut self, params: CompletionParams, @@ -560,6 +599,7 @@ impl Server { self.comp_cache.insert("".into(), comps.clone()); result.extend(comps); } + result.extend(self.neighbor_completion(&uri, arg_pt)); } send_log(format!("completion items: {}", result.len()))?; Ok(Some(CompletionResponse::Array(result))) diff --git a/crates/els/server.rs b/crates/els/server.rs index 454d32a41..ae6bdab9d 100644 --- a/crates/els/server.rs +++ b/crates/els/server.rs @@ -774,6 +774,23 @@ impl Server { ctxs } + pub(crate) fn get_neighbor_ctxs(&self, uri: &NormalizedUrl) -> Vec<&Context> { + let mut ctxs = vec![]; + if let Ok(dir) = uri + .to_file_path() + .and_then(|p| p.parent().unwrap().read_dir().map_err(|_| ())) + { + for neighbor in dir { + let Ok(neighbor) = neighbor else { continue; }; + let uri = NormalizedUrl::from_file_path(neighbor.path()).unwrap(); + if let Some(mod_ctx) = &self.modules.get(&uri) { + ctxs.push(&mod_ctx.context); + } + } + } + ctxs + } + pub(crate) fn get_receiver_ctxs( &self, uri: &NormalizedUrl,