Skip to content

Commit ff496f1

Browse files
mmahroussfda-odoo
authored andcommitted
[IMP] server: completion of inherits model attr
1 parent f26f733 commit ff496f1

File tree

1 file changed

+58
-18
lines changed

1 file changed

+58
-18
lines changed

server/src/features/completion.rs

Lines changed: 58 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ use super::features_utils::TypeInfo;
2323

2424

2525
#[allow(non_camel_case_types)]
26-
#[derive(Debug)]
26+
#[derive(Debug, Clone)]
2727
pub enum ExpectedType {
2828
MODEL_NAME,
2929
DOMAIN(Rc<RefCell<Symbol>>),
@@ -32,9 +32,10 @@ pub enum ExpectedType {
3232
DOMAIN_FIELD(Rc<RefCell<Symbol>>),
3333
DOMAIN_COMPARATOR,
3434
CLASS(Rc<RefCell<Symbol>>),
35-
SIMPLE_FIELD,
35+
SIMPLE_FIELD(Option<OYarn>),
3636
NESTED_FIELD(Option<OYarn>),
3737
METHOD_NAME,
38+
INHERITS,
3839
}
3940

4041
pub struct CompletionFeature;
@@ -159,8 +160,10 @@ fn complete_assign_stmt(session: &mut SessionInfo<'_>, file: &Rc<RefCell<Symbol>
159160
let mut expected_type = vec![];
160161
if stmt_assign.targets.len() == 1 {
161162
if let Some(target_name) = stmt_assign.targets.first().unwrap().as_name_expr() {
162-
if target_name.id == "_inherit" {
163-
expected_type.push(ExpectedType::MODEL_NAME);
163+
match target_name.id.as_str() {
164+
"_inherit" => expected_type.push(ExpectedType::MODEL_NAME),
165+
"_inherits" => expected_type.push(ExpectedType::INHERITS),
166+
_ => {}
164167
}
165168
}
166169
}
@@ -376,7 +379,7 @@ fn complete_expr(expr: &Expr, session: &mut SessionInfo, file: &Rc<RefCell<Symbo
376379
Expr::Lambda(expr_lambda) => compare_lambda(session, file, expr_lambda, offset, is_param, expected_type),
377380
Expr::If(expr_if) => complete_if_expr(session, file, expr_if, offset, is_param, expected_type),
378381
Expr::Dict(expr_dict) => complete_dict(session, file, expr_dict, offset, is_param, expected_type),
379-
Expr::Set(_) => None,
382+
Expr::Set(expr_set) => complete_set(session, file, expr_set, offset, is_param, expected_type),
380383
Expr::ListComp(_) => None,
381384
Expr::SetComp(_) => None,
382385
Expr::DictComp(_) => None,
@@ -460,15 +463,44 @@ fn complete_if_expr(session: &mut SessionInfo, file: &Rc<RefCell<Symbol>>, expr_
460463

461464
fn complete_dict(session: &mut SessionInfo, file: &Rc<RefCell<Symbol>>, expr_dict: &ruff_python_ast::ExprDict, offset: usize, is_param: bool, expected_type: &Vec<ExpectedType>) -> Option<CompletionResponse> {
462465
for dict_item in expr_dict.items.iter() {
463-
if dict_item.key.is_some() {
466+
if let Some(dict_item_key) = &dict_item.key {
467+
// For expected type INHERITS, we want to complete the model name for the key
468+
// and a simple field of type Many2one for the value
469+
if offset > dict_item_key.range().start().to_usize() && offset <= dict_item_key.range().end().to_usize() {
470+
let expected_type= expected_type.iter().map(|e| match e {
471+
ExpectedType::INHERITS => ExpectedType::MODEL_NAME,
472+
_ => e.clone(),
473+
}).collect();
474+
return complete_expr( dict_item_key, session, file, offset, is_param, &expected_type);
475+
}
464476
if offset > dict_item.value.range().start().to_usize() && offset <= dict_item.value.range().end().to_usize() {
465-
return complete_expr( &dict_item.value, session, file, offset, is_param, expected_type);
477+
// if expected type has model name, replace it with simple field
478+
// for _inherits completion
479+
let expected_type = expected_type.iter().map(|e| match e {
480+
ExpectedType::INHERITS => ExpectedType::SIMPLE_FIELD(Some(Sy!("Many2one"))),
481+
_ => e.clone(),
482+
}).collect();
483+
return complete_expr( &dict_item.value, session, file, offset, is_param, &expected_type);
466484
}
467485
}
468486
}
469487
None
470488
}
471489

490+
fn complete_set(session: &mut SessionInfo, file: &Rc<RefCell<Symbol>>, expr_set: &ruff_python_ast::ExprSet, offset: usize, is_param: bool, expected_type: &Vec<ExpectedType>) -> Option<CompletionResponse> {
491+
for set_item in expr_set.elts.iter() {
492+
if offset > set_item.range().start().to_usize() && offset <= set_item.range().end().to_usize() {
493+
// A set expression here is just starting to write the inherits dict
494+
let expected_type= expected_type.iter().map(|e| match e {
495+
ExpectedType::INHERITS => ExpectedType::MODEL_NAME,
496+
_ => e.clone(),
497+
}).collect();
498+
return complete_expr( set_item, session, file, offset, is_param, &expected_type);
499+
}
500+
}
501+
None
502+
}
503+
472504
fn complete_yield(session: &mut SessionInfo, file: &Rc<RefCell<Symbol>>, expr_yield: &ExprYield, offset: usize, is_param: bool, expected_type: &Vec<ExpectedType>) -> Option<CompletionResponse> {
473505
if expr_yield.value.is_some() && offset > expr_yield.value.as_ref().unwrap().range().start().to_usize() && offset <= expr_yield.value.as_ref().unwrap().range().end().to_usize() {
474506
return complete_expr( expr_yield.value.as_ref().unwrap(), session, file, offset, is_param, expected_type);
@@ -522,7 +554,7 @@ fn complete_decorator_call(
522554
let expected_types = if (version_comparison < Ordering::Equal && dec_sym_tree.0.ends_with(&[Sy!("odoo"), Sy!("api")])) ||
523555
(version_comparison >= Ordering::Equal && dec_sym_tree.0.ends_with(&[Sy!("odoo"), Sy!("orm"), Sy!("decorators")])) {
524556
if [vec![Sy!("onchange")], vec![Sy!("constrains")]].contains(&dec_sym_tree.1) && SyncOdoo::is_in_main_entry(session, &dec_sym_tree.0) {
525-
&vec![ExpectedType::SIMPLE_FIELD]
557+
&vec![ExpectedType::SIMPLE_FIELD(None)]
526558
} else if dec_sym_tree.1 == vec![Sy!("depends")] && SyncOdoo::is_in_main_entry(session, &dec_sym_tree.0){
527559
&vec![ExpectedType::NESTED_FIELD(None)]
528560
} else {
@@ -717,7 +749,7 @@ fn complete_string_literal(session: &mut SessionInfo, file: &Rc<RefCell<Symbol>>
717749
ExpectedType::DOMAIN_FIELD(parent) => {
718750
add_nested_field_names(session, &mut items, current_module.clone(), expr_string_literal.value.to_str(), parent.clone(), true, &None);
719751
},
720-
ExpectedType::SIMPLE_FIELD | ExpectedType::NESTED_FIELD(_) | ExpectedType::METHOD_NAME => 'field_block: {
752+
ExpectedType::SIMPLE_FIELD(_) | ExpectedType::NESTED_FIELD(_) | ExpectedType::METHOD_NAME => 'field_block: {
721753
let scope = Symbol::get_scope_symbol(file.clone(), expr_string_literal.range().start().to_u32(), true);
722754
let Some(parent_class) = scope.borrow().get_in_parents(&vec![SymType::CLASS], true).and_then(|p| p.upgrade()) else {
723755
break 'field_block;
@@ -726,16 +758,17 @@ fn complete_string_literal(session: &mut SessionInfo, file: &Rc<RefCell<Symbol>>
726758
break 'field_block;
727759
}
728760
match expected_type {
729-
ExpectedType::SIMPLE_FIELD => add_model_attributes(
730-
session, &mut items, current_module.clone(), parent_class, false, true, false, expr_string_literal.value.to_str()),
761+
ExpectedType::SIMPLE_FIELD(maybe_field_type) => add_model_attributes(
762+
session, &mut items, current_module.clone(), parent_class, false, true, false, expr_string_literal.value.to_str(), maybe_field_type),
731763
ExpectedType::METHOD_NAME => add_model_attributes(
732-
session, &mut items, current_module.clone(), parent_class, false, false, true, expr_string_literal.value.to_str()),
764+
session, &mut items, current_module.clone(), parent_class, false, false, true, expr_string_literal.value.to_str(), &None),
733765
ExpectedType::NESTED_FIELD(maybe_field_type) => add_nested_field_names(
734766
session, &mut items, current_module.clone(), expr_string_literal.value.to_str(), parent_class, false, maybe_field_type),
735767
_ => unreachable!()
736768
}
737769
},
738770
ExpectedType::CLASS(_) => {},
771+
ExpectedType::INHERITS => {},
739772
}
740773
}
741774
Some(CompletionResponse::List(CompletionList {
@@ -764,7 +797,7 @@ fn complete_attribut(session: &mut SessionInfo, file: &Rc<RefCell<Symbol>>, attr
764797
let parent_sym_types = Symbol::follow_ref(&parent_sym_eval, session, &mut None, false, false, None, &mut vec![]);
765798
for parent_sym_type in parent_sym_types.iter() {
766799
let Some(parent_sym) = parent_sym_type.upgrade_weak() else {continue};
767-
add_model_attributes(session, &mut items, from_module.clone(), parent_sym, parent_sym_eval.as_weak().is_super, false, false, attr.attr.id.as_str())
800+
add_model_attributes(session, &mut items, from_module.clone(), parent_sym, parent_sym_eval.as_weak().is_super, false, false, attr.attr.id.as_str(), &None)
768801
}
769802
}
770803
}
@@ -997,17 +1030,24 @@ fn add_model_attributes(
9971030
is_super: bool,
9981031
only_fields: bool,
9991032
only_methods: bool,
1000-
attribute_name: &str
1033+
attribute_name: &str,
1034+
specific_field_type: &Option<OYarn>,
10011035
){
10021036
let all_symbols = Symbol::all_members(&parent_sym, session, true, only_fields, only_methods, from_module.clone(), is_super);
10031037
for (_symbol_name, symbols) in all_symbols {
10041038
//we could use symbol_name to remove duplicated names, but it would hide functions vs variables
1005-
if _symbol_name.starts_with(attribute_name) {
1006-
if let Some((final_sym, dep)) = symbols.first() {
1007-
let context_of_symbol = HashMap::from([(S!("base_attr"), ContextValue::SYMBOL(Rc::downgrade(&parent_sym)))]);
1008-
items.push(build_completion_item_from_symbol(session, vec![final_sym.clone()], context_of_symbol));
1039+
let Some((final_sym, _dep)) = symbols.first() else {
1040+
continue;
1041+
};
1042+
if let Some(field_type) = specific_field_type {
1043+
if !final_sym.borrow().is_specific_field(session, &[field_type.as_str()]) {
1044+
continue;
10091045
}
10101046
}
1047+
if _symbol_name.starts_with(attribute_name) {
1048+
let context_of_symbol = HashMap::from([(S!("base_attr"), ContextValue::SYMBOL(Rc::downgrade(&parent_sym)))]);
1049+
items.push(build_completion_item_from_symbol(session, vec![final_sym.clone()], context_of_symbol));
1050+
}
10111051
}
10121052
}
10131053

0 commit comments

Comments
 (0)