Skip to content

Commit 2fcdedc

Browse files
committed
[IMP] Workspace Symbol request and $/Cancel support
1 parent 8c16351 commit 2fcdedc

File tree

6 files changed

+243
-22
lines changed

6 files changed

+243
-22
lines changed

server/src/core/odoo.rs

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use crate::core::xml_data::OdooData;
55
use crate::core::xml_validation::XmlValidator;
66
use crate::features::document_symbols::DocumentSymbolFeature;
77
use crate::features::references::ReferenceFeature;
8+
use crate::features::workspace_symbols::WorkspaceSymbolFeature;
89
use crate::threads::SessionInfo;
910
use crate::features::completion::CompletionFeature;
1011
use crate::features::definition::DefinitionFeature;
@@ -14,9 +15,9 @@ use std::cell::RefCell;
1415
use std::ffi::OsStr;
1516
use std::rc::{Rc, Weak};
1617
use std::sync::atomic::{AtomicBool, Ordering};
17-
use std::sync::Arc;
18+
use std::sync::{Arc, Mutex};
1819
use std::time::Instant;
19-
use lsp_server::ResponseError;
20+
use lsp_server::{RequestId, ResponseError};
2021
use lsp_types::*;
2122
use request::{RegisterCapability, Request, WorkspaceConfiguration};
2223
use serde_json::Value;
@@ -78,6 +79,8 @@ pub struct SyncOdoo {
7879
pub models: HashMap<OYarn, Rc<RefCell<Model>>>,
7980
pub interrupt_rebuild: Arc<AtomicBool>,
8081
pub terminate_rebuild: Arc<AtomicBool>,
82+
pub current_request_id: Option<RequestId>,
83+
pub running_request_ids: Arc<Mutex<Vec<RequestId>>>, //Arc to Server mutex for cancellation support
8184
pub watched_file_updates: u32,
8285
rebuild_arch: PtrWeakHashSet<Weak<RefCell<Symbol>>>,
8386
rebuild_arch_eval: PtrWeakHashSet<Weak<RefCell<Symbol>>>,
@@ -117,6 +120,8 @@ impl SyncOdoo {
117120
models: HashMap::new(),
118121
interrupt_rebuild: Arc::new(AtomicBool::new(false)),
119122
terminate_rebuild: Arc::new(AtomicBool::new(false)),
123+
current_request_id: None,
124+
running_request_ids: Arc::new(Mutex::new(vec![])),
120125
watched_file_updates: 0,
121126
rebuild_arch: PtrWeakHashSet::new(),
122127
rebuild_arch_eval: PtrWeakHashSet::new(),
@@ -842,6 +847,13 @@ impl SyncOdoo {
842847
false
843848
}
844849

850+
pub fn is_request_cancelled(&self) -> bool {
851+
if let Some(request_id) = self.current_request_id.as_ref() {
852+
return !self.running_request_ids.lock().unwrap().contains(request_id);
853+
}
854+
false
855+
}
856+
845857
pub fn get_file_mgr(&self) -> Rc<RefCell<FileMgr>> {
846858
self.file_mgr.clone()
847859
}
@@ -1629,6 +1641,21 @@ impl Odoo {
16291641
}
16301642
Ok(None)
16311643
}
1644+
1645+
pub fn handle_workspace_symbols(session: &mut SessionInfo<'_>, params: WorkspaceSymbolParams) -> Result<Option<WorkspaceSymbolResponse>, ResponseError> {
1646+
session.log_message(MessageType::INFO, format!("Workspace Symbol requested with query {}",
1647+
params.query,
1648+
));
1649+
WorkspaceSymbolFeature::get_workspace_symbols(session, params.query)
1650+
}
1651+
1652+
pub fn handle_workspace_symbols_resolve(session: &mut SessionInfo<'_>, symbol: WorkspaceSymbol) -> Result<WorkspaceSymbol, ResponseError> {
1653+
session.log_message(MessageType::INFO, format!("Workspace Symbol Resolve for symbol {}",
1654+
symbol.name,
1655+
));
1656+
WorkspaceSymbolFeature::resolve_workspace_symbol(session, &symbol)
1657+
}
1658+
16321659
/// Checks if the given path is a configuration file under one of the workspace folders.
16331660
fn is_config_workspace_file(session: &mut SessionInfo, path: &PathBuf) -> bool {
16341661
for (_, ws_dir) in session.sync_odoo.get_file_mgr().borrow().get_workspace_folders().iter() {

server/src/core/symbols/symbol.rs

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use std::path::PathBuf;
2222
use std::rc::{Rc, Weak};
2323
use std::cell::RefCell;
2424
use std::vec;
25-
use lsp_types::{Diagnostic, DiagnosticTag, Position, Range};
25+
use lsp_types::{Diagnostic, DiagnosticTag, Position, Range, SymbolKind};
2626

2727
use crate::core::symbols::function_symbol::FunctionSymbol;
2828
use crate::core::symbols::module_symbol::ModuleSymbol;
@@ -2354,7 +2354,12 @@ impl Symbol {
23542354
for symbol in root.module_symbols.values().cloned() {
23552355
iter.push(symbol.clone());
23562356
}
2357-
}
2357+
},
2358+
Symbol::DiskDir(d) => {
2359+
for symbol in d.module_symbols.values().cloned() {
2360+
iter.push(symbol.clone());
2361+
}
2362+
},
23582363
_ => {}
23592364
}
23602365
iter.into_iter()
@@ -3050,6 +3055,19 @@ impl Symbol {
30503055
return -1;
30513056
}
30523057

3058+
pub fn get_lsp_symbol_kind(&self) -> SymbolKind {
3059+
match self.typ() {
3060+
SymType::CLASS => SymbolKind::CLASS,
3061+
SymType::FUNCTION => SymbolKind::FUNCTION,
3062+
SymType::VARIABLE => SymbolKind::VARIABLE,
3063+
SymType::FILE | SymType::CSV_FILE | SymType::XML_FILE => SymbolKind::FILE,
3064+
SymType::PACKAGE(_) => SymbolKind::PACKAGE,
3065+
SymType::NAMESPACE => SymbolKind::NAMESPACE,
3066+
SymType::DISK_DIR | SymType::COMPILED => SymbolKind::FILE,
3067+
SymType::ROOT => SymbolKind::NAMESPACE
3068+
}
3069+
}
3070+
30533071
/*fn _debug_print_graph_node(&self, acc: &mut String, level: u32) {
30543072
for _ in 0..level {
30553073
acc.push_str(" ");

server/src/features/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,5 @@ pub mod features_utils;
66
pub mod hover;
77
pub mod node_index_ast;
88
pub mod references;
9+
pub mod workspace_symbols;
910
pub mod xml_ast_utils;
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
use std::{cell::RefCell, rc::Rc};
2+
3+
use lsp_server::ResponseError;
4+
use lsp_types::{Location, WorkspaceLocation, WorkspaceSymbol, WorkspaceSymbolResponse};
5+
use ruff_text_size::{TextRange, TextSize};
6+
7+
use crate::{S, constants::SymType, core::{entry_point::EntryPointType, file_mgr::FileMgr, symbols::symbol::Symbol}, threads::SessionInfo};
8+
9+
pub struct WorkspaceSymbolFeature;
10+
11+
impl WorkspaceSymbolFeature {
12+
13+
pub fn get_workspace_symbols(session: &mut SessionInfo<'_>, query: String) -> Result<Option<WorkspaceSymbolResponse>, ResponseError> {
14+
if query.is_empty() { //It would lead to too much results, let's cancel it
15+
return Ok(None);
16+
}
17+
let mut symbols = vec![];
18+
let ep_mgr = session.sync_odoo.entry_point_mgr.clone();
19+
let mut can_resolve_location_range = false;
20+
if let Some(cap_workspace) = session.sync_odoo.capabilities.workspace.as_ref() {
21+
if let Some(workspace_symb) = cap_workspace.symbol.as_ref() {
22+
if let Some(resolve_support) = workspace_symb.resolve_support.as_ref() {
23+
for resolvable_property in &resolve_support.properties {
24+
if resolvable_property == "location.range" {
25+
can_resolve_location_range = true;
26+
break;
27+
}
28+
}
29+
}
30+
}
31+
}
32+
for entry in ep_mgr.borrow().iter_all() {
33+
if entry.borrow().typ == EntryPointType::BUILTIN || entry.borrow().typ == EntryPointType::PUBLIC { //We don't want to search in builtins
34+
continue;
35+
}
36+
WorkspaceSymbolFeature::browse_symbol(session, &entry.borrow().root, &query, None, None, can_resolve_location_range, &mut symbols);
37+
}
38+
Ok(Some(WorkspaceSymbolResponse::Nested(symbols)))
39+
}
40+
41+
fn browse_symbol(session: &mut SessionInfo, symbol: &Rc<RefCell<Symbol>>, query: &String, parent: Option<String>, parent_path: Option<&String>, can_resolve_location_range: bool, results: &mut Vec<WorkspaceSymbol>) {
42+
let symbol_borrowed = symbol.borrow();
43+
if symbol_borrowed.typ() == SymType::VARIABLE {
44+
return;
45+
}
46+
if symbol_borrowed.typ() == SymType::FILE { //to avoid too many locks
47+
if session.sync_odoo.is_request_cancelled() {
48+
return;
49+
}
50+
}
51+
let container_name = match &parent {
52+
Some(p) => Some(p.clone()),
53+
None => None,
54+
};
55+
let path = symbol_borrowed.paths();
56+
let path = if path.len() == 1 {
57+
Some(&path[0])
58+
} else if path.len() == 0{
59+
parent_path
60+
} else {
61+
None
62+
};
63+
if path.is_some() {
64+
let file_info = session.sync_odoo.get_file_mgr().borrow().get_file_info(path.unwrap());
65+
if let Some(file_info) = file_info {
66+
if symbol_borrowed.has_range() {
67+
let location = if can_resolve_location_range {
68+
lsp_types::OneOf::Right(WorkspaceLocation {
69+
uri: FileMgr::pathname2uri(path.unwrap())
70+
})
71+
} else {
72+
lsp_types::OneOf::Left(Location::new(
73+
FileMgr::pathname2uri(path.unwrap()),
74+
file_info.borrow().text_range_to_range(symbol_borrowed.range())
75+
))
76+
};
77+
let data = if can_resolve_location_range {
78+
Some(lsp_types::LSPAny::Array(vec![
79+
lsp_types::LSPAny::Number(serde_json::Number::from(symbol_borrowed.range().start().to_u32())),
80+
lsp_types::LSPAny::Number(serde_json::Number::from(symbol_borrowed.range().end().to_u32())),
81+
]))
82+
} else {
83+
None
84+
};
85+
results.push(WorkspaceSymbol {
86+
name: symbol_borrowed.name().to_string(),
87+
kind: symbol_borrowed.get_lsp_symbol_kind(),
88+
tags: None,
89+
container_name,
90+
location: location,
91+
data: data,
92+
});
93+
}
94+
}
95+
}
96+
for sym in symbol_borrowed.all_symbols() {
97+
WorkspaceSymbolFeature::browse_symbol(session, &sym, query, Some(symbol_borrowed.name().to_string()), path, can_resolve_location_range, results);
98+
}
99+
}
100+
101+
pub fn resolve_workspace_symbol(session: &mut SessionInfo<'_>, symbol: &WorkspaceSymbol) -> Result<WorkspaceSymbol, ResponseError> {
102+
let mut resolved_symbol = symbol.clone();
103+
let location = match &symbol.location {
104+
lsp_types::OneOf::Left(_) => None,
105+
lsp_types::OneOf::Right(wl) => Some(wl.clone()),
106+
};
107+
if let Some(location) = location {
108+
let uri = FileMgr::uri2pathname(location.uri.as_str());
109+
let file_info = session.sync_odoo.get_file_mgr().borrow().get_file_info(&uri);
110+
if let Some(file_info) = file_info {
111+
if let Some(data) = symbol.data.as_ref() {
112+
if data.is_array() {
113+
let arr = data.as_array().unwrap();
114+
if arr.len() == 2 {
115+
let start_u32 = arr[0].as_u64().unwrap() as u32;
116+
let end_u32 = arr[1].as_u64().unwrap() as u32;
117+
let range = file_info.borrow().text_range_to_range(&TextRange::new(TextSize::new(start_u32), TextSize::new(end_u32)));
118+
resolved_symbol.location = lsp_types::OneOf::Left(Location::new(
119+
location.uri.clone(),
120+
range,
121+
));
122+
return Ok(resolved_symbol)
123+
} else {
124+
return Err(ResponseError { code: 1, message: S!("Unable to resolve Workspace Symbol - Invalid data to resolve range"), data: None })
125+
}
126+
} else {
127+
return Err(ResponseError { code: 1, message: S!("Unable to resolve Workspace Symbol - Invalid data to resolve range"), data: None })
128+
}
129+
} else {
130+
return Err(ResponseError { code: 1, message: S!("Unable to resolve Workspace Symbol - No data to resolve range"), data: None })
131+
}
132+
} else {
133+
return Err(ResponseError { code: 1, message: S!("Unable to resolve Workspace Symbol - No file info"), data: None })
134+
}
135+
} else {
136+
return Err(ResponseError { code: 1, message: S!("Unable to resolve Workspace Symbol - no provided location to resolve"), data: None })
137+
}
138+
}
139+
140+
}

server/src/server.rs

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
1-
use std::{io::Error, panic, sync::{atomic::AtomicBool, Arc, Mutex}, thread::JoinHandle};
1+
use std::{io::Error, panic, sync::{self, Arc, Mutex, atomic::AtomicBool}, thread::JoinHandle};
22

33
use crossbeam_channel::{Receiver, Select, Sender};
44
use lsp_server::{Connection, IoThreads, Message, ProtocolError, RequestId, ResponseError};
5-
use lsp_types::{notification::{DidChangeConfiguration, DidChangeTextDocument, DidChangeWatchedFiles, DidChangeWorkspaceFolders, DidCloseTextDocument,
6-
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};
5+
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}};
76
use serde_json::json;
87
#[cfg(target_os = "linux")]
98
use nix;
@@ -28,8 +27,9 @@ pub struct Server {
2827
delayed_process_thread: JoinHandle<()>,
2928
sender_to_delayed_process: Sender<DelayedProcessingMessage>, //unique channel to delayed process thread
3029
sync_odoo: Arc<Mutex<SyncOdoo>>,
31-
interrupt_rebuild_boolean: Arc<AtomicBool>,
32-
terminate_rebuild_boolean: Arc<AtomicBool>,
30+
interrupt_rebuild_boolean: Arc<AtomicBool>, //ref to the one on sync_odoo
31+
terminate_rebuild_boolean: Arc<AtomicBool>, //ref to the one on sync_odoo
32+
running_request_ids: Arc<Mutex<Vec<RequestId>>>, //ref to the one on sync_odoo, but with dedicated mutex
3333
}
3434

3535
#[derive(Debug)]
@@ -73,6 +73,7 @@ impl Server {
7373
let sync_odoo = Arc::new(Mutex::new(SyncOdoo::new()));
7474
let interrupt_rebuild_boolean = sync_odoo.lock().unwrap().interrupt_rebuild.clone();
7575
let terminate_rebuild_boolean = sync_odoo.lock().unwrap().terminate_rebuild.clone();
76+
let running_request_ids = sync_odoo.lock().unwrap().running_request_ids.clone();
7677
let mut receivers_w_to_s = vec![];
7778
let (sender_to_delayed_process, receiver_delayed_process) = crossbeam_channel::unbounded();
7879
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 {
8384
let main_thread = {
8485
let sync_odoo = sync_odoo.clone();
8586
let sender_to_delayed_process = sender_to_delayed_process.clone();
87+
let running_request_ids = running_request_ids.clone();
8688
std::thread::spawn(move || {
87-
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);
89+
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);
8890
})
8991
};
9092

@@ -108,7 +110,8 @@ impl Server {
108110
delayed_process_thread,
109111
sync_odoo: sync_odoo,
110112
interrupt_rebuild_boolean: interrupt_rebuild_boolean,
111-
terminate_rebuild_boolean
113+
terminate_rebuild_boolean,
114+
running_request_ids: running_request_ids,
112115
}
113116
}
114117

@@ -175,6 +178,12 @@ impl Server {
175178
work_done_progress: Some(false)
176179
},
177180
})),
181+
workspace_symbol_provider: Some(OneOf::Right(WorkspaceSymbolOptions {
182+
resolve_provider: Some(true),
183+
work_done_progress_options: WorkDoneProgressOptions {
184+
work_done_progress: Some(false)
185+
},
186+
})),
178187
workspace: Some(WorkspaceServerCapabilities {
179188
workspace_folders: Some(WorkspaceFoldersServerCapabilities {
180189
supported: Some(true),
@@ -310,6 +319,14 @@ impl Server {
310319
}
311320
self.shutdown_threads("Got a client exit notification. Exiting.");
312321
break;
322+
} else if n.method == Cancel::METHOD {
323+
let cancel_params = serde_json::from_value::<CancelParams>(n.params.clone()).unwrap();
324+
let req_id: RequestId = match cancel_params.id {
325+
lsp_types::NumberOrString::Number(id) => id.into(),
326+
lsp_types::NumberOrString::String(id) => id.into(),
327+
};
328+
self.running_request_ids.lock().unwrap().retain(|id| id != &req_id);
329+
break;
313330
}
314331
}
315332
self.forward_message(msg);
@@ -352,15 +369,10 @@ impl Server {
352369
fn forward_message(&mut self, msg: Message) {
353370
match msg {
354371
Message::Request(r) => {
372+
self.running_request_ids.lock().unwrap().push(r.id.clone());
355373
match r.method.as_str() {
356-
HoverRequest::METHOD | GotoDefinition::METHOD | References::METHOD | DocumentSymbolRequest::METHOD=> {
357-
self.interrupt_rebuild_boolean.store(true, std::sync::atomic::Ordering::SeqCst);
358-
if DEBUG_THREADS {
359-
info!("Sending request to main thread : {} - {}", r.method, r.id);
360-
}
361-
self.req_sender_s_to_main.send(Message::Request(r)).unwrap();
362-
},
363-
Completion::METHOD => {
374+
HoverRequest::METHOD | GotoDefinition::METHOD | References::METHOD | DocumentSymbolRequest::METHOD |
375+
WorkspaceSymbolRequest::METHOD | WorkspaceSymbolResolve::METHOD | Completion::METHOD => {
364376
self.interrupt_rebuild_boolean.store(true, std::sync::atomic::Ordering::SeqCst);
365377
if DEBUG_THREADS {
366378
info!("Sending request to main thread : {} - {}", r.method, r.id);

0 commit comments

Comments
 (0)