@@ -12,7 +12,7 @@ use miette::{diagnostic, miette};
1212use  odoo_lsp:: index:: { index_models,  interner,  PathSymbol } ; 
1313use  ropey:: Rope ; 
1414use  tower_lsp:: lsp_types:: * ; 
15- use  tracing:: { debug,  trace,  warn} ; 
15+ use  tracing:: { debug,  instrument ,   trace,  warn} ; 
1616use  tree_sitter:: { Node ,  Parser ,  QueryCursor ,  QueryMatch ,  Tree } ; 
1717
1818use  odoo_lsp:: model:: { ModelName ,  ModelType ,  ResolveMappedError } ; 
@@ -551,6 +551,7 @@ impl Backend {
551551	} 
552552	/// Resolves the attribute at the cursor offset. 
553553/// Returns `(object, field, range)` 
554+ #[ instrument( level = "trace" ,  skip_all,  ret) ]  
554555	pub  fn  attribute_node_at_offset < ' out > ( 
555556		& ' out  self , 
556557		mut  offset :  usize , 
@@ -562,8 +563,10 @@ impl Backend {
562563		} 
563564		offset = offset. clamp ( 0 ,  contents. len ( )  - 1 ) ; 
564565		let  mut  cursor_node = root. descendant_for_byte_range ( offset,  offset) ?; 
566+ 		let  mut  real_offset = None ; 
565567		if  cursor_node. is_named ( )  && !matches ! ( cursor_node. kind( ) ,  "attribute"  | "identifier" )  { 
566568			// We got our cursor left in the middle of nowhere. 
569+ 			real_offset = Some ( offset) ; 
567570			offset = offset. saturating_sub ( 1 ) ; 
568571			cursor_node = root. descendant_for_byte_range ( offset,  offset) ?; 
569572		} 
@@ -578,15 +581,12 @@ impl Backend {
578581		let  rhs; 
579582		if  !cursor_node. is_named ( )  { 
580583			// We landed on one of the punctuations inside the attribute. 
581- 			// Need to determine which one is it . 
584+ 			// Need to determine which one it is . 
582585			let  dot = cursor_node. descendant_for_byte_range ( offset,  offset) ?; 
583586			lhs = dot. prev_named_sibling ( ) ?; 
584587			rhs = dot. next_named_sibling ( ) . and_then ( |attr| match  attr. kind ( )  { 
585588				"identifier"  => Some ( attr) , 
586- 				// TODO: Unwrap all layers of attributes 
587- 				"attribute"  => attr
588- 					. child_by_field_name ( "object" ) 
589- 					. and_then ( |obj| ( obj. kind ( )  == "identifier" ) . then_some ( obj) ) , 
589+ 				"attribute"  => attr. child_by_field_name ( "attribute" ) , 
590590				_ => None , 
591591			} ) ; 
592592		}  else  if  cursor_node. kind ( )  == "attribute"  { 
@@ -619,13 +619,15 @@ impl Backend {
619619		let  Some ( rhs)  = rhs else  { 
620620			// In single-expression mode, rhs could be empty in which case 
621621			// we return an empty needle/range. 
622- 			return  Some ( ( lhs,  Cow :: from ( "" ) ,  offset + 1 ..offset + 1 ) ) ; 
622+ 			let  offset = real_offset. unwrap_or ( offset) ; 
623+ 			return  Some ( ( lhs,  Cow :: from ( "" ) ,  offset..offset) ) ; 
623624		} ; 
624625		let  ( field,  range)  = if  rhs. range ( ) . start_point . row  != lhs. range ( ) . end_point . row  { 
625626			// tree-sitter has an issue with attributes spanning multiple lines 
626627			// which is NOT valid Python, but allows it anyways because tree-sitter's 
627628			// use cases don't require strict syntax trees. 
628- 			( Cow :: from ( "" ) ,  offset + 1 ..offset + 1 ) 
629+ 			let  offset = real_offset. unwrap_or ( offset) ; 
630+ 			( Cow :: from ( "" ) ,  offset..offset) 
629631		}  else  { 
630632			let  range = rhs. byte_range ( ) ; 
631633			( String :: from_utf8_lossy ( & contents[ range. clone ( ) ] ) ,  range) 
0 commit comments