Skip to content

Commit 53a830f

Browse files
committed
[IMP] list namespace dirs on hover
Instead of displaying the "See also" link, we now list the namespace directories when hovering over a namespace symbol. We also make sure that the documentation section appears before the useful links/directories list section.
1 parent ded73b5 commit 53a830f

File tree

2 files changed

+102
-20
lines changed

2 files changed

+102
-20
lines changed

server/src/features/features_utils.rs

Lines changed: 36 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,9 @@ use ruff_text_size::{Ranged, TextRange, TextSize};
44
use crate::core::file_mgr::FileMgr;
55
use crate::core::odoo::SyncOdoo;
66
use crate::core::symbols::function_symbol::Argument;
7-
use crate::utils::{compare_semver, PathSanitizer};
7+
use crate::utils::compare_semver;
88
use std::cmp::Ordering;
99
use std::collections::HashMap;
10-
use std::path::PathBuf;
1110
use std::rc::Weak;
1211
use std::{cell::RefCell, rc::Rc};
1312

@@ -507,12 +506,12 @@ impl FeaturesUtils {
507506
let from_modules = info_pieces.iter().filter_map(|info| info.from_module.clone().map(|module_rc| module_rc.borrow().name().clone())).unique().collect::<Vec<_>>();
508507
// BLOCK 1: (type) **name** -> inferred_type
509508
block += FeaturesUtils::build_block_1(session, id.type_, &id.name, sym_type_tag, &inferred_types).as_str();
510-
// BLOCK 2: useful links
511-
block += inferred_types.iter().map(|typ| FeaturesUtils::get_useful_link(session, &typ.eval_ptr)).collect::<String>().as_str();
512-
// BLOCK 3: documentation
509+
// BLOCK 2: documentation
513510
if let Some(documentation_block) = FeaturesUtils::get_documentation_block(session, &from_modules, &inferred_types){
514511
block = block + " \n*** \n" + &documentation_block;
515512
}
513+
// BLOCK 3: useful links or directory paths
514+
block += inferred_types.iter().map(|typ| FeaturesUtils::get_location_info(session, &typ.eval_ptr)).collect::<String>().as_str();
516515
blocks.push(block);
517516
}
518517
blocks.iter().join(" \n*** \n")
@@ -674,24 +673,41 @@ impl FeaturesUtils {
674673

675674
}
676675

