@@ -10,6 +10,7 @@ use std::sync::Arc;
1010use lasso:: Spur ;
1111use log:: { debug, warn} ;
1212use miette:: diagnostic;
13+ use odoo_lsp:: component:: ComponentTemplate ;
1314use odoo_lsp:: index:: { interner, PathSymbol } ;
1415use odoo_lsp:: model:: { Field , FieldKind } ;
1516use odoo_lsp:: template:: gather_templates;
@@ -37,6 +38,8 @@ enum RefKind<'a> {
3738 /// An arbitrary Python expression.
3839 /// Includes the relative offset of where the cursor is.
3940 PyExpr ( usize ) ,
41+ /// `<Component />`
42+ Component ,
4043}
4144
4245enum Tag < ' a > {
@@ -328,7 +331,7 @@ impl Backend {
328331 & mut items,
329332 ) ?;
330333 }
331- RefKind :: TName => return Ok ( None ) ,
334+ RefKind :: TName | RefKind :: Component => return Ok ( None ) ,
332335 }
333336
334337 Ok ( Some ( CompletionResponse :: List ( CompletionList {
@@ -384,6 +387,14 @@ impl Backend {
384387 let model = some ! ( self . resolve_type( & model, & Scope :: default ( ) ) ) ;
385388 self . jump_def_field_name ( & field, interner ( ) . resolve ( & model) )
386389 }
390+ Some ( RefKind :: Component ) => {
391+ let component = some ! ( interner( ) . get( needle) ) ;
392+ let component = some ! ( self . index. components. get( & component. into( ) ) ) ;
393+ let Some ( ComponentTemplate :: Name ( template) ) = component. template . as_ref ( ) else {
394+ return Ok ( None ) ;
395+ } ;
396+ self . jump_def_template_name ( interner ( ) . resolve ( template) )
397+ }
387398 None => Ok ( None ) ,
388399 }
389400 }
@@ -413,7 +424,11 @@ impl Backend {
413424 Some ( RefKind :: Ref ( _) ) | Some ( RefKind :: Id ) => self . record_references ( cursor_value, current_module) ,
414425 Some ( RefKind :: TInherit ) | Some ( RefKind :: TCall ) => self . template_references ( cursor_value, true ) ,
415426 Some ( RefKind :: TName ) => self . template_references ( cursor_value, false ) ,
416- Some ( RefKind :: PyExpr ( _) ) | Some ( RefKind :: FieldName ( _) ) | Some ( RefKind :: PropOf ( ..) ) | None => Ok ( None ) ,
427+ Some ( RefKind :: PyExpr ( _) )
428+ | Some ( RefKind :: FieldName ( _) )
429+ | Some ( RefKind :: PropOf ( ..) )
430+ | Some ( RefKind :: Component )
431+ | None => Ok ( None ) ,
417432 }
418433 }
419434 pub fn xml_hover ( & self , params : HoverParams , rope : Rope ) -> miette:: Result < Option < Hover > > {
@@ -513,7 +528,7 @@ impl Backend {
513528 offset_range_to_lsp_range ( range. map_unit ( |rel_unit| ByteOffset ( rel_unit + anchor) ) , rope. clone ( ) ) ,
514529 )
515530 }
516- Some ( RefKind :: TName ) | Some ( RefKind :: PropOf ( ..) ) | None => {
531+ Some ( RefKind :: TName ) | Some ( RefKind :: PropOf ( ..) ) | Some ( RefKind :: Component ) | None => {
517532 #[ cfg( not( debug_assertions) ) ]
518533 return Ok ( None ) ;
519534
@@ -531,6 +546,28 @@ impl Backend {
531546 }
532547 }
533548 }
549+ pub fn xml_code_actions ( & self , params : CodeActionParams , rope : Rope ) -> miette:: Result < Option < CodeActionResponse > > {
550+ let uri = & params. text_document . uri ;
551+ let position = params. range . start ;
552+ let ( slice, offset_at_cursor, _) = self . record_slice ( & rope, uri, position) ?;
553+ let slice_str = Cow :: from ( slice) ;
554+ let mut reader = Tokenizer :: from ( slice_str. as_ref ( ) ) ;
555+
556+ let XmlRefs {
557+ ref_at_cursor,
558+ ref_kind,
559+ ..
560+ } = self . gather_refs ( offset_at_cursor, & mut reader, & slice) ?;
561+ let ( Some ( ( value, _) ) , Some ( RefKind :: Component ) ) = ( ref_at_cursor, ref_kind) else {
562+ return Ok ( None ) ;
563+ } ;
564+
565+ Ok ( Some ( vec ! [ CodeActionOrCommand :: Command ( Command {
566+ title: "Go to Owl component" . to_string( ) ,
567+ command: "goto_owl" . to_string( ) ,
568+ arguments: Some ( vec![ String :: new( ) . into( ) , value. into( ) ] ) ,
569+ } ) ] ) )
570+ }
534571 /// The main function that determines all the information needed
535572 /// to resolve the symbol at the cursor.
536573 fn gather_refs < ' read > (
@@ -569,7 +606,7 @@ impl Backend {
569606
570607 for token in reader {
571608 match token {
572- Ok ( Token :: ElementStart { local, .. } ) => {
609+ Ok ( Token :: ElementStart { local, prefix , .. } ) => {
573610 expect_model_string = false ;
574611 depth += 1 ;
575612 match local. as_str ( ) {
@@ -585,6 +622,10 @@ impl Backend {
585622 }
586623 component if template_mode && component. starts_with ( |c| char:: is_ascii_uppercase ( & c) ) => {
587624 tag = Some ( Tag :: TComponent ( component) ) ;
625+ if prefix. is_empty ( ) && local. range ( ) . contains_end ( offset_at_cursor) {
626+ ref_at_cursor = Some ( ( local. as_str ( ) , local. range ( ) ) ) ;
627+ ref_kind = Some ( RefKind :: Component ) ;
628+ }
588629 }
589630 _ if arch_mode => tag = None ,
590631 _ => { }
0 commit comments