Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,56 @@
"cwd": "${workspaceFolder}/server",
"console": "externalTerminal"
},
{
"name": "Launch Server (gdb)",
"type": "cppdbg",
"request": "launch",
"preLaunchTask": "cargo build",
"program": "${workspaceFolder}/server/target/debug/odoo_ls_server",
"args": ["--use-tcp"],
"cwd": "${workspaceFolder}/server",
"externalConsole": false,
"MIMode": "gdb",
"miDebuggerPath": "rust-gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
},
{
"description": "Set print depth limit to prevent infinite recursion",
"text": "set print max-depth 3",
"ignoreFailures": false
},
]
},
{
"name": "Debug Test (gdb)",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/server/target/debug/deps/test_setup-22eac7e5dc85cac2",
"args": [],
"cwd": "${workspaceFolder}/server",
"environment": [
{ "name": "COMMUNITY_PATH", "value": "/path/to/odoo" }
],
"externalConsole": false,
"MIMode": "gdb",
"miDebuggerPath": "rust-gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
},
{
"description": "Set print depth limit to prevent infinite recursion",
"text": "set print max-depth 3",
"ignoreFailures": false
},
]
},
{
"name": "Launch Server (cppvsdbg)",
"type": "cppvsdbg",
Expand Down
19 changes: 19 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,24 @@
# Changelog

## [1.1.2] - 2025/12/10 - CachedModel fixes

### Server

- Various fixes
- Fix and support for CachedModel introduced in 19.1
- Use a deterministic job queue to avoid random errors caused by different order of symbols
- For that we replace the current HashSet with a FIFO one, so symbols are processed in the queue order

### Fixes

- Fix wiki link for configuration on welcome page
- Avoid having empty paths for addons or additional stubs in cli mode
- Avoid adding model dependencies in orm files to avoid rebuilding base files
- Avoid loading Models defined inside functions, e.g. tests.
- Avoid attempting to rebuild `__iter__` on external files, as their file infos are deleted
- Fix fetching symbols in inheritance tree by early stopping when one is found


## [1.1.1] - 2025/11/24 - Untitled files and Encoding

### Server
Expand Down
2 changes: 1 addition & 1 deletion server/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "odoo_ls_server"
version = "1.1.1"
version = "1.1.2"
edition = "2024"
authors = ["Odoo"]
readme = "../README.md"
Expand Down
4 changes: 2 additions & 2 deletions server/src/cli_backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,11 @@ impl CliBackend {
}

let mut config = ConfigEntry::new();
config.addons_paths = addons_paths.into_iter().map(|p| fs::canonicalize(p).unwrap_or_else(|_| PathBuf::from(S!(""))).sanitize()).collect();
config.addons_paths = addons_paths.into_iter().map(|p| fs::canonicalize(p).unwrap_or_else(|_| PathBuf::from(S!(""))).sanitize()).filter(|x| !x.is_empty()).collect();
config.odoo_path = Some(fs::canonicalize(community_path.unwrap_or(S!(""))).unwrap_or_else(|_| PathBuf::from(S!(""))).sanitize());
config.diag_missing_imports = DiagMissingImportsMode::All;
config.no_typeshed_stubs = self.cli.no_typeshed_stubs;
config.additional_stubs = self.cli.stubs.clone().unwrap_or(vec![]).into_iter().map(|p| fs::canonicalize(p).unwrap_or_else(|_| PathBuf::from(S!(""))).sanitize()).collect();
config.additional_stubs = self.cli.stubs.clone().unwrap_or(vec![]).into_iter().map(|p| fs::canonicalize(p).unwrap_or_else(|_| PathBuf::from(S!(""))).sanitize()).filter(|x| !x.is_empty()).collect();
config.stdlib = self.cli.stdlib.clone().map(|p| fs::canonicalize(p).unwrap_or_else(|_| PathBuf::from(S!(""))).sanitize()).unwrap_or(S!(""));
config.python_path = self.cli.python.clone().unwrap_or(get_python_command().unwrap_or(S!("")));
SyncOdoo::init(&mut session, config);
Expand Down
4 changes: 2 additions & 2 deletions server/src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use core::fmt;


pub const EXTENSION_NAME: &str = "Odoo";
pub const EXTENSION_VERSION: &str = "1.1.1";
pub const EXTENSION_VERSION: &str = "1.1.2";

pub const MAX_WATCHED_FILES_UPDATES_BEFORE_RESTART: u32 = 10;

Expand Down Expand Up @@ -125,7 +125,7 @@ pub const BUILT_IN_LIBS: &[&str] = &["string", "re", "difflib", "textwrap", "un
"cgi", "cgitb", "chunk", "crypt", "imghdr", "imp", "mailcap", "msilib", "nis", "nntplib", "optparse", "ossaudiodev",
"pipes", "smtpd", "sndhdr", "spwd", "sunau", "telnetlib", "uu", "xdrlib", "struct", "codecs"];

pub const CONFIG_WIKI_URL: &str = "https://github.com/odoo/odoo-ls/wiki/Configuration-files";
pub const CONFIG_WIKI_URL: &str = "https://github.com/odoo/odoo-ls/wiki/3.-Configuration-files";

use std::sync::LazyLock;

Expand Down
4 changes: 4 additions & 0 deletions server/src/core/diagnostic_codes_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,10 @@ OLS03022, DiagnosticSetting::Error, "Inverse field is not a Many2one field",
* -> current_model is not right
*/
OLS03023, DiagnosticSetting::Error, "Inverse field {0} is not pointing to the current model {1}, but rather to {2}",
/**
* Models declared in a function are not supported by OdooLS. This info is indicating that no features will be enabled for this model.
*/
OLS03024, DiagnosticSetting::Info, "Models declared in a function are not supported by OdooLS. OdooLS will use it as a normal class for the rest of the function only",
/**
* Form is no longer available on odoo.tests.common, thus it should not be imported from there.
*/
Expand Down
23 changes: 11 additions & 12 deletions server/src/core/odoo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ use crate::core::xml_validation::XmlValidator;
use crate::features::document_symbols::DocumentSymbolFeature;
use crate::features::references::ReferenceFeature;
use crate::features::workspace_symbols::WorkspaceSymbolFeature;
use crate::fifo_ptr_weak_hash_set::FifoPtrWeakHashSet;
use crate::threads::SessionInfo;
use crate::features::completion::CompletionFeature;
use crate::features::definition::DefinitionFeature;
use crate::features::hover::HoverFeature;
use std::collections::HashMap;
use std::cell::RefCell;
use std::ffi::OsStr;
use std::rc::{Rc, Weak};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex};
Expand All @@ -27,7 +27,6 @@ use serde_json::Value;
use tracing::{error, warn, info, trace};

