Skip to content

Commit 3321aeb

Browse files
committed
[IMP] server: basic field diagnostic structure
1 parent 3c23dde commit 3321aeb

File tree

5 files changed

+139
-52
lines changed

5 files changed

+139
-52
lines changed

server/src/core/model.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use crate::constants::BuildSteps;
1212
use crate::constants::OYarn;
1313
use crate::constants::SymType;
1414
use crate::threads::SessionInfo;
15+
use crate::Sy;
1516

1617
use super::symbols::module_symbol::ModuleSymbol;
1718
use super::symbols::symbol::Symbol;
@@ -230,10 +231,10 @@ impl Model {
230231
}
231232

232233
fn all_inherits_helper(&self, session: &mut SessionInfo, from_module: Option<Rc<RefCell<Symbol>>>, visited_models: &mut HashSet<String>) -> (Vec<(Rc<RefCell<Symbol>>, Option<OYarn>)>, Vec<(Rc<RefCell<Symbol>>, Option<OYarn>)>) {
233-
if visited_models.contains(&self.name) {
234+
if visited_models.contains(&self.name.to_string()) {
234235
return (Vec::new(), Vec::new());
235236
}
236-
visited_models.insert(self.name.clone());
237+
visited_models.insert(self.name.to_string());
237238
let mut symbols = Vec::new();
238239
let mut inherits_symbols = Vec::new();
239240
for s in self.symbols.iter() {

server/src/core/xml_arch_builder_rng_validation.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -423,9 +423,19 @@ impl XmlArchBuilder {
423423
None));
424424
}
425425
}
426+
let mut text = None;
427+
let mut text_range = None;
428+
for child in node.children() {
429+
if child.is_text() {
430+
text = child.text().map(|s| s.to_string());
431+
text_range = Some(child.range());
432+
}
433+
}
426434
Some(XmlDataField {
427435
name: oyarn!("{}", node.attribute("name").unwrap()),
428436
range: node.attribute_node("name").unwrap().range(),
437+
text: text,
438+
text_range: text_range,
429439
})
430440
}
431441

