diff --git a/server/src/core/file_mgr.rs b/server/src/core/file_mgr.rs index f113bc67..34a993e0 100644 --- a/server/src/core/file_mgr.rs +++ b/server/src/core/file_mgr.rs @@ -397,10 +397,22 @@ impl FileInfo { Position::new(line as u32, column as u32) } + pub fn try_offset_to_position_with_rope(rope: &Rope, offset: usize) -> Option { + let char = rope.try_byte_to_char(offset).ok()?; + let line = rope.try_char_to_line(char).ok()?; + let first_char_of_line = rope.try_line_to_char(line).ok()?; + let column = char - first_char_of_line; + Some(Position::new(line as u32, column as u32)) + } + pub fn offset_to_position(&self, offset: usize) -> Position { FileInfo::offset_to_position_with_rope(self.file_info_ast.borrow().text_rope.as_ref().expect("no rope provided"), offset) } + pub fn try_offset_to_position(&self, offset: usize) -> Option { + FileInfo::try_offset_to_position_with_rope(self.file_info_ast.borrow().text_rope.as_ref().expect("no rope provided"), offset) + } + pub fn text_range_to_range(&self, range: &TextRange) -> Range { Range { start: self.offset_to_position(range.start().to_usize()), @@ -408,6 +420,13 @@ impl FileInfo { } } + pub fn try_text_range_to_range(&self, range: &TextRange) -> Option { + Some(Range { + start: self.try_offset_to_position(range.start().to_usize())?, + end: self.try_offset_to_position(range.end().to_usize())? + }) + } + pub fn std_range_to_range(&self, range: &std::ops::Range) -> Range { Range { start: self.offset_to_position(range.start), @@ -415,6 +434,13 @@ impl FileInfo { } } + pub fn try_std_range_to_range(&self, range: &std::ops::Range) -> Option { + Some(Range { + start: self.try_offset_to_position(range.start)?, + end: self.try_offset_to_position(range.end)? + }) + } + pub fn position_to_offset_with_rope(rope: &Rope, line: u32, char: u32) -> usize { let line_char = rope.try_line_to_char(line as usize).expect("unable to get char from line"); rope.try_char_to_byte(line_char + char as usize).expect("unable to get byte from char") diff --git a/server/src/core/odoo.rs b/server/src/core/odoo.rs index 40a06ffc..54016288 100644 --- a/server/src/core/odoo.rs +++ b/server/src/core/odoo.rs @@ -5,6 +5,7 @@ use crate::core::xml_data::OdooData; 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::threads::SessionInfo; use crate::features::completion::CompletionFeature; use crate::features::definition::DefinitionFeature; @@ -14,9 +15,9 @@ use std::cell::RefCell; use std::ffi::OsStr; use std::rc::{Rc, Weak}; use std::sync::atomic::{AtomicBool, Ordering}; -use std::sync::Arc; +use std::sync::{Arc, Mutex}; use std::time::Instant; -use lsp_server::ResponseError; +use lsp_server::{RequestId, ResponseError}; use lsp_types::*; use request::{RegisterCapability, Request, WorkspaceConfiguration}; use serde_json::Value; @@ -78,6 +79,8 @@ pub struct SyncOdoo { pub models: HashMap>>, pub interrupt_rebuild: Arc, pub terminate_rebuild: Arc, + pub current_request_id: Option, + pub running_request_ids: Arc>>, //Arc to Server mutex for cancellation support pub watched_file_updates: u32, rebuild_arch: PtrWeakHashSet>>, rebuild_arch_eval: PtrWeakHashSet>>, @@ -117,6 +120,8 @@ impl SyncOdoo { models: HashMap::new(), interrupt_rebuild: Arc::new(AtomicBool::new(false)), terminate_rebuild: Arc::new(AtomicBool::new(false)), + 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(), @@ -842,6 +847,13 @@ impl SyncOdoo { false } + pub fn is_request_cancelled(&self) -> bool { + if let Some(request_id) = self.current_request_id.as_ref() { + return !self.running_request_ids.lock().unwrap().contains(request_id); + } + false + } + pub fn get_file_mgr(&self) -> Rc> { self.file_mgr.clone() } @@ -1629,6 +1641,21 @@ impl Odoo { } Ok(None) } + + pub fn handle_workspace_symbols(session: &mut SessionInfo<'_>, params: WorkspaceSymbolParams) -> Result, ResponseError> { + session.log_message(MessageType::INFO, format!("Workspace Symbol requested with query {}", + params.query, + )); + WorkspaceSymbolFeature::get_workspace_symbols(session, params.query) + } + + pub fn handle_workspace_symbols_resolve(session: &mut SessionInfo<'_>, symbol: WorkspaceSymbol) -> Result { + session.log_message(MessageType::INFO, format!("Workspace Symbol Resolve for symbol {}", + symbol.name, + )); + WorkspaceSymbolFeature::resolve_workspace_symbol(session, &symbol) + } + /// Checks if the given path is a configuration file under one of the workspace folders. fn is_config_workspace_file(session: &mut SessionInfo, path: &PathBuf) -> bool { for (_, ws_dir) in session.sync_odoo.get_file_mgr().borrow().get_workspace_folders().iter() { diff --git a/server/src/core/symbols/symbol.rs b/server/src/core/symbols/symbol.rs index 27953c11..9e15bf46 100644 --- a/server/src/core/symbols/symbol.rs +++ b/server/src/core/symbols/symbol.rs @@ -22,7 +22,7 @@ use std::path::PathBuf; use std::rc::{Rc, Weak}; use std::cell::RefCell; use std::vec; -use lsp_types::{Diagnostic, DiagnosticTag, Position, Range}; +use lsp_types::{Diagnostic, DiagnosticTag, Position, Range, SymbolKind}; use crate::core::symbols::function_symbol::FunctionSymbol; use crate::core::symbols::module_symbol::ModuleSymbol; @@ -2354,7 +2354,12 @@ impl Symbol { for symbol in root.module_symbols.values().cloned() { iter.push(symbol.clone()); } - } + }, + Symbol::DiskDir(d) => { + for symbol in d.module_symbols.values().cloned() { + iter.push(symbol.clone()); + } + }, _ => {} } iter.into_iter() @@ -3050,6 +3055,19 @@ impl Symbol { return -1; } + pub fn get_lsp_symbol_kind(&self) -> SymbolKind { + match self.typ() { + SymType::CLASS => SymbolKind::CLASS, + SymType::FUNCTION => SymbolKind::FUNCTION, + SymType::VARIABLE => SymbolKind::VARIABLE, + SymType::FILE | SymType::CSV_FILE | SymType::XML_FILE => SymbolKind::FILE, + SymType::PACKAGE(_) => SymbolKind::PACKAGE, + SymType::NAMESPACE => SymbolKind::NAMESPACE, + SymType::DISK_DIR | SymType::COMPILED => SymbolKind::FILE, + SymType::ROOT => SymbolKind::NAMESPACE + } + } + /*fn _debug_print_graph_node(&self, acc: &mut String, level: u32) { for _ in 0..level { acc.push_str(" "); diff --git a/server/src/features/mod.rs b/server/src/features/mod.rs index 15be2845..e64f22f5 100644 --- a/server/src/features/mod.rs +++ b/server/src/features/mod.rs @@ -6,4 +6,5 @@ pub mod features_utils; pub mod hover; pub mod node_index_ast; pub mod references; +pub mod workspace_symbols; pub mod xml_ast_utils; \ No newline at end of file diff --git a/server/src/features/workspace_symbols.rs b/server/src/features/workspace_symbols.rs new file mode 100644 index 00000000..b8086b63 --- /dev/null +++ b/server/src/features/workspace_symbols.rs @@ -0,0 +1,186 @@ +use std::{cell::{Ref, RefCell}, rc::Rc}; + +use lsp_server::{ErrorCode, ResponseError}; +use lsp_types::{Location, WorkspaceLocation, WorkspaceSymbol, WorkspaceSymbolResponse}; +use ruff_text_size::{TextRange, TextSize}; + +use crate::{S, constants::{PackageType, SymType}, core::{entry_point::EntryPointType, file_mgr::FileMgr, symbols::symbol::Symbol}, threads::SessionInfo, utils::string_fuzzy_contains}; + +pub struct WorkspaceSymbolFeature; + +impl WorkspaceSymbolFeature { + + pub fn get_workspace_symbols(session: &mut SessionInfo<'_>, query: String) -> Result, ResponseError> { + let mut symbols = vec![]; + let ep_mgr = session.sync_odoo.entry_point_mgr.clone(); + let mut can_resolve_location_range = false; + if let Some(cap_workspace) = session.sync_odoo.capabilities.workspace.as_ref() { + if let Some(workspace_symb) = cap_workspace.symbol.as_ref() { + if let Some(resolve_support) = workspace_symb.resolve_support.as_ref() { + for resolvable_property in &resolve_support.properties { + if resolvable_property == "location.range" { + can_resolve_location_range = true; + break; + } + } + } + } + } + for entry in ep_mgr.borrow().iter_all() { + if entry.borrow().typ == EntryPointType::BUILTIN || entry.borrow().typ == EntryPointType::PUBLIC { //We don't want to search in builtins + continue; + } + if WorkspaceSymbolFeature::browse_symbol(session, &entry.borrow().root, &query, None, None, can_resolve_location_range, &mut symbols) { + return Err(ResponseError { + code: ErrorCode::RequestCanceled as i32, + message: S!("Workspace Symbol request cancelled"), + data: None, + }); + } + } + Ok(Some(WorkspaceSymbolResponse::Nested(symbols))) + } + + fn browse_symbol(session: &mut SessionInfo, symbol: &Rc>, query: &String, parent: Option, parent_path: Option<&String>, can_resolve_location_range: bool, results: &mut Vec) -> bool { + let symbol_borrowed = symbol.borrow(); + if symbol_borrowed.typ() == SymType::VARIABLE { + return false; + } + if symbol_borrowed.typ() == SymType::FILE { //to avoid too many locks + if session.sync_odoo.is_request_cancelled() { + return true; + } + } + let container_name = match &parent { + Some(p) => Some(p.clone()), + None => None, + }; + let path = symbol_borrowed.paths(); + let path = if path.len() == 1 { + Some(&path[0]) + } else if path.len() == 0{ + parent_path + } else { + None + }; + if path.is_some() && symbol_borrowed.has_range() { + //Test if symbol should be returned + if string_fuzzy_contains(&symbol_borrowed.name(), &query) { + WorkspaceSymbolFeature::add_symbol_to_results(session, &symbol_borrowed, &symbol_borrowed.name().to_string(), path.unwrap(), container_name.clone(), Some(symbol_borrowed.range()), can_resolve_location_range, results); + } + //Test if symbol is a model + if symbol_borrowed.typ() == SymType::CLASS && symbol_borrowed.as_class_sym()._model.is_some() { + let model_data = symbol_borrowed.as_class_sym()._model.as_ref().unwrap(); + let model_name = S!("\"") + &model_data.name.to_string() + "\""; + if string_fuzzy_contains(&model_name, &query) { + WorkspaceSymbolFeature::add_symbol_to_results(session, &symbol_borrowed, &model_name, path.unwrap(), container_name.clone(), Some(symbol_borrowed.range()), can_resolve_location_range, results); + } + } + } + if symbol_borrowed.typ() == SymType::PACKAGE(PackageType::MODULE) { + let module = symbol_borrowed.as_module_package(); + for xml_id_name in module.xml_id_locations.keys() { + let xml_name = S!("xmlid.") + xml_id_name; + if string_fuzzy_contains(&xml_name, &query) { + let xml_data = module.get_xml_id(xml_id_name); + for data in xml_data { + let xml_file_symbol = data.get_xml_file_symbol(); + if let Some(xml_file_symbol) = xml_file_symbol { + if let Some(path) = xml_file_symbol.borrow().paths().get(0) { + let range = data.get_range(); + let text_range = TextRange::new(TextSize::new(range.start as u32), TextSize::new(range.end as u32)); + WorkspaceSymbolFeature::add_symbol_to_results(session, &xml_file_symbol.borrow(), &xml_name, path, Some(symbol_borrowed.name().to_string()), Some(&text_range), can_resolve_location_range, results); + } + } + } + } + } + } + for sym in symbol_borrowed.all_symbols() { + if WorkspaceSymbolFeature::browse_symbol(session, &sym, query, Some(symbol_borrowed.name().to_string()), path, can_resolve_location_range, results) { + return true; + } + } + false + } + + fn add_symbol_to_results(session: &mut SessionInfo, symbol: &Ref, name: &String, path: &String, container_name: Option, range: Option<&TextRange>, can_resolve_location_range: bool, results: &mut Vec) { + let location = if can_resolve_location_range { + lsp_types::OneOf::Right(WorkspaceLocation { + uri: FileMgr::pathname2uri(path) + }) + } else { + let file_info = session.sync_odoo.get_file_mgr().borrow().get_file_info(path); + if let Some(file_info) = file_info { + lsp_types::OneOf::Left(Location::new( + FileMgr::pathname2uri(path), + file_info.borrow().text_range_to_range(symbol.range()) + )) + } else { + return; + } + }; + let data = if can_resolve_location_range && range.is_some() { + Some(lsp_types::LSPAny::Array(vec![ + lsp_types::LSPAny::Number(serde_json::Number::from(range.as_ref().unwrap().start().to_u32())), + lsp_types::LSPAny::Number(serde_json::Number::from(range.as_ref().unwrap().end().to_u32())), + ])) + } else { + None + }; + results.push(WorkspaceSymbol { + name: name.clone(), + kind: symbol.get_lsp_symbol_kind(), + tags: None, + container_name, + location: location, + data: data, + }); + } + + pub fn resolve_workspace_symbol(session: &mut SessionInfo<'_>, symbol: &WorkspaceSymbol) -> Result { + let mut resolved_symbol = symbol.clone(); + let location = match &symbol.location { + lsp_types::OneOf::Left(_) => None, + lsp_types::OneOf::Right(wl) => Some(wl.clone()), + }; + if let Some(location) = location { + let uri = FileMgr::uri2pathname(location.uri.as_str()); + let file_info = session.sync_odoo.get_file_mgr().borrow().get_file_info(&uri); + if let Some(file_info) = file_info { + if let Some(data) = symbol.data.as_ref() { + if data.is_array() { + let arr = data.as_array().unwrap(); + if arr.len() == 2 { + let start_u32 = arr[0].as_u64().unwrap() as u32; + let end_u32 = arr[1].as_u64().unwrap() as u32; + let range = file_info.borrow().try_text_range_to_range(&TextRange::new(TextSize::new(start_u32), TextSize::new(end_u32))); + if let Some(range) = range { + resolved_symbol.location = lsp_types::OneOf::Left(Location::new( + location.uri.clone(), + range, + )); + } else { + return Err(ResponseError { + code: ErrorCode::ContentModified as i32, message: S!("Unable to resolve Workspace Symbol - File content modified"), data: None + }) + } + return Ok(resolved_symbol) + } else { + return Err(ResponseError { code: ErrorCode::InternalError as i32, message: S!("Unable to resolve Workspace Symbol - Invalid data to resolve range"), data: None }) + } + } else { + return Err(ResponseError { code: ErrorCode::InternalError as i32, message: S!("Unable to resolve Workspace Symbol - Invalid data to resolve range"), data: None }) + } + } else { + return Err(ResponseError { code: ErrorCode::InternalError as i32, message: S!("Unable to resolve Workspace Symbol - No data to resolve range"), data: None }) + } + } else { + return Err(ResponseError { code: ErrorCode::InternalError as i32, message: S!("Unable to resolve Workspace Symbol - No file info"), data: None }) + } + } else { + return Err(ResponseError { code: ErrorCode::InternalError as i32, message: S!("Unable to resolve Workspace Symbol - no provided location to resolve"), data: None }) + } + } + +} \ No newline at end of file diff --git a/server/src/server.rs b/server/src/server.rs index 6f6e4541..3dbe75de 100644 --- a/server/src/server.rs +++ b/server/src/server.rs @@ -1,9 +1,8 @@ -use std::{io::Error, panic, sync::{atomic::AtomicBool, Arc, Mutex}, thread::JoinHandle}; +use std::{io::Error, panic, sync::{self, Arc, Mutex, atomic::AtomicBool}, thread::JoinHandle}; use crossbeam_channel::{Receiver, Select, Sender}; use lsp_server::{Connection, IoThreads, Message, ProtocolError, RequestId, ResponseError}; -use lsp_types::{notification::{DidChangeConfiguration, DidChangeTextDocument, DidChangeWatchedFiles, DidChangeWorkspaceFolders, DidCloseTextDocument, - DidCreateFiles, DidDeleteFiles, DidOpenTextDocument, DidRenameFiles, DidSaveTextDocument, Notification}, request::{Completion, DocumentSymbolRequest, GotoDefinition, HoverRequest, References, Request, ResolveCompletionItem, Shutdown}, CompletionOptions, DefinitionOptions, DocumentSymbolOptions, FileOperationFilter, FileOperationPattern, FileOperationRegistrationOptions, HoverProviderCapability, InitializeParams, InitializeResult, OneOf, ReferencesOptions, SaveOptions, ServerCapabilities, ServerInfo, TextDocumentSyncCapability, TextDocumentSyncKind, TextDocumentSyncOptions, WorkDoneProgressOptions, WorkspaceFileOperationsServerCapabilities, WorkspaceFoldersServerCapabilities, WorkspaceServerCapabilities}; +use lsp_types::{CancelParams, CompletionOptions, DefinitionOptions, DocumentSymbolOptions, FileOperationFilter, FileOperationPattern, FileOperationRegistrationOptions, HoverProviderCapability, InitializeParams, InitializeResult, OneOf, ReferencesOptions, SaveOptions, ServerCapabilities, ServerInfo, TextDocumentSyncCapability, TextDocumentSyncKind, TextDocumentSyncOptions, WorkDoneProgressOptions, WorkspaceFileOperationsServerCapabilities, WorkspaceFoldersServerCapabilities, WorkspaceServerCapabilities, WorkspaceSymbolOptions, notification::{Cancel, DidChangeConfiguration, DidChangeTextDocument, DidChangeWatchedFiles, DidChangeWorkspaceFolders, DidCloseTextDocument, DidCreateFiles, DidDeleteFiles, DidOpenTextDocument, DidRenameFiles, DidSaveTextDocument, Notification}, request::{Completion, DocumentSymbolRequest, GotoDefinition, HoverRequest, References, Request, ResolveCompletionItem, Shutdown, WorkspaceSymbolRequest, WorkspaceSymbolResolve}}; use serde_json::json; #[cfg(target_os = "linux")] use nix; @@ -28,8 +27,9 @@ pub struct Server { delayed_process_thread: JoinHandle<()>, sender_to_delayed_process: Sender, //unique channel to delayed process thread sync_odoo: Arc>, - interrupt_rebuild_boolean: Arc, - terminate_rebuild_boolean: Arc, + interrupt_rebuild_boolean: Arc, //ref to the one on sync_odoo + terminate_rebuild_boolean: Arc, //ref to the one on sync_odoo + running_request_ids: Arc>>, //ref to the one on sync_odoo, but with dedicated mutex } #[derive(Debug)] @@ -73,6 +73,7 @@ impl Server { let sync_odoo = Arc::new(Mutex::new(SyncOdoo::new())); let interrupt_rebuild_boolean = sync_odoo.lock().unwrap().interrupt_rebuild.clone(); let terminate_rebuild_boolean = sync_odoo.lock().unwrap().terminate_rebuild.clone(); + let running_request_ids = sync_odoo.lock().unwrap().running_request_ids.clone(); let mut receivers_w_to_s = vec![]; let (sender_to_delayed_process, receiver_delayed_process) = crossbeam_channel::unbounded(); let (req_sender_s_to_main, generic_receiver_s_to_main) = crossbeam_channel::unbounded(); //unique channel to dispatch to any ready main thread @@ -83,8 +84,9 @@ impl Server { let main_thread = { let sync_odoo = sync_odoo.clone(); let sender_to_delayed_process = sender_to_delayed_process.clone(); + let running_request_ids = running_request_ids.clone(); std::thread::spawn(move || { - message_processor_thread_main(sync_odoo, generic_receiver_s_to_main, sender_main_to_s.clone(), receiver_s_to_main.clone(), sender_to_delayed_process); + message_processor_thread_main(sync_odoo, generic_receiver_s_to_main, sender_main_to_s.clone(), receiver_s_to_main.clone(), sender_to_delayed_process, running_request_ids); }) }; @@ -108,7 +110,8 @@ impl Server { delayed_process_thread, sync_odoo: sync_odoo, interrupt_rebuild_boolean: interrupt_rebuild_boolean, - terminate_rebuild_boolean + terminate_rebuild_boolean, + running_request_ids: running_request_ids, } } @@ -175,6 +178,12 @@ impl Server { work_done_progress: Some(false) }, })), + workspace_symbol_provider: Some(OneOf::Right(WorkspaceSymbolOptions { + resolve_provider: Some(true), + work_done_progress_options: WorkDoneProgressOptions { + work_done_progress: Some(false) + }, + })), workspace: Some(WorkspaceServerCapabilities { workspace_folders: Some(WorkspaceFoldersServerCapabilities { supported: Some(true), @@ -310,6 +319,14 @@ impl Server { } self.shutdown_threads("Got a client exit notification. Exiting."); break; + } else if n.method == Cancel::METHOD { + let cancel_params = serde_json::from_value::(n.params.clone()).unwrap(); + let req_id: RequestId = match cancel_params.id { + lsp_types::NumberOrString::Number(id) => id.into(), + lsp_types::NumberOrString::String(id) => id.into(), + }; + self.running_request_ids.lock().unwrap().retain(|id| id != &req_id); + continue; } } self.forward_message(msg); @@ -352,15 +369,10 @@ impl Server { fn forward_message(&mut self, msg: Message) { match msg { Message::Request(r) => { + self.running_request_ids.lock().unwrap().push(r.id.clone()); match r.method.as_str() { - HoverRequest::METHOD | GotoDefinition::METHOD | References::METHOD | DocumentSymbolRequest::METHOD=> { - self.interrupt_rebuild_boolean.store(true, std::sync::atomic::Ordering::SeqCst); - if DEBUG_THREADS { - info!("Sending request to main thread : {} - {}", r.method, r.id); - } - self.req_sender_s_to_main.send(Message::Request(r)).unwrap(); - }, - Completion::METHOD => { + HoverRequest::METHOD | GotoDefinition::METHOD | References::METHOD | DocumentSymbolRequest::METHOD | + WorkspaceSymbolRequest::METHOD | WorkspaceSymbolResolve::METHOD | Completion::METHOD => { self.interrupt_rebuild_boolean.store(true, std::sync::atomic::Ordering::SeqCst); if DEBUG_THREADS { info!("Sending request to main thread : {} - {}", r.method, r.id); diff --git a/server/src/threads.rs b/server/src/threads.rs index c7bb760c..dabb66d3 100644 --- a/server/src/threads.rs +++ b/server/src/threads.rs @@ -2,9 +2,9 @@ use std::{collections::VecDeque, path::PathBuf, sync::{Arc, Mutex}, time::Instan use crossbeam_channel::{Receiver, Sender, TryRecvError}; use lsp_server::{Message, RequestId, Response, ResponseError}; -use lsp_types::{notification::{DidChangeConfiguration, DidChangeTextDocument, DidChangeWatchedFiles, DidChangeWorkspaceFolders, +use lsp_types::{CompletionResponse, DocumentSymbolResponse, Hover, Location, LogMessageParams, MessageType, ShowMessageParams, WorkspaceSymbol, WorkspaceSymbolResponse, notification::{DidChangeConfiguration, DidChangeTextDocument, DidChangeWatchedFiles, DidChangeWorkspaceFolders, DidCloseTextDocument, DidCreateFiles, DidDeleteFiles, DidOpenTextDocument, DidRenameFiles, DidSaveTextDocument, LogMessage, - Notification, ShowMessage}, request::{Completion, DocumentSymbolRequest, GotoDefinition, GotoTypeDefinitionResponse, HoverRequest, References, Request, Shutdown}, CompletionResponse, DocumentSymbolResponse, Hover, Location, LogMessageParams, MessageType, ShowMessageParams}; + Notification, ShowMessage}, request::{Completion, DocumentSymbolRequest, GotoDefinition, GotoTypeDefinitionResponse, HoverRequest, References, Request, Shutdown, WorkspaceSymbolRequest, WorkspaceSymbolResolve}}; use serde::{de::DeserializeOwned, Serialize}; use serde_json::Value; use tracing::{error, info, warn}; @@ -153,6 +153,18 @@ fn to_value(result: Result, ResponseEr (value, error) } +fn to_value_not_null(result: Result) -> (Option, Option) { + let value = match &result { + Ok(r) => Some(serde_json::json!(r)), + Err(_) => None + }; + let mut error = None; + if result.is_err() { + error = Some(result.unwrap_err()); + } + (value, error) +} + #[derive(Debug)] pub struct UpdateFileIndexData { pub path: PathBuf, @@ -252,7 +264,7 @@ pub fn delayed_changes_process_thread(sender_session: Sender, receiver_ } } -pub fn message_processor_thread_main(sync_odoo: Arc>, generic_receiver: Receiver, sender: Sender, receiver: Receiver, delayed_process_sender: Sender) { +pub fn message_processor_thread_main(sync_odoo: Arc>, generic_receiver: Receiver, sender: Sender, receiver: Receiver, delayed_process_sender: Sender, running_request_ids: Arc>>) { let mut buffer = VecDeque::new(); loop { // Drain all available messages into buffer @@ -295,6 +307,7 @@ pub fn message_processor_thread_main(sync_odoo: Arc>, generic_re if let Some(msg) = buffer.pop_front() { match msg { Message::Request(r) => { + sync_odoo.lock().unwrap().current_request_id = Some(r.id.clone()); let (value, error) = match r.method.as_str() { HoverRequest::METHOD => { let mut session = create_session!(sender, receiver, sync_odoo, delayed_process_sender); @@ -315,6 +328,14 @@ pub fn message_processor_thread_main(sync_odoo: Arc>, generic_re let mut session = create_session!(sender, receiver, sync_odoo, delayed_process_sender); to_value::(Odoo::handle_document_symbols(&mut session, serde_json::from_value(r.params).unwrap())) }, + WorkspaceSymbolRequest::METHOD => { + let mut session = create_session!(sender, receiver, sync_odoo, delayed_process_sender); + to_value::(Odoo::handle_workspace_symbols(&mut session, serde_json::from_value(r.params).unwrap())) + }, + WorkspaceSymbolResolve::METHOD => { + let mut session = create_session!(sender, receiver, sync_odoo, delayed_process_sender); + to_value_not_null::(Odoo::handle_workspace_symbols_resolve(&mut session, serde_json::from_value(r.params).unwrap())) + }, Completion::METHOD => { let mut session = create_session!(sender, receiver, sync_odoo, delayed_process_sender); SyncOdoo::process_rebuilds(&mut session, true); @@ -326,6 +347,8 @@ pub fn message_processor_thread_main(sync_odoo: Arc>, generic_re data: None }))} }; + sync_odoo.lock().unwrap().current_request_id = None; + running_request_ids.lock().unwrap().retain(|id| id != &r.id); sender.send(Message::Response(Response { id: r.id, result: value, error: error })).unwrap(); }, Message::Notification(n) => { diff --git a/server/src/utils.rs b/server/src/utils.rs index 85902788..717706ed 100644 --- a/server/src/utils.rs +++ b/server/src/utils.rs @@ -334,6 +334,25 @@ pub fn is_python_path(path: &String) -> bool { } } +pub fn string_fuzzy_contains(string: &str, pattern: &str) -> bool { + let mut pattern_char_iter = pattern.chars(); + let mut pattern_char = match pattern_char_iter.next() { + Some(c) => c.to_ascii_lowercase(), + None => return true, + }; + for char in string.chars() { + if char.to_ascii_lowercase() == pattern_char { + pattern_char = match pattern_char_iter.next() { + Some(c) => c.to_ascii_lowercase(), + None => { + return true; + } + }; + } + } + false +} + #[macro_export] macro_rules! warn_or_panic { ($($arg:tt)*) => {