From eebb3a93c1786e8598c995d868fdc7ee7374a9f6 Mon Sep 17 00:00:00 2001 From: M Mahrous Date: Fri, 23 Jan 2026 15:34:32 +0100 Subject: [PATCH] [IMP] Handle asset odoo nodes --- server/src/core/diagnostic_codes_list.rs | 40 ++++++ .../core/xml_arch_builder_rng_validation.rs | 127 +++++++++++++++++- server/src/core/xml_data.rs | 15 +++ server/src/core/xml_validation.rs | 7 +- .../module_for_diagnostics/data/bikes.xml | 27 ++++ server/tests/diagnostics/ols05000s.rs | 10 ++ 6 files changed, 223 insertions(+), 3 deletions(-) diff --git a/server/src/core/diagnostic_codes_list.rs b/server/src/core/diagnostic_codes_list.rs index 90660c0b..f66d1cf1 100644 --- a/server/src/core/diagnostic_codes_list.rs +++ b/server/src/core/diagnostic_codes_list.rs @@ -457,4 +457,44 @@ OLS05056, DiagnosticSetting::Error, "Model '{0}' not found", * Field not found in model */ OLS05057, DiagnosticSetting::Error, "Field '{0}' not found in model '{1}'", +/** +* The attribute is not valid for nodes. +*/ +OLS05058, DiagnosticSetting::Error, "Invalid attribute {0} in asset node", +/** +* A node must have an id attribute. +*/ +OLS05059, DiagnosticSetting::Error, "asset node must contain an id attribute", +/** +* A node must have a name attribute. +*/ +OLS05060, DiagnosticSetting::Error, "asset node must contain a name attribute", +/** +* The attribute is not valid for nodes inside . +*/ +OLS05061, DiagnosticSetting::Error, "Invalid attribute {0} in the bundle node under asset", +/** +* The attribute is not valid for nodes inside . +*/ +OLS05062, DiagnosticSetting::Error, "Invalid attribute {0} in the path node under asset", +/** +* Only bundle, path, or field nodes are allowed as children of . +*/ +OLS05063, DiagnosticSetting::Error, "Invalid child node {0} in asset node. Only bundle, path, or field allowed", +/** +* bundle node is required inside . +*/ +OLS05064, DiagnosticSetting::Error, "Bundle node is required inside ", +/** +* path node is required inside . +*/ +OLS05065, DiagnosticSetting::Error, "Path node is required inside ", +/** +* Bundle node in asset cannot be empty. +*/ +OLS05066, DiagnosticSetting::Error, "Bundle node in asset cannot be empty", +/** +* Path node in asset cannot be empty. +*/ +OLS05067, DiagnosticSetting::Error, "Path node in asset cannot be empty", } diff --git a/server/src/core/xml_arch_builder_rng_validation.rs b/server/src/core/xml_arch_builder_rng_validation.rs index c25d2a0c..354ee4a1 100644 --- a/server/src/core/xml_arch_builder_rng_validation.rs +++ b/server/src/core/xml_arch_builder_rng_validation.rs @@ -3,7 +3,7 @@ use std::rc::Rc; use lsp_types::{Diagnostic, Position, Range}; use roxmltree::Node; -use crate::{constants::OYarn, core::{diagnostics::{create_diagnostic, DiagnosticCode}, odoo::SyncOdoo, xml_data::{OdooData, XmlDataDelete, OdooDataField, XmlDataMenuItem, OdooDataRecord, XmlDataTemplate}}, oyarn, threads::SessionInfo, Sy}; +use crate::{Sy, constants::OYarn, core::{diagnostics::{DiagnosticCode, create_diagnostic}, odoo::SyncOdoo, xml_data::{OdooData, OdooDataField, OdooDataRecord, XmlDataAsset, XmlDataDelete, XmlDataMenuItem, XmlDataTemplate}}, oyarn, threads::SessionInfo}; use super::xml_arch_builder::XmlArchBuilder; @@ -36,7 +36,9 @@ impl XmlArchBuilder { || self.load_template(session, &child, diagnostics) || self.load_delete(session, &child, diagnostics) || self.load_function(session, &child, diagnostics) - || child.is_text() || child.is_comment()) { + || self.load_asset(session, &child, diagnostics) + || child.is_text() || child.is_comment() + ) { if let Some(diagnostic) = create_diagnostic(session, DiagnosticCode::OLS05005, &[child.tag_name().name(), node.tag_name().name()]) { diagnostics.push( Diagnostic { @@ -603,4 +605,125 @@ impl XmlArchBuilder { } true } + + fn load_asset(&mut self, session: &mut SessionInfo, node: &Node, diagnostics: &mut Vec) -> bool { + if node.tag_name().name() != "asset" { return false; } + // Validate required attributes: id, name + let mut found_id = None; + let mut has_name = false; + for attr in node.attributes() { + match attr.name() { + "id" => { found_id = Some(attr.value().to_string()); }, + "name" => { has_name = true; }, + "active" => {}, + _ => { + if let Some(diagnostic) = create_diagnostic(session, DiagnosticCode::OLS05058, &[attr.name()]) { + diagnostics.push(Diagnostic { + range: Range { start: Position::new(attr.range().start as u32, 0), end: Position::new(attr.range().end as u32, 0) }, + ..diagnostic.clone() + }); + } + } + } + } + if found_id.is_none() { + if let Some(diagnostic) = create_diagnostic(session, DiagnosticCode::OLS05059, &[]) { + diagnostics.push(Diagnostic { + range: Range { start: Position::new(node.range().start as u32, 0), end: Position::new(node.range().end as u32, 0) }, + ..diagnostic.clone() + }); + } + } + else if !has_name { + if let Some(diagnostic) = create_diagnostic(session, DiagnosticCode::OLS05060, &[]) { + diagnostics.push(Diagnostic { + range: Range { start: Position::new(node.range().start as u32, 0), end: Position::new(node.range().end as u32, 0) }, + ..diagnostic.clone() + }); + } + } + // Validate children: must be bundle, path, or field + let (mut has_bundle, mut has_path) = (false, false); + for child in node.children().filter(|n| n.is_element()) { + match child.tag_name().name() { + "bundle" => { + has_bundle = true; + for attr in child.attributes() { + if attr.name() != "directive" { + if let Some(diagnostic) = create_diagnostic(session, DiagnosticCode::OLS05061, &[attr.name()]) { + diagnostics.push(Diagnostic { + range: Range { start: Position::new(attr.range().start as u32, 0), end: Position::new(attr.range().end as u32, 0) }, + ..diagnostic.clone() + }); + } + } + } + if child.text().is_none() { + if let Some(diagnostic) = create_diagnostic(session, DiagnosticCode::OLS05066, &[]) { + diagnostics.push(Diagnostic { + range: Range { start: Position::new(child.range().start as u32, 0), end: Position::new(child.range().end as u32, 0) }, + ..diagnostic.clone() + }); + } + } + }, + "path" => { + has_path = true; + if child.attributes().count() > 0 { + for attr in child.attributes() { + if let Some(diagnostic) = create_diagnostic(session, DiagnosticCode::OLS05062, &[attr.name()]) { + diagnostics.push(Diagnostic { + range: Range { start: Position::new(attr.range().start as u32, 0), end: Position::new(attr.range().end as u32, 0) }, + ..diagnostic.clone() + }); + } + } + } + if child.text().is_none() { + if let Some(diagnostic) = create_diagnostic(session, DiagnosticCode::OLS05067, &[]) { + diagnostics.push(Diagnostic { + range: Range { start: Position::new(child.range().start as u32, 0), end: Position::new(child.range().end as u32, 0) }, + ..diagnostic.clone() + }); + } + } + }, + "field" => { + self.load_field(session, &child, diagnostics); + }, + "active" => {}, + _ => { + if let Some(diagnostic) = create_diagnostic(session, DiagnosticCode::OLS05063, &[child.tag_name().name()]) { + diagnostics.push(Diagnostic { + range: Range { start: Position::new(child.range().start as u32, 0), end: Position::new(child.range().end as u32, 0) }, + ..diagnostic.clone() + }); + } + } + } + } + if !has_bundle { + if let Some(diagnostic) = create_diagnostic(session, DiagnosticCode::OLS05064, &[]) { + diagnostics.push(Diagnostic { + range: Range { start: Position::new(node.range().start as u32, 0), end: Position::new(node.range().end as u32, 0) }, + ..diagnostic.clone() + }); + } + } + if !has_path { + if let Some(diagnostic) = create_diagnostic(session, DiagnosticCode::OLS05065, &[]) { + diagnostics.push(Diagnostic { + range: Range { start: Position::new(node.range().start as u32, 0), end: Position::new(node.range().end as u32, 0) }, + ..diagnostic.clone() + }); + } + } + let data = OdooData::ASSET(XmlDataAsset { + file_symbol: Rc::downgrade(&self.xml_symbol), + xml_id: found_id.clone().map(|id| oyarn!("{}", id)), + range: node.range().clone(), + }); + self.on_operation_creation(session, found_id, node, data, diagnostics); + true + } } \ No newline at end of file diff --git a/server/src/core/xml_data.rs b/server/src/core/xml_data.rs index 1fa3870d..a39950f1 100644 --- a/server/src/core/xml_data.rs +++ b/server/src/core/xml_data.rs @@ -8,6 +8,7 @@ pub enum OdooData { RECORD(OdooDataRecord), MENUITEM(XmlDataMenuItem), TEMPLATE(XmlDataTemplate), + ASSET(XmlDataAsset), DELETE(XmlDataDelete), } @@ -43,6 +44,13 @@ pub struct XmlDataTemplate { pub range: Range, } +#[derive(Debug, Clone)] +pub struct XmlDataAsset { + pub file_symbol: Weak>, + pub xml_id: Option, + pub range: Range, +} + #[derive(Debug, Clone)] pub struct XmlDataDelete { pub file_symbol: Weak>, @@ -67,6 +75,9 @@ impl OdooData { OdooData::DELETE(delete) => { delete.file_symbol = Rc::downgrade(xml_symbol); }, + OdooData::ASSET(asset) => { + asset.file_symbol = Rc::downgrade(xml_symbol); + } } } @@ -76,6 +87,7 @@ impl OdooData { OdooData::MENUITEM(menu_item) => menu_item.range.clone(), OdooData::TEMPLATE(template) => template.range.clone(), OdooData::DELETE(delete) => delete.range.clone(), + OdooData::ASSET(asset) => asset.range.clone(), } } @@ -103,6 +115,9 @@ impl OdooData { }, OdooData::DELETE(delete) => { Some(delete.file_symbol.clone()) + }, + OdooData::ASSET(asset) => { + Some(asset.file_symbol.clone()) } } } diff --git a/server/src/core/xml_validation.rs b/server/src/core/xml_validation.rs index a0ec8cce..5f257cf6 100644 --- a/server/src/core/xml_validation.rs +++ b/server/src/core/xml_validation.rs @@ -3,7 +3,7 @@ use std::{cell::RefCell, cmp::Ordering, collections::{HashMap, HashSet}, rc::Rc} use lsp_types::{Diagnostic, Position, Range}; use tracing::{info, trace}; -use crate::{Sy, constants::{BuildSteps, DEBUG_STEPS, OYarn}, core::{diagnostics::{DiagnosticCode, create_diagnostic}, entry_point::{EntryPoint, EntryPointType}, evaluation::ContextValue, file_mgr::FileInfo, model::Model, odoo::SyncOdoo, symbols::symbol::Symbol, xml_data::{OdooData, OdooDataRecord, XmlDataDelete, XmlDataMenuItem, XmlDataTemplate}}, oyarn, threads::SessionInfo, utils::compare_semver}; +use crate::{Sy, constants::{BuildSteps, DEBUG_STEPS, OYarn}, core::{diagnostics::{DiagnosticCode, create_diagnostic}, entry_point::{EntryPoint, EntryPointType}, evaluation::ContextValue, file_mgr::FileInfo, model::Model, odoo::SyncOdoo, symbols::symbol::Symbol, xml_data::{OdooData, OdooDataRecord, XmlDataAsset, XmlDataDelete, XmlDataMenuItem, XmlDataTemplate}}, oyarn, threads::SessionInfo, utils::compare_semver}; @@ -67,6 +67,7 @@ impl XmlValidator { OdooData::MENUITEM(xml_data_menu_item) => self.validate_menu_item(session, module, xml_data_menu_item, diagnostics, dependencies, model_dependencies, missing_model_dependencies), OdooData::TEMPLATE(xml_data_template) => self.validate_template(session, module, xml_data_template, diagnostics, dependencies, model_dependencies, missing_model_dependencies), OdooData::DELETE(xml_data_delete) => self.validate_delete(session, module, xml_data_delete, diagnostics, dependencies, model_dependencies, missing_model_dependencies), + OdooData::ASSET(xml_data_asset) => self.validate_asset(session, module, xml_data_asset, diagnostics, dependencies, model_dependencies, missing_model_dependencies), } } fn validate_record(&self, session: &mut SessionInfo, module: &Rc>, xml_data_record: &OdooDataRecord, diagnostics: &mut Vec, dependencies: &mut Vec>>, model_dependencies: &mut Vec>>, missing_model_dependencies: &mut HashSet) { @@ -254,4 +255,8 @@ impl XmlValidator { fn validate_delete(&self, _session: &mut SessionInfo, _module: &Rc>, _xml_data_delete: &XmlDataDelete, _diagnostics: &mut Vec, _dependencies: &mut Vec>>, _model_dependencies: &mut Vec>>, _missing_model_dependencies: &mut HashSet) { } + + fn validate_asset(&self, _session: &mut SessionInfo, _module: &Rc>, _xml_data_asset: &XmlDataAsset, _diagnostics: &mut Vec, _dependencies: &mut Vec>>, _model_dependencies: &mut Vec>>, _missing_model_dependencies: &mut HashSet) { + + } } \ No newline at end of file diff --git a/server/tests/data/addons/module_for_diagnostics/data/bikes.xml b/server/tests/data/addons/module_for_diagnostics/data/bikes.xml index 0103174a..9d4321d2 100644 --- a/server/tests/data/addons/module_for_diagnostics/data/bikes.xml +++ b/server/tests/data/addons/module_for_diagnostics/data/bikes.xml @@ -92,4 +92,31 @@ module_2.empty_model wrong.model + + data + ols_tests/tests/something.scss + + + data + ols_tests/tests/something.scss + + + data + ols_tests/tests/something.scss + + + data + ols_tests/tests/something.scss + + + + ols_tests/tests/something.scss + + + data + + + + + \ No newline at end of file diff --git a/server/tests/diagnostics/ols05000s.rs b/server/tests/diagnostics/ols05000s.rs index 5d699148..3820bf24 100644 --- a/server/tests/diagnostics/ols05000s.rs +++ b/server/tests/diagnostics/ols05000s.rs @@ -92,6 +92,16 @@ fn test_ols05000s_xml_file() { check_xml_diag("OLS05057", 91); check_xml_diag("OLS05055", 92); check_xml_diag("OLS05056", 93); + check_xml_diag("OLS05058", 94); + check_xml_diag("OLS05059", 98); + check_xml_diag("OLS05060", 102); + check_xml_diag("OLS05061", 107); + check_xml_diag("OLS05062", 108); + check_xml_diag("OLS05063", 109); + check_xml_diag("OLS05064", 112); + check_xml_diag("OLS05065", 115); + check_xml_diag("OLS05066", 118); + check_xml_diag("OLS05067", 119); } #[test]