server/src/core/xml_data.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ pub struct XmlDataRecord {
2828
pub struct XmlDataField {
2929
pub name: OYarn,
3030
pub range: Range<usize>,
31+
pub text: Option<String>,
32+
pub text_range: Option<Range<usize>>,
3133
}
3234

3335
#[derive(Debug, Clone)]

server/src/core/xml_validation.rs

Lines changed: 67 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::{cell::RefCell, collections::HashMap, hash::Hash, path::PathBuf, rc::Rc
33
use lsp_types::{Diagnostic, Position, Range};
44
use tracing::{info, trace};
55

6-
use crate::{constants::{BuildSteps, SymType, DEBUG_STEPS, EXTENSION_NAME}, core::{entry_point::{EntryPoint, EntryPointType}, evaluation::ContextValue, file_mgr::FileInfo, model::Model, odoo::SyncOdoo, symbols::symbol::Symbol, xml_data::{XmlData, XmlDataActWindow, XmlDataDelete, XmlDataMenuItem, XmlDataRecord, XmlDataReport, XmlDataTemplate}}, threads::SessionInfo, S};
6+
use crate::{constants::{BuildSteps, OYarn, SymType, DEBUG_STEPS, EXTENSION_NAME}, core::{entry_point::{EntryPoint, EntryPointType}, evaluation::ContextValue, file_mgr::FileInfo, model::Model, odoo::SyncOdoo, symbols::symbol::Symbol, xml_data::{XmlData, XmlDataActWindow, XmlDataDelete, XmlDataMenuItem, XmlDataRecord, XmlDataReport, XmlDataTemplate}}, threads::SessionInfo, Sy, S};
77

88

99

@@ -89,33 +89,65 @@ impl XmlValidator {
8989
dependencies.push(main_sym.borrow().get_file().unwrap().upgrade().unwrap());
9090
}
9191
let all_fields = Symbol::all_fields(&main_symbols[0], session, Some(module.clone()));
92+
self.validate_fields(session, xml_data_record, &all_fields, diagnostics);
93+
}
94+
95+
fn validate_fields(&self, session: &mut SessionInfo, xml_data_record: &XmlDataRecord, all_fields: &HashMap<OYarn, Vec<(Rc<RefCell<Symbol>>, Option<OYarn>)>>, diagnostics: &mut Vec<Diagnostic>) {
96+
//Compute mandatory fields
9297
let mut mandatory_fields: Vec<String> = vec![];
93-
// for (field_name, field_sym) in all_fields.iter() {
94-
// for (fs, deps) in field_sym.iter() {
95-
// if deps.is_none() {
96-
// let has_required = fs.borrow().evaluations().unwrap_or(&vec![]).iter()
97-
// .any(|eval|
98-
// eval.symbol.get_symbol_as_weak(session, &mut None, diagnostics, None)
99-
// .context.get("required").unwrap_or(&ContextValue::BOOLEAN(false)).as_bool()
100-
// );
101-
// let has_default = fs.borrow().evaluations().unwrap_or(&vec![]).iter()
102-
// .any(|eval|
103-
// eval.symbol.get_symbol_as_weak(session, &mut None, diagnostics, None)
104-
// .context.contains_key("default")
105-
// );
106-
// if has_required && !has_default {
107-
// mandatory_fields.push(field_name.clone());
108-
// }
109-
// }
110-
// }
111-
// }
98+
for (field_name, field_sym) in all_fields.iter() {
99+
for (fs, deps) in field_sym.iter() {
100+
if deps.is_none() {
101+
let has_required = fs.borrow().evaluations().unwrap_or(&vec![]).iter()
102+
.any(|eval|
103+
eval.symbol.get_symbol_as_weak(session, &mut None, diagnostics, None)
104+
.context.get("required").unwrap_or(&ContextValue::BOOLEAN(false)).as_bool()
105+
);
106+
let has_default = fs.borrow().evaluations().unwrap_or(&vec![]).iter()
107+
.any(|eval|
108+
eval.symbol.get_symbol_as_weak(session, &mut None, diagnostics, None)
109+
.context.contains_key("default")
110+
);
111+
if has_required && !has_default {
112+
mandatory_fields.push(field_name.to_string());
113+
}
114+
}
115+
}
116+
}
117+
//check each field in the record
112118
for field in &xml_data_record.fields {
119+
//Check that the field belong to the model
113120
let declared_field = all_fields.get(&field.name);
114-
if let Some(declared_field) = declared_field {
121+
if let Some(_declared_field) = declared_field {
115122
mandatory_fields.retain(|f| f != &field.name.as_str());
116-
//TODO Check type
123+
//Check specific attributes
124+
match xml_data_record.model.0.as_str() {
125+
"ir.ui.view" => {
126+
if field.name == "model" && field.text.is_some() && field.text_range.is_some() {
127+
//TODO text that field.text is a valid model
128+
let model = session.sync_odoo.models.get(&Sy!(field.text.as_ref().unwrap())).cloned();
129+
let mut main_sym = vec![];
130+
if let Some(model) = model {
131+
let from_module = self.xml_symbol.borrow().find_module();
132+
main_sym = model.borrow().get_main_symbols(session, from_module);
133+
}
134+
if main_sym.is_empty() {
135+
diagnostics.push(Diagnostic::new(
136+
Range::new(Position::new(field.text_range.as_ref().unwrap().start.try_into().unwrap(), 0), Position::new(field.text_range.as_ref().unwrap().end.try_into().unwrap(), 0)),
137+
Some(lsp_types::DiagnosticSeverity::ERROR),
138+
Some(lsp_types::NumberOrString::String(S!("OLS30453"))),
139+
Some(EXTENSION_NAME.to_string()),
140+
format!("Model '{}' not found", field.text.as_ref().unwrap()),
141+
None,
142+
None
143+
))
144+
}
145+
}
146+
},
147+
_ => {}
148+
}
149+
//TODO check type
117150
} else {
118-
119151
diagnostics.push(Diagnostic::new(
120152
Range::new(Position::new(field.range.start.try_into().unwrap(), 0), Position::new(field.range.end.try_into().unwrap(), 0)),
121153
Some(lsp_types::DiagnosticSeverity::ERROR),
@@ -127,16 +159,18 @@ impl XmlValidator {
127159
));
128160
}
129161
}
130-
// if mandatory_fields.len() > 0 {
131-
// diagnostics.push(Diagnostic::new(
132-
// Range::new(Position::new(xml_data_record.range.start.try_into().unwrap(), 0), Position::new(xml_data_record.range.end.try_into().unwrap(), 0)),
133-
// Some(lsp_types::DiagnosticSeverity::ERROR),
134-
// Some(lsp_types::NumberOrString::String(S!("OLS30452"))),
135-
// Some(EXTENSION_NAME.to_string()),
136-
// format!("Some mandatory fields are not declared in the record: {:?}", mandatory_fields),
137-
// None,
138-
// None
139-
// ));
162+
//Diagnostic if some mandatory fields are not detected
163+
// if !mandatory_fields.is_empty() {
164+
// We have to check that remaining fields are not declared in an inherited record or is automatically field (delegate=True)
165+
// diagnostics.push(Diagnostic::new(
166+
// Range::new(Position::new(xml_data_record.range.start.try_into().unwrap(), 0), Position::new(xml_data_record.range.end.try_into().unwrap(), 0)),
167+
// Some(lsp_types::DiagnosticSeverity::ERROR),
168+
// Some(lsp_types::NumberOrString::String(S!("OLS30452"))),
169+
// Some(EXTENSION_NAME.to_string()),
170+
// format!("Some mandatory fields are not declared in the record: {:?}", mandatory_fields),
171+
// None,
172+
// None
173+
// ));
140174
// }
141175
}
142176

server/src/features/xml_ast_utils.rs

Lines changed: 57 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use std::{cell::RefCell, collections::HashMap, ops::Range, rc::Rc};
22

33
use roxmltree::Node;
44

5-
use crate::{core::{evaluation::ContextValue, symbols::symbol::Symbol, xml_data::XmlData}, threads::SessionInfo, S};
5+
use crate::{core::{evaluation::ContextValue, symbols::symbol::Symbol, xml_data::XmlData}, threads::SessionInfo, Sy, S};
66

77
pub enum XmlAstResult {
88
SYMBOL(Rc<RefCell<Symbol>>),
@@ -54,49 +54,45 @@ impl XmlAstUtils {
5454
}
5555
}
5656
}
57+
} else if node.is_text() {
58+
XmlAstUtils::visit_text(session, &node, offset, from_module, ctxt, results);
5759
}
5860
}
5961

