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