@@ -719,22 +719,15 @@ fn offset_in_range(offset: u32, range: (u32, u32)) -> bool {
719719}
720720
721721fn line_end_position ( content : & str , byte_offset : usize ) -> Position {
722- let line_start = content[ ..byte_offset]
723- . rfind ( '\n' )
724- . map ( |i| i + 1 )
725- . unwrap_or ( 0 ) ;
726722 let line_end = content[ byte_offset..]
727723 . find ( '\n' )
728724 . map ( |i| byte_offset + i)
729725 . unwrap_or ( content. len ( ) ) ;
730726
731- Position {
732- line : content[ ..line_start]
733- . bytes ( )
734- . filter ( |b| * b == b'\n' )
735- . count ( ) as u32 ,
736- character : content[ line_start..line_end] . chars ( ) . count ( ) as u32 ,
737- }
727+ // Delegate to the canonical converter so the `character` column is
728+ // counted in UTF-16 code units (per the LSP spec), consistent with
729+ // every other position the server emits.
730+ offset_to_position ( content, line_end)
738731}
739732
740733/// Check whether the argument at `arg_offset` is a simple variable whose
@@ -1030,6 +1023,43 @@ class User {
10301023 assert ! ( !hints. iter( ) . any( |hint| hint. position. line == 2 ) ) ;
10311024 }
10321025
1026+ #[ test]
1027+ fn declaration_count_hint_column_uses_utf16_units ( ) {
1028+ let backend = Backend :: new_test ( ) ;
1029+ let uri = "file:///test.php" ;
1030+ // The declaration line ends with a non-BMP character (2 UTF-16
1031+ // code units, 1 Unicode scalar), so a chars-based column would be
1032+ // one short of the LSP-mandated UTF-16 column.
1033+ let content = "<?php\n class User {} // \u{1F600} \n " ;
1034+
1035+ backend. update_ast ( uri, content) ;
1036+ backend. workspace_indexed . store ( true , Ordering :: Release ) ;
1037+
1038+ let hints = backend
1039+ . handle_inlay_hints (
1040+ uri,
1041+ content,
1042+ Range {
1043+ start : Position {
1044+ line : 0 ,
1045+ character : 0 ,
1046+ } ,
1047+ end : Position {
1048+ line : 2 ,
1049+ character : 0 ,
1050+ } ,
1051+ } ,
1052+ )
1053+ . unwrap_or_default ( ) ;
1054+
1055+ let class_hint = hints
1056+ . iter ( )
1057+ . find ( |hint| hint. position . line == 1 )
1058+ . expect ( "expected a reference-count hint on the class declaration line" ) ;
1059+ // "class User {} // " is 17 UTF-16 units; the emoji adds 2 → 19.
1060+ assert_eq ! ( class_hint. position. character, 19 ) ;
1061+ }
1062+
10331063 #[ test]
10341064 fn test_should_suppress_simple_variable_match ( ) {
10351065 let content = "$needle, $haystack" ;
0 commit comments