6062
fn visit_record(session: &mut SessionInfo<'_>, node: &Node, offset: usize, from_module: Option<Rc<RefCell<Symbol>>>, ctxt: &mut HashMap<String, ContextValue>, results: &mut (Vec<XmlAstResult>, Option<Range<usize>>)) {
6163
for attr in node.attributes() {
6264
if attr.name() == "model" {
6365
let model_name = attr.value().to_string();
64-
ctxt.insert(S!("model"), ContextValue::STRING(model_name.clone()));
66+
ctxt.insert(S!("record_model"), ContextValue::STRING(model_name.clone()));
6567
if attr.range_value().start <= offset && attr.range_value().end >= offset {
66-
if let Some(model) = session.sync_odoo.models.get(&model_name).cloned() {
68+
if let Some(model) = session.sync_odoo.models.get(&Sy!(model_name)).cloned() {
6769
results.0.extend(model.borrow().all_symbols(session, from_module.clone(), false).iter().filter(|s| s.1.is_none()).map(|s| XmlAstResult::SYMBOL(s.0.clone())));
6870
results.1 = Some(attr.range_value());
6971
}
7072
}
7173
} else if attr.name() == "id" {
7274
if attr.range_value().start <= offset && attr.range_value().end >= offset {
73-
let xml_ids = session.sync_odoo.get_xml_ids(&from_module.clone().unwrap(), attr.value(), &attr.range(), &mut vec![]);
74-
for xml_data in xml_ids.iter() {
75-
match xml_data {
76-
XmlData::RECORD(r) => {
77-
results.0.push(XmlAstResult::XML_DATA(r.file_symbol.upgrade().unwrap(), r.range.clone()));
78-
results.1 = Some(attr.range_value());
79-
},
80-
_ => {}
81-
}
82-
}
75+
XmlAstUtils::add_xml_id_result(session, attr.value(), &from_module.as_ref().unwrap(), attr.range_value(), results);
76+
results.1 = Some(attr.range_value());
8377
}
8478
}
8579
}
8680
for child in node.children() {
8781
XmlAstUtils::visit_node(session, &child, offset, from_module.clone(), ctxt, results);
8882
}
83+
ctxt.remove(&S!("record_model"));
8984
}
9085

9186
fn visit_field(session: &mut SessionInfo<'_>, node: &Node, offset: usize, from_module: Option<Rc<RefCell<Symbol>>>, ctxt: &mut HashMap<String, ContextValue>, results: &mut (Vec<XmlAstResult>, Option<Range<usize>>)) {
9287
for attr in node.attributes() {
93-
if attr.range_value().start <= offset && attr.range_value().end >= offset {
94-
if attr.name() == "name" {
95-
let model_name = ctxt.get(&S!("model")).cloned().unwrap_or(ContextValue::STRING(S!(""))).as_string();
88+
if attr.name() == "name" {
89+
ctxt.insert(S!("field_name"), ContextValue::STRING(attr.value().to_string()));
90+
if attr.range_value().start <= offset && attr.range_value().end >= offset {
91+
let model_name = ctxt.get(&S!("record_model")).cloned().unwrap_or(ContextValue::STRING(S!(""))).as_string();
9692
if model_name.is_empty() {
9793
continue;
9894
}
99-
if let Some(model) = session.sync_odoo.models.get(&model_name).cloned() {
95+
if let Some(model) = session.sync_odoo.models.get(&Sy!(model_name)).cloned() {
10096
for symbol in model.borrow().all_symbols(session, from_module.clone(), true) {
10197
if symbol.1.is_none() {
10298
let content = symbol.0.borrow().get_content_symbol(attr.value(), u32::MAX);
@@ -108,11 +104,55 @@ impl XmlAstUtils {
108104
results.1 = Some(attr.range_value());
109105
}
110106
}
107+
} else if attr.name() == "ref" {
108+
if attr.range_value().start <= offset && attr.range_value().end >= offset {
109+
let mut field_name = "";
110+
for attr in node.attributes() {
111+
if attr.name() == "name" {
112+
field_name = attr.value();
113+
}
114+
}
115+
if field_name == "inherit_id" {
116+
XmlAstUtils::add_xml_id_result(session, attr.value(), &from_module.as_ref().unwrap(), attr.range_value(), results);
117+
results.1 = Some(attr.range_value());
118+
}
119+
}
111120
}
112121
}
113122
for child in node.children() {
114123
XmlAstUtils::visit_node(session, &child, offset, from_module.clone(), ctxt, results);
115124
}
125+
ctxt.remove(&S!("field_name"));
126+
}
127+
128+
fn visit_text(session: &mut SessionInfo, node: &Node, offset: usize, from_module: Option<Rc<RefCell<Symbol>>>, ctxt: &mut HashMap<String, ContextValue>, results: &mut (Vec<XmlAstResult>, Option<Range<usize>>)) {
129+
if node.range().start <= offset && node.range().end >= offset {
130+
let model = ctxt.get(&S!("record_model")).cloned().unwrap_or(ContextValue::STRING(S!(""))).as_string();
131+
let field = ctxt.get(&S!("field_name")).cloned().unwrap_or(ContextValue::STRING(S!(""))).as_string();
132+
if model.is_empty() || field.is_empty() {
133+
return;
134+
}
135+
if model == "ir.ui.view" {
136+
if field == "model" {
137+
if let Some(model) = session.sync_odoo.models.get(node.text().unwrap()).cloned() {
138+
results.0.extend(model.borrow().all_symbols(session, from_module.clone(), false).iter().filter(|s| s.1.is_none()).map(|s| XmlAstResult::SYMBOL(s.0.clone())));
139+
results.1 = Some(node.range());
140+
}
141+
}
142+
}
143+
}
144+
}
145+
146+
fn add_xml_id_result(session: &mut SessionInfo, xml_id: &str, file_symbol: &Rc<RefCell<Symbol>>, range: Range<usize>, results: &mut (Vec<XmlAstResult>, Option<Range<usize>>)) {
147+
let xml_ids = session.sync_odoo.get_xml_ids(file_symbol, xml_id, &range, &mut vec![]);
148+
for xml_data in xml_ids.iter() {
149+
match xml_data {
150+
XmlData::RECORD(r) => {
151+
results.0.push(XmlAstResult::XML_DATA(r.file_symbol.upgrade().unwrap(), r.range.clone()));
152+
},
153+
_ => {}
154+
}
155+
}
116156
}
117157

118158
}

0 commit comments

Comments
 (0)