pytest_language_server/providers/
references.rs1use super::Backend;
4use tower_lsp_server::jsonrpc::Result;
5use tower_lsp_server::ls_types::*;
6use tracing::{debug, info};
7
8impl Backend {
9 pub async fn handle_references(
11 &self,
12 params: ReferenceParams,
13 ) -> Result<Option<Vec<Location>>> {
14 let uri = params.text_document_position.text_document.uri;
15 let position = params.text_document_position.position;
16
17 info!(
18 "references request: uri={:?}, line={}, char={}",
19 uri, position.line, position.character
20 );
21
22 if let Some(file_path) = self.uri_to_path(&uri) {
23 info!(
24 "Looking for fixture references at {:?}:{}:{}",
25 file_path, position.line, position.character
26 );
27
28 if let Some(fixture_name) = self.fixture_db.find_fixture_at_position(
30 &file_path,
31 position.line,
32 position.character,
33 ) {
34 info!(
35 "Found fixture: {}, determining which definition to use",
36 fixture_name
37 );
38
39 let current_line = Self::lsp_line_to_internal(position.line);
40 info!(
41 "Current cursor position: line {} (1-indexed), char {}",
42 current_line, position.character
43 );
44
45 let target_definition = self.fixture_db.find_fixture_definition(
48 &file_path,
49 position.line,
50 position.character,
51 );
52
53 let (references, definition_to_include) = if let Some(definition) =
54 target_definition
55 {
56 info!(
57 "Found definition via usage at {:?}:{}, finding references that resolve to it",
58 definition.file_path, definition.line
59 );
60 let refs = self.fixture_db.find_references_for_definition(&definition);
62 (refs, Some(definition))
63 } else {
64 let target_line = Self::lsp_line_to_internal(position.line);
67 if let Some(definition_at_line) = self.fixture_db.get_definition_at_line(
68 &file_path,
69 target_line,
70 &fixture_name,
71 ) {
72 info!(
73 "Found definition at cursor position {:?}:{}, finding references that resolve to it",
74 file_path, target_line
75 );
76 let refs = self
77 .fixture_db
78 .find_references_for_definition(&definition_at_line);
79 (refs, Some(definition_at_line))
80 } else {
81 info!(
82 "No specific definition found at cursor, finding all references by name"
83 );
84 (self.fixture_db.find_fixture_references(&fixture_name), None)
86 }
87 };
88
89 if references.is_empty() && definition_to_include.is_none() {
90 info!("No references found for fixture: {}", fixture_name);
91 return Ok(None);
92 }
93
94 info!(
95 "Found {} references for fixture: {}",
96 references.len(),
97 fixture_name
98 );
99
100 for (i, r) in references.iter().enumerate() {
102 debug!(
103 " Reference {}: {:?}:{} (chars {}-{})",
104 i,
105 r.file_path.file_name(),
106 r.line,
107 r.start_char,
108 r.end_char
109 );
110 }
111
112 let has_current_position = references
114 .iter()
115 .any(|r| r.file_path == file_path && r.line == current_line);
116 info!(
117 "Current position (line {}) in references: {}",
118 current_line, has_current_position
119 );
120
121 let mut locations = Vec::new();
123
124 if let Some(ref def) = definition_to_include {
126 let Some(def_uri) = self.path_to_uri(&def.file_path) else {
127 return Ok(None);
128 };
129
130 let def_line = Self::internal_line_to_lsp(def.line);
131 let def_location = Location {
132 uri: def_uri,
133 range: Self::create_point_range(def_line, 0),
134 };
135 locations.push(def_location);
136 }
137
138 let mut skipped_count = 0;
141 for reference in &references {
142 if let Some(ref def) = definition_to_include {
144 if reference.file_path == def.file_path && reference.line == def.line {
145 debug!(
146 "Skipping reference at {:?}:{} (same as definition location)",
147 reference.file_path, reference.line
148 );
149 skipped_count += 1;
150 continue;
151 }
152 }
153
154 let Some(ref_uri) = self.path_to_uri(&reference.file_path) else {
155 continue;
156 };
157
158 let ref_line = Self::internal_line_to_lsp(reference.line);
159 let location = Location {
160 uri: ref_uri,
161 range: Self::create_range(
162 ref_line,
163 reference.start_char as u32,
164 ref_line,
165 reference.end_char as u32,
166 ),
167 };
168 debug!(
169 "Adding reference location: {:?}:{} (chars {}-{})",
170 reference.file_path.file_name(),
171 reference.line,
172 reference.start_char,
173 reference.end_char
174 );
175 locations.push(location);
176 }
177
178 info!(
179 "Returning {} locations (definition: {}, references: {}/{}, skipped: {})",
180 locations.len(),
181 if definition_to_include.is_some() {
182 1
183 } else {
184 0
185 },
186 references.len() - skipped_count,
187 references.len(),
188 skipped_count
189 );
190 return Ok(Some(locations));
191 } else {
192 info!("No fixture found at this position");
193 }
194 }
195
196 Ok(None)
197 }
198}