@@ -901,4 +901,89 @@ await _client.WaitForNotificationAsync(
901901 s ! [ "name" ] ? . GetValue < string > ( ) ? . Contains ( "animal" , StringComparison . OrdinalIgnoreCase ) == true ) ;
902902 hasAnimal . Should ( ) . BeTrue ( "at least one symbol should have a name containing 'animal'" ) ;
903903 }
904+
905+ [ Fact ]
906+ public async Task Hover_OverMethodName_RangeNarrowedToName ( )
907+ {
908+ await _client . InitializeAsync ( ) ;
909+
910+ var uri = "file:///test_hover_narrow_method.spy" ;
911+ // Mirrors async_with_basic.spy structure
912+ var source = "class AsyncResource:\n def __init__(self):\n pass\n \n async def __aenter__(self) -> AsyncResource:\n print(\" entering\" )\n return self\n \n async def __aexit__(self):\n print(\" exiting\" )\n \n async def main():\n async with AsyncResource() as r:\n print(\" inside\" )" ;
913+ await _client . DidOpenAsync ( uri , source ) ;
914+
915+ // Wait for diagnostics to ensure analysis is complete
916+ await _client . WaitForNotificationAsync (
917+ "textDocument/publishDiagnostics" ,
918+ TimeSpan . FromSeconds ( 15 ) ) ;
919+
920+ // Hover over '__aenter__' on line 4 (0-based), character 14 (0-based)
921+ // Source line 5 (1-based): " async def __aenter__(self) -> AsyncResource:"
922+ // 0-based: 0123456789012345
923+ // ^ col 14 = start of "__aenter__"
924+ var hover = await _client . HoverAsync ( uri , 4 , 14 ) ;
925+
926+ hover . Should ( ) . NotBeNull ( "hover over a method name should return information" ) ;
927+
928+ var contents = hover ! [ "contents" ] ;
929+ contents . Should ( ) . NotBeNull ( ) ;
930+ var hoverText = contents ! . ToJsonString ( ) ;
931+ hoverText . Should ( ) . Contain ( "__aenter__" ) ;
932+
933+ // Verify the range is narrowed to just the method name, not the whole function body
934+ var range = hover [ "range" ] ;
935+ range . Should ( ) . NotBeNull ( "hover should include a range" ) ;
936+
937+ var startLine = range ! [ "start" ] ! [ "line" ] ! . GetValue < int > ( ) ;
938+ var startChar = range [ "start" ] ! [ "character" ] ! . GetValue < int > ( ) ;
939+ var endLine = range [ "end" ] ! [ "line" ] ! . GetValue < int > ( ) ;
940+ var endChar = range [ "end" ] ! [ "character" ] ! . GetValue < int > ( ) ;
941+
942+ startLine . Should ( ) . Be ( 4 , "highlight should start on the method name line" ) ;
943+ endLine . Should ( ) . Be ( 4 , "highlight should end on the same line (not span multi-line)" ) ;
944+ startChar . Should ( ) . Be ( 14 , "highlight should start at '__aenter__'" ) ;
945+ endChar . Should ( ) . Be ( 24 , "highlight should end after '__aenter__' (10 chars)" ) ;
946+ }
947+
948+ [ Fact ]
949+ public async Task Hover_OverReturnKeyword_RangeNarrowedToKeyword ( )
950+ {
951+ await _client . InitializeAsync ( ) ;
952+
953+ var uri = "file:///test_hover_narrow_return.spy" ;
954+ var source = "def greet() -> str:\n return \" hello\" \n def main():\n greet()" ;
955+ await _client . DidOpenAsync ( uri , source ) ;
956+
957+ // Wait for diagnostics to ensure analysis is complete
958+ await _client . WaitForNotificationAsync (
959+ "textDocument/publishDiagnostics" ,
960+ TimeSpan . FromSeconds ( 15 ) ) ;
961+
962+ // Hover over 'return' on line 1 (0-based), character 4 (0-based)
963+ // Source line 2 (1-based): " return \"hello\""
964+ // 0-based: 01234
965+ // ^ col 4 = start of "return"
966+ var hover = await _client . HoverAsync ( uri , 1 , 4 ) ;
967+
968+ hover . Should ( ) . NotBeNull ( "hover over return keyword should return information" ) ;
969+
970+ var contents = hover ! [ "contents" ] ;
971+ contents . Should ( ) . NotBeNull ( ) ;
972+ var hoverText = contents ! . ToJsonString ( ) ;
973+ hoverText . Should ( ) . Contain ( "return" ) ;
974+
975+ // Verify the range is narrowed to just the 'return' keyword
976+ var range = hover [ "range" ] ;
977+ range . Should ( ) . NotBeNull ( "hover should include a range" ) ;
978+
979+ var startLine = range ! [ "start" ] ! [ "line" ] ! . GetValue < int > ( ) ;
980+ var startChar = range [ "start" ] ! [ "character" ] ! . GetValue < int > ( ) ;
981+ var endLine = range [ "end" ] ! [ "line" ] ! . GetValue < int > ( ) ;
982+ var endChar = range [ "end" ] ! [ "character" ] ! . GetValue < int > ( ) ;
983+
984+ startLine . Should ( ) . Be ( 1 , "highlight should start on the return line" ) ;
985+ endLine . Should ( ) . Be ( 1 , "highlight should end on the same line" ) ;
986+ startChar . Should ( ) . Be ( 4 , "highlight should start at 'return'" ) ;
987+ endChar . Should ( ) . Be ( 10 , "highlight should end after 'return' (6 chars)" ) ;
988+ }
904989}
0 commit comments