Produce hover information for a variable.
(
&self,
name: &str,
uri: &str,
content: &str,
cursor_offset: u32,
current_class: Option<&ClassInfo>,
ctx: &FileContext,
)
| 908 | |
| 909 | /// Produce hover information for a variable. |
| 910 | fn hover_variable( |
| 911 | &self, |
| 912 | name: &str, |
| 913 | uri: &str, |
| 914 | content: &str, |
| 915 | cursor_offset: u32, |
| 916 | current_class: Option<&ClassInfo>, |
| 917 | ctx: &FileContext, |
| 918 | ) -> Option<Hover> { |
| 919 | let var_name = format!("${}", name); |
| 920 | |
| 921 | // When the cursor is on the `$` of an assignment like |
| 922 | // `$x = new Foo()`, the cursor offset equals the assignment |
| 923 | // statement's start offset. The variable resolution pipeline |
| 924 | // skips statements whose start is at or after the cursor |
| 925 | // (`stmt.start >= cursor_offset`), so the assignment is |
| 926 | // excluded. Nudge the offset by one byte so the statement's |
| 927 | // start is strictly less than the cursor, allowing the |
| 928 | // assignment to be included. We only do this for assignments |
| 929 | // (not parameters, foreach, etc.) where `effective_from` |
| 930 | // differs from the definition offset. |
| 931 | let mut cursor_offset = cursor_offset; |
| 932 | if self |
| 933 | .lookup_var_def_effective_from(uri, name, cursor_offset) |
| 934 | .is_some() |
| 935 | { |
| 936 | let offset = cursor_offset as usize; |
| 937 | if let Some(ch) = content.get(offset..).and_then(|s| s.chars().next()) { |
| 938 | cursor_offset += ch.len_utf8() as u32; |
| 939 | } |
| 940 | } |
| 941 | |
| 942 | // $this resolves to the enclosing class, but not inside static methods. |
| 943 | if name == "this" { |
| 944 | let in_static = self |
| 945 | .symbol_maps |
| 946 | .read() |
| 947 | .get(uri) |
| 948 | .is_some_and(|map| map.is_in_static_method(cursor_offset)); |
| 949 | if !in_static && let Some(cc) = current_class { |
| 950 | let ns_line = namespace_line(cc.file_namespace.as_deref()); |
| 951 | return Some(make_hover(format!( |
| 952 | "```php\n<?php\n{}$this = {}\n```", |
| 953 | ns_line, cc.name |
| 954 | ))); |
| 955 | } |
| 956 | return Some(make_hover("```php\n<?php\n$this\n```".to_string())); |
| 957 | } |
| 958 | |
| 959 | let class_loader = self.class_loader(ctx); |
| 960 | let function_loader = self.function_loader(ctx); |
| 961 | let constant_loader = self.constant_loader(); |
| 962 | let loaders = crate::completion::resolver::Loaders { |
| 963 | function_loader: Some(&function_loader as &dyn Fn(&str) -> Option<FunctionInfo>), |
| 964 | constant_loader: Some(&constant_loader), |
| 965 | }; |
| 966 | |
| 967 | // Try the type-string path first. This preserves generic |
no test coverage detected