diff --git a/server/src/core/python_odoo_builder.rs b/server/src/core/python_odoo_builder.rs index 49357f44..418462f6 100644 --- a/server/src/core/python_odoo_builder.rs +++ b/server/src/core/python_odoo_builder.rs @@ -17,6 +17,15 @@ use crate::{oyarn, Sy, S}; use super::evaluation::{ContextValue, Evaluation, EvaluationSymbolPtr, EvaluationValue}; +pub const MAGIC_FIELDS: [&str; 6] = [ + "id", + "display_name", + "create_uid", + "create_date", + "write_uid", + "write_date" +]; + pub struct PythonOdooBuilder { symbol: Rc>, } diff --git a/server/src/features/ast_utils.rs b/server/src/features/ast_utils.rs index e85f04eb..8ed629b5 100644 --- a/server/src/features/ast_utils.rs +++ b/server/src/features/ast_utils.rs @@ -5,7 +5,7 @@ use crate::constants::{BuildStatus, BuildSteps, SymType}; use crate::core::evaluation::{AnalyzeAstResult, Context, ContextValue, Evaluation, ExprOrIdent}; use crate::core::odoo::SyncOdoo; use crate::core::symbols::symbol::Symbol; -use crate::core::file_mgr::FileInfo; +use crate::core::file_mgr::{FileInfo, FileInfoAst}; use crate::threads::SessionInfo; use crate::S; use ruff_python_ast::visitor::{Visitor, walk_expr, walk_stmt, walk_alias, walk_except_handler, walk_parameter, walk_keyword, walk_pattern_keyword, walk_type_param, walk_pattern}; @@ -17,21 +17,22 @@ pub struct AstUtils {} impl AstUtils { - pub fn get_symbols(session: &mut SessionInfo, file_symbol: &Rc>, file_info: &Rc>, offset: u32) -> (AnalyzeAstResult, Option, Option) { - let mut expr: Option = None; + pub fn get_expr<'a>(file_info_ast: &'a FileInfoAst, offset: u32) -> (Option>, Option) { + let mut expr: Option> = None; let mut call_expr: Option = None; - let file_info_ast = file_info.borrow().file_info_ast.clone(); - let file_info_ast = file_info_ast.borrow(); for stmt in file_info_ast.get_stmts().unwrap().iter() { (expr, call_expr) = ExprFinderVisitor::find_expr_at(stmt, offset); if expr.is_some() { break; } } - let Some(expr) = expr else { + if expr.is_none() { warn!("expr not found"); - return (AnalyzeAstResult::default(), None, None); - }; + } + (expr, call_expr) + } + + pub fn get_symbols(session: &mut SessionInfo, file_symbol: &Rc>, offset: u32, expr: &ExprOrIdent) -> (AnalyzeAstResult, Option) { let parent_symbol = Symbol::get_scope_symbol(file_symbol.clone(), offset, matches!(expr, ExprOrIdent::Parameter(_))); AstUtils::build_scope(session, &parent_symbol); let from_module; @@ -45,7 +46,7 @@ impl AstUtils { (S!("range"), ContextValue::RANGE(expr.range())) ])); let analyse_ast_result: AnalyzeAstResult = Evaluation::analyze_ast(session, &expr, parent_symbol.clone(), &expr.range().end(), &mut context,false, &mut vec![]); - (analyse_ast_result, Some(expr.range()), call_expr) + (analyse_ast_result, Some(expr.range())) } diff --git a/server/src/features/definition.rs b/server/src/features/definition.rs index 7c5b2a4c..3757c73b 100644 --- a/server/src/features/definition.rs +++ b/server/src/features/definition.rs @@ -5,14 +5,15 @@ use std::path::PathBuf; use std::{cell::RefCell, rc::Rc}; use crate::constants::SymType; -use crate::core::evaluation::{Evaluation, EvaluationValue}; +use crate::core::evaluation::{Evaluation, EvaluationValue, ExprOrIdent}; use crate::core::file_mgr::{FileInfo, FileMgr}; use crate::core::odoo::SyncOdoo; +use crate::core::python_odoo_builder::MAGIC_FIELDS; use crate::core::symbols::symbol::Symbol; use crate::features::ast_utils::AstUtils; use crate::features::features_utils::FeaturesUtils; use crate::features::xml_ast_utils::{XmlAstResult, XmlAstUtils}; -use crate::oyarn; +use crate::{S, oyarn}; use crate::threads::SessionInfo; use crate::utils::PathSanitizer as _; @@ -148,6 +149,49 @@ impl DefinitionFeature { compute_symbols.len() > 0 } + pub fn add_display_name_compute_methods(session: &mut SessionInfo, links: &mut Vec, expr: &ExprOrIdent, file_symbol: &Rc>, offset: usize) { + // now we want `_compute_display_name` definition(s) + // we need the symbol of the model/ then we run get member symbol + // to do that, we need the expr, match it to attribute, get the value, get its evals + // with those evals, we run get_member_symbol on `_compute_display_name` + let crate::core::evaluation::ExprOrIdent::Expr(Expr::Attribute(attr_expr)) = expr else { + return; + }; + let (analyse_ast_result, _range) = AstUtils::get_symbols(session, file_symbol, offset as u32, &crate::core::evaluation::ExprOrIdent::Expr(&attr_expr.value)); + let eval_ptrs = analyse_ast_result.evaluations.iter().flat_map(|eval| Symbol::follow_ref(eval.symbol.get_symbol_ptr(), session, &mut None, false, false, None)).collect::>(); + let maybe_module = file_symbol.borrow().find_module(); + let symbols = eval_ptrs.iter().flat_map(|eval_ptr| { + let Some(symbol) = eval_ptr.upgrade_weak() else { + return vec![]; + }; + symbol.borrow().get_member_symbol(session, &S!("_compute_display_name"), maybe_module.clone(), false, false, true, false).0 + }).collect::>(); + for symbol in symbols { + if let Some(file) = symbol.borrow().get_file() { + for path in file.upgrade().unwrap().borrow().paths().iter() { + let full_path = match file.upgrade().unwrap().borrow().typ() { + SymType::PACKAGE(_) => PathBuf::from(path).join(format!("__init__.py{}", file.upgrade().unwrap().borrow().as_package().i_ext())).sanitize(), + _ => path.clone() + }; + let range = if symbol.borrow().has_range() { + if symbol.borrow().range().contains(TextSize::new(offset as u32)) { + continue; //skip if we are already on the definition + } + session.sync_odoo.get_file_mgr().borrow().text_range_to_range(session, &full_path, &symbol.borrow().range()) + } else { + Range::default() + }; + links.push(LocationLink{ + origin_selection_range: None, + target_uri: FileMgr::pathname2uri(&full_path), + target_selection_range: range, + target_range: range, + }); + } + } + } + } + pub fn get_location(session: &mut SessionInfo, file_symbol: &Rc>, file_info: &Rc>, @@ -155,12 +199,41 @@ impl DefinitionFeature { character: u32 ) -> Option { let offset = file_info.borrow().position_to_offset(line, character); - let (analyse_ast_result, _range, call_expr) = AstUtils::get_symbols(session, file_symbol, file_info, offset as u32); + let file_info_ast_clone = file_info.borrow().file_info_ast.clone(); + let file_info_ast_ref = file_info_ast_clone.borrow(); + let (expr, call_expr) = AstUtils::get_expr(&file_info_ast_ref, offset as u32); + let Some(expr) = expr else { + return None; + }; + let (analyse_ast_result, _range) = AstUtils::get_symbols(session, file_symbol, offset as u32, &expr); if analyse_ast_result.evaluations.is_empty() { return None; } let mut links = vec![]; let mut evaluations = analyse_ast_result.evaluations.clone(); + // Filter out magic fields + let mut dislay_name_found = false; + evaluations.retain(|eval| { + // Filter out, variables, whose parents are a class, whose name is one of the magic fields, and have the same range as their parent + let eval_sym = eval.symbol.get_symbol(session, &mut None, &mut vec![], None); + let Some(eval_sym) = eval_sym.upgrade_weak() else { return true; }; + if !MAGIC_FIELDS.contains(&eval_sym.borrow().name().as_str()) || eval_sym.borrow().typ() != SymType::VARIABLE || !eval_sym.borrow().is_field(session) { + return true; + } + if eval_sym.borrow().name() == "display_name" { + dislay_name_found = true; + } + let Some(parent_sym) = eval_sym.borrow().parent().and_then(|parent| parent.upgrade()) else { return true; }; + if parent_sym.borrow().typ() != SymType::CLASS { + return true; + } + eval_sym.borrow().range() != parent_sym.borrow().range() + }); + if dislay_name_found { + DefinitionFeature::add_display_name_compute_methods(session, &mut links, &expr, file_symbol, offset); + } + drop(expr); + drop(file_info_ast_ref); let mut index = 0; while index < evaluations.len() { let eval = evaluations[index].clone(); diff --git a/server/src/features/hover.rs b/server/src/features/hover.rs index c570324c..921fedbf 100644 --- a/server/src/features/hover.rs +++ b/server/src/features/hover.rs @@ -16,11 +16,19 @@ impl HoverFeature { pub fn hover_python(session: &mut SessionInfo, file_symbol: &Rc>, file_info: &Rc>, line: u32, character: u32) -> Option { let offset = file_info.borrow().position_to_offset(line, character); - let (analyse_ast_result, range, call_expr) = AstUtils::get_symbols(session, file_symbol, file_info, offset as u32); + let file_info_ast_clone = file_info.borrow().file_info_ast.clone(); + let file_info_ast_ref = file_info_ast_clone.borrow(); + let (expr, call_expr) = AstUtils::get_expr(&file_info_ast_ref, offset as u32); + let Some(expr) = expr else { + return None; + }; + let (analyse_ast_result, range) = AstUtils::get_symbols(session, file_symbol, offset as u32, &expr); let evals = analyse_ast_result.evaluations; if evals.is_empty() { return None; }; + drop(expr); + drop(file_info_ast_ref); let range = Some(file_info.borrow().text_range_to_range(&range.unwrap())); Some(Hover { contents: HoverContents::Markup(MarkupContent {