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:: { PackageType , 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+ let mut symbols = vec ! [ ] ;
15+ let ep_mgr = session. sync_odoo . entry_point_mgr . clone ( ) ;
16+ let mut can_resolve_location_range = false ;
17+ if let Some ( cap_workspace) = session. sync_odoo . capabilities . workspace . as_ref ( ) {
18+ if let Some ( workspace_symb) = cap_workspace. symbol . as_ref ( ) {
19+ if let Some ( resolve_support) = workspace_symb. resolve_support . as_ref ( ) {
20+ for resolvable_property in & resolve_support. properties {
21+ if resolvable_property == "location.range" {
22+ can_resolve_location_range = true ;
23+ break ;
24+ }
25+ }
26+ }
27+ }
28+ }
29+ for entry in ep_mgr. borrow ( ) . iter_all ( ) {
30+ if entry. borrow ( ) . typ == EntryPointType :: BUILTIN || entry. borrow ( ) . typ == EntryPointType :: PUBLIC { //We don't want to search in builtins
31+ continue ;
32+ }
33+ if WorkspaceSymbolFeature :: browse_symbol ( session, & entry. borrow ( ) . root , & query, None , None , can_resolve_location_range, & mut symbols) {
34+ return Err ( ResponseError {
35+ code : ErrorCode :: RequestCanceled as i32 ,
36+ message : S ! ( "Workspace Symbol request cancelled" ) ,
37+ data : None ,
38+ } ) ;
39+ }
40+ }
41+ Ok ( Some ( WorkspaceSymbolResponse :: Nested ( symbols) ) )
42+ }
43+
44+ /**
45+ * Return true if the request has been cancelled and the cancellation should be propagated
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 ( ) , Some ( symbol_borrowed. range ( ) ) , 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. clone ( ) , Some ( symbol_borrowed. range ( ) ) , can_resolve_location_range, results) ;
80+ }
81+ }
82+ }
83+ if symbol_borrowed. typ ( ) == SymType :: PACKAGE ( PackageType :: MODULE ) {
84+ let module = symbol_borrowed. as_module_package ( ) ;
85+ for xml_id_name in module. xml_id_locations . keys ( ) {
86+ let xml_name = S ! ( "xmlid." ) + xml_id_name;
87+ if string_fuzzy_contains ( & xml_name, & query) {
88+ let xml_data = module. get_xml_id ( xml_id_name) ;
89+ for data in xml_data {
90+ let xml_file_symbol = data. get_xml_file_symbol ( ) ;
91+ if let Some ( xml_file_symbol) = xml_file_symbol {
92+ if let Some ( path) = xml_file_symbol. borrow ( ) . paths ( ) . get ( 0 ) {
93+ let range = data. get_range ( ) ;
94+ let text_range = TextRange :: new ( TextSize :: new ( range. start as u32 ) , TextSize :: new ( range. end as u32 ) ) ;
95+ 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) ;
96+ }
97+ }
98+ }
99+ }
100+ }
101+ }
102+ for sym in symbol_borrowed. all_symbols ( ) {
103+ if WorkspaceSymbolFeature :: browse_symbol ( session, & sym, query, Some ( symbol_borrowed. name ( ) . to_string ( ) ) , path, can_resolve_location_range, results) {
104+ return true ;
105+ }
106+ }
107+ false
108+ }
109+
110+ fn add_symbol_to_results ( session : & mut SessionInfo , symbol : & Ref < Symbol > , name : & String , path : & String , container_name : Option < String > , range : Option < & TextRange > , can_resolve_location_range : bool , results : & mut Vec < WorkspaceSymbol > ) {
111+ let location = if can_resolve_location_range {
112+ lsp_types:: OneOf :: Right ( WorkspaceLocation {
113+ uri : FileMgr :: pathname2uri ( path)
114+ } )
115+ } else {
116+ let file_info = session. sync_odoo . get_file_mgr ( ) . borrow ( ) . get_file_info ( path) ;
117+ if let Some ( file_info) = file_info {
118+ lsp_types:: OneOf :: Left ( Location :: new (
119+ FileMgr :: pathname2uri ( path) ,
120+ file_info. borrow ( ) . text_range_to_range ( symbol. range ( ) )
121+ ) )
122+ } else {
123+ return ;
124+ }
125+ } ;
126+ let data = if can_resolve_location_range && range. is_some ( ) {
127+ Some ( lsp_types:: LSPAny :: Array ( vec ! [
128+ lsp_types:: LSPAny :: Number ( serde_json:: Number :: from( range. as_ref( ) . unwrap( ) . start( ) . to_u32( ) ) ) ,
129+ lsp_types:: LSPAny :: Number ( serde_json:: Number :: from( range. as_ref( ) . unwrap( ) . end( ) . to_u32( ) ) ) ,
130+ ] ) )
131+ } else {
132+ None
133+ } ;
134+ results. push ( WorkspaceSymbol {
135+ name : name. clone ( ) ,
136+ kind : symbol. get_lsp_symbol_kind ( ) ,
137+ tags : None ,
138+ container_name,
139+ location : location,
140+ data : data,
141+ } ) ;
142+ }
143+
144+ pub fn resolve_workspace_symbol ( session : & mut SessionInfo < ' _ > , symbol : & WorkspaceSymbol ) -> Result < WorkspaceSymbol , ResponseError > {
145+ let mut resolved_symbol = symbol. clone ( ) ;
146+ let location = match & symbol. location {
147+ lsp_types:: OneOf :: Left ( _) => None ,
148+ lsp_types:: OneOf :: Right ( wl) => Some ( wl. clone ( ) ) ,
149+ } ;
150+ if let Some ( location) = location {
151+ let uri = FileMgr :: uri2pathname ( location. uri . as_str ( ) ) ;
152+ let file_info = session. sync_odoo . get_file_mgr ( ) . borrow ( ) . get_file_info ( & uri) ;
153+ if let Some ( file_info) = file_info {
154+ if let Some ( data) = symbol. data . as_ref ( ) {
155+ if data. is_array ( ) {
156+ let arr = data. as_array ( ) . unwrap ( ) ;
157+ if arr. len ( ) == 2 {
158+ let start_u32 = arr[ 0 ] . as_u64 ( ) . unwrap ( ) as u32 ;
159+ let end_u32 = arr[ 1 ] . as_u64 ( ) . unwrap ( ) as u32 ;
160+ let range = file_info. borrow ( ) . try_text_range_to_range ( & TextRange :: new ( TextSize :: new ( start_u32) , TextSize :: new ( end_u32) ) ) ;
161+ if let Some ( range) = range {
162+ resolved_symbol. location = lsp_types:: OneOf :: Left ( Location :: new (
163+ location. uri . clone ( ) ,
164+ range,
165+ ) ) ;
166+ } else {
167+ return Err ( ResponseError {
168+ code : ErrorCode :: ContentModified as i32 , message : S ! ( "Unable to resolve Workspace Symbol - File content modified" ) , data : None
169+ } )
170+ }
171+ return Ok ( resolved_symbol)
172+ } else {
173+ return Err ( ResponseError { code : ErrorCode :: InternalError as i32 , message : S ! ( "Unable to resolve Workspace Symbol - Invalid data to resolve range" ) , data : None } )
174+ }
175+ } else {
176+ return Err ( ResponseError { code : ErrorCode :: InternalError as i32 , message : S ! ( "Unable to resolve Workspace Symbol - Invalid data to resolve range" ) , data : None } )
177+ }
178+ } else {
179+ return Err ( ResponseError { code : ErrorCode :: InternalError as i32 , message : S ! ( "Unable to resolve Workspace Symbol - No data to resolve range" ) , data : None } )
180+ }
181+ } else {
182+ return Err ( ResponseError { code : ErrorCode :: InternalError as i32 , message : S ! ( "Unable to resolve Workspace Symbol - No file info" ) , data : None } )
183+ }
184+ } else {
185+ return Err ( ResponseError { code : ErrorCode :: InternalError as i32 , message : S ! ( "Unable to resolve Workspace Symbol - no provided location to resolve" ) , data : None } )
186+ }
187+ }
188+
189+ }
0 commit comments