Skip to content

Commit 6da452b

Browse files
committed
Fix inlay position calculation
1 parent dc4f1b1 commit 6da452b

1 file changed

Lines changed: 41 additions & 11 deletions

File tree

src/inlay_hints.rs

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -719,22 +719,15 @@ fn offset_in_range(offset: u32, range: (u32, u32)) -> bool {
719719
}
720720

721721
fn 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\nclass 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

Comments
 (0)