Skip to content

Commit 114d852

Browse files
committed
[FIX] Rewrite delayed queue
Delayed queue is now shorter and more efficient, only doing process_rebuild of validation steps. Features are now able to rebuild arch and arch_eval step on the fly before answering the request. This way index is always matching the ropes, and arch is valid too. Validation is delayed to inactivity period, but it is not blocking requests. Validation (arch and arch_eval) of functions can be done on the fly too
1 parent fe6ed81 commit 114d852

File tree

7 files changed

+234
-197
lines changed

7 files changed

+234
-197
lines changed

server/src/constants.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ use core::fmt;
55
pub const EXTENSION_NAME: &str = "Odoo";
66
pub const EXTENSION_VERSION: &str = "1.0.2";
77

8+
pub const MAX_WATCHED_FILES_UPDATES_BEFORE_RESTART: u32 = 10;
9+
810
pub const DEBUG_ODOO_BUILDER: bool = false;
911
pub const DEBUG_MEMORY: bool = false;
1012
pub const DEBUG_THREADS: bool = false;

server/src/core/odoo.rs

Lines changed: 38 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use std::collections::HashMap;
1313
use std::cell::RefCell;
1414
use std::ffi::OsStr;
1515
use std::rc::{Rc, Weak};
16-
use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
16+
use std::sync::atomic::{AtomicBool, Ordering};
1717
use std::sync::Arc;
1818
use std::time::Instant;
1919
use lsp_server::ResponseError;
@@ -78,7 +78,7 @@ pub struct SyncOdoo {
7878
pub models: HashMap<OYarn, Rc<RefCell<Model>>>,
7979
pub interrupt_rebuild: Arc<AtomicBool>,
8080
pub terminate_rebuild: Arc<AtomicBool>,
81-
pub watched_file_updates: Arc<AtomicU32>,
81+
pub watched_file_updates: u32,
8282
rebuild_arch: PtrWeakHashSet<Weak<RefCell<Symbol>>>,
8383
rebuild_arch_eval: PtrWeakHashSet<Weak<RefCell<Symbol>>>,
8484
rebuild_validation: PtrWeakHashSet<Weak<RefCell<Symbol>>>,
@@ -117,7 +117,7 @@ impl SyncOdoo {
117117
models: HashMap::new(),
118118
interrupt_rebuild: Arc::new(AtomicBool::new(false)),
119119
terminate_rebuild: Arc::new(AtomicBool::new(false)),
120-
watched_file_updates: Arc::new(AtomicU32::new(0)),
120+
watched_file_updates: 0,
121121
rebuild_arch: PtrWeakHashSet::new(),
122122
rebuild_arch_eval: PtrWeakHashSet::new(),
123123
rebuild_validation: PtrWeakHashSet::new(),
@@ -151,7 +151,7 @@ impl SyncOdoo {
151151
session.sync_odoo.state_init = InitState::NOT_READY;
152152
session.sync_odoo.load_odoo_addons = true;
153153
session.sync_odoo.need_rebuild = false;
154-
session.sync_odoo.watched_file_updates = Arc::new(AtomicU32::new(0));
154+
session.sync_odoo.watched_file_updates = 0;
155155
//drop all entries, except entries of opened files
156156
session.sync_odoo.entry_point_mgr.borrow_mut().reset_entry_points(false);
157157
SyncOdoo::init(session, config);
@@ -297,7 +297,7 @@ impl SyncOdoo {
297297
}
298298
let _builtins_rc_symbol = Symbol::create_from_path(session, &builtins_path, disk_dir_builtins[0].clone(), false);
299299
session.sync_odoo.add_to_rebuild_arch(_builtins_rc_symbol.unwrap());
300-
SyncOdoo::process_rebuilds(session)
300+
SyncOdoo::process_rebuilds(session, false)
301301
}
302302

303303
pub fn build_database(session: &mut SessionInfo) {
@@ -402,7 +402,7 @@ impl SyncOdoo {
402402
_ => panic!("Root symbol is not a package or namespace (> 18.0)")
403403
}
404404
session.sync_odoo.has_odoo_main_entry = true; // set it now has we need it to parse base addons
405-
if !SyncOdoo::process_rebuilds(session){
405+
if !SyncOdoo::process_rebuilds(session, false){
406406
return false;
407407
}
408408
//search common odoo addons path
@@ -477,7 +477,7 @@ impl SyncOdoo {
477477
}
478478
}
479479
}
480-
if !SyncOdoo::process_rebuilds(session){
480+
if !SyncOdoo::process_rebuilds(session, false){
481481
return;
482482
}
483483
//println!("{}", self.symbols.as_ref().unwrap().borrow_mut().debug_print_graph());
@@ -601,8 +601,11 @@ impl SyncOdoo {
601601
session.sync_odoo.must_reload_paths.retain(|x| x.0.upgrade().is_some());
602602
}
603603

604-
pub fn process_rebuilds(session: &mut SessionInfo) -> bool {
604+
pub fn process_rebuilds(session: &mut SessionInfo, no_validation: bool) -> bool {
605605
session.sync_odoo.interrupt_rebuild.store(false, Ordering::SeqCst);
606+
if session.sync_odoo.watched_file_updates > MAX_WATCHED_FILES_UPDATES_BEFORE_RESTART {
607+
return false;
608+
}
606609
SyncOdoo::add_from_self_reload(session);
607610
session.sync_odoo.import_cache = Some(ImportCache{ modules: HashMap::new(), main_modules: HashMap::new() });
608611
let mut already_arch_rebuilt: HashSet<Tree> = HashSet::new();
@@ -649,12 +652,18 @@ impl SyncOdoo {
649652
continue;
650653
}
651654
already_validation_rebuilt.insert(tree);
652-
if session.sync_odoo.state_init == InitState::ODOO_READY && session.sync_odoo.interrupt_rebuild.load(Ordering::SeqCst) {
653-
session.sync_odoo.interrupt_rebuild.store(false, Ordering::SeqCst);
654-
session.log_message(MessageType::INFO, S!("Rebuild interrupted"));
655-
session.request_delayed_rebuild();
656-
session.sync_odoo.add_to_validations(sym_rc.clone());
657-
return true;
655+
if session.sync_odoo.state_init == InitState::ODOO_READY {
656+
let mut no_validation = no_validation;
657+
if session.sync_odoo.interrupt_rebuild.load(Ordering::SeqCst) {
658+
session.sync_odoo.interrupt_rebuild.store(false, Ordering::SeqCst);
659+
session.log_message(MessageType::INFO, S!("Rebuild interrupted"));
660+
no_validation = true;
661+
}
662+
if no_validation {
663+
session.request_delayed_rebuild();
664+
session.sync_odoo.add_to_validations(sym_rc.clone());
665+
return true;
666+
}
658667
}
659668
let typ = sym_rc.borrow().typ();
660669
match typ {
@@ -672,9 +681,11 @@ impl SyncOdoo {
672681
}
673682
if session.sync_odoo.need_rebuild {
674683
session.log_message(MessageType::INFO, S!("Rebuild required. Resetting database on breaktime..."));
675-
SessionInfo::request_reload(session);
684+
info!("Odoo version change detected. OdooLS is restarting");
685+
session.send_notification("$Odoo/restartNeeded", ());
676686
}
677687
session.sync_odoo.import_cache = None;
688+
session.sync_odoo.watched_file_updates = 0;
678689
trace!("Leaving rebuild with remaining tasks: {:?} - {:?} - {:?}", session.sync_odoo.rebuild_arch.len(), session.sync_odoo.rebuild_arch_eval.len(), session.sync_odoo.rebuild_validation.len());
679690
true
680691
}
@@ -925,7 +936,7 @@ impl SyncOdoo {
925936
if !found_an_entry {
926937
info!("Path {} not found. Creating new entry", path.to_str().expect("unable to stringify path"));
927938
if EntryPointMgr::create_new_custom_entry_for_path(session, &path_in_tree.sanitize(), &path.sanitize()) {
928-
SyncOdoo::process_rebuilds(session);
939+
SyncOdoo::process_rebuilds(session, false);
929940
return SyncOdoo::get_symbol_of_opened_file(session, path)
930941
}
931942
}
@@ -978,7 +989,7 @@ impl SyncOdoo {
978989
}
979990
}
980991
}
981-
SyncOdoo::process_rebuilds(session);
992+
SyncOdoo::process_rebuilds(session, false);
982993
}
983994

984995
pub fn get_rebuild_queue_size(&self) -> usize {
@@ -1124,6 +1135,7 @@ impl Odoo {
11241135
session.show_message(MessageType::ERROR, format!("Selected configuration ({}) is abstract. Please select a valid configuration and restart.", config.name));
11251136
return;
11261137
}
1138+
session.update_delay_thread_delay_duration(config.auto_refresh_delay);
11271139
SyncOdoo::init(session, config);
11281140
session.log_message(MessageType::LOG, format!("End building database in {} seconds. {} detected modules.",
11291141
(std::time::Instant::now() - start).as_secs(),
@@ -1419,7 +1431,7 @@ impl Odoo {
14191431
}
14201432
}
14211433
EntryPointMgr::create_new_custom_entry_for_path(session, &tree_path.sanitize(), &path.sanitize());
1422-
SyncOdoo::process_rebuilds(session);
1434+
SyncOdoo::process_rebuilds(session, false);
14231435
} else if updated {
14241436
Odoo::update_file_index(session, path, true, false);
14251437
}
@@ -1499,21 +1511,21 @@ impl Odoo {
14991511
let _ = SyncOdoo::_unload_path(session, &PathBuf::from(&old_path), false);
15001512
FileMgr::delete_path(session, &old_path);
15011513
session.sync_odoo.entry_point_mgr.borrow_mut().remove_entries_with_path(&old_path);
1502-
SyncOdoo::process_rebuilds(session);
1514+
SyncOdoo::process_rebuilds(session, false);
15031515
//2 - create new document
15041516
let new_path_buf = PathBuf::from(new_path.clone());
15051517
let new_path_updated = new_path_buf.to_tree_path().sanitize();
15061518
Odoo::search_symbols_to_rebuild(session, &new_path_updated);
1507-
SyncOdoo::process_rebuilds(session);
1519+
SyncOdoo::process_rebuilds(session, false);
15081520
let tree = session.sync_odoo.path_to_main_entry_tree(&new_path_buf);
15091521
if let Some(tree) = tree {
15101522
if new_path_buf.is_file() && session.sync_odoo.get_main_entry().borrow().root.borrow().get_symbol(&tree, u32::MAX).is_empty() {
15111523
//file has not been added to main entry. Let's build a new entry point
15121524
EntryPointMgr::create_new_custom_entry_for_path(session, &new_path_updated, &new_path_buf.sanitize());
1513-
SyncOdoo::process_rebuilds(session);
1525+
SyncOdoo::process_rebuilds(session, false);
15141526
}
15151527
}
1516-
SyncOdoo::process_rebuilds(session);
1528+
SyncOdoo::process_rebuilds(session, false);
15171529
}
15181530
}
15191531

@@ -1528,7 +1540,7 @@ impl Odoo {
15281540
Odoo::search_symbols_to_rebuild(session, &path_updated);
15291541
session.sync_odoo.entry_point_mgr.borrow_mut().clean_entries();
15301542
}
1531-
SyncOdoo::process_rebuilds(session);
1543+
SyncOdoo::process_rebuilds(session, false);
15321544
//Now let's test if the symbol has been added to main entry tree or not
15331545
for f in params.files.iter() {
15341546
let path = FileMgr::uri2pathname(&f.uri);
@@ -1537,7 +1549,7 @@ impl Odoo {
15371549
if PathBuf::from(&path).is_file() && (tree.is_none() || session.sync_odoo.get_main_entry().borrow().root.borrow().get_symbol(&tree.unwrap(), u32::MAX).is_empty()) {
15381550
//file has not been added to main entry. Let's build a new entry point
15391551
EntryPointMgr::create_new_custom_entry_for_path(session, &path_updated, &path);
1540-
SyncOdoo::process_rebuilds(session);
1552+
SyncOdoo::process_rebuilds(session, false);
15411553
}
15421554
}
15431555
}
@@ -1554,7 +1566,7 @@ impl Odoo {
15541566
FileMgr::delete_path(session, &path);
15551567
session.sync_odoo.entry_point_mgr.borrow_mut().remove_entries_with_path(&path);
15561568
}
1557-
SyncOdoo::process_rebuilds(session);
1569+
SyncOdoo::process_rebuilds(session, false);
15581570
}
15591571

15601572
pub fn handle_did_change(session: &mut SessionInfo, params: DidChangeTextDocumentParams) {
@@ -1652,6 +1664,7 @@ impl Odoo {
16521664
session.sync_odoo.config = new_config;
16531665
// Recalculate diagnostic filters
16541666
session.sync_odoo.get_file_mgr().borrow_mut().update_all_file_diagnostic_filters(session);
1667+
session.update_delay_thread_delay_duration(session.sync_odoo.config.auto_refresh_delay);
16551668
}
16561669
}
16571670
Err(err) => {

server/src/features/ast_utils.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
use std::collections::HashMap;
22
use std::rc::Rc;
33
use std::cell::RefCell;
4+
use crate::constants::{BuildStatus, BuildSteps, SymType};
45
use crate::core::evaluation::{AnalyzeAstResult, Context, ContextValue, Evaluation, ExprOrIdent};
6+
use crate::core::odoo::SyncOdoo;
57
use crate::core::symbols::symbol::Symbol;
68
use crate::core::file_mgr::FileInfo;
79
use crate::threads::SessionInfo;
@@ -31,6 +33,7 @@ impl AstUtils {
3133
return (AnalyzeAstResult::default(), None, None);
3234
};
3335
let parent_symbol = Symbol::get_scope_symbol(file_symbol.clone(), offset, matches!(expr, ExprOrIdent::Parameter(_)));
36+
AstUtils::build_scope(session, &parent_symbol);
3437
let from_module;
3538
if let Some(module) = file_symbol.borrow().find_module() {
3639
from_module = ContextValue::MODULE(Rc::downgrade(&module));
@@ -58,6 +61,20 @@ impl AstUtils {
5861
}
5962
}
6063

64+
pub fn build_scope(session: &mut SessionInfo<'_>, scope: &Rc<RefCell<Symbol>>) {
65+
if scope.borrow().typ() == SymType::FUNCTION {
66+
let parent_func = scope.borrow().get_in_parents(&vec![SymType::FUNCTION], true);
67+
let scope_to_test = parent_func.and_then(|w| w.upgrade());
68+
let scope_to_test = scope_to_test.as_ref().unwrap_or(scope);
69+
if scope_to_test.borrow().as_func().arch_status == BuildStatus::PENDING {
70+
SyncOdoo::build_now(session, scope_to_test, BuildSteps::ARCH);
71+
}
72+
if scope_to_test.borrow().as_func().arch_eval_status == BuildStatus::PENDING {
73+
SyncOdoo::build_now(session, scope_to_test, BuildSteps::ARCH_EVAL);
74+
}
75+
}
76+
}
77+
6178
}
6279

6380

server/src/features/completion.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,12 @@ use lsp_types::{CompletionItem, CompletionItemKind, CompletionItemLabelDetails,
66
use ruff_python_ast::{Decorator, ExceptHandler, Expr, ExprAttribute, ExprIf, ExprName, ExprSubscript, ExprYield, Stmt, StmtGlobal, StmtImport, StmtImportFrom, StmtNonlocal};
77
use ruff_text_size::{Ranged, TextSize};
88

9-
use crate::constants::{OYarn, SymType};
9+
use crate::constants::{BuildStatus, BuildSteps, OYarn, SymType};
1010
use crate::core::evaluation::{Context, ContextValue, Evaluation, EvaluationSymbol, EvaluationSymbolPtr, EvaluationSymbolWeak};
1111
use crate::core::import_resolver;
1212
use crate::core::odoo::SyncOdoo;
1313
use crate::core::symbols::module_symbol::ModuleSymbol;
14+
use crate::features::ast_utils::AstUtils;
1415
use crate::threads::SessionInfo;
1516
use crate::utils::compare_semver;
1617
use crate::{oyarn, Sy, S};
@@ -537,6 +538,7 @@ fn complete_decorator_call(
537538
return None; // All the decorators we handle have at least one arg for now
538539
}
539540
let scope = Symbol::get_scope_symbol(file.clone(), offset as u32, false);
541+
AstUtils::build_scope(session, &scope);
540542
let dec_evals = Evaluation::eval_from_ast(session, &decorator_base, scope.clone(), max_infer, false, &mut vec![]).0;
541543
let mut followed_evals = vec![];
542544
for eval in dec_evals {
@@ -580,6 +582,7 @@ fn complete_call(session: &mut SessionInfo, file: &Rc<RefCell<Symbol>>, expr_cal
580582
return complete_expr( &expr_call.func, session, file, offset, is_param, expected_type);
581583
}
582584
let scope = Symbol::get_scope_symbol(file.clone(), offset as u32, is_param);
585+
AstUtils::build_scope(session, &scope);
583586
let callable_evals = Evaluation::eval_from_ast(session, &expr_call.func, scope, &expr_call.func.range().start(), false, &mut vec![]).0;
584587
for (arg_index, arg) in expr_call.arguments.args.iter().enumerate() {
585588
if offset > arg.range().start().to_usize() && offset <= arg.range().end().to_usize() {
@@ -751,6 +754,7 @@ fn complete_string_literal(session: &mut SessionInfo, file: &Rc<RefCell<Symbol>>
751754
},
752755
ExpectedType::SIMPLE_FIELD(_) | ExpectedType::NESTED_FIELD(_) | ExpectedType::METHOD_NAME => 'field_block: {
753756
let scope = Symbol::get_scope_symbol(file.clone(), expr_string_literal.range().start().to_u32(), true);
757+
AstUtils::build_scope(session, &scope);
754758
let Some(parent_class) = scope.borrow().get_in_parents(&vec![SymType::CLASS], true).and_then(|p| p.upgrade()) else {
755759
break 'field_block;
756760
};
@@ -784,6 +788,7 @@ fn complete_attribut(session: &mut SessionInfo, file: &Rc<RefCell<Symbol>>, attr
784788
//As symbols are not rebuilt, boundaries are not rights, and a "return self." at the end of a function/class body would be out of scope.
785789
//Temporary, by using the start of expr, we can hope that it is still in the right scope.
786790
let scope = Symbol::get_scope_symbol(file.clone(), start_expr, is_param);
791+
AstUtils::build_scope(session, &scope);
787792
if offset > attr.value.range().start().to_usize() && offset <= attr.value.range().end().to_usize() {
788793
return complete_expr( &attr.value, session, file, offset, is_param, expected_type);
789794
} else {
@@ -810,6 +815,7 @@ fn complete_attribut(session: &mut SessionInfo, file: &Rc<RefCell<Symbol>>, attr
810815

811816
fn complete_subscript(session: &mut SessionInfo, file: &Rc<RefCell<Symbol>>, expr_subscript: &ExprSubscript, offset: usize, is_param: bool, _expected_type: &Vec<ExpectedType>) -> Option<CompletionResponse> {
812817
let scope = Symbol::get_scope_symbol(file.clone(), offset as u32, is_param);
818+
AstUtils::build_scope(session, &scope);
813819
let subscripted = Evaluation::eval_from_ast(session, &expr_subscript.value, scope.clone(), &expr_subscript.value.range().start(), false, &mut vec![]).0;
814820
for eval in subscripted.iter() {
815821
let eval_symbol = eval.symbol.get_symbol(session, &mut None, &mut vec![], Some(scope.clone()));
@@ -847,6 +853,7 @@ fn complete_name_expression(session: &mut SessionInfo, file: &Rc<RefCell<Symbol>
847853

848854
fn complete_name(session: &mut SessionInfo, file: &Rc<RefCell<Symbol>>, offset: usize, is_param: bool, name: &String) -> Option<CompletionResponse> {
849855
let scope = Symbol::get_scope_symbol(file.clone(), offset as u32, is_param);
856+
AstUtils::build_scope(session, &scope);
850857
let symbols = Symbol::get_all_inferred_names(&scope, name, offset as u32);
851858
Some(CompletionResponse::List(CompletionList {
852859
is_incomplete: false,

server/src/server.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::{collections::HashMap, io::Error, panic, sync::{atomic::AtomicBool, Arc, Mutex}, thread::JoinHandle};
1+
use std::{io::Error, panic, sync::{atomic::AtomicBool, Arc, Mutex}, thread::JoinHandle};
22

33
use crossbeam_channel::{Receiver, Select, Sender};
44
use lsp_server::{Connection, IoThreads, Message, ProtocolError, RequestId, ResponseError};

0 commit comments

Comments
 (0)