use std::collections::HashSet;
use weak_table::PtrWeakHashSet;
use std::process::Command;
use std::fs;
use std::path::{Path, PathBuf};
Expand Down Expand Up @@ -86,9 +85,9 @@ pub struct SyncOdoo {
pub current_request_id: Option<RequestId>,
pub running_request_ids: Arc<Mutex<Vec<RequestId>>>, //Arc to Server mutex for cancellation support
pub watched_file_updates: u32,
rebuild_arch: PtrWeakHashSet<Weak<RefCell<Symbol>>>,
rebuild_arch_eval: PtrWeakHashSet<Weak<RefCell<Symbol>>>,
rebuild_validation: PtrWeakHashSet<Weak<RefCell<Symbol>>>,
rebuild_arch: FifoPtrWeakHashSet<RefCell<Symbol>>,
rebuild_arch_eval: FifoPtrWeakHashSet<RefCell<Symbol>>,
rebuild_validation: FifoPtrWeakHashSet<RefCell<Symbol>>,
pub state_init: InitState,
pub must_reload_paths: Vec<(Weak<RefCell<Symbol>>, String)>,
pub load_odoo_addons: bool, //indicate if we want to load odoo addons or not
Expand Down Expand Up @@ -131,9 +130,9 @@ impl SyncOdoo {
current_request_id: None,
running_request_ids: Arc::new(Mutex::new(vec![])),
watched_file_updates: 0,
rebuild_arch: PtrWeakHashSet::new(),
rebuild_arch_eval: PtrWeakHashSet::new(),
rebuild_validation: PtrWeakHashSet::new(),
rebuild_arch: FifoPtrWeakHashSet::new(),
rebuild_arch_eval: FifoPtrWeakHashSet::new(),
rebuild_validation: FifoPtrWeakHashSet::new(),
state_init: InitState::NOT_READY,
must_reload_paths: vec![],
load_odoo_addons: true,
Expand Down Expand Up @@ -161,9 +160,9 @@ impl SyncOdoo {
session.sync_odoo.stdlib_dir = SyncOdoo::default_stdlib();
session.sync_odoo.modules = HashMap::new();
session.sync_odoo.models = HashMap::new();
session.sync_odoo.rebuild_arch = PtrWeakHashSet::new();
session.sync_odoo.rebuild_arch_eval = PtrWeakHashSet::new();
session.sync_odoo.rebuild_validation = PtrWeakHashSet::new();
session.sync_odoo.rebuild_arch = FifoPtrWeakHashSet::new();
session.sync_odoo.rebuild_arch_eval = FifoPtrWeakHashSet::new();
session.sync_odoo.rebuild_validation = FifoPtrWeakHashSet::new();
session.sync_odoo.state_init = InitState::NOT_READY;
session.sync_odoo.load_odoo_addons = true;
session.sync_odoo.need_rebuild = false;
Expand Down Expand Up @@ -537,7 +536,7 @@ impl SyncOdoo {
let mut selected_sym: Option<Rc<RefCell<Symbol>>> = None;
let mut selected_count: u32 = 999999999;
let mut current_count: u32;
for sym in set {
for sym in set.iter() {
current_count = 0;
let file = sym.borrow().get_file().unwrap().upgrade().unwrap();
let file = file.borrow();
Expand Down
20 changes: 16 additions & 4 deletions server/src/core/python_arch_eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -711,8 +711,17 @@ impl PythonArchEval {
self.visit_sub_stmts(session, &class_stmt.body);
self.sym_stack.pop();
if !self.sym_stack[0].borrow().is_external() && self.sym_stack[0].borrow().get_entry().is_some_and(|e| e.borrow().typ == EntryPointType::MAIN) {
let odoo_builder_diags = PythonOdooBuilder::new(class_sym_rc).load(session);
self.diagnostics.extend(odoo_builder_diags);
if class_sym_rc.borrow().get_in_parents(&vec![SymType::FUNCTION], true).is_some() {
if let Some(diagnostic) = create_diagnostic(&session, DiagnosticCode::OLS03024, &[]) {
self.diagnostics.push(Diagnostic {
range: FileMgr::textRange_to_temporary_Range(&class_stmt.name.range),
..diagnostic
});
}
} else {
let odoo_builder_diags = PythonOdooBuilder::new(class_sym_rc).load(session);
self.diagnostics.extend(odoo_builder_diags);
}
}
session.current_noqa = old_noqa;
}
Expand Down Expand Up @@ -824,8 +833,11 @@ impl PythonArchEval {
if symbol_type.typ() == SymType::CLASS {
let (iter, _) = symbol_type.get_member_symbol(session, &S!("__iter__"), None, true, false, false, false, false);
if iter.len() == 1 {
SyncOdoo::build_now(session, &iter[0], BuildSteps::ARCH_EVAL);
SyncOdoo::build_now(session, &iter[0], BuildSteps::VALIDATION);
if !iter[0].borrow().is_external() { //we can't rebuild functions of external files
SyncOdoo::build_now(session, &iter[0], BuildSteps::ARCH);
SyncOdoo::build_now(session, &iter[0], BuildSteps::ARCH_EVAL);
SyncOdoo::build_now(session, &iter[0], BuildSteps::VALIDATION);
}
if iter[0].borrow().evaluations().is_some() && iter[0].borrow().evaluations().unwrap().len() == 1 {
let iter = iter[0].borrow();
let eval_iter = &iter.evaluations().unwrap()[0];
Expand Down
22 changes: 15 additions & 7 deletions server/src/core/python_arch_eval_hooks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -754,23 +754,31 @@ impl PythonArchEvalHooks {
let Some(ContextValue::STRING(s)) = context.get(&S!("args")) else {
return res
};
let maybe_model = session.sync_odoo.models.get(&oyarn!("{}", s));
let maybe_model = session.sync_odoo.models.get(&oyarn!("{}", s)).cloned();
let has_class_in_parents = scope.as_ref().map(|scope| scope.borrow().get_in_parents(&vec![SymType::CLASS], true).is_some()).unwrap_or(false);
if maybe_model.map(|m| m.borrow_mut().has_symbols()).unwrap_or(false) {
if maybe_model.as_ref().map(|m| m.borrow_mut().has_symbols()).unwrap_or(false) {
let Some(model) = maybe_model else {unreachable!()};
let module = context.get(&S!("module"));
let from_module = if let Some(ContextValue::MODULE(m)) = module {
m.upgrade().clone()
} else {
None
};
if let Some(scope) = scope
if let Some(scope_file) = scope
.and_then(|s| s.borrow().get_file())
.and_then(|w| w.upgrade()) {
let env_files = session.sync_odoo.get_symbol(session.sync_odoo.config.odoo_path.as_ref().unwrap(), &(vec![Sy!("odoo"), Sy!("api")], vec![]), u32::MAX);
let env_file = env_files.last().unwrap();
if !Rc::ptr_eq(env_file, &scope) {
scope.borrow_mut().add_model_dependencies(model);
//exclude orm files
if compare_semver(session.sync_odoo.full_version.as_str(), "18.1") < Ordering::Equal {
let env_files = session.sync_odoo.get_symbol(session.sync_odoo.config.odoo_path.as_ref().unwrap(), &(vec![Sy!("odoo"), Sy!("api")], vec![]), u32::MAX);
let env_file = env_files.last().unwrap();
if !Rc::ptr_eq(env_file, &scope_file) {
scope_file.borrow_mut().add_model_dependencies(&model);
}
} else {
let tree = scope_file.borrow().get_main_entry_tree(session);
if !tree.0.starts_with(&[Sy!("odoo"), Sy!("orm")]) {
scope_file.borrow_mut().add_model_dependencies(&model);
}
}
}
let model = model.clone();
Expand Down
Loading