1+ use std:: { cell:: { Ref , RefCell } , rc:: Rc } ;
2+
3+ use lsp_server:: { ErrorCode , 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 , utils:: string_fuzzy_contains} ;
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+ if WorkspaceSymbolFeature :: browse_symbol ( session, & entry. borrow ( ) . root , & query, None , None , can_resolve_location_range, & mut symbols) {
37+ return Err ( ResponseError {
38+ code : ErrorCode :: RequestCanceled as i32 ,
39+ message : S ! ( "Workspace Symbol request cancelled" ) ,
40+ data : None ,
41+ } ) ;
42+ }
43+ }
44+ Ok ( Some ( WorkspaceSymbolResponse :: Nested ( symbols) ) )
45+ }
46+
47+ 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 > ) -> bool {
48+ let symbol_borrowed = symbol. borrow ( ) ;
49+ if symbol_borrowed. typ ( ) == SymType :: VARIABLE {
50+ return false ;
51+ }
52+ if symbol_borrowed. typ ( ) == SymType :: FILE { //to avoid too many locks
53+ if session. sync_odoo . is_request_cancelled ( ) {
54+ return true ;
55+ }
56+ }
57+ let container_name = match & parent {
58+ Some ( p) => Some ( p. clone ( ) ) ,
59+ None => None ,
60+ } ;
61+ let path = symbol_borrowed. paths ( ) ;
62+ let path = if path. len ( ) == 1 {
63+ Some ( & path[ 0 ] )
64+ } else if path. len ( ) == 0 {
65+ parent_path
66+ } else {
67+ None
68+ } ;
69+ if path. is_some ( ) && symbol_borrowed. has_range ( ) {
70+ //Test if symbol should be returned
71+ if string_fuzzy_contains ( & symbol_borrowed. name ( ) , & query) {
72+ WorkspaceSymbolFeature :: add_symbol_to_results ( session, & symbol_borrowed, & symbol_borrowed. name ( ) . to_string ( ) , path. unwrap ( ) , container_name. clone ( ) , can_resolve_location_range, results) ;
73+ }
74+ //Test if symbol is a model
75+ if symbol_borrowed. typ ( ) == SymType :: CLASS && symbol_borrowed. as_class_sym ( ) . _model . is_some ( ) {
76+ let model_data = symbol_borrowed. as_class_sym ( ) . _model . as_ref ( ) . unwrap ( ) ;
77+ let model_name = S ! ( "\" " ) + & model_data. name . to_string ( ) + "\" " ;
78+ if string_fuzzy_contains ( & model_name, & query) {
79+ WorkspaceSymbolFeature :: add_symbol_to_results ( session, & symbol_borrowed, & model_name, path. unwrap ( ) , container_name, can_resolve_location_range, results) ;
80+ }
81+ }
82+ }
83+ for sym in symbol_borrowed. all_symbols ( ) {
84+ if WorkspaceSymbolFeature :: browse_symbol ( session, & sym, query, Some ( symbol_borrowed. name ( ) . to_string ( ) ) , path, can_resolve_location_range, results) {
85+ return true ;
86+ }
87+ }
88+ false
89+ }
90+
91+ fn add_symbol_to_results ( session : & mut SessionInfo , symbol : & Ref < Symbol > , name : & String , path : & String , container_name : Option < String > , can_resolve_location_range : bool , results : & mut Vec < WorkspaceSymbol > ) {
92+ let location = if can_resolve_location_range {
93+ lsp_types:: OneOf :: Right ( WorkspaceLocation {
94+ uri : FileMgr :: pathname2uri ( path)
95+ } )
96+ } else {
97+ let file_info = session. sync_odoo . get_file_mgr ( ) . borrow ( ) . get_file_info ( path) ;
98+ if let Some ( file_info) = file_info {
99+ lsp_types:: OneOf :: Left ( Location :: new (
100+ FileMgr :: pathname2uri ( path) ,
101+ file_info. borrow ( ) . text_range_to_range ( symbol. range ( ) )
102+ ) )
103+ } else {
104+ return ;
105+ }
106+ } ;
107+ let data = if can_resolve_location_range {
108+ Some ( lsp_types:: LSPAny :: Array ( vec ! [
109+ lsp_types:: LSPAny :: Number ( serde_json:: Number :: from( symbol. range( ) . start( ) . to_u32( ) ) ) ,
110+ lsp_types:: LSPAny :: Number ( serde_json:: Number :: from( symbol. range( ) . end( ) . to_u32( ) ) ) ,
111+ ] ) )
112+ } else {
113+ None
114+ } ;
115+ results. push ( WorkspaceSymbol {
116+ name : name. clone ( ) ,
117+ kind : symbol. get_lsp_symbol_kind ( ) ,
118+ tags : None ,
119+ container_name,
120+ location : location,
121+ data : data,
122+ } ) ;
123+ }
124+
125+ pub fn resolve_workspace_symbol ( session : & mut SessionInfo < ' _ > , symbol : & WorkspaceSymbol ) -> Result < WorkspaceSymbol , ResponseError > {
126+ let mut resolved_symbol = symbol. clone ( ) ;
127+ let location = match & symbol. location {
128+ lsp_types:: OneOf :: Left ( _) => None ,
129+ lsp_types:: OneOf :: Right ( wl) => Some ( wl. clone ( ) ) ,
130+ } ;
131+ if let Some ( location) = location {
132+ let uri = FileMgr :: uri2pathname ( location. uri . as_str ( ) ) ;
133+ let file_info = session. sync_odoo . get_file_mgr ( ) . borrow ( ) . get_file_info ( & uri) ;
134+ if let Some ( file_info) = file_info {
135+ if let Some ( data) = symbol. data . as_ref ( ) {
136+ if data. is_array ( ) {
137+ let arr = data. as_array ( ) . unwrap ( ) ;
138+ if arr. len ( ) == 2 {
139+ let start_u32 = arr[ 0 ] . as_u64 ( ) . unwrap ( ) as u32 ;
140+ let end_u32 = arr[ 1 ] . as_u64 ( ) . unwrap ( ) as u32 ;
141+ let range = file_info. borrow ( ) . try_text_range_to_range ( & TextRange :: new ( TextSize :: new ( start_u32) , TextSize :: new ( end_u32) ) ) ;
142+ if let Some ( range) = range {
143+ resolved_symbol. location = lsp_types:: OneOf :: Left ( Location :: new (
144+ location. uri . clone ( ) ,
145+ range,
146+ ) ) ;
147+ } else {
148+ return Err ( ResponseError {
149+ code : ErrorCode :: ContentModified as i32 , message : S ! ( "Unable to resolve Workspace Symbol - File content modified" ) , data : None
150+ } )
151+ }
152+ return Ok ( resolved_symbol)
153+ } else {
154+ return Err ( ResponseError { code : ErrorCode :: InternalError as i32 , message : S ! ( "Unable to resolve Workspace Symbol - Invalid data to resolve range" ) , data : None } )
155+ }
156+ } else {
157+ return Err ( ResponseError { code : ErrorCode :: InternalError as i32 , message : S ! ( "Unable to resolve Workspace Symbol - Invalid data to resolve range" ) , data : None } )
158+ }
159+ } else {
160+ return Err ( ResponseError { code : ErrorCode :: InternalError as i32 , message : S ! ( "Unable to resolve Workspace Symbol - No data to resolve range" ) , data : None } )
161+ }
162+ } else {
163+ return Err ( ResponseError { code : ErrorCode :: InternalError as i32 , message : S ! ( "Unable to resolve Workspace Symbol - No file info" ) , data : None } )
164+ }
165+ } else {
166+ return Err ( ResponseError { code : ErrorCode :: InternalError as i32 , message : S ! ( "Unable to resolve Workspace Symbol - no provided location to resolve" ) , data : None } )
167+ }
168+ }
169+
170+ }
0 commit comments