677-
/// Finds and returns useful links for an evaluation
678-
fn get_useful_link(_session: &mut SessionInfo, typ: &EvaluationSymbolPtr) -> String {
676+
/// Finds and returns useful links or directory locations for an evaluation
677+
fn get_location_info(_session: &mut SessionInfo, typ: &EvaluationSymbolPtr) -> String {
679678
// Possibly add more links in the future
680679
let Some(typ) = typ.upgrade_weak() else {
681-
return S!("")
680+
return S!("");
682681
};
683-
let paths = &typ.borrow().paths();
684-
if paths.len() == 1 { //we won't put a link to a namespace
685-
let type_ref = typ.borrow();
686-
let base_path = match type_ref.typ() {
687-
SymType::PACKAGE(_) => PathBuf::from(paths.first().unwrap().clone()).join(format!("__init__.py{}", type_ref.as_package().i_ext())).sanitize(),
688-
_ => paths.first().unwrap().clone()
689-
};
690-
let path = FileMgr::pathname2uri(&base_path);
691-
let range = if type_ref.is_file_content() { type_ref.range().start().to_u32() } else { 0 };
692-
format!(" \n*** \nSee also: [{}]({}#{}){}", type_ref.name().as_str(), path.as_str(), range, " \n")
693-
} else {
694-
S!("")
682+
let symbol = &*typ.borrow();
683+
let lb = FeaturesUtils::get_line_break(_session);
684+
match symbol {
685+
Symbol::Namespace(ns) => {
686+
// List namespace directories
687+
let paths = ns.paths();
688+
let name = &ns.name;
689+
match paths.len() {
690+
0 => S!(""),
691+
1 => format!(" \n*** \n`{name}` namespace directory: `{}`{lb}", paths[0]),
692+
_ => {
693+
let path_list = paths.iter().map(|p| format!("- `{p}`")).join(lb);
694+
format!(" \n*** \n`{name}` namespace directories:{lb}{path_list}{lb}")
695+
}
696+
}
697+
}
698+
Symbol::Package(_)
699+
| Symbol::File(_)
700+
| Symbol::XmlFileSymbol(_)
701+
| Symbol::CsvFileSymbol(_) => {
702+
// Get useful link
703+
let uri = FileMgr::pathname2uri(&symbol.get_symbol_first_path());
704+
let range = if symbol.is_file_content() { symbol.range().start().to_u32() } else { 0 };
705+
format!( " \n*** \nSee also: [{}]({}#{}){lb}", symbol.name(), uri.as_str(), range)
706+
}
707+
Symbol::Compiled(c) => {
708+
format!(" \n*** \n`{}` is a binary at `{}`{lb}", c.name, c.path)
709+
}
710+
_ => S!(""),
695711
}
696712
}
697713

server/tests/test_get_symbol.rs

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,72 @@ fn test_hover_on_model_field_and_method() {
150150
);
151151
}
152152

153+
#[test]
154+
fn test_hover_on_namespace_and_module() {
155+
// Setup server and session with test addons
156+
let mut odoo = setup::setup::setup_server(true);
157+
let test_addons_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests").join("data").join("addons");
158+
let test_file = test_addons_path.join("module_1").join("models").join("base_test_models.py").sanitize();
159+
160+
// Ensure the test file exists
161+
assert!(PathBuf::from(&test_file).exists(), "Test file does not exist: {}", test_file);
162+
let mut session = setup::setup::create_session(&mut odoo);
163+
164+
// Get file symbol and file info
165+
let file_mgr = session.sync_odoo.get_file_mgr();
166+
let file_info = file_mgr.borrow().get_file_info(&test_file).unwrap();
167+
let Some(file_symbol) = SyncOdoo::get_symbol_of_opened_file(
168+
&mut session,
169+
&PathBuf::from(&test_file)
170+
) else {
171+
panic!("Failed to get file symbol");
172+
};
173+
174+
// Test hover on namespace: "odoo.addons" in line 2: from odoo.addons.module_1.constants import ...
175+
// Position: line 1 (0-indexed), character at "addons" (~10-16)
176+
let hover_namespace = test_utils::get_hover_markdown(&mut session, &file_symbol, &file_info, 1, 12).unwrap_or_default();
177+
178+
// Should show namespace symbol type
179+
assert!(
180+
hover_namespace.contains("(namespace)"),
181+
"Hover on namespace should show '(namespace)' type. Got: {}", hover_namespace
182+
);
183+
184+
// Should show "addons" as the name
185+
assert!(
186+
hover_namespace.contains("addons"),
187+
"Hover on namespace should show namespace name. Got: {}", hover_namespace
188+
);
189+
190+
// Should list directories instead of "See also" link
191+
assert!(
192+
hover_namespace.contains("directories:"),
193+
"Hover on namespace should list directories. Got: {}", hover_namespace
194+
);
195+
196+
// Should NOT contain "See also:" link
197+
assert!(
198+
!hover_namespace.contains("See also:"),
199+
"Hover on namespace should NOT show 'See also' link. Got: {}", hover_namespace
200+
);
201+
202+
// Test hover on Odoo module: "module_1" in line 2: from odoo.addons.module_1.constants import ...
203+
// Position: line 1 (0-indexed), character at "module_1" (~17-24)
204+
let hover_module = test_utils::get_hover_markdown(&mut session, &file_symbol, &file_info, 1, 20).unwrap_or_default();
205+
206+
// Should show package type, module name and "Module" inferred type
207+
assert!(
208+
hover_module.contains("(package) module_1: Module"),
209+
"Hover on Odoo module should show package type, module_1 as name and 'Module' inferred type. Got: {}", hover_module
210+
);
211+
212+
// Module should show "See also" link
213+
assert!(
214+
hover_module.contains("See also:"),
215+
"Hover on Odoo module should show 'See also' link. Got: {}", hover_module
216+
);
217+
}
218+
153219
#[test]
154220
fn test_definition() {
155221
// Setup server and session with test addons

0 commit comments

Comments
 (0)